From 44ee83462d7aa70db52f763e4f79ef554ad086b1 Mon Sep 17 00:00:00 2001 From: jCabala Date: Thu, 18 Jun 2026 22:42:58 +0100 Subject: [PATCH 01/12] feat: Scaffolded necessary lemmass and 2 main poillars of the logup completeness proof --- ArkLib/ProofSystem/Logup/Protocol.lean | 12 +- .../Logup/Security/Completeness.lean | 505 +++++++++++++++++- .../ProofSystem/Logup/Sumcheck/Security.lean | 38 ++ .../Logup/Sumcheck/SumcheckBridge.lean | 7 + .../Logup/Sumcheck/SumcheckPolynomial.lean | 95 +++- 5 files changed, 642 insertions(+), 15 deletions(-) create mode 100644 ArkLib/ProofSystem/Logup/Sumcheck/Security.lean diff --git a/ArkLib/ProofSystem/Logup/Protocol.lean b/ArkLib/ProofSystem/Logup/Protocol.lean index 8716086d14..e36f325ded 100644 --- a/ArkLib/ProofSystem/Logup/Protocol.lean +++ b/ArkLib/ProofSystem/Logup/Protocol.lean @@ -188,19 +188,19 @@ variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) variable (params : ProtocolParams M) @[grind] -private def outerChallengeXIdx : (outerPSpec F n params).ChallengeIdx := +def outerChallengeXIdx : (outerPSpec F n params).ChallengeIdx := ⟨1, rfl⟩ @[grind] -private def outerChallengeBatchIdx : (outerPSpec F n params).ChallengeIdx := +def outerChallengeBatchIdx : (outerPSpec F n params).ChallengeIdx := ⟨3, rfl⟩ @[grind] -private def outerMultiplicityMessageIdx : (outerPSpec F n params).MessageIdx := +def outerMultiplicityMessageIdx : (outerPSpec F n params).MessageIdx := ⟨0, rfl⟩ @[grind] -private def outerHelpersMessageIdx : (outerPSpec F n params).MessageIdx := +def outerHelpersMessageIdx : (outerPSpec F n params).MessageIdx := ⟨2, rfl⟩ /-- The verifier for the outer LogUp phase. -/ @@ -213,7 +213,9 @@ noncomputable def outerVerifier : -- TODO: Replace the current table-scan rejection check with a faithful sampler -- for x ∉ { -t(u) : u ∈ H }. for u in (Finset.univ : Finset (Hypercube n)).toList do - let tAtU : F ← query (spec := [OStmtIn F n M]ₒ) ⟨InputOracleIdx.table, signPoint F u⟩ + let tAtU : F ← OptionT.lift <| OracleComp.liftComp + (OracleComp.lift <| OracleSpec.query + (show [OStmtIn F n M]ₒ.Domain from ⟨InputOracleIdx.table, signPoint F u⟩)) _ guard (x + tAtU ≠ 0) let batch : BatchingChallenge F n params.numGroups := challenges (outerChallengeBatchIdx F n M params) diff --git a/ArkLib/ProofSystem/Logup/Security/Completeness.lean b/ArkLib/ProofSystem/Logup/Security/Completeness.lean index c6cea978d6..a73748fa04 100644 --- a/ArkLib/ProofSystem/Logup/Security/Completeness.lean +++ b/ArkLib/ProofSystem/Logup/Security/Completeness.lean @@ -1,16 +1,242 @@ import ArkLib.OracleReduction.Security.Basic +import ArkLib.OracleReduction.Composition.Sequential.Append import ArkLib.ProofSystem.Logup.Protocol +import ArkLib.ProofSystem.Logup.Sumcheck.Security /-! # LogUp Completeness -Main completeness statement for Protocol 2 of `paper.txt`. +Main completeness statement for the LogUp protocol. + +`logupOracleReduction` is `outerOracleReduction ++ₚ sumcheckOracleReduction`, so completeness is +proved compositionally via `append_completeness`: the outer phase reaches `logupMidRelation` (with +the pole-rejection error), the embedded sumcheck phase carries it to `outputRelation` with no extra +error. The two halves are the remaining obligations. -/ open scoped NNReal namespace Logup +section OuterAlgebra + +variable {F : Type} [Field F] [Fintype F] [DecidableEq F] {n M K : ℕ} + +/-- For honest helpers (`hₖ = ∑ᵢ mᵢ/φᵢ`) the domain-identity term vanishes pointwise, away from +poles (`φᵢ ≠ 0`). -/ +theorem domainIdentityTerm_eq_zero (groups : PartialSumGroups M K) + (oStmt : ∀ i, OStmtIn F n M i) (mult : MultilinearOracle F n) + (helpers : HelperMessages F n K) (x : F) (k : Fin K) (u : Hypercube n) + (hh : evalOnHypercube (helpers k) u = helperValue groups oStmt mult x k u) + (hφ : ∀ i ∈ groups k, termPhi oStmt x i u ≠ 0) : + domainIdentityTerm groups oStmt mult helpers x k u = 0 := by + rw [domainIdentityTerm, denominatorProduct, hh, helperValue, Finset.sum_mul, sub_eq_zero] + refine Finset.sum_congr rfl (fun i hi => ?_) + rw [← Finset.mul_prod_erase _ _ hi] + field_simp [hφ i hi] + +/-- The honest log-derivative identity (heart of LogUp): with the normalized multiplicity, the table +side equals the column side. Needs every column value to occur in the table (`hcols`) and the table +counts to be nonzero in `F` (`hchar`, from `charLarge`). Holds even at poles, since `x/0 = 0`. -/ +theorem honest_multiplicity_identity (oStmt : ∀ i, OStmtIn F n M i) (x : F) + (hcols : ∀ j : Fin M, ∀ u : Hypercube n, ∃ v : Hypercube n, + evalOnHypercube (columnOracle oStmt j) u = evalOnHypercube (tableOracle oStmt) v) + (hchar : ∀ a : F, tableMultiplicityCount oStmt a ≠ 0 → + (tableMultiplicityCount oStmt a : F) ≠ 0) : + (∑ u : Hypercube n, + evalOnHypercube (honestMultiplicity oStmt) u / (x + evalOnHypercube (tableOracle oStmt) u)) + = ∑ j : Fin M, ∑ u : Hypercube n, + (1 : F) / (x + evalOnHypercube (columnOracle oStmt j) u) := by + classical + -- per-value cancellation + have key : ∀ a : F, + tableMultiplicityCount oStmt a • + ((lookupMultiplicityCount oStmt a : F) / (tableMultiplicityCount oStmt a : F) / (x + a)) + = lookupMultiplicityCount oStmt a • ((1 : F) / (x + a)) := by + intro a + by_cases hT : tableMultiplicityCount oStmt a = 0 + · have hL : lookupMultiplicityCount oStmt a = 0 := by + rw [lookupMultiplicityCount, Finset.card_eq_zero, Finset.filter_eq_empty_iff] + rintro ⟨j, u⟩ - hja + obtain ⟨v, hv⟩ := hcols j u + have hav : evalOnHypercube (tableOracle oStmt) v = a := hv.symm.trans hja + rw [tableMultiplicityCount] at hT + exact absurd hT (Finset.card_ne_zero_of_mem + (show v ∈ Finset.univ.filter + fun w => evalOnHypercube (tableOracle oStmt) w = a by simp [hav])) + simp [hT, hL] + · rw [nsmul_eq_mul, nsmul_eq_mul] + have hTF := hchar a hT + field_simp + -- group the table side by table value + have hLHS : + (∑ u : Hypercube n, evalOnHypercube (honestMultiplicity oStmt) u / + (x + evalOnHypercube (tableOracle oStmt) u)) + = ∑ a : F, tableMultiplicityCount oStmt a • + ((lookupMultiplicityCount oStmt a : F) / (tableMultiplicityCount oStmt a : F) + / (x + a)) := by + rw [← Finset.sum_fiberwise (Finset.univ : Finset (Hypercube n)) + (fun u => evalOnHypercube (tableOracle oStmt) u)] + refine Finset.sum_congr rfl (fun a _ => ?_) + have h : ∀ u ∈ Finset.univ.filter + (fun u => evalOnHypercube (tableOracle oStmt) u = a), + evalOnHypercube (honestMultiplicity oStmt) u / + (x + evalOnHypercube (tableOracle oStmt) u) + = (lookupMultiplicityCount oStmt a : F) / (tableMultiplicityCount oStmt a : F) + / (x + a) := by + intro u hu + simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hu + show (lookupMultiplicityCount oStmt (evalOnHypercube (tableOracle oStmt) u) : F) / + (tableMultiplicityCount oStmt (evalOnHypercube (tableOracle oStmt) u) : F) / + (x + evalOnHypercube (tableOracle oStmt) u) + = (lookupMultiplicityCount oStmt a : F) / (tableMultiplicityCount oStmt a : F) / (x + a) + rw [hu] + rw [Finset.sum_congr rfl h, Finset.sum_const] + rfl + -- group the column side by column value + have hRHS : + (∑ j : Fin M, ∑ u : Hypercube n, (1 : F) / + (x + evalOnHypercube (columnOracle oStmt j) u)) + = ∑ a : F, lookupMultiplicityCount oStmt a • ((1 : F) / (x + a)) := by + rw [← Finset.sum_product', Finset.univ_product_univ, + ← Finset.sum_fiberwise (Finset.univ : Finset (Fin M × Hypercube n)) + (fun p => evalOnHypercube (columnOracle oStmt p.1) p.2)] + refine Finset.sum_congr rfl (fun a _ => ?_) + have h : ∀ p ∈ Finset.univ.filter + (fun p : Fin M × Hypercube n => + evalOnHypercube (columnOracle oStmt p.1) p.2 = a), + (1 : F) / (x + evalOnHypercube (columnOracle oStmt p.1) p.2) = (1 : F) / (x + a) := by + intro p hp + simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hp + simp only [hp] + rw [Finset.sum_congr rfl h, Finset.sum_const] + rfl + rw [hLHS, hRHS] + exact Finset.sum_congr rfl (fun a _ => key a) + +/-- The canonical groups partition the term indices `{0,…,M}`, so summing group-by-group equals +summing over all terms. -/ +theorem sum_canonicalGroups (params : ProtocolParams M) (g : TermIdx M → F) : + (∑ k : Fin params.numGroups, ∑ i ∈ canonicalGroups params k, g i) = ∑ i : TermIdx M, g i := by + classical + have hℓ := params.sumSize_pos + have hidx : ∀ i : TermIdx M, i.val / params.sumSize < params.numGroups := by + intro i + have hiM : i.val ≤ M := Nat.lt_succ_iff.mp i.isLt + have hle : i.val / params.sumSize ≤ M / params.sumSize := Nat.div_le_div_right hiM + rw [ProtocolParams.numGroups, Nat.add_div_right _ hℓ] + omega + rw [← Finset.sum_fiberwise Finset.univ + (fun i : TermIdx M => (⟨i.val / params.sumSize, hidx i⟩ : Fin params.numGroups)) g] + refine Finset.sum_congr rfl (fun k _ => ?_) + congr 1 + ext i + simp only [canonicalGroups, ProtocolParams.group, Finset.mem_filter, Finset.mem_univ, true_and, + Fin.ext_iff] + constructor + · rintro ⟨h1, h2⟩ + have ha : k.val ≤ i.val / params.sumSize := (Nat.le_div_iff_mul_le hℓ).mpr h1 + have hb : i.val / params.sumSize < k.val + 1 := (Nat.div_lt_iff_lt_mul hℓ).mpr h2 + omega + · intro h + exact ⟨(Nat.le_div_iff_mul_le hℓ).mp (by omega), (Nat.div_lt_iff_lt_mul hℓ).mp (by omega)⟩ + +/-- The honest batched claim sums to zero: `∑ᵤ Q(u) = 0` for the honest multiplicity and helpers, +away from poles. Combines `domainIdentityTerm_eq_zero`, `sum_canonicalGroups`, and +`honest_multiplicity_identity`. -/ +theorem logupOuterClaim_zero (params : ProtocolParams M) (oStmtIn : ∀ i, OStmtIn F n M i) + (x : F) (z : Fin n → F) (lam : Fin params.numGroups → F) + (hcols : ∀ j : Fin M, ∀ u : Hypercube n, ∃ v : Hypercube n, + evalOnHypercube (columnOracle oStmtIn j) u = evalOnHypercube (tableOracle oStmtIn) v) + (hchar : ∀ a : F, tableMultiplicityCount oStmtIn a ≠ 0 → + (tableMultiplicityCount oStmtIn a : F) ≠ 0) + (hpoles : ∀ (i : TermIdx M) (u : Hypercube n), termPhi oStmtIn x i u ≠ 0) : + (∑ u : Hypercube n, + qOnHypercube (canonicalGroups params) oStmtIn (honestMultiplicity oStmtIn) + (honestHelpers params oStmtIn x) x z lam u) = 0 := by + -- honest helpers kill the domain-identity term, leaving `∑ₖ helperValue` + have hq : ∀ u : Hypercube n, + qOnHypercube (canonicalGroups params) oStmtIn (honestMultiplicity oStmtIn) + (honestHelpers params oStmtIn x) x z lam u + = ∑ k : Fin params.numGroups, + helperValue (canonicalGroups params) oStmtIn (honestMultiplicity oStmtIn) x k u := by + intro u + simp only [qOnHypercube] + refine Finset.sum_congr rfl (fun k _ => ?_) + rw [domainIdentityTerm_eq_zero (canonicalGroups params) oStmtIn (honestMultiplicity oStmtIn) + (honestHelpers params oStmtIn x) x k u rfl (fun i _ => hpoles i u), mul_zero, add_zero] + rfl + -- each helper expands to the per-group term sum, which partitions to the full term sum + have hsum : ∀ u : Hypercube n, + (∑ k : Fin params.numGroups, + helperValue (canonicalGroups params) oStmtIn (honestMultiplicity oStmtIn) x k u) + = ∑ i : TermIdx M, + termNumerator (honestMultiplicity oStmtIn) i u / termPhi oStmtIn x i u := by + intro u + simp only [helperValue] + exact sum_canonicalGroups params + (fun i => termNumerator (honestMultiplicity oStmtIn) i u / termPhi oStmtIn x i u) + -- split the term sum into the table term and the column terms + have hterm : ∀ u : Hypercube n, + (∑ i : TermIdx M, termNumerator (honestMultiplicity oStmtIn) i u / termPhi oStmtIn x i u) + = evalOnHypercube (honestMultiplicity oStmtIn) u / + (x + evalOnHypercube (tableOracle oStmtIn) u) + + ∑ j : Fin M, (-1 : F) / (x + evalOnHypercube (columnOracle oStmtIn j) u) := by + intro u + have hcol : ∀ j : Fin M, + termNumerator (honestMultiplicity oStmtIn) (Fin.succ j) u / + termPhi oStmtIn x (Fin.succ j) u + = (-1 : F) / (x + evalOnHypercube (columnOracle oStmtIn j) u) := by + intro j + have htt : termToInput (Fin.succ j : TermIdx M) = InputOracleIdx.column j := by + simp only [termToInput, Fin.val_succ, Nat.succ_ne_zero, ↓reduceDIte] + congr 1 + simp only [termNumerator, termPhi, htt, numerator, phi] + rw [Fin.sum_univ_succ] + refine congrArg₂ (· + ·) rfl ?_ + exact Finset.sum_congr rfl (fun j _ => hcol j) + simp_rw [hq, hsum, hterm] + rw [Finset.sum_add_distrib, honest_multiplicity_identity oStmtIn x hcols hchar, + Finset.sum_comm (f := fun u j => (-1 : F) / (x + evalOnHypercube (columnOracle oStmtIn j) u)), + ← Finset.sum_add_distrib] + refine Finset.sum_eq_zero (fun j _ => ?_) + rw [← Finset.sum_add_distrib] + exact Finset.sum_eq_zero (fun u _ => by ring) + +/-- The polynomial `Q` agrees with `qOnHypercube` on the signed hypercube — a structural fact (no +honesty needed), from `logupQPolynomial_eval_signPoint`. -/ +theorem logupRowsAgree (params : ProtocolParams M) (hs : (-1 : F) ≠ 1) + (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) : + logupSumcheckPolynomialRowsAgree F n M params stmt oStmt := + fun u => logupQPolynomial_eval_signPoint F n M params hs stmt oStmt u + +/-- The table poles `{x : ∃ u, x + t(u) = 0} = {-t(u) : u ∈ H}` number at most `|H|`. This is the +counting fact behind the pole-rejection completeness error. -/ +theorem pole_card_le (oStmt : ∀ i, OStmtIn F n M i) : + (Finset.univ.filter (fun x : F => ∃ u : Hypercube n, + x + evalOnHypercube (tableOracle oStmt) u = 0)).card + ≤ Fintype.card (Hypercube n) := by + classical + calc (Finset.univ.filter (fun x : F => ∃ u : Hypercube n, + x + evalOnHypercube (tableOracle oStmt) u = 0)).card + ≤ (Finset.univ.image + (fun u : Hypercube n => -evalOnHypercube (tableOracle oStmt) u)).card := by + apply Finset.card_le_card + intro x hx + simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hx + obtain ⟨u, hu⟩ := hx + exact Finset.mem_image.mpr ⟨u, Finset.mem_univ u, (eq_neg_of_add_eq_zero_left hu).symm⟩ + _ ≤ Fintype.card (Hypercube n) := by + rw [← Finset.card_univ]; exact Finset.card_image_le + +/-- A `LagrangeOracle` query at a signed-hypercube point returns the oracle's value there. -/ +theorem lagrange_answer_signPoint (hs : (-1 : F) ≠ 1) (oracle : MultilinearOracle F n) + (a : Hypercube n) : + OracleInterface.answer oracle (signPoint F a) = evalOnHypercube oracle a := + lagrangeOracleEval_signPoint F n hs oracle a + +end OuterAlgebra + section Completeness variable {ι : Type} (oSpec : OracleSpec ι) @@ -20,21 +246,282 @@ variable (n M : ℕ) variable (params : ProtocolParams M) variable {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) -/-- Completeness error forced by the current rejection-based model of the `x` challenge. - -Protocol 2 samples `x` from the complement of the table poles. The current verifier samples from -all of `F` and rejects poles, so the intended completeness statement carries this explicit bad-`x` -probability. Once the challenge sampler is modeled as the complement distribution, this should -collapse to perfect completeness. --/ +/-- Completeness error from the current `x`-sampling model: the verifier samples `x` from all of `F` +and rejects table poles, so completeness carries this bad-`x` probability. It would be `0` if `x` +were sampled from the pole complement, as the LogUp protocol intends. -/ noncomputable def logupCompletenessError (F : Type) [Fintype F] (n : ℕ) : ℝ≥0 := (Fintype.card (Hypercube n) : ℝ≥0) / (Fintype.card F) +/-- `simulateQ` distributes over a `forIn` loop in `OptionT (OracleComp spec)`: the OptionT +sibling of `simulateQ_list_forIn`, built on `simulateQ_optionT_bind`. -/ +theorem simulateQ_optionT_list_forIn {ι : Type} {spec : OracleSpec ι} + {nn : Type → Type*} [Monad nn] [LawfulMonad nn] (impl : QueryImpl spec nn) + {α β : Type} (xs : List α) (init : β) + (f : α → β → OptionT (OracleComp spec) (ForInStep β)) : + simulateQ impl (forIn xs init f : OptionT (OracleComp spec) β) + = (forIn xs init (fun a b => simulateQ impl (f a b)) : OptionT nn β) := by + induction xs generalizing init with + | nil => rfl + | cons x xs ih => + rw [List.forIn_cons, List.forIn_cons, simulateQ_optionT_bind] + congr 1 + funext step + cases step with + | done b => rfl + | yield b => exact ih b + +/-- `simulateQ` distributes over `OptionT.map` (the `<$>` form), the OptionT sibling of +`simulateQ_map`. -/ +theorem simulateQ_optionT_map {ι : Type} {spec : OracleSpec ι} + {nn : Type → Type*} [Monad nn] [LawfulMonad nn] (impl : QueryImpl spec nn) + {α β : Type} (f : α → β) (x : OptionT (OracleComp spec) α) : + simulateQ impl (f <$> x : OptionT (OracleComp spec) β) + = (f <$> simulateQ impl x : OptionT nn β) := by + rw [map_eq_pure_bind, simulateQ_optionT_bind, map_eq_pure_bind] + rfl + +/-- `simulateQ` leaves a `guard` unchanged: `guard` has no queries, so its simulation is itself. -/ +theorem simulateQ_optionT_guard {ι : Type} {spec : OracleSpec ι} + {nn : Type → Type*} [Monad nn] [LawfulMonad nn] (impl : QueryImpl spec nn) + (P : Prop) [Decidable P] : + simulateQ impl (guard P : OptionT (OracleComp spec) Unit) = (guard P : OptionT nn Unit) := by + show simulateQ impl (if P then pure () else failure : OptionT (OracleComp spec) Unit) + = (if P then pure () else failure : OptionT nn Unit) + by_cases hP : P + · rw [if_pos hP, if_pos hP]; rfl + · rw [if_neg hP, if_neg hP]; rfl + +/-- Resolve `simulateQ` over a `bind`-then-`guard` loop step in `OptionT (OracleComp spec)`: +`simulateQ` passes through the bind and leaves the guard on the simulated value. -/ +theorem simulateQ_optionT_bind_guard {ι : Type} {spec : OracleSpec ι} + {nn : Type → Type*} [Monad nn] [LawfulMonad nn] (impl : QueryImpl spec nn) + {γ : Type} (mx : OptionT (OracleComp spec) γ) (Pf : γ → Prop) [DecidablePred Pf] : + simulateQ impl ((mx >>= + fun t => (fun _ => ForInStep.yield PUnit.unit) <$> guard (Pf t)) : + OptionT (OracleComp spec) (ForInStep PUnit)) + = ((simulateQ impl mx >>= + fun t => (fun _ => ForInStep.yield PUnit.unit) <$> guard (Pf t)) : + OptionT nn (ForInStep PUnit)) := by + rw [simulateQ_optionT_bind] + congr 1 + funext t + rw [simulateQ_optionT_map, simulateQ_optionT_guard] + +/-- A guarded `forIn` loop in `OptionT (OracleComp spec)` only lands in `support` (succeeds) when +every guard passed. The key fact behind the verifier's pole-rejection check. -/ +theorem guarded_forIn_succeeds {ι : Type} {spec : OracleSpec ι} + {β : Type} (L : List β) (P : β → Prop) [DecidablePred P] (r : PUnit) : + r ∈ support (forIn L PUnit.unit + (fun u (_ : PUnit) => (fun _ => ForInStep.yield PUnit.unit) <$> + (guard (P u) : OptionT (OracleComp spec) PUnit))) → + ∀ u ∈ L, P u := by + induction L with + | nil => intro _ u hu; simp at hu + | cons a L' ih => + intro hr u hu + rw [List.forIn_cons] at hr + by_cases hPa : P a + · simp only [guard, hPa, if_true, map_pure, pure_bind] at hr + rcases List.mem_cons.mp hu with rfl | hu' + · exact hPa + · exact ih hr u hu' + · simp [guard, hPa] at hr + +open OracleComp OracleSpec in +/-- Simulating the outer verifier's table queries against the honest oracles `oStmt` turns the +oracle-accessing pole-rejection scan into a query-free `evalOnHypercube` loop. This is the LogUp +analog of `Sumcheck.Spec.SingleRound.oracleVerifier_eq_verifier`: it does the `simOracle2` peel +once, so the completeness proof never has to. -/ +theorem outerVerify_simulateQ_eq (stmt : StmtIn F n M) (oStmt : ∀ i, OStmtIn F n M i) + (messages : ∀ i, (outerPSpec F n params).Message i) + (challenges : ∀ i, (outerPSpec F n params).Challenge i) : + simulateQ (OracleInterface.simOracle2 oSpec oStmt messages) + ((outerVerifier oSpec F n M params).verify stmt challenges) + = (do + let x : F := challenges (outerChallengeXIdx F n M params) + for u in (Finset.univ : Finset (Hypercube n)).toList do + guard (x + evalOnHypercube (tableOracle oStmt) u ≠ 0) + let batch : BatchingChallenge F n params.numGroups := + challenges (outerChallengeBatchIdx F n M params) + pure { xChallenge := x, zChallenge := batch.1, batchingScalars := batch.2 } + : OptionT (OracleComp oSpec) (StmtAfterOuter F n M params)) := by + classical + -- Per iteration, simulating the lifted table query against `oStmt` returns the honest table + -- value. Proved standalone (the multi-lemma `SubSpec`/`simOracle0` routing chains here but not + -- under the `forIn` binder); as a single rewrite rule it then fires under the binder below. + have hquery : ∀ a : Hypercube n, + simulateQ (QueryImpl.liftTarget (OracleComp oSpec) (QueryImpl.id oSpec) + + QueryImpl.liftTarget (OracleComp oSpec) + ((OracleInterface.simOracle0 (OStmtIn F n M) oStmt).add + (OracleInterface.simOracle0 (outerPSpec F n params).Message messages))) + (liftM (OracleSpec.query (spec := [OStmtIn F n M]ₒ) + ⟨InputOracleIdx.table, signPoint F a⟩)) + = (pure (evalOnHypercube (tableOracle oStmt) a) : OracleComp oSpec F) := by + intro a + rw [← lagrange_answer_signPoint Fact.out (tableOracle oStmt) a] + rfl + simp only [outerVerifier, OracleInterface.simOracle2, QueryImpl.addLift_def, + simulateQ_optionT_bind, simulateQ_optionT_list_forIn, simulateQ_optionT_map, + simulateQ_optionT_guard, simulateQ_optionT_lift, simulateQ_pure, simulateQ_map, + QueryImpl.add_apply_inr, QueryImpl.add_apply_inl, QueryImpl.liftTarget_apply, + QueryImpl.add, OracleInterface.simOracle0, + OracleComp.liftComp_bind, OracleComp.liftComp_pure, OracleComp.liftComp_query, + OracleComp.liftComp_map, OracleQuery.cont_query, OracleQuery.input_query, + OptionT.lift, OptionT.mk, id_map, id_eq, Function.comp, + bind_pure_comp, map_pure, pure_bind, bind_assoc] + -- Reduce the table query per element: discharge the `forIn` binder via `congr`/`funext`, then + -- `rw [hquery]` (which works outside binders where `simp` could not match it). + congr 1 + congr 1 + funext a b + rw [← lagrange_answer_signPoint Fact.out (tableOracle oStmt) a] + rfl + +/-- Four-round unfolding of `Fin.induction` (the analog of `Fin.induction_two`), for the outer +LogUp prover's `runToRound`. -/ +private theorem Fin.induction_four {motive : Fin 5 → Sort*} {zero : motive 0} + {succ : ∀ i : Fin 4, motive i.castSucc → motive i.succ} : + Fin.induction (motive := motive) zero succ (Fin.last 4) + = succ 3 (succ 2 (succ 1 (succ 0 zero))) := rfl + +/-- Completeness of the outer LogUp phase: the honest outer prover reaches `logupMidRelation`, +except with the pole-sampling error. + +The membership content of `logupMidRelation` is fully proved: `logupRowsAgree` (the polynomial +agreement) and `logupOuterClaim_zero` (the zero-sum claim, valid whenever `x` avoids the table +poles). What remains is the **monad-execution shell**: unfolding the honest 4-message +`Reduction.run` (prover sends `m`, gets `x`, sends helpers, gets `(z,λ)`; verifier runs the +pole-rejection guard loop), showing `{x not a pole} ⊆ {success}`, and bounding +`P(x is a pole) ≤ |H|/|F|`. This is intricate `OracleComp`/`probEvent` reasoning (cf. the proved +template `Sumcheck.Spec.SingleRound.Simpler.reduction_perfectCompleteness`), the last open piece. -/ +theorem logup_outer_completeness [Inhabited F] : + (outerOracleReduction oSpec F n M params).completeness init impl + (inputRelation F n M) (logupMidRelation F n M params) (logupCompletenessError F n) := by + unfold OracleReduction.completeness Reduction.completeness + rintro ⟨stmt, oStmt⟩ ⟨⟩ hIn + simp only [outerOracleReduction, OracleReduction.toReduction, Reduction.run, Prover.run, + Verifier.run, Prover.runToRound, outerProver, Fin.induction_four, + Prover.processRound, outerPSpec] + repeat' split <;> rename_i hd <;> first | exact absurd hd (by decide) | skip + simp only [ProtocolSpec.getChallenge, liftM, monadLift, MonadLift.monadLift, + MonadLiftT.monadLift, OracleComp.liftComp_pure, OracleComp.liftComp_query, + bind_pure_comp, map_pure, pure_bind, bind_assoc, Functor.map_map, Function.comp, + QueryImpl.addLift_def, QueryImpl.simulateQ_add_liftComp_right, + QueryImpl.simulateQ_add_liftComp_left, simulateQ_query, simulateQ_pure, simulateQ_bind, + simulateQ_map, StateT.run_bind, StateT.run_pure, StateT.run_map, + ProtocolSpec.challengeQueryImpl] + rw [ge_iff_le, probEvent_ext (q := fun _ => True) ?allSuccess, probEvent_True_eq_sub] + · -- pole-probability bound `Pr[⊥] ≤ |H|/|F|` + refine tsub_le_tsub_left ?_ 1 + sorry + · -- every non-failing output satisfies the success predicate + intro out hout + rw [OptionT.mem_support_iff] at hout + simp only [OptionT.run_mk, support_bind, Set.mem_iUnion] at hout + obtain ⟨s, -, hout⟩ := hout + simp only [StateT.run'_eq, support_map, Set.mem_image] at hout + obtain ⟨⟨_, s'⟩, hout, rfl⟩ := hout + erw [simulateQ_bind, StateT.run_bind] at hout + rw [mem_support_bind_iff] at hout + obtain ⟨⟨pres, s2⟩, hpres, hver⟩ := hout + simp only [OptionT.lift, OptionT.mk] at hpres + erw [simulateQ_map, StateT.run_map] at hpres + rw [support_map, Set.mem_image] at hpres + obtain ⟨⟨pval, sp⟩, hpval, hpeq⟩ := hpres + erw [simulateQ_map, StateT.run_map] at hpval + rw [support_map, Set.mem_image] at hpval + obtain ⟨⟨a, sa⟩, ha, hpval_eq⟩ := hpval + erw [simulateQ_bind, StateT.run_bind] at ha + rw [mem_support_bind_iff] at ha + obtain ⟨⟨b, sb⟩, hb, ha3⟩ := ha + erw [simulateQ_map, StateT.run_map] at ha3 + rw [support_map, Set.mem_image] at ha3 + obtain ⟨⟨zlam, szlam⟩, hzlam, ha3eq⟩ := ha3 + -- round 1: peel `hb` to reach the `x` challenge query + erw [simulateQ_map, StateT.run_map] at hb + rw [support_map, Set.mem_image] at hb + obtain ⟨⟨c, sc⟩, hc, hbeq⟩ := hb + erw [simulateQ_bind, StateT.run_bind] at hc + rw [mem_support_bind_iff] at hc + obtain ⟨⟨d, sd⟩, hd, hc2⟩ := hc + erw [simulateQ_map, StateT.run_map] at hc2 + rw [support_map, Set.mem_image] at hc2 + obtain ⟨⟨xval, sx⟩, hx, hc2eq⟩ := hc2 + -- round 0 is deterministic (a pure `honestMultiplicity` send) + erw [simulateQ_map, StateT.run_map] at hd + rw [support_map, Set.mem_image] at hd + obtain ⟨⟨e, se⟩, he, hdeq⟩ := hd + erw [simulateQ_pure, StateT.run_pure] at he + rw [support_pure, Set.mem_singleton_iff] at he + obtain ⟨rfl, rfl⟩ := Prod.mk.inj he + -- substitute the prover-side equation chain to make `pval`/`pres` concrete + obtain ⟨rfl, rfl⟩ := Prod.mk.inj hdeq + obtain ⟨rfl, rfl⟩ := Prod.mk.inj hc2eq + obtain ⟨rfl, rfl⟩ := Prod.mk.inj hbeq + obtain ⟨rfl, rfl⟩ := Prod.mk.inj ha3eq + obtain ⟨rfl, rfl⟩ := Prod.mk.inj hpval_eq + obtain ⟨rfl, rfl⟩ := Prod.mk.inj hpeq + -- peel the verifier (match on `some pval` resolves; then its bind) + simp only at hver + erw [simulateQ_bind, StateT.run_bind] at hver + rw [mem_support_bind_iff] at hver + obtain ⟨⟨vstmt, sv⟩, hverify, hvout⟩ := hver + -- Replace the verifier's table-query pole-rejection scan by the clean `evalOnHypercube` loop. + simp only [OracleVerifier.toVerifier] at hverify + rw [outerVerify_simulateQ_eq] at hverify + -- The verifier accepted: `vstmt = some _` (the `none` branch of `hvout` yields `none ≠ some`). + rcases vstmt with _ | ⟨vStmtOut, vOracles⟩ + · simp only [simulateQ_pure, StateT.run_pure, support_pure, Set.mem_singleton_iff] at hvout + simp at hvout + · -- The honest output: `out` pairs the prover view with the verifier's accepted statement. + dsimp only [Option.getM] at hvout + simp only [simulateQ_map, simulateQ_pure, StateT.run_map, StateT.run_pure, + support_map, support_pure, Set.mem_image, Set.mem_singleton_iff, pure_bind, + Functor.map_map, Function.comp, map_pure] at hvout + skip + + +/-- Lens-completeness for the LogUp→Sumcheck lens: `proj` is the zero-sum instance +(`logupSumcheckRelationInput_of_rowsAgree`), `lift` is trivial as `outputRelation = univ`. -/ +instance logupSumcheckLensComplete : + (logupSumcheckContextLens F n M params).toContext.IsComplete + (logupMidRelation F n M params) + (Sumcheck.Spec.relationRound F n (logupSumcheckDegree M params) (signDomain F Fact.out) 0) + outputRelation + (Sumcheck.Spec.relationRound F n (logupSumcheckDegree M params) (signDomain F Fact.out) + (Fin.last n)) + ((logupConcreteSumcheckOracleReduction oSpec F n M params Fact.out).toReduction.compatContext + (logupSumcheckContextLens F n M params).toContext) where + proj_complete := by + rintro ⟨stmt, oStmt⟩ ⟨⟩ h + exact logupSumcheckRelationInput_of_rowsAgree F n M params (hSigns := Fact.out) h.1 h.2 + lift_complete := by + intro _ _ _ _ _ _ _ + simp [outputRelation] + +/-- Completeness of the embedded sumcheck phase: it carries `logupMidRelation` to `outputRelation` +with no extra error, by reusing the generic sumcheck's perfect completeness through the +LogUp-to-Sumcheck context lens. -/ +theorem logupSumcheckPhaseCompleteness : + (sumcheckOracleReduction oSpec F n M params).completeness init impl + (logupMidRelation F n M params) outputRelation 0 := + OracleReduction.liftContext_perfectCompleteness + (lens := logupSumcheckContextLens F n M params) + (lensComplete := logupSumcheckLensComplete oSpec F n M params) + (Sumcheck.Spec.oracleReduction_perfectCompleteness + F (logupSumcheckDegree M params) (signDomain F Fact.out) n oSpec) + /-- Main ArkLib completeness theorem for LogUp Protocol 2. -/ theorem logup_completeness : (logupOracleReduction oSpec F n M params).completeness init impl (inputRelation F n M) outputRelation (logupCompletenessError F n) := by - sorry + letI : Inhabited F := ⟨0⟩ + have happ := OracleReduction.append_completeness.{0, 0, 0, 0} + (outerOracleReduction oSpec F n M params) + (sumcheckOracleReduction oSpec F n M params) + (logup_outer_completeness oSpec F n M params init impl) + (logupSumcheckPhaseCompleteness oSpec F n M params init impl) + simpa only [add_zero] using happ end Completeness diff --git a/ArkLib/ProofSystem/Logup/Sumcheck/Security.lean b/ArkLib/ProofSystem/Logup/Sumcheck/Security.lean new file mode 100644 index 0000000000..3718a88fa3 --- /dev/null +++ b/ArkLib/ProofSystem/Logup/Sumcheck/Security.lean @@ -0,0 +1,38 @@ +import ArkLib.OracleReduction.Security.Basic +import ArkLib.ProofSystem.Sumcheck.Spec.General + +/-! +# Full Oracle Sum-check Completeness (LogUp-local) + +Perfect completeness for the full oracle sum-check reduction, which LogUp's embedded phase needs but +`General.lean` does not provide (it has the full *non-oracle* and single-round oracle versions only). + +Placeholder: currently a `sorry`, to be replaced by the proved theorem and ideally upstreamed. +-/ + +namespace Sumcheck + +open Polynomial MvPolynomial OracleSpec OracleComp ProtocolSpec Finset + +namespace Spec + +variable (R : Type) [CommSemiring R] (deg : ℕ) {m : ℕ} (D : Fin m ↪ R) (n : ℕ) +variable {ι : Type} (oSpec : OracleSpec ι) +variable [DecidableEq R] [SampleableType R] +variable {σ : Type} {init : ProbComp σ} {impl : QueryImpl oSpec (StateT σ ProbComp)} + +/-- Perfect completeness for the full sum-check oracle reduction. + +Prove by `seqCompose_perfectCompleteness` over `SingleRound.oracleReduction_perfectCompleteness`, +as `reduction_perfectCompleteness` does for the non-oracle version. -/ +theorem oracleReduction_perfectCompleteness : + (oracleReduction R deg D n oSpec).perfectCompleteness init impl + (relationRound R n deg D 0) (relationRound R n deg D (.last n)) := + OracleReduction.seqCompose_perfectCompleteness + (rel := relationRound R n deg D) + (R := SingleRound.oracleReduction R n deg D oSpec) + (h := fun i => SingleRound.oracleReduction_perfectCompleteness i) + +end Spec + +end Sumcheck diff --git a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean index 1d7638b86a..4e283ff536 100644 --- a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean +++ b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean @@ -77,6 +77,13 @@ noncomputable def logupOuterSumcheckClaim qOnHypercube (canonicalGroups params) (fun i => oStmt (.input i)) (oStmt .multiplicity) (oStmt .helpers) stmt.xChallenge stmt.zChallenge stmt.batchingScalars u +/-- Relation handed from the outer LogUp phase to the embedded sumcheck: the polynomial bridge +agrees on the hypercube and the outer algebra produced a genuine zero-sum claim. -/ +def logupMidRelation : + Set ((StmtAfterOuter F n M params × (∀ i, OStmtAfterOuter F n M params i)) × Unit) := + { x | logupSumcheckPolynomialRowsAgree F n M params x.1.1 x.1.2 ∧ + logupOuterSumcheckClaim F n M params x.1.1 x.1.2 = 0 } + /-- Semantic agreement between final oracle-query answers and the retained LogUp oracles. -/ def logupPointEvaluationsAgree (r : Fin n → F) diff --git a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean index cd1d634616..344bc0f4d7 100644 --- a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean +++ b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean @@ -83,7 +83,7 @@ private theorem signedLinearFactor_degreeOf (a : F) (i j : Fin n) : _ ≤ 0 + 0 := by gcongr · exact (MvPolynomial.degreeOf_C (R := F) a i).le - · exact (MvPolynomial.degreeOf_X_of_ne (R := F) i j hij).le + · exact (MvPolynomial.degreeOf_X_of_ne (R := F) hij).le _ = 0 := by omega exact (MvPolynomial.degreeOf_add_le i _ _).trans (max_le hone hmul) @@ -173,6 +173,45 @@ private theorem multilinearOraclePolynomial_eval (oracle : MultilinearOracle F n simp [multilinearOraclePolynomial, signedMLEPolynomial_eval, lagrangeOracleEval, evalOnHypercube] +/-- On the signed hypercube the Lagrange kernel is a delta function. -/ +theorem lagrangeKernel_signPoint (hs : (-1 : F) ≠ 1) (v u : Hypercube n) : + lagrangeKernel F v (signPoint F u) = if v = u then 1 else 0 := by + have h2 : (2 : F) ≠ 0 := fun h => hs (by linear_combination -h) + unfold lagrangeKernel lagrangeKernelAtPoint + by_cases h : v = u + · subst h + rw [if_pos rfl] + have hfac : ∀ j, 1 + signPoint F v j * signPoint F v j = (2 : F) := by + intro j + show (1 : F) + bitToSign F (v j) * bitToSign F (v j) = 2 + generalize v j = b + fin_cases b <;> norm_num [bitToSign] + rw [Finset.prod_congr rfl (fun j _ => hfac j), Finset.prod_const, Finset.card_univ, + Fintype.card_fin, inv_mul_cancel₀ (pow_ne_zero n h2)] + · rw [if_neg h] + obtain ⟨j, hj⟩ := Function.ne_iff.mp h + have hfac : 1 + signPoint F v j * signPoint F u j = 0 := by + show (1 : F) + bitToSign F (v j) * bitToSign F (u j) = 0 + generalize hvj : v j = b at hj + generalize huj : u j = c at hj + fin_cases b <;> fin_cases c <;> simp_all [bitToSign] + rw [Finset.prod_eq_zero (Finset.mem_univ j) hfac, mul_zero] + +/-- The MLE of an oracle, evaluated at a signed-hypercube point, recovers the oracle's value. -/ +theorem lagrangeOracleEval_signPoint (hs : (-1 : F) ≠ 1) + (oracle : MultilinearOracle F n) (u : Hypercube n) : + lagrangeOracleEval oracle (signPoint F u) = evalOnHypercube oracle u := by + unfold lagrangeOracleEval + rw [Finset.sum_congr rfl (fun v _ => by rw [lagrangeKernel_signPoint F n hs v u])] + simp only [mul_ite, mul_one, mul_zero, Finset.sum_ite_eq', Finset.mem_univ, if_true, + evalOnHypercube] + +private theorem multilinearOraclePolynomial_eval_sign (hs : (-1 : F) ≠ 1) + (oracle : MultilinearOracle F n) (u : Hypercube n) : + MvPolynomial.eval (signPoint F u) (multilinearOraclePolynomial F n oracle) + = evalOnHypercube oracle u := by + rw [multilinearOraclePolynomial_eval, lagrangeOracleEval_signPoint F n hs] + private noncomputable def inputOraclePolynomial (oStmt : ∀ i, OStmtAfterOuter F n M params i) (idx : InputOracleIdx M) : MvPolynomial (Fin n) F := @@ -411,6 +450,60 @@ theorem logupQPolynomial_degreeOf omega exact (MvPolynomial.degreeOf_add_le i _ _).trans (max_le hHelper hProduct) +private theorem termPhiPolynomial_eval_sign (hs : (-1 : F) ≠ 1) + (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) + (i : TermIdx M) (u : Hypercube n) : + MvPolynomial.eval (signPoint F u) (termPhiPolynomial F n M params stmt oStmt i) + = termPhi (fun idx => oStmt (.input idx)) stmt.xChallenge i u := by + rcases h : termToInput i with _ | c <;> + simp only [termPhiPolynomial, termPhi, phi, h, inputOraclePolynomial, tableOracle, + columnOracle, map_add, MvPolynomial.eval_C, multilinearOraclePolynomial_eval_sign F n hs] + +private theorem termNumeratorPolynomial_eval_sign (hs : (-1 : F) ≠ 1) + (oStmt : ∀ i, OStmtAfterOuter F n M params i) (i : TermIdx M) (u : Hypercube n) : + MvPolynomial.eval (signPoint F u) (termNumeratorPolynomial F n M params oStmt i) + = termNumerator (oStmt .multiplicity) i u := by + unfold termNumeratorPolynomial termNumerator numerator + cases termToInput i with + | table => simp only [multiplicityPolynomial, multilinearOraclePolynomial_eval_sign F n hs] + | column c => simp + +private theorem domainIdentityPolynomial_eval_sign (hs : (-1 : F) ≠ 1) + (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) + (k : Fin params.numGroups) (u : Hypercube n) : + MvPolynomial.eval (signPoint F u) (domainIdentityPolynomial F n M params stmt oStmt k) + = domainIdentityTerm (canonicalGroups params) (fun idx => oStmt (.input idx)) + (oStmt .multiplicity) (oStmt .helpers) stmt.xChallenge k u := by + rw [domainIdentityPolynomial, map_sub, map_mul, map_prod, domainIdentityTerm, + denominatorProduct, map_sum] + congr 1 + · congr 1 + · simp only [helperPolynomial]; exact multilinearOraclePolynomial_eval_sign F n hs _ u + · exact Finset.prod_congr rfl + (fun i _ => termPhiPolynomial_eval_sign F n M params hs stmt oStmt i u) + · refine Finset.sum_congr rfl (fun i _ => ?_) + rw [map_mul, map_prod, termNumeratorPolynomial_eval_sign F n M params hs] + congr 1 + exact Finset.prod_congr rfl + (fun j _ => termPhiPolynomial_eval_sign F n M params hs stmt oStmt j u) + +/-- `logupQPolynomial` restricted to the signed hypercube agrees with `qOnHypercube`. -/ +theorem logupQPolynomial_eval_signPoint (hs : (-1 : F) ≠ 1) + (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) + (u : Hypercube n) : + MvPolynomial.eval (signPoint F u) (logupQPolynomial F n M params stmt oStmt) + = qOnHypercube (canonicalGroups params) (fun i => oStmt (.input i)) (oStmt .multiplicity) + (oStmt .helpers) stmt.xChallenge stmt.zChallenge stmt.batchingScalars u := by + rw [logupQPolynomial, qOnHypercube, map_sum] + refine Finset.sum_congr rfl (fun k _ => ?_) + rw [map_add, map_mul, map_mul, MvPolynomial.eval_C, + show MvPolynomial.eval (signPoint F u) (helperPolynomial F n M params oStmt k) + = evalOnHypercube ((oStmt .helpers) k) u from by + simp only [helperPolynomial]; exact multilinearOraclePolynomial_eval_sign F n hs _ u, + show MvPolynomial.eval (signPoint F u) (lagrangeKernelPolynomial F n stmt.zChallenge) + = lagrangeKernel F u stmt.zChallenge from by rw [lagrangeKernelPolynomial_eval]; rfl, + domainIdentityPolynomial_eval_sign F n M params hs] + end SumcheckPolynomial end Logup From 9c4ad8a8184b339fd0739f8410742b6637c44633 Mon Sep 17 00:00:00 2001 From: jCabala Date: Fri, 19 Jun 2026 10:48:56 +0100 Subject: [PATCH 02/12] feat: Finished the non-probabilsitic part of the LogUp completeness proof --- .../Logup/Security/Completeness.lean | 334 +++++++++++++----- 1 file changed, 246 insertions(+), 88 deletions(-) diff --git a/ArkLib/ProofSystem/Logup/Security/Completeness.lean b/ArkLib/ProofSystem/Logup/Security/Completeness.lean index a73748fa04..92f88d7c32 100644 --- a/ArkLib/ProofSystem/Logup/Security/Completeness.lean +++ b/ArkLib/ProofSystem/Logup/Security/Completeness.lean @@ -22,6 +22,7 @@ section OuterAlgebra variable {F : Type} [Field F] [Fintype F] [DecidableEq F] {n M K : ℕ} +omit [Fintype F] [DecidableEq F] in /-- For honest helpers (`hₖ = ∑ᵢ mᵢ/φᵢ`) the domain-identity term vanishes pointwise, away from poles (`φᵢ ≠ 0`). -/ theorem domainIdentityTerm_eq_zero (groups : PartialSumGroups M K) @@ -37,11 +38,12 @@ theorem domainIdentityTerm_eq_zero (groups : PartialSumGroups M K) /-- The honest log-derivative identity (heart of LogUp): with the normalized multiplicity, the table side equals the column side. Needs every column value to occur in the table (`hcols`) and the table -counts to be nonzero in `F` (`hchar`, from `charLarge`). Holds even at poles, since `x/0 = 0`. -/ +counts used by nonzero lookup multiplicities to be nonzero in `F` (`hchar`, from `charLarge`). +Holds even at poles, since `x/0 = 0`. -/ theorem honest_multiplicity_identity (oStmt : ∀ i, OStmtIn F n M i) (x : F) (hcols : ∀ j : Fin M, ∀ u : Hypercube n, ∃ v : Hypercube n, evalOnHypercube (columnOracle oStmt j) u = evalOnHypercube (tableOracle oStmt) v) - (hchar : ∀ a : F, tableMultiplicityCount oStmt a ≠ 0 → + (hchar : ∀ a : F, lookupMultiplicityCount oStmt a ≠ 0 → (tableMultiplicityCount oStmt a : F) ≠ 0) : (∑ u : Hypercube n, evalOnHypercube (honestMultiplicity oStmt) u / (x + evalOnHypercube (tableOracle oStmt) u)) @@ -66,8 +68,10 @@ theorem honest_multiplicity_identity (oStmt : ∀ i, OStmtIn F n M i) (x : F) fun w => evalOnHypercube (tableOracle oStmt) w = a by simp [hav])) simp [hT, hL] · rw [nsmul_eq_mul, nsmul_eq_mul] - have hTF := hchar a hT - field_simp + by_cases hL : lookupMultiplicityCount oStmt a = 0 + · simp [hL] + · have hTF := hchar a hL + field_simp -- group the table side by table value have hLHS : (∑ u : Hypercube n, evalOnHypercube (honestMultiplicity oStmt) u / @@ -114,6 +118,7 @@ theorem honest_multiplicity_identity (oStmt : ∀ i, OStmtIn F n M i) (x : F) rw [hLHS, hRHS] exact Finset.sum_congr rfl (fun a _ => key a) +omit [Fintype F] [DecidableEq F] in /-- The canonical groups partition the term indices `{0,…,M}`, so summing group-by-group equals summing over all terms. -/ theorem sum_canonicalGroups (params : ProtocolParams M) (g : TermIdx M → F) : @@ -148,7 +153,7 @@ theorem logupOuterClaim_zero (params : ProtocolParams M) (oStmtIn : ∀ i, OStmt (x : F) (z : Fin n → F) (lam : Fin params.numGroups → F) (hcols : ∀ j : Fin M, ∀ u : Hypercube n, ∃ v : Hypercube n, evalOnHypercube (columnOracle oStmtIn j) u = evalOnHypercube (tableOracle oStmtIn) v) - (hchar : ∀ a : F, tableMultiplicityCount oStmtIn a ≠ 0 → + (hchar : ∀ a : F, lookupMultiplicityCount oStmtIn a ≠ 0 → (tableMultiplicityCount oStmtIn a : F) ≠ 0) (hpoles : ∀ (i : TermIdx M) (u : Hypercube n), termPhi oStmtIn x i u ≠ 0) : (∑ u : Hypercube n, @@ -203,6 +208,7 @@ theorem logupOuterClaim_zero (params : ProtocolParams M) (oStmtIn : ∀ i, OStmt rw [← Finset.sum_add_distrib] exact Finset.sum_eq_zero (fun u _ => by ring) +omit [Fintype F] [DecidableEq F] in /-- The polynomial `Q` agrees with `qOnHypercube` on the signed hypercube — a structural fact (no honesty needed), from `logupQPolynomial_eval_signPoint`. -/ theorem logupRowsAgree (params : ProtocolParams M) (hs : (-1 : F) ≠ 1) @@ -229,6 +235,7 @@ theorem pole_card_le (oStmt : ∀ i, OStmtIn F n M i) : _ ≤ Fintype.card (Hypercube n) := by rw [← Finset.card_univ]; exact Finset.card_image_le +omit [Fintype F] [DecidableEq F] in /-- A `LagrangeOracle` query at a signed-hypercube point returns the oracle's value there. -/ theorem lagrange_answer_signPoint (hs : (-1 : F) ≠ 1) (oracle : MultilinearOracle F n) (a : Hypercube n) : @@ -246,6 +253,10 @@ variable (n M : ℕ) variable (params : ProtocolParams M) variable {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) +local instance instOracleCompLawfulMonad' {τ : Type} (spec : OracleSpec τ) : + LawfulMonad (OracleComp spec) := + OracleComp.instLawfulMonad spec + /-- Completeness error from the current `x`-sampling model: the verifier samples `x` from all of `F` and rejects table poles, so completeness carries this bad-`x` probability. It would be `0` if `x` were sampled from the pole complement, as the LogUp protocol intends. -/ @@ -254,7 +265,7 @@ noncomputable def logupCompletenessError (F : Type) [Fintype F] (n : ℕ) : ℝ /-- `simulateQ` distributes over a `forIn` loop in `OptionT (OracleComp spec)`: the OptionT sibling of `simulateQ_list_forIn`, built on `simulateQ_optionT_bind`. -/ -theorem simulateQ_optionT_list_forIn {ι : Type} {spec : OracleSpec ι} +private theorem simulateQ_optionT_list_forIn {ι : Type} {spec : OracleSpec ι} {nn : Type → Type*} [Monad nn] [LawfulMonad nn] (impl : QueryImpl spec nn) {α β : Type} (xs : List α) (init : β) (f : α → β → OptionT (OracleComp spec) (ForInStep β)) : @@ -270,18 +281,8 @@ theorem simulateQ_optionT_list_forIn {ι : Type} {spec : OracleSpec ι} | done b => rfl | yield b => exact ih b -/-- `simulateQ` distributes over `OptionT.map` (the `<$>` form), the OptionT sibling of -`simulateQ_map`. -/ -theorem simulateQ_optionT_map {ι : Type} {spec : OracleSpec ι} - {nn : Type → Type*} [Monad nn] [LawfulMonad nn] (impl : QueryImpl spec nn) - {α β : Type} (f : α → β) (x : OptionT (OracleComp spec) α) : - simulateQ impl (f <$> x : OptionT (OracleComp spec) β) - = (f <$> simulateQ impl x : OptionT nn β) := by - rw [map_eq_pure_bind, simulateQ_optionT_bind, map_eq_pure_bind] - rfl - /-- `simulateQ` leaves a `guard` unchanged: `guard` has no queries, so its simulation is itself. -/ -theorem simulateQ_optionT_guard {ι : Type} {spec : OracleSpec ι} +private theorem simulateQ_optionT_guard {ι : Type} {spec : OracleSpec ι} {nn : Type → Type*} [Monad nn] [LawfulMonad nn] (impl : QueryImpl spec nn) (P : Prop) [Decidable P] : simulateQ impl (guard P : OptionT (OracleComp spec) Unit) = (guard P : OptionT nn Unit) := by @@ -291,43 +292,62 @@ theorem simulateQ_optionT_guard {ι : Type} {spec : OracleSpec ι} · rw [if_pos hP, if_pos hP]; rfl · rw [if_neg hP, if_neg hP]; rfl -/-- Resolve `simulateQ` over a `bind`-then-`guard` loop step in `OptionT (OracleComp spec)`: -`simulateQ` passes through the bind and leaves the guard on the simulated value. -/ -theorem simulateQ_optionT_bind_guard {ι : Type} {spec : OracleSpec ι} - {nn : Type → Type*} [Monad nn] [LawfulMonad nn] (impl : QueryImpl spec nn) - {γ : Type} (mx : OptionT (OracleComp spec) γ) (Pf : γ → Prop) [DecidablePred Pf] : - simulateQ impl ((mx >>= - fun t => (fun _ => ForInStep.yield PUnit.unit) <$> guard (Pf t)) : - OptionT (OracleComp spec) (ForInStep PUnit)) - = ((simulateQ impl mx >>= - fun t => (fun _ => ForInStep.yield PUnit.unit) <$> guard (Pf t)) : - OptionT nn (ForInStep PUnit)) := by - rw [simulateQ_optionT_bind] - congr 1 - funext t - rw [simulateQ_optionT_map, simulateQ_optionT_guard] - -/-- A guarded `forIn` loop in `OptionT (OracleComp spec)` only lands in `support` (succeeds) when -every guard passed. The key fact behind the verifier's pole-rejection check. -/ -theorem guarded_forIn_succeeds {ι : Type} {spec : OracleSpec ι} - {β : Type} (L : List β) (P : β → Prop) [DecidablePred P] (r : PUnit) : - r ∈ support (forIn L PUnit.unit - (fun u (_ : PUnit) => (fun _ => ForInStep.yield PUnit.unit) <$> - (guard (P u) : OptionT (OracleComp spec) PUnit))) → - ∀ u ∈ L, P u := by +/-- A list of abort-on-`P` checks can only return successfully when every check avoided `P`. -/ +private theorem guarded_foldlM_succeeds {ι : Type} {spec : OracleSpec ι} + {β γ : Type} (L : List β) (P : β → Prop) [DecidablePred P] (r : γ) : + r ∈ support (List.foldlM (m := OptionT (OracleComp spec)) + (fun (_ : γ) (u : β) => + if P u then failure else pure r) r L) → + ∀ u ∈ L, ¬ P u := by induction L with | nil => intro _ u hu; simp at hu | cons a L' ih => - intro hr u hu - rw [List.forIn_cons] at hr - by_cases hPa : P a - · simp only [guard, hPa, if_true, map_pure, pure_bind] at hr - rcases List.mem_cons.mp hu with rfl | hu' - · exact hPa - · exact ih hr u hu' - · simp [guard, hPa] at hr + intro h u hu + rw [List.foldlM_cons] at h + by_cases hPa : P a + · simp [hPa] at h + · simp only [hPa, ↓reduceIte, pure_bind, OptionT.mem_support_iff] at h + rcases List.mem_cons.mp hu with rfl | hu' + · exact hPa + · exact ih h u hu' + +private theorem support_bind_exists {m : Type → Type*} [Monad m] [LawfulMonad m] + [MonadLiftT m SetM] [LawfulMonadLiftT m SetM] + {α β : Type} (x : m α) (f : α → m β) {y : β} + (hy : y ∈ support (x >>= f)) : ∃ a, a ∈ support x ∧ y ∈ support (f a) := by + simpa [mem_support_bind_iff] using hy + +private theorem support_pure_eq {m : Type → Type*} [Monad m] [LawfulMonad m] + [MonadLiftT m SetM] [LawfulMonadLiftT m SetM] + {α : Type} {x y : α} (h : y ∈ support (pure x : m α)) : y = x := by + simpa [mem_support_pure_iff] using h + +private theorem mem_support_of_mem_support_liftComp {ι τ α : Type} {spec : OracleSpec ι} + {superSpec : OracleSpec τ} [MonadLiftT (OracleQuery spec) (OracleQuery superSpec)] + (oa : OracleComp spec α) (x : α) : + x ∈ support (oa.liftComp superSpec) → x ∈ support oa := by + intro hx + induction oa using OracleComp.inductionOn generalizing x with + | pure y => + simpa using hx + | query_bind q oa ih => + rw [OracleComp.liftComp_bind, mem_support_bind_iff] at hx + rw [mem_support_bind_iff] + obtain ⟨u, _hu, hx⟩ := hx + exact ⟨u, OracleComp.mem_support_query q u, ih u x hx⟩ + +private theorem support_simulateQ_run_fst_subset {ι : Type} {spec : OracleSpec ι} + {m : Type → Type*} [Monad m] [LawfulMonad m] [MonadLiftT m SetM] + [LawfulMonadLiftT m SetM] {σ α : Type} + (impl : QueryImpl spec (StateT σ m)) {oa : OracleComp spec α} {s s' : σ} {y : α} + (h : (y, s') ∈ support ((simulateQ impl oa).run s)) : + y ∈ support oa := + OracleComp.support_simulateQ_run'_subset impl oa s (by + rw [StateT.run'_eq, support_map, Set.mem_image] + exact ⟨(y, s'), h, rfl⟩) open OracleComp OracleSpec in +omit [SampleableType F] in /-- Simulating the outer verifier's table queries against the honest oracles `oStmt` turns the oracle-accessing pole-rejection scan into a query-free `evalOnHypercube` loop. This is the LogUp analog of `Sumcheck.Spec.SingleRound.oracleVerifier_eq_verifier`: it does the `simOracle2` peel @@ -345,32 +365,11 @@ theorem outerVerify_simulateQ_eq (stmt : StmtIn F n M) (oStmt : ∀ i, OStmtIn F challenges (outerChallengeBatchIdx F n M params) pure { xChallenge := x, zChallenge := batch.1, batchingScalars := batch.2 } : OptionT (OracleComp oSpec) (StmtAfterOuter F n M params)) := by - classical - -- Per iteration, simulating the lifted table query against `oStmt` returns the honest table - -- value. Proved standalone (the multi-lemma `SubSpec`/`simOracle0` routing chains here but not - -- under the `forIn` binder); as a single rewrite rule it then fires under the binder below. - have hquery : ∀ a : Hypercube n, - simulateQ (QueryImpl.liftTarget (OracleComp oSpec) (QueryImpl.id oSpec) + - QueryImpl.liftTarget (OracleComp oSpec) - ((OracleInterface.simOracle0 (OStmtIn F n M) oStmt).add - (OracleInterface.simOracle0 (outerPSpec F n params).Message messages))) - (liftM (OracleSpec.query (spec := [OStmtIn F n M]ₒ) - ⟨InputOracleIdx.table, signPoint F a⟩)) - = (pure (evalOnHypercube (tableOracle oStmt) a) : OracleComp oSpec F) := by - intro a - rw [← lagrange_answer_signPoint Fact.out (tableOracle oStmt) a] - rfl simp only [outerVerifier, OracleInterface.simOracle2, QueryImpl.addLift_def, - simulateQ_optionT_bind, simulateQ_optionT_list_forIn, simulateQ_optionT_map, - simulateQ_optionT_guard, simulateQ_optionT_lift, simulateQ_pure, simulateQ_map, - QueryImpl.add_apply_inr, QueryImpl.add_apply_inl, QueryImpl.liftTarget_apply, - QueryImpl.add, OracleInterface.simOracle0, - OracleComp.liftComp_bind, OracleComp.liftComp_pure, OracleComp.liftComp_query, - OracleComp.liftComp_map, OracleQuery.cont_query, OracleQuery.input_query, - OptionT.lift, OptionT.mk, id_map, id_eq, Function.comp, - bind_pure_comp, map_pure, pure_bind, bind_assoc] - -- Reduce the table query per element: discharge the `forIn` binder via `congr`/`funext`, then - -- `rw [hquery]` (which works outside binders where `simp` could not match it). + simulateQ_optionT_bind, simulateQ_optionT_list_forIn, + simulateQ_optionT_guard, OracleComp.liftComp_query, OracleQuery.cont_query, OracleQuery.input_query, + OptionT.lift, OptionT.mk, id_map] + -- Reduce the table query under the `forIn` binder. congr 1 congr 1 funext a b @@ -404,12 +403,8 @@ theorem logup_outer_completeness [Inhabited F] : Prover.processRound, outerPSpec] repeat' split <;> rename_i hd <;> first | exact absurd hd (by decide) | skip simp only [ProtocolSpec.getChallenge, liftM, monadLift, MonadLift.monadLift, - MonadLiftT.monadLift, OracleComp.liftComp_pure, OracleComp.liftComp_query, - bind_pure_comp, map_pure, pure_bind, bind_assoc, Functor.map_map, Function.comp, - QueryImpl.addLift_def, QueryImpl.simulateQ_add_liftComp_right, - QueryImpl.simulateQ_add_liftComp_left, simulateQ_query, simulateQ_pure, simulateQ_bind, - simulateQ_map, StateT.run_bind, StateT.run_pure, StateT.run_map, - ProtocolSpec.challengeQueryImpl] + MonadLiftT.monadLift, OracleComp.liftComp_pure, bind_pure_comp, map_pure, + QueryImpl.addLift_def] rw [ge_iff_le, probEvent_ext (q := fun _ => True) ?allSuccess, probEvent_True_eq_sub] · -- pole-probability bound `Pr[⊥] ≤ |H|/|F|` refine tsub_le_tsub_left ?_ 1 @@ -461,6 +456,8 @@ theorem logup_outer_completeness [Inhabited F] : obtain ⟨rfl, rfl⟩ := Prod.mk.inj ha3eq obtain ⟨rfl, rfl⟩ := Prod.mk.inj hpval_eq obtain ⟨rfl, rfl⟩ := Prod.mk.inj hpeq + change F at xval + change BatchingChallenge F n params.numGroups at zlam -- peel the verifier (match on `some pval` resolves; then its bind) simp only at hver erw [simulateQ_bind, StateT.run_bind] at hver @@ -470,16 +467,176 @@ theorem logup_outer_completeness [Inhabited F] : simp only [OracleVerifier.toVerifier] at hverify rw [outerVerify_simulateQ_eq] at hverify -- The verifier accepted: `vstmt = some _` (the `none` branch of `hvout` yields `none ≠ some`). - rcases vstmt with _ | ⟨vStmtOut, vOracles⟩ + rcases vstmt with _ | vAccepted · simp only [simulateQ_pure, StateT.run_pure, support_pure, Set.mem_singleton_iff] at hvout simp at hvout - · -- The honest output: `out` pairs the prover view with the verifier's accepted statement. - dsimp only [Option.getM] at hvout - simp only [simulateQ_map, simulateQ_pure, StateT.run_map, StateT.run_pure, - support_map, support_pure, Set.mem_image, Set.mem_singleton_iff, pure_bind, - Functor.map_map, Function.comp, map_pure] at hvout - skip - + · rcases vAccepted with _ | vAccepted + · simp [Option.getM] at hvout + have hvoutBase := support_simulateQ_run_fst_subset + (impl + QueryImpl.liftTarget (StateT σ ProbComp) + (ProtocolSpec.challengeQueryImpl (pSpec := outerPSpec F n params))) hvout + letI : + (i : (outerPSpec F n params).ChallengeIdx) → + OracleInterface ((outerPSpec F n params).Challenge i) := + ProtocolSpec.challengeOracleInterface + letI : LawfulMonad (OracleComp (oSpec + [(outerPSpec F n params).Challenge]ₒ)) := + OracleComp.instLawfulMonad _ + change some out ∈ support + ((_ <$> (failure : + OptionT (OracleComp (oSpec + [(outerPSpec F n params).Challenge]ₒ)) _)).run) at hvoutBase + rw [OptionT.run_map, OptionT.run_failure] at hvoutBase + rw [support_map, support_pure, Set.mem_image] at hvoutBase + obtain ⟨a, ha, hmap⟩ := hvoutBase + rw [Set.mem_singleton_iff] at ha + subst a + simp at hmap + · -- The honest output: `out` pairs the prover view with the verifier's accepted statement. + rw [show (some (some vAccepted), sv).1 = some (some vAccepted) by rfl] at hvout + simp only [Option.getM_some, map_pure] at hvout + obtain ⟨rfl, rfl⟩ := hvout + simp at hverify + have hverifyBase := support_simulateQ_run_fst_subset + (impl + QueryImpl.liftTarget (StateT σ ProbComp) + (ProtocolSpec.challengeQueryImpl (pSpec := outerPSpec F n params))) hverify + letI : + (i : (outerPSpec F n params).ChallengeIdx) → + OracleInterface ((outerPSpec F n params).Challenge i) := + ProtocolSpec.challengeOracleInterface + letI : LawfulMonad (OracleComp (oSpec + [(outerPSpec F n params).Challenge]ₒ)) := + OracleComp.instLawfulMonad _ + dsimp only [OptionT.mk] at hverifyBase + rw [@map_eq_pure_bind _ _ _ (OracleComp.instMonad _) (OracleComp.instLawfulMonad _) _ _] + at hverifyBase + erw [@simulateQ_bind _ _ _ _ _ (OracleComp.instMonad _) + _ (OracleComp.instLawfulMonad _) _ _] at hverifyBase + simp only [simulateQ_pure] at hverifyBase + obtain ⟨foldOut, hfoldSim, hfoldPure⟩ := support_bind_exists _ _ hverifyBase + have hfoldPureEq := support_pure_eq + (m := OracleComp (oSpec + [(outerPSpec F n params).Challenge]ₒ)) hfoldPure + cases foldOut with + | none => + simp at hfoldPureEq + | some foldUnit => + cases foldUnit + simp only [Option.map_some, Option.some.injEq] at hfoldPureEq + subst vAccepted + have hfoldRawRun := mem_support_of_mem_support_liftComp _ _ hfoldSim + let transcript0 : ProtocolSpec.Transcript (0 : Fin 5) (outerPSpec F n params) := + default + let transcript1 : ProtocolSpec.Transcript (1 : Fin 5) (outerPSpec F n params) := + ProtocolSpec.Transcript.concat (m := (0 : Fin 4)) + (honestMultiplicity oStmt) transcript0 + let transcript2 : ProtocolSpec.Transcript (2 : Fin 5) (outerPSpec F n params) := + ProtocolSpec.Transcript.concat (m := (1 : Fin 4)) xval transcript1 + let transcript3 : ProtocolSpec.Transcript (3 : Fin 5) (outerPSpec F n params) := + ProtocolSpec.Transcript.concat (m := (2 : Fin 4)) + (honestHelpers params oStmt xval) transcript2 + let transcript : ProtocolSpec.Transcript (4 : Fin 5) (outerPSpec F n params) := + ProtocolSpec.Transcript.concat (m := (3 : Fin 4)) zlam transcript3 + have hfoldRaw' : () ∈ support (List.foldlM + (m := OptionT (OracleComp oSpec)) + (fun (_ : Unit) (a : Hypercube n) => + if xval + evalOnHypercube (tableOracle oStmt) a = 0 then + failure + else pure ()) + () Finset.univ.toList) := by + rw [OptionT.mem_support_iff] + simpa [transcript, transcript3, transcript2, transcript1, transcript0, tableOracle, + ProtocolSpec.FullTranscript.challenges, ProtocolSpec.Transcript.concat, + outerChallengeXIdx, Fin.snoc] using hfoldRawRun + have hguard : + ∀ u ∈ (Finset.univ : Finset (Hypercube n)).toList, + xval + evalOnHypercube (tableOracle oStmt) u ≠ 0 := + guarded_foldlM_succeeds + (spec := oSpec) + (Finset.univ.toList) + (fun a : Hypercube n => xval + evalOnHypercube (tableOracle oStmt) a = 0) + () + hfoldRaw' + have hNoTablePoles : + ∀ u : Hypercube n, xval + evalOnHypercube (tableOracle oStmt) u ≠ 0 := by + intro u + exact hguard u (by simp) + have hcols : ∀ j : Fin M, ∀ u : Hypercube n, ∃ v : Hypercube n, + evalOnHypercube (columnOracle oStmt j) u = + evalOnHypercube (tableOracle oStmt) v := by + simpa [inputRelation] using hIn + have hchar : ∀ a : F, lookupMultiplicityCount oStmt a ≠ 0 → + (tableMultiplicityCount oStmt a : F) ≠ 0 := by + intro a hlookup + classical + have hlookupCard : + ((Finset.univ : Finset (Fin M × Hypercube n)).filter fun ix => + evalOnHypercube (columnOracle oStmt ix.1) ix.2 = a).card ≠ 0 := by + simpa [lookupMultiplicityCount] using hlookup + obtain ⟨⟨j, u⟩, hju⟩ := Finset.card_ne_zero.mp hlookupCard + simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hju + obtain ⟨v, hv⟩ := hcols j u + have hvTable : evalOnHypercube (tableOracle oStmt) v = a := hv.symm.trans hju + have htablePos : 0 < tableMultiplicityCount oStmt a := by + rw [tableMultiplicityCount, Finset.card_pos] + exact ⟨v, by simp [hvTable]⟩ + have hMpos : 0 < M := lt_of_le_of_lt (Nat.zero_le j.val) j.isLt + have htable_le_card : + tableMultiplicityCount oStmt a ≤ Fintype.card (Hypercube n) := by + rw [tableMultiplicityCount, ← Finset.card_univ] + exact Finset.card_filter_le _ _ + have hcard_hypercube : Fintype.card (Hypercube n) = 2 ^ n := by + simp [Hypercube] + have hpow_le : 2 ^ n ≤ M * 2 ^ n := by + have hMone : 1 ≤ M := Nat.succ_le_of_lt hMpos + nth_rewrite 1 [← Nat.one_mul (2 ^ n)] + exact Nat.mul_le_mul_right (2 ^ n) hMone + have htable_lt_char : tableMultiplicityCount oStmt a < ringChar F := by + calc + tableMultiplicityCount oStmt a ≤ Fintype.card (Hypercube n) := htable_le_card + _ = 2 ^ n := hcard_hypercube + _ ≤ M * 2 ^ n := hpow_le + _ < ringChar F := stmt.charLarge + intro hzero + have hdvd : ringChar F ∣ tableMultiplicityCount oStmt a := + (ringChar.spec F (tableMultiplicityCount oStmt a)).1 hzero + exact (Nat.not_dvd_of_pos_of_lt htablePos htable_lt_char) hdvd + have hpoles : ∀ (i : TermIdx M) (u : Hypercube n), termPhi oStmt xval i u ≠ 0 := by + intro i u + cases hti : termToInput i with + | table => + rw [termPhi, hti] + simpa [phi, tableOracle] using hNoTablePoles u + | column j => + obtain ⟨v, hv⟩ := hcols j u + rw [termPhi, hti, phi] + simpa [hv] using hNoTablePoles v + let stmtAfter : StmtAfterOuter F n M params := + { xChallenge := xval, zChallenge := zlam.1, batchingScalars := zlam.2 } + let oStmtAfter : ∀ i, OStmtAfterOuter F n M params i := + fun + | .input i => oStmt i + | .multiplicity => honestMultiplicity oStmt + | .helpers => honestHelpers params oStmt xval + have hmid : ((stmtAfter, oStmtAfter), ()) ∈ logupMidRelation F n M params := by + constructor + · exact logupRowsAgree params Fact.out stmtAfter oStmtAfter + · simpa [stmtAfter, oStmtAfter, logupOuterSumcheckClaim] using + logupOuterClaim_zero (F := F) (n := n) (M := M) params oStmt xval + zlam.1 zlam.2 hcols hchar hpoles + constructor + · intro _; trivial + · intro _ + simp [outerVerifier, outerChallengeXIdx, outerChallengeBatchIdx, + ProtocolSpec.FullTranscript.challenges, ProtocolSpec.FullTranscript.messages, + ProtocolSpec.Transcript.concat, Fin.snoc] + constructor + · convert hmid using 2 + apply Prod.ext + · rfl + · funext i + cases i <;> simp [oStmtAfter, outerMultiplicityMessageIdx, + outerHelpersMessageIdx] + · funext i + cases i <;> simp [outerMultiplicityMessageIdx, + outerHelpersMessageIdx] + /-- Lens-completeness for the LogUp→Sumcheck lens: `proj` is the zero-sum instance (`logupSumcheckRelationInput_of_rowsAgree`), `lift` is trivial as `outputRelation = univ`. -/ @@ -499,6 +656,7 @@ instance logupSumcheckLensComplete : intro _ _ _ _ _ _ _ simp [outputRelation] +omit [Fintype F] in /-- Completeness of the embedded sumcheck phase: it carries `logupMidRelation` to `outputRelation` with no extra error, by reusing the generic sumcheck's perfect completeness through the LogUp-to-Sumcheck context lens. -/ From b4713f35f3194af4961a522c804dcd38cd6a767d Mon Sep 17 00:00:00 2001 From: jCabala Date: Fri, 19 Jun 2026 11:42:33 +0100 Subject: [PATCH 03/12] feat: Added more elaborate comments and linked the paper --- ArkLib/ProofSystem/Logup/Common.lean | 11 ++++++++++- ArkLib/ProofSystem/Logup/Protocol.lean | 16 +++++++++++++++- .../Logup/Security/Completeness.lean | 8 ++------ .../ProofSystem/Logup/Security/Soundness.lean | 3 ++- ArkLib/ProofSystem/Logup/Sumcheck/Security.lean | 13 ++++++++++--- .../Logup/Sumcheck/SumcheckBridge.lean | 17 +++++++++++++++-- .../Logup/Sumcheck/SumcheckPolynomial.lean | 11 ++++++++--- 7 files changed, 62 insertions(+), 17 deletions(-) diff --git a/ArkLib/ProofSystem/Logup/Common.lean b/ArkLib/ProofSystem/Logup/Common.lean index c270a6615e..2502f0357d 100644 --- a/ArkLib/ProofSystem/Logup/Common.lean +++ b/ArkLib/ProofSystem/Logup/Common.lean @@ -10,7 +10,16 @@ import ArkLib.OracleReduction.Basic /-! # LogUp Common Definitions -Domain types, protocol statements, and the outer-round algebra shared across all LogUp modules. +Shared vocabulary for the LogUp lookup argument formalization, following Protocol 2 of Haböck's +LogUp paper (Cryptology ePrint Archive, Paper 2022/1530, +). + +The protocol checks that every value in the `M` lookup-column oracles occurs somewhere in the table +oracle. Rows are indexed by the signed hypercube `H = {±1}^n`, represented here as bit vectors and +embedded into a field by `signPoint`. The table oracle and lookup-column oracles are stored in +Lagrange form, so a query at `z : Fin n → F` is the inner product against the kernel `L_H(., z)`. + +This file collects the common definitions and algebraic tools reused by the other LogUp files. -/ namespace Logup diff --git a/ArkLib/ProofSystem/Logup/Protocol.lean b/ArkLib/ProofSystem/Logup/Protocol.lean index e36f325ded..c0d10dc6bc 100644 --- a/ArkLib/ProofSystem/Logup/Protocol.lean +++ b/ArkLib/ProofSystem/Logup/Protocol.lean @@ -4,7 +4,21 @@ import ArkLib.ProofSystem.Logup.Sumcheck.SumcheckBridge /-! # LogUp Protocol -Outer and full protocol specs, prover, verifier, and oracle reductions for LogUp Protocol 2. +Protocol specs, honest prover, verifier, and oracle reductions for Protocol 2 of Haböck's LogUp +lookup argument (Cryptology ePrint Archive, Paper 2022/1530, +). + +The formalization is split into two ArkLib reductions. + +* The outer LogUp phase sends the honest multiplicity oracle `m`, samples the logarithmic-derivative + challenge `x`, sends helper oracles for the partial-sum groups, and samples the batching + challenge `(z, lambda)`. These challenges and retained oracles define the zero-sum claim that the + next phase checks. +* The embedded phase reuses ArkLib's generic sumcheck reduction through the LogUp sumcheck layer in + `Logup/Sumcheck/`. + +The main exported objects are `outerOracleReduction`, `sumcheckOracleReduction`, and +`logupOracleReduction`. -/ namespace Logup diff --git a/ArkLib/ProofSystem/Logup/Security/Completeness.lean b/ArkLib/ProofSystem/Logup/Security/Completeness.lean index 92f88d7c32..74fd48a75c 100644 --- a/ArkLib/ProofSystem/Logup/Security/Completeness.lean +++ b/ArkLib/ProofSystem/Logup/Security/Completeness.lean @@ -6,12 +6,8 @@ import ArkLib.ProofSystem.Logup.Sumcheck.Security /-! # LogUp Completeness -Main completeness statement for the LogUp protocol. - -`logupOracleReduction` is `outerOracleReduction ++ₚ sumcheckOracleReduction`, so completeness is -proved compositionally via `append_completeness`: the outer phase reaches `logupMidRelation` (with -the pole-rejection error), the embedded sumcheck phase carries it to `outputRelation` with no extra -error. The two halves are the remaining obligations. +Completeness statements for Protocol 2 of Haböck's LogUp lookup argument (Cryptology ePrint +Archive, Paper 2022/1530, ). -/ open scoped NNReal diff --git a/ArkLib/ProofSystem/Logup/Security/Soundness.lean b/ArkLib/ProofSystem/Logup/Security/Soundness.lean index 2c13f9e5dc..810b0722d7 100644 --- a/ArkLib/ProofSystem/Logup/Security/Soundness.lean +++ b/ArkLib/ProofSystem/Logup/Security/Soundness.lean @@ -4,7 +4,8 @@ import ArkLib.ProofSystem.Logup.Protocol /-! # LogUp Soundness -Main soundness statement for Protocol 2 of `paper.txt`. +Soundness target for Protocol 2 of Haböck's LogUp lookup argument (Cryptology ePrint Archive, +Paper 2022/1530, ). -/ open scoped NNReal diff --git a/ArkLib/ProofSystem/Logup/Sumcheck/Security.lean b/ArkLib/ProofSystem/Logup/Sumcheck/Security.lean index 3718a88fa3..d6a723ce66 100644 --- a/ArkLib/ProofSystem/Logup/Sumcheck/Security.lean +++ b/ArkLib/ProofSystem/Logup/Sumcheck/Security.lean @@ -4,10 +4,17 @@ import ArkLib.ProofSystem.Sumcheck.Spec.General /-! # Full Oracle Sum-check Completeness (LogUp-local) -Perfect completeness for the full oracle sum-check reduction, which LogUp's embedded phase needs but -`General.lean` does not provide (it has the full *non-oracle* and single-round oracle versions only). +LogUp-local completeness bridge for the full oracle sumcheck reduction used by Protocol 2 of +Haböck's LogUp paper (Cryptology ePrint Archive, Paper 2022/1530, +). -Placeholder: currently a `sorry`, to be replaced by the proved theorem and ideally upstreamed. +`ArkLib.ProofSystem.Sumcheck.Spec.General` provides the full non-oracle and single-round oracle +perfect-completeness theorems. LogUp needs the full composed oracle version for its embedded +sumcheck phase, so this file states the composed theorem by chaining the single-round result with +`OracleReduction.seqCompose_perfectCompleteness`. + +This file is intentionally LogUp-local for now; the theorem is a candidate for upstreaming into the +generic sumcheck development. -/ namespace Sumcheck diff --git a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean index 4e283ff536..af37f67c8d 100644 --- a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean +++ b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean @@ -4,8 +4,21 @@ import ArkLib.ProofSystem.Sumcheck.Spec.General /-! # LogUp Sumcheck Bridge -Packages `logupQPolynomial` from `SumcheckPolynomial.lean` into ArkLib's Sumcheck interface -types, then connects it to the generic Sumcheck relation, verifier, reduction, and context lift. +Bridge from the LogUp outer transcript to ArkLib's generic sumcheck reduction, for Protocol 2 of +Haböck's LogUp paper (Cryptology ePrint Archive, Paper 2022/1530, +). + +The outer phase produces an `x` challenge, batching data `(z, lambda)`, the original input oracles, +the honest multiplicity oracle, and helper oracles. From these data, `SumcheckPolynomial.lean` +constructs the LogUp polynomial `Q`. This file packages `Q` as the single bounded-degree oracle +statement expected by `Sumcheck.Spec`, states the zero-sum relation that starts the embedded +sumcheck, and defines the context lens/reduction that runs generic sumcheck after the outer phase. + +The main bridge predicates are: + +* `logupMidRelation`, the handoff relation from outer LogUp into sumcheck; +* `logupSumcheckRelationInput`, the corresponding generic sumcheck initial relation; and +* `logupOutputRelationFromSumcheck`, the final LogUp check after sumcheck. -/ namespace Logup diff --git a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean index 344bc0f4d7..4117df6433 100644 --- a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean +++ b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean @@ -4,9 +4,14 @@ import ArkLib.ProofSystem.Logup.Common /-! # LogUp Sumcheck Polynomial -Constructs `logupQPolynomial`, an `MvPolynomial (Fin n) F` whose restriction to the signed -hypercube agrees with `qOnHypercube` from `Common.lean`, and proves its individual degree is -at most `M + 3`. Depends only on `Common.lean` and Mathlib — no ArkLib Sumcheck types. +Construction of the concrete polynomial used by LogUp's embedded sumcheck, following Protocol 2 of +Haböck's LogUp paper (Cryptology ePrint Archive, Paper 2022/1530, +). + +The outer LogUp algebra defines a row-wise expression `qOnHypercube`. This file builds its +multilinear-extension-style polynomial `logupQPolynomial : MvPolynomial (Fin n) F`, proves that it +agrees with `qOnHypercube` on the signed hypercube, and proves the individual-degree bound +`degreeOf ≤ M + 3` needed by the generic sumcheck. -/ namespace Logup From 74e47909f7dbbdfb0b3901f8b6406b0916039ed5 Mon Sep 17 00:00:00 2001 From: jCabala Date: Sat, 20 Jun 2026 11:16:21 +0100 Subject: [PATCH 04/12] feat: Elaborated comments and moved sumcheck security into sumcheck directory --- ArkLib/ProofSystem/Logup/Common.lean | 76 ++-- ArkLib/ProofSystem/Logup/Protocol.lean | 318 ++++++++++++--- .../Logup/Security/Completeness.lean | 366 ++++++++---------- .../ProofSystem/Logup/Security/Soundness.lean | 3 +- .../ProofSystem/Logup/Sumcheck/Security.lean | 45 --- .../Logup/Sumcheck/SumcheckBridge.lean | 208 ++++------ .../Logup/Sumcheck/SumcheckPolynomial.lean | 360 ++++++----------- ArkLib/ProofSystem/Sumcheck/Spec/General.lean | 9 + 8 files changed, 675 insertions(+), 710 deletions(-) delete mode 100644 ArkLib/ProofSystem/Logup/Sumcheck/Security.lean diff --git a/ArkLib/ProofSystem/Logup/Common.lean b/ArkLib/ProofSystem/Logup/Common.lean index 2502f0357d..d55dd6f6a5 100644 --- a/ArkLib/ProofSystem/Logup/Common.lean +++ b/ArkLib/ProofSystem/Logup/Common.lean @@ -4,6 +4,7 @@ import Mathlib.Data.Fin.Basic import Mathlib.Data.Finset.Basic import Mathlib.Data.Fintype.Basic import Mathlib.Tactic +import ArkLib.Data.MvPolynomial.Multilinear import ArkLib.OracleReduction.Composition.Sequential.Append import ArkLib.OracleReduction.Basic @@ -14,11 +15,6 @@ Shared vocabulary for the LogUp lookup argument formalization, following Protoco LogUp paper (Cryptology ePrint Archive, Paper 2022/1530, ). -The protocol checks that every value in the `M` lookup-column oracles occurs somewhere in the table -oracle. Rows are indexed by the signed hypercube `H = {±1}^n`, represented here as bit vectors and -embedded into a field by `signPoint`. The table oracle and lookup-column oracles are stored in -Lagrange form, so a query at `z : Fin n → F` is the inner product against the kernel `L_H(., z)`. - This file collects the common definitions and algebraic tools reused by the other LogUp files. -/ @@ -28,41 +24,19 @@ universe u open scoped BigOperators -/-- The boolean hypercube with `2^n` points. - -The paper writes this domain as `H = {±1}^n`; we use bit vectors as row indices and embed -them into a field by `bitToSign`. --/ +/-- The Boolean hypercube with `2^n` points. -/ @[reducible] def Hypercube (n : ℕ) : Type := Fin n → Fin 2 -/-- Embed a hypercube bit as a `{±1}` field element. -/ -def bitToSign (F : Type u) [One F] [Neg F] (b : Fin 2) : F := - if b = 0 then -1 else 1 - -/-- Embed a hypercube row as a point of `{±1}^n ⊂ Fⁿ`. -/ -def signPoint (F : Type u) [One F] [Neg F] {n : ℕ} (u : Hypercube n) : Fin n → F := - fun j => bitToSign F (u j) - -/-- The Lagrange kernel `L_H(x, z) = 2^{-n} * ∏ᵢ (1 + xᵢ zᵢ)` for `H = {±1}^n`. -/ -noncomputable def lagrangeKernelAtPoint (F : Type u) [Field F] {n : ℕ} - (x z : Fin n → F) : F := - ((2 : F) ^ n)⁻¹ * ∏ j : Fin n, (1 + x j * z j) - -/-- The Lagrange kernel with the first argument restricted to a hypercube row. -/ -noncomputable def lagrangeKernel (F : Type u) [Field F] {n : ℕ} - (u : Hypercube n) (z : Fin n → F) : F := - lagrangeKernelAtPoint F (signPoint F u) z - -/-- A Lagrange oracle is a function on `H`; queries ask for inner products with `L_H(., z)`. -/ +/-- A Lagrange oracle is a function on `H`; queries evaluate its Boolean MLE. -/ structure LagrangeOracle (F : Type u) (n : ℕ) where values : Hypercube n → F instance {F : Type u} {n : ℕ} : CoeFun (LagrangeOracle F n) (fun _ => Hypercube n → F) where coe oracle := oracle.values -/-- A multilinear oracle over the row variables of `H = {±1}^n`, represented in Lagrange form. -/ +/-- A multilinear oracle over Boolean row variables, represented in Lagrange form. -/ @[reducible] def MultilinearOracle (F : Type u) (n : ℕ) : Type u := LagrangeOracle F n @@ -72,19 +46,26 @@ def evalOnHypercube {F : Type u} {n : ℕ} (p : MultilinearOracle F n) (u : Hypercube n) : F := p u -/-- Lagrange-oracle queries are exactly the paper's inner products with `L_H(., z)`. -/ +/-- Lagrange-oracle queries evaluate the Boolean multilinear extension at `z`. -/ noncomputable instance instLagrangeOracleInterface {F : Type u} [Field F] {n : ℕ} : OracleInterface (LagrangeOracle F n) where Query := Fin n → F toOC.spec := (Fin n → F) →ₒ F toOC.impl z := do let oracle ← read - return ∑ u : Hypercube n, oracle u * lagrangeKernel F u z + return MvPolynomial.eval z (MvPolynomial.MLE oracle.values) /-- The semantic value returned by querying a Lagrange-form multilinear oracle at `z`. -/ noncomputable def lagrangeOracleEval {F : Type u} [Field F] {n : ℕ} (oracle : MultilinearOracle F n) (z : Fin n → F) : F := - ∑ u : Hypercube n, oracle u * lagrangeKernel F u z + MvPolynomial.eval z (MvPolynomial.MLE oracle.values) + +/-- Querying a Boolean-hypercube row returns the stored value at that row. -/ +theorem lagrangeOracleEval_hypercube {F : Type u} [Field F] {n : ℕ} + (oracle : MultilinearOracle F n) (u : Hypercube n) : + lagrangeOracleEval oracle (u : Fin n → F) = evalOnHypercube oracle u := by + change MvPolynomial.eval (u : Fin n → F) (MvPolynomial.MLE oracle.values) = oracle.values u + simp /-- Input oracle labels for Protocol 2: one table oracle and `M` lookup-column oracles. -/ inductive InputOracleIdx (M : ℕ) where @@ -132,16 +113,10 @@ def group {M : ℕ} (params : ProtocolParams M) (k : Fin params.numGroups) : end ProtocolParams -/-- Public parameter assumptions for Protocol 2. - -The paper fixes a finite field with characteristic larger than `M * 2^n`; we also record that -`-1` and `1` are distinct so that `{±1}^n` is genuinely a Boolean hypercube in the field. --/ +/-- Public parameter assumptions for Protocol 2. -/ structure StmtIn (F : Type u) [Field F] [Fintype F] (n M : ℕ) : Type u where /-- The paper's characteristic condition `char(F) > M * 2^n`. -/ charLarge : M * 2 ^ n < ringChar F - /-- The two signs used for the hypercube are distinct. -/ - signsDistinct : (-1 : F) ≠ 1 /-- Input oracle statements: the table `t` and lookup columns `fᵢ`, as multilinear oracles. -/ @[reducible, simp] @@ -225,7 +200,7 @@ variable (F : Type) [Field F] [Fintype F] (n M : ℕ) (params : ProtocolParams M structure StmtAfterOuter where /-- The logarithmic-derivative challenge `x`. -/ xChallenge : F - /-- The Lagrange-kernel point `z ∈ Fⁿ`. -/ + /-- The equality-kernel point `z ∈ Fⁿ`. -/ zChallenge : Fin n → F /-- The batching scalars `λ₁, ..., λ_K`. -/ batchingScalars : Fin params.numGroups → F @@ -283,7 +258,7 @@ structure OuterTranscriptData (F : Type) [Field F] [Fintype F] (n M : ℕ) xChallenge : F /-- The helper functions `h₁, ..., h_K : H → F`. -/ helpers : HelperMessages F n params.numGroups - /-- The Lagrange-kernel point `z ∈ Fⁿ`. -/ + /-- The equality-kernel point `z ∈ Fⁿ`. -/ zChallenge : Fin n → F /-- The batching scalars `λ₁, ..., λ_K`. -/ batchingScalars : Fin params.numGroups → F @@ -369,7 +344,8 @@ noncomputable def qOnHypercube (groups : PartialSumGroups M K) (batchingScalars : Fin K → F) (u : Hypercube n) : F := ∑ k : Fin K, ( evalOnHypercube (helpers k) u + - lagrangeKernel F u zChallenge * batchingScalars k * + MvPolynomial.eval (u : Fin n → F) (MvPolynomial.eqPolynomial zChallenge) * + batchingScalars k * domainIdentityTerm groups oStmt multiplicity helpers xChallenge k u) /-- Point evaluations queried by the verifier at the final sumcheck point `r`. -/ @@ -383,6 +359,16 @@ structure PointEvaluations (F : Type) (M K : ℕ) where /-- Claimed values `hₖ(r)`. -/ helpers : Fin K → F +/-- Semantic agreement between final oracle-query answers and the retained LogUp oracles. -/ +def logupPointEvaluationsAgree + (r : Fin n → F) + (oStmt : ∀ i, OStmtAfterOuter F n M params i) + (evals : PointEvaluations F M params.numGroups) : Prop := + evals.multiplicity = lagrangeOracleEval (oStmt .multiplicity) r ∧ + evals.table = lagrangeOracleEval (oStmt (.input .table)) r ∧ + (∀ i : Fin M, evals.columns i = lagrangeOracleEval (oStmt (.input (.column i))) r) ∧ + ∀ k : Fin params.numGroups, evals.helpers k = lagrangeOracleEval ((oStmt .helpers) k) r + /-- Denominator term at the final sumcheck point, reconstructed from oracle query answers. -/ def phiAtPoint (xChallenge : F) (evals : PointEvaluations F M K) : InputOracleIdx M → F @@ -410,13 +396,13 @@ noncomputable def domainIdentityAtPoint (groups : PartialSumGroups M K) termNumeratorAtPoint evals i * ∏ j ∈ (groups k).erase i, termPhiAtPoint xChallenge evals j -/-- The verifier's final check value `Q(L_H(r,z), m(r), φᵢ(r), hₖ(r))` from paper (19). -/ +/-- The verifier's final check value `Q(eq(r,z), m(r), φᵢ(r), hₖ(r))` from paper (19). -/ noncomputable def qAtPoint (groups : PartialSumGroups M K) (xChallenge : F) (zChallenge rChallenge : Fin n → F) (batchingScalars : Fin K → F) (evals : PointEvaluations F M K) : F := ∑ k : Fin K, ( evals.helpers k + - lagrangeKernelAtPoint F rChallenge zChallenge * batchingScalars k * + MvPolynomial.eval rChallenge (MvPolynomial.eqPolynomial zChallenge) * batchingScalars k * domainIdentityAtPoint groups xChallenge evals k) /-- Predicate for paper step 4: final oracle-query answers match the sumcheck's expected value. -/ diff --git a/ArkLib/ProofSystem/Logup/Protocol.lean b/ArkLib/ProofSystem/Logup/Protocol.lean index c0d10dc6bc..3ae0043a98 100644 --- a/ArkLib/ProofSystem/Logup/Protocol.lean +++ b/ArkLib/ProofSystem/Logup/Protocol.lean @@ -8,17 +8,59 @@ Protocol specs, honest prover, verifier, and oracle reductions for Protocol 2 of lookup argument (Cryptology ePrint Archive, Paper 2022/1530, ). -The formalization is split into two ArkLib reductions. +The protocol checks that every value in the `M` lookup-column oracles occurs somewhere in the table +oracle. + +## Protocol Specification + +Protocol 2 is parameterized by: +- a field `F`; +- a row dimension `n`, with rows indexed by the Boolean hypercube; +- `M` lookup-column oracles `fᵢ : H → F` and one table oracle `t : H → F`; +- a partial-sum size `ell`, stored in `ProtocolParams`, which determines the number `K` of helper + oracles; and +- the characteristic condition `char(F) > M * 2^n`. + +The input relation says that every value appearing in any lookup column also appears somewhere in +the table. The protocol proves this relation as follows. + +1. The prover sends the normalized multiplicity oracle `m`, which records how often table values are + used by the lookup columns. The verifier samples the logarithmic-derivative challenge `x`. +2. Using `x`, the prover sends helper oracles `h₁, ..., h_K` for the partial-sum groups of the + logarithmic-derivative identity. +3. The verifier samples a point `z : Fin n → F` and batching scalars `λ₁, ..., λ_K`. These data turn + the helper identities into one batched polynomial `Q`, and the prover and verifier run generic + sumcheck on the claim `∑ u ∈ H, Q(u) = 0`. +4. Sumcheck leaves a final point `r` and a claimed value `v = Q(r)`. The verifier queries the + retained LogUp oracles at `r`, reconstructs `Q(r)`, and accepts only if this reconstructed value + equals `v`. + +### Formalization + +The LogUp paper writes the row domain as `{±1}^n`; this formalization uses the affine-equivalent +Boolean hypercube together with ArkLib's existing equality polynomial and +[`MLE`](ArkLib/Data/MvPolynomial/Multilinear.lean) definitions. + +The formalization is split into three ArkLib reductions. + +* The outer LogUp phase sends the multiplicity oracle `m`, samples the challenge `x`, sends the + helper oracles, and samples the batching challenge `(z, lambda)`. At the end of this phase, the + verifier has the data needed to state a single zero-sum claim about the LogUp polynomial `Q`. +* The sumcheck phase turns the values retained from the outer phase into the polynomial `Q`, then + runs ArkLib's generic sumcheck protocol on the claim that `Q` sums to zero. The translation tools + and polynomial construction live in the + [`Logup/Sumcheck`](ArkLib/ProofSystem/Logup/Sumcheck/) directory, with the bridge connecting the + LogUp-specific data to the generic sumcheck input and output. +* The final zero-round phase queries the retained LogUp oracles at sumcheck's final point and checks + that those oracle values really give the value claimed by sumcheck. + +This file defines the transcript shapes; the prover-side objects for the outer, sumcheck, +final-check, and full protocol; the matching verifier-side objects; and then the oracle reductions +for each phase and for the composed protocol. + +The main artefacts that define the protocol are `pSpec`, `logupProver`, `logupVerifier`, +and `logupOracleReduction`. -* The outer LogUp phase sends the honest multiplicity oracle `m`, samples the logarithmic-derivative - challenge `x`, sends helper oracles for the partial-sum groups, and samples the batching - challenge `(z, lambda)`. These challenges and retained oracles define the zero-sum claim that the - next phase checks. -* The embedded phase reuses ArkLib's generic sumcheck reduction through the LogUp sumcheck layer in - `Logup/Sumcheck/`. - -The main exported objects are `outerOracleReduction`, `sumcheckOracleReduction`, and -`logupOracleReduction`. -/ namespace Logup @@ -80,37 +122,67 @@ instance instOuterPSpecChallengeSampleable end ProtocolSpec +section FinalCheckSpec + +/-- The final LogUp point check has no transcript messages; it only queries retained oracles. -/ +@[reducible] +def finalCheckPSpec : ProtocolSpec 0 := + !p[] + +end FinalCheckSpec + section FullProtocolSpec open ProtocolSpec -/-- Protocol 2 transcript shape: the outer LogUp messages followed by ArkLib's generic sumcheck. -/ +/-- Protocol 2 before the final point check: outer LogUp followed by generic sumcheck. -/ @[reducible] -noncomputable def pSpec (F : Type) [Field F] [Fintype F] [DecidableEq F] - (n M : ℕ) (params : ProtocolParams M) : - ProtocolSpec (4 + Fin.vsum (fun _ : Fin n => 2)) := +noncomputable def pSpecBeforeFinal (F : Type) [Field F] [Fintype F] [DecidableEq F] + (n M : ℕ) (params : ProtocolParams M) := outerPSpec F n params ++ₚ logupSumcheckPSpec F n M params -/-- The full LogUp prover messages are oracle-accessible: outer LogUp messages followed by the +/-- Protocol 2 transcript shape: outer LogUp, ArkLib's generic sumcheck, and the final point check. +-/ +@[reducible] +noncomputable def pSpec (F : Type) [Field F] [Fintype F] [DecidableEq F] + (n M : ℕ) (params : ProtocolParams M) := + pSpecBeforeFinal F n M params ++ₚ finalCheckPSpec + +/-- The prover messages before the final check are oracle-accessible: outer LogUp followed by the embedded sumcheck messages. -/ +noncomputable instance instPSpecBeforeFinalMessageOracleInterface + {F : Type} [Field F] [Fintype F] [DecidableEq F] {n M : ℕ} + {params : ProtocolParams M} : + ∀ i, OracleInterface ((pSpecBeforeFinal F n M params).Message i) := by + unfold pSpecBeforeFinal + exact ProtocolSpec.instOracleInterfaceMessageAppend + +/-- The full LogUp prover messages are oracle-accessible: outer LogUp and sumcheck messages; the +final point check has no messages. -/ noncomputable instance instPSpecMessageOracleInterface {F : Type} [Field F] [Fintype F] [DecidableEq F] {n M : ℕ} {params : ProtocolParams M} : ∀ i, OracleInterface ((pSpec F n M params).Message i) := by - change ∀ i, - OracleInterface (((outerPSpec F n params) ++ₚ logupSumcheckPSpec F n M params).Message i) + unfold pSpec exact ProtocolSpec.instOracleInterfaceMessageAppend -/-- The full LogUp verifier challenges are sampleable: outer LogUp challenges followed by the +/-- The verifier challenges before the final check are sampleable: outer LogUp followed by the embedded sumcheck challenges. -/ +noncomputable instance instPSpecBeforeFinalChallengeSampleable + {F : Type} [Field F] [Fintype F] [DecidableEq F] [SampleableType F] {n M : ℕ} + {params : ProtocolParams M} : + ∀ i, SampleableType ((pSpecBeforeFinal F n M params).Challenge i) := by + letI : Inhabited F := ⟨0⟩ + unfold pSpecBeforeFinal + exact ProtocolSpec.instSampleableTypeChallengeAppend + +/-- The full LogUp verifier challenges are sampleable: the final point check has no challenges. -/ noncomputable instance instPSpecChallengeSampleable {F : Type} [Field F] [Fintype F] [DecidableEq F] [SampleableType F] {n M : ℕ} {params : ProtocolParams M} : ∀ i, SampleableType ((pSpec F n M params).Challenge i) := by letI : Inhabited F := ⟨0⟩ - change ∀ i, - SampleableType (((outerPSpec F n params) ++ₚ - logupSumcheckPSpec F n M params).Challenge i) + unfold pSpec exact ProtocolSpec.instSampleableTypeChallengeAppend end FullProtocolSpec @@ -163,27 +235,92 @@ noncomputable def outerProver : end OuterProver +section ConcreteSumcheckReduction + +variable {ι : Type} (oSpec : OracleSpec ι) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) +variable (params : ProtocolParams M) + +/-- ArkLib's generic sumcheck prover specialized to LogUp's Boolean domain and degree bound. -/ +noncomputable def logupConcreteSumcheckOracleProver [SampleableType F] : + OracleProver oSpec (LogupSumcheckStmtIn F n M params) + (LogupSumcheckOracleStatement F n M params) Unit + (LogupSumcheckStmtOut F n M params) + (LogupSumcheckOracleStatement F n M params) Unit + (logupSumcheckPSpec F n M params) := + (Sumcheck.Spec.oracleReduction F (logupSumcheckDegree M params) + (booleanDomain F) n oSpec).prover + +/-- ArkLib's generic sumcheck verifier specialized to LogUp's Boolean domain and degree bound. -/ +noncomputable def logupConcreteSumcheckOracleVerifier [SampleableType F] : + OracleVerifier oSpec (LogupSumcheckStmtIn F n M params) + (LogupSumcheckOracleStatement F n M params) + (LogupSumcheckStmtOut F n M params) + (LogupSumcheckOracleStatement F n M params) + (logupSumcheckPSpec F n M params) := + Sumcheck.Spec.oracleVerifier F (logupSumcheckDegree M params) + (booleanDomain F) n oSpec + +/-- ArkLib's generic sumcheck reduction specialized to LogUp's Boolean domain and degree bound. -/ +noncomputable def logupConcreteSumcheckOracleReduction [SampleableType F] : + OracleReduction oSpec (LogupSumcheckStmtIn F n M params) + (LogupSumcheckOracleStatement F n M params) Unit + (LogupSumcheckStmtOut F n M params) + (LogupSumcheckOracleStatement F n M params) Unit + (logupSumcheckPSpec F n M params) := + Sumcheck.Spec.oracleReduction F (logupSumcheckDegree M params) + (booleanDomain F) n oSpec + +end ConcreteSumcheckReduction + section SumcheckProver variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [Fact ((-1 : F) ≠ 1)] - [SampleableType F] (n M : ℕ) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [SampleableType F] (n M : ℕ) variable (params : ProtocolParams M) /-- The prover for the embedded sumcheck phase of LogUp Protocol 2. -/ -noncomputable def sumcheckProver [SampleableType F] : +noncomputable def sumcheckProver : OracleProver oSpec (StmtAfterOuter F n M params) (OStmtAfterOuter F n M params) Unit - (StmtOut) (OStmtOut) Unit + (StmtAfterSumcheck F n M params) (OStmtAfterSumcheck F n M params) Unit (logupSumcheckPSpec F n M params) := - (sumcheckOracleReduction oSpec F n M params).prover + let lens : + OracleContext.Lens.{0, 0, 0, 0} + (StmtAfterOuter F n M params) (StmtAfterSumcheck F n M params) + (LogupSumcheckStmtIn F n M params) (LogupSumcheckStmtOut F n M params) + (OStmtAfterOuter F n M params) (OStmtAfterSumcheck F n M params) + (LogupSumcheckOracleStatement F n M params) + (LogupSumcheckOracleStatement F n M params) + Unit Unit Unit Unit := + logupSumcheckContextLens F n M params + (logupConcreteSumcheckOracleProver oSpec F n M params).liftContext lens end SumcheckProver +section FinalCheckProver + +variable {ι : Type} (oSpec : OracleSpec ι) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) +variable (params : ProtocolParams M) + +/-- The prover for the final LogUp point check; there are no messages in this phase. -/ +noncomputable def finalCheckProver : + OracleProver oSpec (StmtAfterSumcheck F n M params) (OStmtAfterSumcheck F n M params) Unit + StmtOut OStmtOut Unit + finalCheckPSpec where + PrvState := fun _ => StmtAfterSumcheck F n M params × + (∀ i, OStmtAfterSumcheck F n M params i) + input := fun ⟨ctx, _⟩ => ctx + sendMessage := fun i => Fin.elim0 i + receiveChallenge := fun i => Fin.elim0 i + output := fun _ => pure ((((), fun i => Fin.elim0 i), ())) + +end FinalCheckProver + section FullProver variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [Fact ((-1 : F) ≠ 1)] - [SampleableType F] (n M : ℕ) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [SampleableType F] (n M : ℕ) variable (params : ProtocolParams M) /-- The full LogUp prover, composed from the outer prover and embedded sumcheck prover. -/ @@ -191,7 +328,9 @@ noncomputable def logupProver : OracleProver oSpec (StmtIn F n M) (OStmtIn F n M) (WitIn F n M params) (StmtOut) (OStmtOut) Unit (pSpec F n M params) := - Prover.append (outerProver oSpec F n M params) (sumcheckProver oSpec F n M params) + Prover.append + (Prover.append (outerProver oSpec F n M params) (sumcheckProver oSpec F n M params)) + (finalCheckProver oSpec F n M params) end FullProver @@ -224,13 +363,9 @@ noncomputable def outerVerifier : (outerPSpec F n params) where verify := fun _ challenges => do let x : F := challenges (outerChallengeXIdx F n M params) - -- TODO: Replace the current table-scan rejection check with a faithful sampler - -- for x ∉ { -t(u) : u ∈ H }. - for u in (Finset.univ : Finset (Hypercube n)).toList do - let tAtU : F ← OptionT.lift <| OracleComp.liftComp - (OracleComp.lift <| OracleSpec.query - (show [OStmtIn F n M]ₒ.Domain from ⟨InputOracleIdx.table, signPoint F u⟩)) _ - guard (x + tAtU ≠ 0) + -- Following Remark 3 of the LogUp paper, the verifier samples `x` uniformly and does not + -- scan the table to reject poles. Pole challenges are treated as bad/failing inputs for the + -- honest handoff, and `Completeness.lean` accounts for that event. let batch : BatchingChallenge F n params.numGroups := challenges (outerChallengeBatchIdx F n M params) pure { xChallenge := x, zChallenge := batch.1, batchingScalars := batch.2 } @@ -257,32 +392,93 @@ end OuterVerifier section SumcheckVerifier variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [Fact ((-1 : F) ≠ 1)] (n M : ℕ) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) variable (params : ProtocolParams M) /-- The verifier for the embedded sumcheck phase of LogUp Protocol 2. -/ noncomputable def sumcheckVerifier [SampleableType F] : OracleVerifier oSpec (StmtAfterOuter F n M params) (OStmtAfterOuter F n M params) - (StmtOut) (OStmtOut) + (StmtAfterSumcheck F n M params) (OStmtAfterSumcheck F n M params) (logupSumcheckPSpec F n M params) := - (sumcheckOracleReduction oSpec F n M params).verifier + let lens : + OracleContext.Lens.{0, 0, 0, 0} + (StmtAfterOuter F n M params) (StmtAfterSumcheck F n M params) + (LogupSumcheckStmtIn F n M params) (LogupSumcheckStmtOut F n M params) + (OStmtAfterOuter F n M params) (OStmtAfterSumcheck F n M params) + (LogupSumcheckOracleStatement F n M params) + (LogupSumcheckOracleStatement F n M params) + Unit Unit Unit Unit := + logupSumcheckContextLens F n M params + (logupConcreteSumcheckOracleVerifier oSpec F n M params).liftContext lens.stmt end SumcheckVerifier +section FinalCheckVerifier + +variable {ι : Type} (oSpec : OracleSpec ι) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) +variable (params : ProtocolParams M) + +/-- Query one retained LogUp oracle during the final point check. -/ +private def finalCheckQuery + (i : OuterOracleIdx M) + (q : (instOStmtAfterOuterOracleInterface (F := F) (n := n) (params := params) i).Query) : + OptionT + (OracleComp (oSpec + ([OStmtAfterSumcheck F n M params]ₒ + [finalCheckPSpec.Message]ₒ))) + ((instOStmtAfterOuterOracleInterface (F := F) (n := n) (params := params) i).Response q) := + OptionT.lift <| OracleComp.liftComp + (OracleComp.lift <| + OracleSpec.query (show [OStmtAfterSumcheck F n M params]ₒ.Domain from ⟨i, q⟩)) + _ + +/-- The verifier's final Protocol 2 check: reconstruct `Q(r)` from retained LogUp oracle +evaluations and compare it with the expected value output by sumcheck. -/ +noncomputable def finalCheckVerifier : + OracleVerifier oSpec (StmtAfterSumcheck F n M params) (OStmtAfterSumcheck F n M params) + StmtOut OStmtOut + finalCheckPSpec where + verify := fun stmt _ => do + let r : Fin n → F := stmt.finalClaim.challenges + let expectedValue : F := stmt.finalClaim.target + let multiplicity ← finalCheckQuery oSpec F n M params .multiplicity r + let table ← finalCheckQuery oSpec F n M params (.input .table) r + let columnValues ← (Vector.finRange M).mapM + (fun i => finalCheckQuery oSpec F n M params (.input (.column i)) r) + let helperValues ← (Vector.finRange params.numGroups).mapM + (fun k => finalCheckQuery oSpec F n M params .helpers ⟨k, r⟩) + let evals : PointEvaluations F M params.numGroups := + { multiplicity := multiplicity + table := table + columns := fun i => columnValues[i] + helpers := fun k => helperValues[k] } + guard (qAtPoint (canonicalGroups params) stmt.outer.xChallenge stmt.outer.zChallenge r + stmt.outer.batchingScalars evals = expectedValue) + pure () + + embed := + { toFun := fun i => Fin.elim0 i + inj' := fun i => Fin.elim0 i } + + hEq := fun i => Fin.elim0 i + +end FinalCheckVerifier + section FullVerifier variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [Fact ((-1 : F) ≠ 1)] - [SampleableType F] (n M : ℕ) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [SampleableType F] (n M : ℕ) variable (params : ProtocolParams M) /-- The full LogUp verifier, obtained by composing the outer verifier with the embedded sumcheck -verifier. -/ +verifier and final point check. -/ noncomputable def logupVerifier : OracleVerifier oSpec (StmtIn F n M) (OStmtIn F n M) (StmtOut) (OStmtOut) (pSpec F n M params) := - OracleVerifier.append (outerVerifier oSpec F n M params) (sumcheckVerifier oSpec F n M params) + OracleVerifier.append + (OracleVerifier.append (outerVerifier oSpec F n M params) + (sumcheckVerifier oSpec F n M params)) + (finalCheckVerifier oSpec F n M params) end FullVerifier @@ -302,11 +498,43 @@ noncomputable def outerOracleReduction : end OuterReduction +section SumcheckReduction + +variable {ι : Type} (oSpec : OracleSpec ι) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [SampleableType F] (n M : ℕ) +variable (params : ProtocolParams M) + +/-- The embedded LogUp sumcheck phase, obtained by lifting ArkLib's generic Sumcheck reduction +through the LogUp-to-Sumcheck context lens. -/ +noncomputable def sumcheckOracleReduction : + OracleReduction oSpec (StmtAfterOuter F n M params) (OStmtAfterOuter F n M params) Unit + (StmtAfterSumcheck F n M params) (OStmtAfterSumcheck F n M params) Unit + (logupSumcheckPSpec F n M params) where + prover := sumcheckProver oSpec F n M params + verifier := sumcheckVerifier oSpec F n M params + +end SumcheckReduction + +section FinalCheckReduction + +variable {ι : Type} (oSpec : OracleSpec ι) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) +variable (params : ProtocolParams M) + +/-- The final LogUp point check as an ArkLib oracle reduction. -/ +noncomputable def finalCheckOracleReduction : + OracleReduction oSpec (StmtAfterSumcheck F n M params) (OStmtAfterSumcheck F n M params) Unit + (StmtOut) (OStmtOut) Unit + finalCheckPSpec where + prover := finalCheckProver oSpec F n M params + verifier := finalCheckVerifier oSpec F n M params + +end FinalCheckReduction + section FullReduction variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [Fact ((-1 : F) ≠ 1)] - [SampleableType F] (n M : ℕ) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [SampleableType F] (n M : ℕ) variable (params : ProtocolParams M) /-- The full LogUp Protocol as an ArkLib oracle reduction. -/ diff --git a/ArkLib/ProofSystem/Logup/Security/Completeness.lean b/ArkLib/ProofSystem/Logup/Security/Completeness.lean index 74fd48a75c..5063503a5d 100644 --- a/ArkLib/ProofSystem/Logup/Security/Completeness.lean +++ b/ArkLib/ProofSystem/Logup/Security/Completeness.lean @@ -1,7 +1,7 @@ import ArkLib.OracleReduction.Security.Basic import ArkLib.OracleReduction.Composition.Sequential.Append +import ArkLib.ProofSystem.Sumcheck.Spec.General import ArkLib.ProofSystem.Logup.Protocol -import ArkLib.ProofSystem.Logup.Sumcheck.Security /-! # LogUp Completeness @@ -86,7 +86,7 @@ theorem honest_multiplicity_identity (oStmt : ∀ i, OStmtIn F n M i) (x : F) / (x + a) := by intro u hu simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hu - show (lookupMultiplicityCount oStmt (evalOnHypercube (tableOracle oStmt) u) : F) / + change (lookupMultiplicityCount oStmt (evalOnHypercube (tableOracle oStmt) u) : F) / (tableMultiplicityCount oStmt (evalOnHypercube (tableOracle oStmt) u) : F) / (x + evalOnHypercube (tableOracle oStmt) u) = (lookupMultiplicityCount oStmt a : F) / (tableMultiplicityCount oStmt a : F) / (x + a) @@ -204,16 +204,8 @@ theorem logupOuterClaim_zero (params : ProtocolParams M) (oStmtIn : ∀ i, OStmt rw [← Finset.sum_add_distrib] exact Finset.sum_eq_zero (fun u _ => by ring) -omit [Fintype F] [DecidableEq F] in -/-- The polynomial `Q` agrees with `qOnHypercube` on the signed hypercube — a structural fact (no -honesty needed), from `logupQPolynomial_eval_signPoint`. -/ -theorem logupRowsAgree (params : ProtocolParams M) (hs : (-1 : F) ≠ 1) - (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) : - logupSumcheckPolynomialRowsAgree F n M params stmt oStmt := - fun u => logupQPolynomial_eval_signPoint F n M params hs stmt oStmt u - /-- The table poles `{x : ∃ u, x + t(u) = 0} = {-t(u) : u ∈ H}` number at most `|H|`. This is the -counting fact behind the pole-rejection completeness error. -/ +counting fact behind the pole-sampling completeness error. -/ theorem pole_card_le (oStmt : ∀ i, OStmtIn F n M i) : (Finset.univ.filter (fun x : F => ∃ u : Hypercube n, x + evalOnHypercube (tableOracle oStmt) u = 0)).card @@ -232,19 +224,18 @@ theorem pole_card_le (oStmt : ∀ i, OStmtIn F n M i) : rw [← Finset.card_univ]; exact Finset.card_image_le omit [Fintype F] [DecidableEq F] in -/-- A `LagrangeOracle` query at a signed-hypercube point returns the oracle's value there. -/ -theorem lagrange_answer_signPoint (hs : (-1 : F) ≠ 1) (oracle : MultilinearOracle F n) +/-- A `LagrangeOracle` query at a Boolean-hypercube point returns the oracle's value there. -/ +theorem lagrange_answer_hypercube (oracle : MultilinearOracle F n) (a : Hypercube n) : - OracleInterface.answer oracle (signPoint F a) = evalOnHypercube oracle a := - lagrangeOracleEval_signPoint F n hs oracle a + OracleInterface.answer oracle (a : Fin n → F) = evalOnHypercube oracle a := + lagrangeOracleEval_hypercube oracle a end OuterAlgebra section Completeness variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [Fact ((-1 : F) ≠ 1)] - [SampleableType F] +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [SampleableType F] variable (n M : ℕ) variable (params : ProtocolParams M) variable {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) @@ -253,9 +244,9 @@ local instance instOracleCompLawfulMonad' {τ : Type} (spec : OracleSpec τ) : LawfulMonad (OracleComp spec) := OracleComp.instLawfulMonad spec -/-- Completeness error from the current `x`-sampling model: the verifier samples `x` from all of `F` -and rejects table poles, so completeness carries this bad-`x` probability. It would be `0` if `x` -were sampled from the pole complement, as the LogUp protocol intends. -/ +/-- Completeness error from the current `x`-sampling model: the verifier samples `x` from all of +`F`. Following Remark 3 of the LogUp paper, table-pole challenges are treated as bad inputs for +the honest handoff rather than rejected by an exponential verifier scan. -/ noncomputable def logupCompletenessError (F : Type) [Fintype F] (n : ℕ) : ℝ≥0 := (Fintype.card (Hypercube n) : ℝ≥0) / (Fintype.card F) @@ -282,7 +273,7 @@ private theorem simulateQ_optionT_guard {ι : Type} {spec : OracleSpec ι} {nn : Type → Type*} [Monad nn] [LawfulMonad nn] (impl : QueryImpl spec nn) (P : Prop) [Decidable P] : simulateQ impl (guard P : OptionT (OracleComp spec) Unit) = (guard P : OptionT nn Unit) := by - show simulateQ impl (if P then pure () else failure : OptionT (OracleComp spec) Unit) + change simulateQ impl (if P then pure () else failure : OptionT (OracleComp spec) Unit) = (if P then pure () else failure : OptionT nn Unit) by_cases hP : P · rw [if_pos hP, if_pos hP]; rfl @@ -342,12 +333,11 @@ private theorem support_simulateQ_run_fst_subset {ι : Type} {spec : OracleSpec rw [StateT.run'_eq, support_map, Set.mem_image] exact ⟨(y, s'), h, rfl⟩) +set_option linter.unusedSectionVars false in open OracleComp OracleSpec in omit [SampleableType F] in -/-- Simulating the outer verifier's table queries against the honest oracles `oStmt` turns the -oracle-accessing pole-rejection scan into a query-free `evalOnHypercube` loop. This is the LogUp -analog of `Sumcheck.Spec.SingleRound.oracleVerifier_eq_verifier`: it does the `simOracle2` peel -once, so the completeness proof never has to. -/ +/-- Simulating the scan-free outer verifier against the honest oracles leaves only the public +challenge data packaged as the outer statement. -/ theorem outerVerify_simulateQ_eq (stmt : StmtIn F n M) (oStmt : ∀ i, OStmtIn F n M i) (messages : ∀ i, (outerPSpec F n params).Message i) (challenges : ∀ i, (outerPSpec F n params).Challenge i) : @@ -355,21 +345,11 @@ theorem outerVerify_simulateQ_eq (stmt : StmtIn F n M) (oStmt : ∀ i, OStmtIn F ((outerVerifier oSpec F n M params).verify stmt challenges) = (do let x : F := challenges (outerChallengeXIdx F n M params) - for u in (Finset.univ : Finset (Hypercube n)).toList do - guard (x + evalOnHypercube (tableOracle oStmt) u ≠ 0) let batch : BatchingChallenge F n params.numGroups := challenges (outerChallengeBatchIdx F n M params) pure { xChallenge := x, zChallenge := batch.1, batchingScalars := batch.2 } : OptionT (OracleComp oSpec) (StmtAfterOuter F n M params)) := by - simp only [outerVerifier, OracleInterface.simOracle2, QueryImpl.addLift_def, - simulateQ_optionT_bind, simulateQ_optionT_list_forIn, - simulateQ_optionT_guard, OracleComp.liftComp_query, OracleQuery.cont_query, OracleQuery.input_query, - OptionT.lift, OptionT.mk, id_map] - -- Reduce the table query under the `forIn` binder. - congr 1 - congr 1 - funext a b - rw [← lagrange_answer_signPoint Fact.out (tableOracle oStmt) a] + simp [outerVerifier, outerChallengeXIdx, outerChallengeBatchIdx] rfl /-- Four-round unfolding of `Fin.induction` (the analog of `Fin.induction_two`), for the outer @@ -379,16 +359,8 @@ private theorem Fin.induction_four {motive : Fin 5 → Sort*} {zero : motive 0} Fin.induction (motive := motive) zero succ (Fin.last 4) = succ 3 (succ 2 (succ 1 (succ 0 zero))) := rfl -/-- Completeness of the outer LogUp phase: the honest outer prover reaches `logupMidRelation`, -except with the pole-sampling error. - -The membership content of `logupMidRelation` is fully proved: `logupRowsAgree` (the polynomial -agreement) and `logupOuterClaim_zero` (the zero-sum claim, valid whenever `x` avoids the table -poles). What remains is the **monad-execution shell**: unfolding the honest 4-message -`Reduction.run` (prover sends `m`, gets `x`, sends helpers, gets `(z,λ)`; verifier runs the -pole-rejection guard loop), showing `{x not a pole} ⊆ {success}`, and bounding -`P(x is a pole) ≤ |H|/|F|`. This is intricate `OracleComp`/`probEvent` reasoning (cf. the proved -template `Sumcheck.Spec.SingleRound.Simpler.reduction_perfectCompleteness`), the last open piece. -/ +/-- Completeness of the outer LogUp phase: the honest outer prover reaches the zero-sum handoff +relation, except with the pole-sampling error. -/ theorem logup_outer_completeness [Inhabited F] : (outerOracleReduction oSpec F n M params).completeness init impl (inputRelation F n M) (logupMidRelation F n M params) (logupCompletenessError F n) := by @@ -401,12 +373,11 @@ theorem logup_outer_completeness [Inhabited F] : simp only [ProtocolSpec.getChallenge, liftM, monadLift, MonadLift.monadLift, MonadLiftT.monadLift, OracleComp.liftComp_pure, bind_pure_comp, map_pure, QueryImpl.addLift_def] - rw [ge_iff_le, probEvent_ext (q := fun _ => True) ?allSuccess, probEvent_True_eq_sub] - · -- pole-probability bound `Pr[⊥] ≤ |H|/|F|` - refine tsub_le_tsub_left ?_ 1 - sorry - · -- every non-failing output satisfies the success predicate - intro out hout + refine ge_trans (probEvent_mono + (p := fun out => ∀ u : Hypercube n, + out.2.1.xChallenge + evalOnHypercube (tableOracle oStmt) u ≠ 0) ?goodOutputs) ?goodProb + · -- every output with a non-pole outer challenge satisfies the success predicate + intro out hout hGood rw [OptionT.mem_support_iff] at hout simp only [OptionT.run_mk, support_bind, Set.mem_iUnion] at hout obtain ⟨s, -, hout⟩ := hout @@ -459,7 +430,8 @@ theorem logup_outer_completeness [Inhabited F] : erw [simulateQ_bind, StateT.run_bind] at hver rw [mem_support_bind_iff] at hver obtain ⟨⟨vstmt, sv⟩, hverify, hvout⟩ := hver - -- Replace the verifier's table-query pole-rejection scan by the clean `evalOnHypercube` loop. + -- The scan-free verifier packages the sampled challenges; the non-pole assumption comes from + -- the good-challenge event above. simp only [OracleVerifier.toVerifier] at hverify rw [outerVerify_simulateQ_eq] at hverify -- The verifier accepted: `vstmt = some _` (the `none` branch of `hvout` yields `none ≠ some`). @@ -467,7 +439,8 @@ theorem logup_outer_completeness [Inhabited F] : · simp only [simulateQ_pure, StateT.run_pure, support_pure, Set.mem_singleton_iff] at hvout simp at hvout · rcases vAccepted with _ | vAccepted - · simp [Option.getM] at hvout + · simp only [OStmtAfterOuter, OStmtIn, MultiplicityMessage, HelperMessages, + Nat.reduceAdd, Fin.vcons_fin_zero, BatchingChallenge] at hvout have hvoutBase := support_simulateQ_run_fst_subset (impl + QueryImpl.liftTarget (StateT σ ProbComp) (ProtocolSpec.challengeQueryImpl (pSpec := outerPSpec F n params))) hvout @@ -478,8 +451,10 @@ theorem logup_outer_completeness [Inhabited F] : letI : LawfulMonad (OracleComp (oSpec + [(outerPSpec F n params).Challenge]ₒ)) := OracleComp.instLawfulMonad _ change some out ∈ support - ((_ <$> (failure : - OptionT (OracleComp (oSpec + [(outerPSpec F n params).Challenge]ₒ)) _)).run) at hvoutBase + ((_ <$> + (failure : + OptionT (OracleComp (oSpec + [(outerPSpec F n params).Challenge]ₒ)) _)).run) + at hvoutBase rw [OptionT.run_map, OptionT.run_failure] at hvoutBase rw [support_map, support_pure, Set.mem_image] at hvoutBase obtain ⟨a, ha, hmap⟩ := hvoutBase @@ -490,7 +465,7 @@ theorem logup_outer_completeness [Inhabited F] : rw [show (some (some vAccepted), sv).1 = some (some vAccepted) by rfl] at hvout simp only [Option.getM_some, map_pure] at hvout obtain ⟨rfl, rfl⟩ := hvout - simp at hverify + simp only [OStmtAfterOuter, OStmtIn, MultiplicityMessage, HelperMessages] at hverify have hverifyBase := support_simulateQ_run_fst_subset (impl + QueryImpl.liftTarget (StateT σ ProbComp) (ProtocolSpec.challengeQueryImpl (pSpec := outerPSpec F n params))) hverify @@ -500,182 +475,161 @@ theorem logup_outer_completeness [Inhabited F] : ProtocolSpec.challengeOracleInterface letI : LawfulMonad (OracleComp (oSpec + [(outerPSpec F n params).Challenge]ₒ)) := OracleComp.instLawfulMonad _ - dsimp only [OptionT.mk] at hverifyBase - rw [@map_eq_pure_bind _ _ _ (OracleComp.instMonad _) (OracleComp.instLawfulMonad _) _ _] - at hverifyBase - erw [@simulateQ_bind _ _ _ _ _ (OracleComp.instMonad _) - _ (OracleComp.instLawfulMonad _) _ _] at hverifyBase - simp only [simulateQ_pure] at hverifyBase - obtain ⟨foldOut, hfoldSim, hfoldPure⟩ := support_bind_exists _ _ hverifyBase - have hfoldPureEq := support_pure_eq - (m := OracleComp (oSpec + [(outerPSpec F n params).Challenge]ₒ)) hfoldPure - cases foldOut with - | none => - simp at hfoldPureEq - | some foldUnit => - cases foldUnit - simp only [Option.map_some, Option.some.injEq] at hfoldPureEq - subst vAccepted - have hfoldRawRun := mem_support_of_mem_support_liftComp _ _ hfoldSim - let transcript0 : ProtocolSpec.Transcript (0 : Fin 5) (outerPSpec F n params) := - default - let transcript1 : ProtocolSpec.Transcript (1 : Fin 5) (outerPSpec F n params) := - ProtocolSpec.Transcript.concat (m := (0 : Fin 4)) - (honestMultiplicity oStmt) transcript0 - let transcript2 : ProtocolSpec.Transcript (2 : Fin 5) (outerPSpec F n params) := - ProtocolSpec.Transcript.concat (m := (1 : Fin 4)) xval transcript1 - let transcript3 : ProtocolSpec.Transcript (3 : Fin 5) (outerPSpec F n params) := - ProtocolSpec.Transcript.concat (m := (2 : Fin 4)) - (honestHelpers params oStmt xval) transcript2 - let transcript : ProtocolSpec.Transcript (4 : Fin 5) (outerPSpec F n params) := - ProtocolSpec.Transcript.concat (m := (3 : Fin 4)) zlam transcript3 - have hfoldRaw' : () ∈ support (List.foldlM - (m := OptionT (OracleComp oSpec)) - (fun (_ : Unit) (a : Hypercube n) => - if xval + evalOnHypercube (tableOracle oStmt) a = 0 then - failure - else pure ()) - () Finset.univ.toList) := by - rw [OptionT.mem_support_iff] - simpa [transcript, transcript3, transcript2, transcript1, transcript0, tableOracle, - ProtocolSpec.FullTranscript.challenges, ProtocolSpec.Transcript.concat, - outerChallengeXIdx, Fin.snoc] using hfoldRawRun - have hguard : - ∀ u ∈ (Finset.univ : Finset (Hypercube n)).toList, - xval + evalOnHypercube (tableOracle oStmt) u ≠ 0 := - guarded_foldlM_succeeds - (spec := oSpec) - (Finset.univ.toList) - (fun a : Hypercube n => xval + evalOnHypercube (tableOracle oStmt) a = 0) - () - hfoldRaw' - have hNoTablePoles : - ∀ u : Hypercube n, xval + evalOnHypercube (tableOracle oStmt) u ≠ 0 := by - intro u - exact hguard u (by simp) - have hcols : ∀ j : Fin M, ∀ u : Hypercube n, ∃ v : Hypercube n, - evalOnHypercube (columnOracle oStmt j) u = - evalOnHypercube (tableOracle oStmt) v := by - simpa [inputRelation] using hIn - have hchar : ∀ a : F, lookupMultiplicityCount oStmt a ≠ 0 → - (tableMultiplicityCount oStmt a : F) ≠ 0 := by - intro a hlookup - classical - have hlookupCard : - ((Finset.univ : Finset (Fin M × Hypercube n)).filter fun ix => - evalOnHypercube (columnOracle oStmt ix.1) ix.2 = a).card ≠ 0 := by - simpa [lookupMultiplicityCount] using hlookup - obtain ⟨⟨j, u⟩, hju⟩ := Finset.card_ne_zero.mp hlookupCard - simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hju + have hverifyEq := support_pure_eq + (m := OracleComp (oSpec + [(outerPSpec F n params).Challenge]ₒ)) hverifyBase + simp only [Option.some.injEq] at hverifyEq + subst vAccepted + have hNoTablePoles : + ∀ u : Hypercube n, xval + evalOnHypercube (tableOracle oStmt) u ≠ 0 := by + intro u + simpa [outerVerifier, outerChallengeXIdx, outerChallengeBatchIdx, + ProtocolSpec.FullTranscript.challenges, ProtocolSpec.Transcript.concat, Fin.snoc] + using hGood u + have hcols : ∀ j : Fin M, ∀ u : Hypercube n, ∃ v : Hypercube n, + evalOnHypercube (columnOracle oStmt j) u = + evalOnHypercube (tableOracle oStmt) v := by + simpa [inputRelation] using hIn + have hchar : ∀ a : F, lookupMultiplicityCount oStmt a ≠ 0 → + (tableMultiplicityCount oStmt a : F) ≠ 0 := by + intro a hlookup + classical + have hlookupCard : + ((Finset.univ : Finset (Fin M × Hypercube n)).filter fun ix => + evalOnHypercube (columnOracle oStmt ix.1) ix.2 = a).card ≠ 0 := by + simpa [lookupMultiplicityCount] using hlookup + obtain ⟨⟨j, u⟩, hju⟩ := Finset.card_ne_zero.mp hlookupCard + simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hju + obtain ⟨v, hv⟩ := hcols j u + have hvTable : evalOnHypercube (tableOracle oStmt) v = a := hv.symm.trans hju + have htablePos : 0 < tableMultiplicityCount oStmt a := by + rw [tableMultiplicityCount, Finset.card_pos] + exact ⟨v, by simp [hvTable]⟩ + have hMpos : 0 < M := lt_of_le_of_lt (Nat.zero_le j.val) j.isLt + have htable_le_card : + tableMultiplicityCount oStmt a ≤ Fintype.card (Hypercube n) := by + rw [tableMultiplicityCount, ← Finset.card_univ] + exact Finset.card_filter_le _ _ + have hcard_hypercube : Fintype.card (Hypercube n) = 2 ^ n := by + simp [Hypercube] + have hpow_le : 2 ^ n ≤ M * 2 ^ n := by + have hMone : 1 ≤ M := Nat.succ_le_of_lt hMpos + nth_rewrite 1 [← Nat.one_mul (2 ^ n)] + exact Nat.mul_le_mul_right (2 ^ n) hMone + have htable_lt_char : tableMultiplicityCount oStmt a < ringChar F := by + calc + tableMultiplicityCount oStmt a ≤ Fintype.card (Hypercube n) := htable_le_card + _ = 2 ^ n := hcard_hypercube + _ ≤ M * 2 ^ n := hpow_le + _ < ringChar F := stmt.charLarge + intro hzero + have hdvd : ringChar F ∣ tableMultiplicityCount oStmt a := + (ringChar.spec F (tableMultiplicityCount oStmt a)).1 hzero + exact (Nat.not_dvd_of_pos_of_lt htablePos htable_lt_char) hdvd + have hpoles : ∀ (i : TermIdx M) (u : Hypercube n), termPhi oStmt xval i u ≠ 0 := by + intro i u + cases hti : termToInput i with + | table => + rw [termPhi, hti] + simpa [phi, tableOracle] using hNoTablePoles u + | column j => obtain ⟨v, hv⟩ := hcols j u - have hvTable : evalOnHypercube (tableOracle oStmt) v = a := hv.symm.trans hju - have htablePos : 0 < tableMultiplicityCount oStmt a := by - rw [tableMultiplicityCount, Finset.card_pos] - exact ⟨v, by simp [hvTable]⟩ - have hMpos : 0 < M := lt_of_le_of_lt (Nat.zero_le j.val) j.isLt - have htable_le_card : - tableMultiplicityCount oStmt a ≤ Fintype.card (Hypercube n) := by - rw [tableMultiplicityCount, ← Finset.card_univ] - exact Finset.card_filter_le _ _ - have hcard_hypercube : Fintype.card (Hypercube n) = 2 ^ n := by - simp [Hypercube] - have hpow_le : 2 ^ n ≤ M * 2 ^ n := by - have hMone : 1 ≤ M := Nat.succ_le_of_lt hMpos - nth_rewrite 1 [← Nat.one_mul (2 ^ n)] - exact Nat.mul_le_mul_right (2 ^ n) hMone - have htable_lt_char : tableMultiplicityCount oStmt a < ringChar F := by - calc - tableMultiplicityCount oStmt a ≤ Fintype.card (Hypercube n) := htable_le_card - _ = 2 ^ n := hcard_hypercube - _ ≤ M * 2 ^ n := hpow_le - _ < ringChar F := stmt.charLarge - intro hzero - have hdvd : ringChar F ∣ tableMultiplicityCount oStmt a := - (ringChar.spec F (tableMultiplicityCount oStmt a)).1 hzero - exact (Nat.not_dvd_of_pos_of_lt htablePos htable_lt_char) hdvd - have hpoles : ∀ (i : TermIdx M) (u : Hypercube n), termPhi oStmt xval i u ≠ 0 := by - intro i u - cases hti : termToInput i with - | table => - rw [termPhi, hti] - simpa [phi, tableOracle] using hNoTablePoles u - | column j => - obtain ⟨v, hv⟩ := hcols j u - rw [termPhi, hti, phi] - simpa [hv] using hNoTablePoles v - let stmtAfter : StmtAfterOuter F n M params := - { xChallenge := xval, zChallenge := zlam.1, batchingScalars := zlam.2 } - let oStmtAfter : ∀ i, OStmtAfterOuter F n M params i := - fun - | .input i => oStmt i - | .multiplicity => honestMultiplicity oStmt - | .helpers => honestHelpers params oStmt xval - have hmid : ((stmtAfter, oStmtAfter), ()) ∈ logupMidRelation F n M params := by - constructor - · exact logupRowsAgree params Fact.out stmtAfter oStmtAfter - · simpa [stmtAfter, oStmtAfter, logupOuterSumcheckClaim] using - logupOuterClaim_zero (F := F) (n := n) (M := M) params oStmt xval - zlam.1 zlam.2 hcols hchar hpoles - constructor - · intro _; trivial - · intro _ - simp [outerVerifier, outerChallengeXIdx, outerChallengeBatchIdx, - ProtocolSpec.FullTranscript.challenges, ProtocolSpec.FullTranscript.messages, - ProtocolSpec.Transcript.concat, Fin.snoc] - constructor - · convert hmid using 2 - apply Prod.ext - · rfl - · funext i - cases i <;> simp [oStmtAfter, outerMultiplicityMessageIdx, - outerHelpersMessageIdx] - · funext i - cases i <;> simp [outerMultiplicityMessageIdx, - outerHelpersMessageIdx] - - -/-- Lens-completeness for the LogUp→Sumcheck lens: `proj` is the zero-sum instance -(`logupSumcheckRelationInput_of_rowsAgree`), `lift` is trivial as `outputRelation = univ`. -/ + rw [termPhi, hti, phi] + simpa [hv] using hNoTablePoles v + let stmtAfter : StmtAfterOuter F n M params := + { xChallenge := xval, zChallenge := zlam.1, batchingScalars := zlam.2 } + let oStmtAfter : ∀ i, OStmtAfterOuter F n M params i := + fun + | .input i => oStmt i + | .multiplicity => honestMultiplicity oStmt + | .helpers => honestHelpers params oStmt xval + have hmid : ((stmtAfter, oStmtAfter), ()) ∈ logupMidRelation F n M params := by + simpa [stmtAfter, oStmtAfter, logupMidRelation, logupOuterSumcheckClaim] using + logupOuterClaim_zero (F := F) (n := n) (M := M) params oStmt xval + zlam.1 zlam.2 hcols hchar hpoles + simp [outerVerifier, outerChallengeXIdx, outerChallengeBatchIdx, + ProtocolSpec.FullTranscript.challenges, ProtocolSpec.FullTranscript.messages, + ProtocolSpec.Transcript.concat, Fin.snoc] + constructor + · convert hmid using 2 + apply Prod.ext + · rfl + · funext i + cases i <;> simp [oStmtAfter, outerMultiplicityMessageIdx, + outerHelpersMessageIdx] + · funext i + cases i <;> simp [outerMultiplicityMessageIdx, + outerHelpersMessageIdx] + · -- pole-probability bound `Pr[x avoids all table poles] ≥ 1 - |H|/|F|` + sorry + + +/-- Lens-completeness for the LogUp→Sumcheck lens: `proj` builds the zero-sum instance, and `lift` +retains the outer LogUp data together with sumcheck's final valid point claim. -/ instance logupSumcheckLensComplete : (logupSumcheckContextLens F n M params).toContext.IsComplete (logupMidRelation F n M params) - (Sumcheck.Spec.relationRound F n (logupSumcheckDegree M params) (signDomain F Fact.out) 0) - outputRelation - (Sumcheck.Spec.relationRound F n (logupSumcheckDegree M params) (signDomain F Fact.out) + (Sumcheck.Spec.relationRound F n (logupSumcheckDegree M params) (booleanDomain F) 0) + (logupAfterSumcheckRelation F n M params) + (Sumcheck.Spec.relationRound F n (logupSumcheckDegree M params) (booleanDomain F) (Fin.last n)) - ((logupConcreteSumcheckOracleReduction oSpec F n M params Fact.out).toReduction.compatContext + ((logupConcreteSumcheckOracleReduction oSpec F n M params).toReduction.compatContext (logupSumcheckContextLens F n M params).toContext) where proj_complete := by rintro ⟨stmt, oStmt⟩ ⟨⟩ h - exact logupSumcheckRelationInput_of_rowsAgree F n M params (hSigns := Fact.out) h.1 h.2 + exact logupSumcheckRelationInput_of_zero F n M params h lift_complete := by - intro _ _ _ _ _ _ _ - simp [outputRelation] + intro outerStmt outerWit innerStmtOut innerWitOut hCompat _ hRelOut + have hOStmt : + innerStmtOut.2 = logupSumcheckOracleStmt F n M params outerStmt.1 outerStmt.2 := by + -- The generic sumcheck prover preserves its polynomial oracle statement through every round. + -- A small support-preservation lemma for `Sumcheck.Spec.oracleReduction` should discharge + -- this from `hCompat`. + sorry + cases innerWitOut + have hPair : + (innerStmtOut.1, logupSumcheckOracleStmt F n M params outerStmt.1 outerStmt.2) = + innerStmtOut := by + cases innerStmtOut + simpa using hOStmt.symm + simpa [logupSumcheckContextLens, logupAfterSumcheckRelation, hPair] using hRelOut omit [Fintype F] in -/-- Completeness of the embedded sumcheck phase: it carries `logupMidRelation` to `outputRelation` -with no extra error, by reusing the generic sumcheck's perfect completeness through the -LogUp-to-Sumcheck context lens. -/ +/-- Completeness of the embedded sumcheck phase: it carries `logupMidRelation` to the retained +final sumcheck claim with no extra error, by reusing the generic sumcheck's perfect completeness +through the LogUp-to-Sumcheck context lens. -/ theorem logupSumcheckPhaseCompleteness : (sumcheckOracleReduction oSpec F n M params).completeness init impl - (logupMidRelation F n M params) outputRelation 0 := + (logupMidRelation F n M params) (logupAfterSumcheckRelation F n M params) 0 := OracleReduction.liftContext_perfectCompleteness (lens := logupSumcheckContextLens F n M params) (lensComplete := logupSumcheckLensComplete oSpec F n M params) (Sumcheck.Spec.oracleReduction_perfectCompleteness - F (logupSumcheckDegree M params) (signDomain F Fact.out) n oSpec) + F (logupSumcheckDegree M params) (booleanDomain F) n oSpec) + +/-- Completeness of the final LogUp point check: once sumcheck's final claim is valid for the +retained LogUp polynomial, the verifier's oracle queries reconstruct the same value. -/ +theorem finalCheckCompleteness : + (finalCheckOracleReduction oSpec F n M params).completeness init impl + (logupAfterSumcheckRelation F n M params) outputRelation 0 := by + sorry /-- Main ArkLib completeness theorem for LogUp Protocol 2. -/ theorem logup_completeness : (logupOracleReduction oSpec F n M params).completeness init impl (inputRelation F n M) outputRelation (logupCompletenessError F n) := by letI : Inhabited F := ⟨0⟩ - have happ := OracleReduction.append_completeness.{0, 0, 0, 0} + have hOuterSumcheck := OracleReduction.append_completeness.{0, 0, 0, 0} (outerOracleReduction oSpec F n M params) (sumcheckOracleReduction oSpec F n M params) (logup_outer_completeness oSpec F n M params init impl) (logupSumcheckPhaseCompleteness oSpec F n M params init impl) - simpa only [add_zero] using happ + have hFull := OracleReduction.append_completeness.{0, 0, 0, 0} + ((outerOracleReduction oSpec F n M params).append + (sumcheckOracleReduction oSpec F n M params)) + (finalCheckOracleReduction oSpec F n M params) + hOuterSumcheck + (finalCheckCompleteness oSpec F n M params init impl) + simpa only [add_zero] using hFull end Completeness diff --git a/ArkLib/ProofSystem/Logup/Security/Soundness.lean b/ArkLib/ProofSystem/Logup/Security/Soundness.lean index 810b0722d7..e234a37108 100644 --- a/ArkLib/ProofSystem/Logup/Security/Soundness.lean +++ b/ArkLib/ProofSystem/Logup/Security/Soundness.lean @@ -15,8 +15,7 @@ namespace Logup section Soundness variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [Fact ((-1 : F) ≠ 1)] - [SampleableType F] +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [SampleableType F] variable (n M : ℕ) variable (params : ProtocolParams M) variable {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) diff --git a/ArkLib/ProofSystem/Logup/Sumcheck/Security.lean b/ArkLib/ProofSystem/Logup/Sumcheck/Security.lean deleted file mode 100644 index d6a723ce66..0000000000 --- a/ArkLib/ProofSystem/Logup/Sumcheck/Security.lean +++ /dev/null @@ -1,45 +0,0 @@ -import ArkLib.OracleReduction.Security.Basic -import ArkLib.ProofSystem.Sumcheck.Spec.General - -/-! -# Full Oracle Sum-check Completeness (LogUp-local) - -LogUp-local completeness bridge for the full oracle sumcheck reduction used by Protocol 2 of -Haböck's LogUp paper (Cryptology ePrint Archive, Paper 2022/1530, -). - -`ArkLib.ProofSystem.Sumcheck.Spec.General` provides the full non-oracle and single-round oracle -perfect-completeness theorems. LogUp needs the full composed oracle version for its embedded -sumcheck phase, so this file states the composed theorem by chaining the single-round result with -`OracleReduction.seqCompose_perfectCompleteness`. - -This file is intentionally LogUp-local for now; the theorem is a candidate for upstreaming into the -generic sumcheck development. --/ - -namespace Sumcheck - -open Polynomial MvPolynomial OracleSpec OracleComp ProtocolSpec Finset - -namespace Spec - -variable (R : Type) [CommSemiring R] (deg : ℕ) {m : ℕ} (D : Fin m ↪ R) (n : ℕ) -variable {ι : Type} (oSpec : OracleSpec ι) -variable [DecidableEq R] [SampleableType R] -variable {σ : Type} {init : ProbComp σ} {impl : QueryImpl oSpec (StateT σ ProbComp)} - -/-- Perfect completeness for the full sum-check oracle reduction. - -Prove by `seqCompose_perfectCompleteness` over `SingleRound.oracleReduction_perfectCompleteness`, -as `reduction_perfectCompleteness` does for the non-oracle version. -/ -theorem oracleReduction_perfectCompleteness : - (oracleReduction R deg D n oSpec).perfectCompleteness init impl - (relationRound R n deg D 0) (relationRound R n deg D (.last n)) := - OracleReduction.seqCompose_perfectCompleteness - (rel := relationRound R n deg D) - (R := SingleRound.oracleReduction R n deg D oSpec) - (h := fun i => SingleRound.oracleReduction_perfectCompleteness i) - -end Spec - -end Sumcheck diff --git a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean index af37f67c8d..e11c986f2f 100644 --- a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean +++ b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean @@ -4,21 +4,22 @@ import ArkLib.ProofSystem.Sumcheck.Spec.General /-! # LogUp Sumcheck Bridge -Bridge from the LogUp outer transcript to ArkLib's generic sumcheck reduction, for Protocol 2 of +Adapter between LogUp's outer phase and ArkLib's generic sumcheck protocol, for Protocol 2 of Haböck's LogUp paper (Cryptology ePrint Archive, Paper 2022/1530, ). -The outer phase produces an `x` challenge, batching data `(z, lambda)`, the original input oracles, -the honest multiplicity oracle, and helper oracles. From these data, `SumcheckPolynomial.lean` -constructs the LogUp polynomial `Q`. This file packages `Q` as the single bounded-degree oracle -statement expected by `Sumcheck.Spec`, states the zero-sum relation that starts the embedded -sumcheck, and defines the context lens/reduction that runs generic sumcheck after the outer phase. +The outer phase leaves LogUp-specific data: challenges, input oracles, the multiplicity oracle, and +helper oracles. `SumcheckPolynomial.lean` builds the polynomial `Q` from that data. This file +packages `Q` as the single polynomial oracle expected by `Sumcheck.Spec` and states the zero-sum +claim that starts the sumcheck phase. -The main bridge predicates are: +The main artefact is `logupSumcheckContextLens`. It takes the output of the outer LogUp phase and presents it to generic sumcheck as +that zero-sum claim for `Q`. When sumcheck finishes, the lens packages the final sumcheck point and +claimed value back together with the retained LogUp data, so the final LogUp verifier can query the +original oracles and check the claim. -* `logupMidRelation`, the handoff relation from outer LogUp into sumcheck; -* `logupSumcheckRelationInput`, the corresponding generic sumcheck initial relation; and -* `logupOutputRelationFromSumcheck`, the final LogUp check after sumcheck. +The supporting definitions here describe the sumcheck transcript shape, the packaged `Q` oracle, and +the relations used to state that this translation is correct. -/ namespace Logup @@ -46,6 +47,17 @@ abbrev LogupSumcheckStmtIn (_params : ProtocolParams M) : Type := abbrev LogupSumcheckStmtOut (_params : ProtocolParams M) : Type := Sumcheck.Spec.StatementRound F n (.last n) +/-- LogUp state after the embedded sumcheck, before the final oracle-query check. -/ +structure StmtAfterSumcheck where + /-- The outer transcript data retained for reconstructing `Q(r)`. -/ + outer : StmtAfterOuter F n M params + /-- Sumcheck's final point claim `Q(r) = v`. -/ + finalClaim : LogupSumcheckStmtOut F n M params + +/-- Oracle statements retained after sumcheck for the final LogUp point check. -/ +abbrev OStmtAfterSumcheck : OuterOracleIdx M → Type := + OStmtAfterOuter F n M params + /-- The generic sumcheck oracle statement: LogUp's bounded-degree `Q` polynomial. -/ abbrev LogupSumcheckOracleStatement : Unit → Type := Sumcheck.Spec.OracleStatement F n (logupSumcheckDegree M params) @@ -73,15 +85,6 @@ noncomputable def logupSumcheckOracleStmt ∀ i, LogupSumcheckOracleStatement F n M params i := fun _ => logupSumcheckPolynomial F n M params stmt oStmt -/-- The row-wise claim that `logupQPolynomial` agrees with `qOnHypercube` on `H`. -/ -def logupSumcheckPolynomialRowsAgree - (stmt : StmtAfterOuter F n M params) - (oStmt : ∀ i, OStmtAfterOuter F n M params i) : Prop := - ∀ u : Hypercube n, - MvPolynomial.eval (signPoint F u) (logupSumcheckPolynomial F n M params stmt oStmt).1 = - qOnHypercube (canonicalGroups params) (fun i => oStmt (.input i)) (oStmt .multiplicity) - (oStmt .helpers) stmt.xChallenge stmt.zChallenge stmt.batchingScalars u - /-- The LogUp zero-sum claim that is fed to the generic sumcheck. -/ noncomputable def logupOuterSumcheckClaim (stmt : StmtAfterOuter F n M params) @@ -90,22 +93,11 @@ noncomputable def logupOuterSumcheckClaim qOnHypercube (canonicalGroups params) (fun i => oStmt (.input i)) (oStmt .multiplicity) (oStmt .helpers) stmt.xChallenge stmt.zChallenge stmt.batchingScalars u -/-- Relation handed from the outer LogUp phase to the embedded sumcheck: the polynomial bridge -agrees on the hypercube and the outer algebra produced a genuine zero-sum claim. -/ +/-- Relation handed from the outer LogUp phase to the embedded sumcheck: the outer algebra produced +a genuine zero-sum claim. -/ def logupMidRelation : Set ((StmtAfterOuter F n M params × (∀ i, OStmtAfterOuter F n M params i)) × Unit) := - { x | logupSumcheckPolynomialRowsAgree F n M params x.1.1 x.1.2 ∧ - logupOuterSumcheckClaim F n M params x.1.1 x.1.2 = 0 } - -/-- Semantic agreement between final oracle-query answers and the retained LogUp oracles. -/ -def logupPointEvaluationsAgree - (r : Fin n → F) - (oStmt : ∀ i, OStmtAfterOuter F n M params i) - (evals : PointEvaluations F M params.numGroups) : Prop := - evals.multiplicity = lagrangeOracleEval (oStmt .multiplicity) r ∧ - evals.table = lagrangeOracleEval (oStmt (.input .table)) r ∧ - (∀ i : Fin M, evals.columns i = lagrangeOracleEval (oStmt (.input (.column i))) r) ∧ - ∀ k : Fin params.numGroups, evals.helpers k = lagrangeOracleEval ((oStmt .helpers) k) r + { x | logupOuterSumcheckClaim F n M params x.1.1 x.1.2 = 0 } end SumcheckInterface @@ -114,80 +106,86 @@ section SumcheckBridge variable (F : Type) [Field F] (n M : ℕ) variable (params : ProtocolParams M) -/-- The `{±1}` sumcheck domain, packaged in the form expected by `Sumcheck.Spec`. -/ -def signDomain (hSigns : (-1 : F) ≠ 1) : Fin 2 ↪ F where - toFun := bitToSign F +/-- The Boolean sumcheck domain, packaged in the form expected by `Sumcheck.Spec`. -/ +def booleanDomain : Fin 2 ↪ F where + toFun := fun b => (b : F) inj' := by intro a b h fin_cases a <;> fin_cases b · rfl - · exact absurd h hSigns - · exact absurd h.symm hSigns + · simp at h + · simp at h · rfl +/-- Relation after embedded sumcheck: the final sumcheck point claim is valid for the retained +LogUp polynomial. The final LogUp phase consumes this by querying the retained oracles at that +point and recomputing `Q(r)`. -/ +def logupAfterSumcheckRelation : + Set ((StmtAfterSumcheck F n M params × (∀ i, OStmtAfterSumcheck F n M params i)) × Unit) := + { x | + ((x.1.1.finalClaim, + logupSumcheckOracleStmt F n M params x.1.1.outer x.1.2), ()) ∈ + Sumcheck.Spec.relationRound F n (logupSumcheckDegree M params) (booleanDomain F) + (Fin.last n) } + private theorem sum_piFinset_map_univ_eq_sum_hypercube (D : Fin 2 ↪ F) (f : (Fin n → F) → F) : (∑ x ∈ Fintype.piFinset fun _ : Fin n => Finset.univ.map D, f x) = ∑ u : Hypercube n, f (fun j => D (u j)) := by - classical - symm - refine Finset.sum_nbij (s := (Finset.univ : Finset (Hypercube n))) - (t := Fintype.piFinset fun _ : Fin n => Finset.univ.map D) - (i := fun u j => D (u j)) ?hi ?hinj ?hsurj ?hfg - · intro u _ - rw [Fintype.mem_piFinset] - intro j - exact Finset.mem_map.mpr ⟨u j, Finset.mem_univ _, rfl⟩ - · intro u _ v _ huv - funext j - exact D.injective (congr_fun huv j) - · intro x hx + let e : Hypercube n ↪ (Fin n → F) := Function.Embedding.arrowCongrRight D + change (∑ x ∈ Fintype.piFinset fun _ : Fin n => Finset.univ.map D, f x) = + ∑ u : Hypercube n, f (e u) + rw [← Finset.sum_map] + congr 1 + ext x + constructor + · intro hx + rw [Fintype.mem_piFinset] at hx have hx_coord : ∀ j : Fin n, ∃ b : Fin 2, D b = x j := by intro j - have hxj := (Fintype.mem_piFinset.mp hx) j - rcases Finset.mem_map.mp hxj with ⟨b, _, hb⟩ + rcases Finset.mem_map.mp (hx j) with ⟨b, _, hb⟩ exact ⟨b, hb⟩ let u : Hypercube n := fun j => Classical.choose (hx_coord j) - refine ⟨u, Finset.mem_univ _, ?_⟩ - funext j - exact Classical.choose_spec (hx_coord j) - · intro u _ - rfl - + exact Finset.mem_map.mpr ⟨u, Finset.mem_univ _, by + funext j + exact Classical.choose_spec (hx_coord j)⟩ + · intro hx + rw [Fintype.mem_piFinset] + intro j + rcases Finset.mem_map.mp hx with ⟨u, _, rfl⟩ + exact Finset.mem_map.mpr ⟨u j, Finset.mem_univ _, rfl⟩ /-- The initial generic Sumcheck relation induced by a LogUp outer transcript. -/ -def logupSumcheckRelationInput (hSigns : (-1 : F) ≠ 1) +def logupSumcheckRelationInput (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) : Prop := ((logupInitialSumcheckStatement F n M params, logupSumcheckOracleStmt F n M params stmt oStmt), ()) ∈ - Sumcheck.Spec.relationRound F n (logupSumcheckDegree M params) (signDomain F hSigns) 0 + Sumcheck.Spec.relationRound F n (logupSumcheckDegree M params) (booleanDomain F) 0 -/-- If the polynomial bridge agrees on the hypercube and LogUp's outer algebra proves a zero sum, -then the generic Sumcheck input relation is exactly the claim sent to Sumcheck. -/ -theorem logupSumcheckRelationInput_of_rowsAgree - {hSigns : (-1 : F) ≠ 1} +/-- If LogUp's outer algebra proves a zero sum, then the generic Sumcheck input relation is exactly +the claim sent to Sumcheck. -/ +theorem logupSumcheckRelationInput_of_zero {stmt : StmtAfterOuter F n M params} {oStmt : ∀ i, OStmtAfterOuter F n M params i} - (hRows : logupSumcheckPolynomialRowsAgree F n M params stmt oStmt) (hZero : logupOuterSumcheckClaim F n M params stmt oStmt = 0) : - logupSumcheckRelationInput F n M params hSigns stmt oStmt := by + logupSumcheckRelationInput F n M params stmt oStmt := by unfold logupSumcheckRelationInput Sumcheck.Spec.relationRound simp only [Fin.coe_ofNat_eq_mod, Nat.zero_mod, Nat.sub_zero, logupInitialSumcheckStatement, Set.mem_setOf_eq, Fin.elim0_append, logupSumcheckOracleStmt] change - (∑ x ∈ Fintype.piFinset fun _ : Fin n => Finset.univ.map (signDomain F hSigns), + (∑ x ∈ Fintype.piFinset fun _ : Fin n => Finset.univ.map (booleanDomain F), MvPolynomial.eval ((x ∘ Fin.cast (by omega)) ∘ Fin.cast (by omega)) (logupSumcheckPolynomial F n M params stmt oStmt).val) = 0 rw [sum_piFinset_map_univ_eq_sum_hypercube - (F := F) (n := n) (D := signDomain F hSigns) + (F := F) (n := n) (D := booleanDomain F) (f := fun x => MvPolynomial.eval ((x ∘ Fin.cast (by omega)) ∘ Fin.cast (by omega)) (logupSumcheckPolynomial F n M params stmt oStmt).val)] calc (∑ u : Hypercube n, MvPolynomial.eval - ((((fun j => (signDomain F hSigns) (u j)) ∘ Fin.cast (by omega)) ∘ + ((((fun j => (booleanDomain F) (u j)) ∘ Fin.cast (by omega)) ∘ Fin.cast (by omega))) (logupSumcheckPolynomial F n M params stmt oStmt).val) = @@ -195,7 +193,8 @@ theorem logupSumcheckRelationInput_of_rowsAgree rw [logupOuterSumcheckClaim] apply Finset.sum_congr rfl intro u _ - simpa [signDomain, signPoint] using hRows u + simpa [booleanDomain, logupSumcheckPolynomial] using + logupQPolynomial_eval_hypercube F n M params stmt oStmt u _ = 0 := hZero /-- The obligations needed to replace the current abstract embedded sumcheck by ArkLib's generic @@ -203,11 +202,10 @@ sumcheck plus LogUp's final oracle-query check. -/ structure LogupSumcheckBridge (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) where - rowsAgree : logupSumcheckPolynomialRowsAgree F n M params stmt oStmt claimZero : logupOuterSumcheckClaim F n M params stmt oStmt = 0 finalEval : ∀ (r : Fin n → F) (evals : PointEvaluations F M params.numGroups), - logupPointEvaluationsAgree F n M params r oStmt evals → + logupPointEvaluationsAgree (F := F) (n := n) (M := M) params r oStmt evals → MvPolynomial.eval r (logupSumcheckPolynomial F n M params stmt oStmt).1 = qAtPoint (canonicalGroups params) stmt.xChallenge stmt.zChallenge r stmt.batchingScalars evals @@ -215,53 +213,23 @@ structure LogupSumcheckBridge end SumcheckBridge -section ConcreteSumcheckReduction - -variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) -variable (params : ProtocolParams M) - -/-- The existing generic Sumcheck oracle verifier specialized to LogUp's domain and degree bound. -/ -noncomputable def logupConcreteSumcheckOracleVerifier [SampleableType F] - (hSigns : (-1 : F) ≠ 1) : - OracleVerifier oSpec (LogupSumcheckStmtIn F n M params) - (LogupSumcheckOracleStatement F n M params) - (LogupSumcheckStmtOut F n M params) - (LogupSumcheckOracleStatement F n M params) - (logupSumcheckPSpec F n M params) := - Sumcheck.Spec.oracleVerifier F (logupSumcheckDegree M params) - (signDomain F hSigns) n oSpec - -/-- The existing generic Sumcheck oracle reduction specialized to LogUp's domain and degree -bound. -/ -noncomputable def logupConcreteSumcheckOracleReduction [SampleableType F] - (hSigns : (-1 : F) ≠ 1) : - OracleReduction oSpec (LogupSumcheckStmtIn F n M params) - (LogupSumcheckOracleStatement F n M params) Unit - (LogupSumcheckStmtOut F n M params) - (LogupSumcheckOracleStatement F n M params) Unit - (logupSumcheckPSpec F n M params) := - Sumcheck.Spec.oracleReduction F (logupSumcheckDegree M params) - (signDomain F hSigns) n oSpec - -end ConcreteSumcheckReduction - section SumcheckLift variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [Fact ((-1 : F) ≠ 1)] (n M : ℕ) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) variable (params : ProtocolParams M) /-- Context lens from LogUp's retained outer state to ArkLib's generic Sumcheck state. -The projection builds the generic zero-sum claim and its single polynomial oracle. The lift drops -the generic Sumcheck output because the top-level LogUp protocol returns only success/failure. +The projection builds the generic zero-sum claim and its single polynomial oracle. The lift keeps +the outer LogUp data and retained oracles together with Sumcheck's final point claim, so the next +LogUp phase can perform the paper's final oracle-query check. -/ noncomputable def logupSumcheckContextLens : OracleContext.Lens - (StmtAfterOuter F n M params) StmtOut + (StmtAfterOuter F n M params) (StmtAfterSumcheck F n M params) (LogupSumcheckStmtIn F n M params) (LogupSumcheckStmtOut F n M params) - (OStmtAfterOuter F n M params) OStmtOut + (OStmtAfterOuter F n M params) (OStmtAfterSumcheck F n M params) (LogupSumcheckOracleStatement F n M params) (LogupSumcheckOracleStatement F n M params) Unit Unit Unit Unit where @@ -269,30 +237,12 @@ noncomputable def logupSumcheckContextLens : ⟨fun ctx => (logupInitialSumcheckStatement F n M params, logupSumcheckOracleStmt F n M params ctx.1 ctx.2), - fun _ _ => ((), fun i => Fin.elim0 i)⟩ + fun ctx inner => + ({ outer := ctx.1, finalClaim := inner.1 }, ctx.2)⟩ wit := ⟨fun _ => (), fun _ _ => ()⟩ -/-- The embedded LogUp sumcheck phase, obtained by lifting ArkLib's generic Sumcheck reduction -through the LogUp-to-Sumcheck context lens. -/ -noncomputable def sumcheckOracleReduction [SampleableType F] : - OracleReduction oSpec (StmtAfterOuter F n M params) (OStmtAfterOuter F n M params) Unit - (StmtOut) (OStmtOut) Unit - (logupSumcheckPSpec F n M params) := - let lens : - OracleContext.Lens.{0, 0, 0, 0} - (StmtAfterOuter F n M params) StmtOut - (LogupSumcheckStmtIn F n M params) (LogupSumcheckStmtOut F n M params) - (OStmtAfterOuter F n M params) OStmtOut - (LogupSumcheckOracleStatement F n M params) - (LogupSumcheckOracleStatement F n M params) - Unit Unit Unit Unit := - logupSumcheckContextLens F n M params - (logupConcreteSumcheckOracleReduction oSpec F n M params - (Fact.out : (-1 : F) ≠ 1)).liftContext - lens - end SumcheckLift end Logup diff --git a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean index 4117df6433..b9bc27ff66 100644 --- a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean +++ b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean @@ -1,4 +1,4 @@ -import ArkLib.Data.MvPolynomial.Degrees +import ArkLib.Data.MvPolynomial.Multilinear import ArkLib.ProofSystem.Logup.Common /-! @@ -8,10 +8,14 @@ Construction of the concrete polynomial used by LogUp's embedded sumcheck, follo Haböck's LogUp paper (Cryptology ePrint Archive, Paper 2022/1530, ). -The outer LogUp algebra defines a row-wise expression `qOnHypercube`. This file builds its -multilinear-extension-style polynomial `logupQPolynomial : MvPolynomial (Fin n) F`, proves that it -agrees with `qOnHypercube` on the signed hypercube, and proves the individual-degree bound -`degreeOf ≤ M + 3` needed by the generic sumcheck. +This file defines the polynomial `logupQPolynomial : MvPolynomial (Fin n) F` used as LogUp's +sumcheck instance after the outer phase. + +It proves direct facts about this polynomial, namely: +* its individual-degree bound `degreeOf ≤ M + 3`, +* its agreement with `qOnHypercube` on the +Boolean hypercube, and +* its agreement with the final-query reconstruction at sumcheck's final point. -/ namespace Logup @@ -23,216 +27,29 @@ section SumcheckPolynomial variable (F : Type) [Field F] (n M : ℕ) variable (params : ProtocolParams M) -/-- The `{±1}` Lagrange basis polynomial for one hypercube row. -/ -private noncomputable def signedBasisPolynomial (u : Hypercube n) : - MvPolynomial (Fin n) F := - MvPolynomial.C (((2 : F) ^ n)⁻¹) * - ∏ j : Fin n, (1 + MvPolynomial.C (bitToSign F (u j)) * MvPolynomial.X j) - -/-- Multilinear extension of a function on the signed hypercube `{±1}ⁿ`. -/ -private noncomputable def signedMLEPolynomial (values : Hypercube n → F) : - MvPolynomial (Fin n) F := - ∑ u : Hypercube n, MvPolynomial.C (values u) * signedBasisPolynomial F n u - -/-- The polynomial whose value at `r` is `L_H(r, z)`. -/ -private noncomputable def lagrangeKernelPolynomial (z : Fin n → F) : - MvPolynomial (Fin n) F := - MvPolynomial.C (((2 : F) ^ n)⁻¹) * - ∏ j : Fin n, (1 + MvPolynomial.C (z j) * MvPolynomial.X j) - -private theorem signedBasisPolynomial_eval (u : Hypercube n) (r : Fin n → F) : - MvPolynomial.eval r (signedBasisPolynomial F n u) = lagrangeKernel F u r := by - simp [signedBasisPolynomial, lagrangeKernel, lagrangeKernelAtPoint, signPoint] - -private theorem signedMLEPolynomial_eval (values : Hypercube n → F) (r : Fin n → F) : - MvPolynomial.eval r (signedMLEPolynomial F n values) = - ∑ u : Hypercube n, values u * lagrangeKernel F u r := by - simp [signedMLEPolynomial, signedBasisPolynomial_eval] - -private theorem lagrangeKernelPolynomial_eval (z r : Fin n → F) : - MvPolynomial.eval r (lagrangeKernelPolynomial F n z) = - lagrangeKernelAtPoint F r z := by - simp [lagrangeKernelPolynomial, lagrangeKernelAtPoint, mul_comm] - -private theorem signedLinearFactor_degreeOf (a : F) (i j : Fin n) : - MvPolynomial.degreeOf i (1 + MvPolynomial.C a * MvPolynomial.X j) ≤ - if i = j then 1 else 0 := by - by_cases hij : i = j - · subst i - simp only [↓reduceIte] - have hone : - MvPolynomial.degreeOf j (1 : MvPolynomial (Fin n) F) ≤ 1 := by - simp - have hmul : - MvPolynomial.degreeOf j (MvPolynomial.C a * MvPolynomial.X j) ≤ 1 := by - calc - _ ≤ MvPolynomial.degreeOf j (MvPolynomial.C a) + - MvPolynomial.degreeOf j (MvPolynomial.X j) := by - exact MvPolynomial.degreeOf_mul_le j _ _ - _ ≤ 0 + 1 := by - gcongr - · exact (MvPolynomial.degreeOf_C (R := F) a j).le - · exact MvPolynomial.degreeOf_X_le (R := F) j j - _ = 1 := by omega - exact (MvPolynomial.degreeOf_add_le j _ _).trans (max_le hone hmul) - · simp only [hij, ↓reduceIte] - have hone : - MvPolynomial.degreeOf i (1 : MvPolynomial (Fin n) F) ≤ 0 := by - simp - have hmul : - MvPolynomial.degreeOf i (MvPolynomial.C a * MvPolynomial.X j) ≤ 0 := by - calc - _ ≤ MvPolynomial.degreeOf i (MvPolynomial.C a) + - MvPolynomial.degreeOf i (MvPolynomial.X j) := by - exact MvPolynomial.degreeOf_mul_le i _ _ - _ ≤ 0 + 0 := by - gcongr - · exact (MvPolynomial.degreeOf_C (R := F) a i).le - · exact (MvPolynomial.degreeOf_X_of_ne (R := F) hij).le - _ = 0 := by omega - exact (MvPolynomial.degreeOf_add_le i _ _).trans (max_le hone hmul) - -private theorem signedBasisPolynomial_degreeOf (u : Hypercube n) (i : Fin n) : - MvPolynomial.degreeOf i (signedBasisPolynomial F n u) ≤ 1 := by - calc - _ ≤ MvPolynomial.degreeOf i (MvPolynomial.C (((2 : F) ^ n)⁻¹)) + - MvPolynomial.degreeOf i - (∏ j : Fin n, (1 + MvPolynomial.C (bitToSign F (u j)) * MvPolynomial.X j)) := by - exact MvPolynomial.degreeOf_mul_le i _ _ - _ ≤ 0 + - MvPolynomial.degreeOf i - (∏ j : Fin n, (1 + MvPolynomial.C (bitToSign F (u j)) * MvPolynomial.X j)) := by - gcongr - exact (MvPolynomial.degreeOf_C (R := F) (((2 : F) ^ n)⁻¹) i).le - _ = - MvPolynomial.degreeOf i - (∏ j : Fin n, (1 + MvPolynomial.C (bitToSign F (u j)) * MvPolynomial.X j)) := by - simp - _ ≤ ∑ j : Fin n, - MvPolynomial.degreeOf i (1 + MvPolynomial.C (bitToSign F (u j)) * MvPolynomial.X j) := by - exact MvPolynomial.degreeOf_prod_le i _ _ - _ ≤ ∑ j : Fin n, if i = j then 1 else 0 := by - apply Finset.sum_le_sum - intro j _ - exact signedLinearFactor_degreeOf F n (bitToSign F (u j)) i j - _ = 1 := by - norm_num - -private theorem signedMLEPolynomial_degreeOf (values : Hypercube n → F) (i : Fin n) : - MvPolynomial.degreeOf i (signedMLEPolynomial F n values) ≤ 1 := by - classical - calc - _ ≤ (Finset.univ : Finset (Hypercube n)).sup - fun u => MvPolynomial.degreeOf i - (MvPolynomial.C (values u) * signedBasisPolynomial F n u) := by - exact MvPolynomial.degreeOf_sum_le i _ _ - _ ≤ 1 := by - apply Finset.sup_le - intro u _ - calc - MvPolynomial.degreeOf i (MvPolynomial.C (values u) * signedBasisPolynomial F n u) - ≤ MvPolynomial.degreeOf i (MvPolynomial.C (values u)) + - MvPolynomial.degreeOf i (signedBasisPolynomial F n u) := by - exact MvPolynomial.degreeOf_mul_le i _ _ - _ ≤ 0 + 1 := by - gcongr - · exact (MvPolynomial.degreeOf_C (R := F) (values u) i).le - · exact signedBasisPolynomial_degreeOf F n u i - _ = 1 := by omega - -private theorem lagrangeKernelPolynomial_degreeOf (z : Fin n → F) (i : Fin n) : - MvPolynomial.degreeOf i (lagrangeKernelPolynomial F n z) ≤ 1 := by - calc - _ ≤ MvPolynomial.degreeOf i (MvPolynomial.C (((2 : F) ^ n)⁻¹)) + - MvPolynomial.degreeOf i - (∏ j : Fin n, (1 + MvPolynomial.C (z j) * MvPolynomial.X j)) := by - exact MvPolynomial.degreeOf_mul_le i _ _ - _ ≤ 0 + - MvPolynomial.degreeOf i - (∏ j : Fin n, (1 + MvPolynomial.C (z j) * MvPolynomial.X j)) := by - gcongr - exact (MvPolynomial.degreeOf_C (R := F) (((2 : F) ^ n)⁻¹) i).le - _ = - MvPolynomial.degreeOf i - (∏ j : Fin n, (1 + MvPolynomial.C (z j) * MvPolynomial.X j)) := by - simp - _ ≤ ∑ j : Fin n, - MvPolynomial.degreeOf i (1 + MvPolynomial.C (z j) * MvPolynomial.X j) := by - exact MvPolynomial.degreeOf_prod_le i _ _ - _ ≤ ∑ j : Fin n, if i = j then 1 else 0 := by - apply Finset.sum_le_sum - intro j _ - exact signedLinearFactor_degreeOf F n (z j) i j - _ = 1 := by - norm_num - -/-- Polynomial extension of one retained Lagrange-form multilinear oracle. -/ -private noncomputable def multilinearOraclePolynomial (oracle : MultilinearOracle F n) : - MvPolynomial (Fin n) F := - signedMLEPolynomial F n fun u => evalOnHypercube oracle u - -private theorem multilinearOraclePolynomial_eval (oracle : MultilinearOracle F n) - (r : Fin n → F) : - MvPolynomial.eval r (multilinearOraclePolynomial F n oracle) = - lagrangeOracleEval oracle r := by - simp [multilinearOraclePolynomial, signedMLEPolynomial_eval, lagrangeOracleEval, - evalOnHypercube] - -/-- On the signed hypercube the Lagrange kernel is a delta function. -/ -theorem lagrangeKernel_signPoint (hs : (-1 : F) ≠ 1) (v u : Hypercube n) : - lagrangeKernel F v (signPoint F u) = if v = u then 1 else 0 := by - have h2 : (2 : F) ≠ 0 := fun h => hs (by linear_combination -h) - unfold lagrangeKernel lagrangeKernelAtPoint - by_cases h : v = u - · subst h - rw [if_pos rfl] - have hfac : ∀ j, 1 + signPoint F v j * signPoint F v j = (2 : F) := by - intro j - show (1 : F) + bitToSign F (v j) * bitToSign F (v j) = 2 - generalize v j = b - fin_cases b <;> norm_num [bitToSign] - rw [Finset.prod_congr rfl (fun j _ => hfac j), Finset.prod_const, Finset.card_univ, - Fintype.card_fin, inv_mul_cancel₀ (pow_ne_zero n h2)] - · rw [if_neg h] - obtain ⟨j, hj⟩ := Function.ne_iff.mp h - have hfac : 1 + signPoint F v j * signPoint F u j = 0 := by - show (1 : F) + bitToSign F (v j) * bitToSign F (u j) = 0 - generalize hvj : v j = b at hj - generalize huj : u j = c at hj - fin_cases b <;> fin_cases c <;> simp_all [bitToSign] - rw [Finset.prod_eq_zero (Finset.mem_univ j) hfac, mul_zero] - -/-- The MLE of an oracle, evaluated at a signed-hypercube point, recovers the oracle's value. -/ -theorem lagrangeOracleEval_signPoint (hs : (-1 : F) ≠ 1) - (oracle : MultilinearOracle F n) (u : Hypercube n) : - lagrangeOracleEval oracle (signPoint F u) = evalOnHypercube oracle u := by - unfold lagrangeOracleEval - rw [Finset.sum_congr rfl (fun v _ => by rw [lagrangeKernel_signPoint F n hs v u])] - simp only [mul_ite, mul_one, mul_zero, Finset.sum_ite_eq', Finset.mem_univ, if_true, - evalOnHypercube] - -private theorem multilinearOraclePolynomial_eval_sign (hs : (-1 : F) ≠ 1) +private theorem oraclePolynomial_eval_hypercube (oracle : MultilinearOracle F n) (u : Hypercube n) : - MvPolynomial.eval (signPoint F u) (multilinearOraclePolynomial F n oracle) + MvPolynomial.eval (u : Fin n → F) (MvPolynomial.MLE oracle.values) = evalOnHypercube oracle u := by - rw [multilinearOraclePolynomial_eval, lagrangeOracleEval_signPoint F n hs] + change MvPolynomial.eval (u : Fin n → F) (MvPolynomial.MLE oracle.values) = oracle.values u + simp private noncomputable def inputOraclePolynomial (oStmt : ∀ i, OStmtAfterOuter F n M params i) (idx : InputOracleIdx M) : MvPolynomial (Fin n) F := match idx with - | .table => multilinearOraclePolynomial F n (oStmt (.input .table)) - | .column i => multilinearOraclePolynomial F n (oStmt (.input (.column i))) + | .table => MvPolynomial.MLE (oStmt (.input .table)).values + | .column i => MvPolynomial.MLE (oStmt (.input (.column i))).values private noncomputable def multiplicityPolynomial (oStmt : ∀ i, OStmtAfterOuter F n M params i) : MvPolynomial (Fin n) F := - multilinearOraclePolynomial F n (oStmt .multiplicity) + MvPolynomial.MLE (oStmt .multiplicity).values private noncomputable def helperPolynomial (oStmt : ∀ i, OStmtAfterOuter F n M params i) (k : Fin params.numGroups) : MvPolynomial (Fin n) F := - multilinearOraclePolynomial F n ((oStmt .helpers) k) + MvPolynomial.MLE ((oStmt .helpers) k).values private noncomputable def termPhiPolynomial (stmt : StmtAfterOuter F n M params) @@ -258,12 +75,6 @@ private noncomputable def domainIdentityPolynomial ∏ j ∈ (canonicalGroups params k).erase i, termPhiPolynomial F n M params stmt oStmt j -private theorem multilinearOraclePolynomial_degreeOf (oracle : MultilinearOracle F n) - (i : Fin n) : - MvPolynomial.degreeOf i (multilinearOraclePolynomial F n oracle) ≤ 1 := by - simpa [multilinearOraclePolynomial] using - signedMLEPolynomial_degreeOf F n (fun u => evalOnHypercube oracle u) i - private theorem inputOraclePolynomial_degreeOf (oStmt : ∀ i, OStmtAfterOuter F n M params i) (idx : InputOracleIdx M) (i : Fin n) : @@ -271,21 +82,23 @@ private theorem inputOraclePolynomial_degreeOf cases idx with | table => simpa [inputOraclePolynomial] using - multilinearOraclePolynomial_degreeOf F n (oStmt (.input .table)) i + (MvPolynomial.MLE_degreeOf (R := F) ((oStmt (.input .table)).values) i) | column j => simpa [inputOraclePolynomial] using - multilinearOraclePolynomial_degreeOf F n (oStmt (.input (.column j))) i + (MvPolynomial.MLE_degreeOf (R := F) ((oStmt (.input (.column j))).values) i) private theorem multiplicityPolynomial_degreeOf (oStmt : ∀ i, OStmtAfterOuter F n M params i) (i : Fin n) : - MvPolynomial.degreeOf i (multiplicityPolynomial F n M params oStmt) ≤ 1 := - multilinearOraclePolynomial_degreeOf F n (oStmt .multiplicity) i + MvPolynomial.degreeOf i (multiplicityPolynomial F n M params oStmt) ≤ 1 := by + simpa [multiplicityPolynomial] using + (MvPolynomial.MLE_degreeOf (R := F) ((oStmt .multiplicity).values) i) private theorem helperPolynomial_degreeOf (oStmt : ∀ i, OStmtAfterOuter F n M params i) (k : Fin params.numGroups) (i : Fin n) : - MvPolynomial.degreeOf i (helperPolynomial F n M params oStmt k) ≤ 1 := - multilinearOraclePolynomial_degreeOf F n ((oStmt .helpers) k) i + MvPolynomial.degreeOf i (helperPolynomial F n M params oStmt k) ≤ 1 := by + simpa [helperPolynomial] using + (MvPolynomial.MLE_degreeOf (R := F) (((oStmt .helpers) k).values) i) private theorem termPhiPolynomial_degreeOf (stmt : StmtAfterOuter F n M params) @@ -405,7 +218,7 @@ noncomputable def logupQPolynomial MvPolynomial (Fin n) F := ∑ k : Fin params.numGroups, ( helperPolynomial F n M params oStmt k + - lagrangeKernelPolynomial F n stmt.zChallenge * + MvPolynomial.eqPolynomial stmt.zChallenge * MvPolynomial.C (stmt.batchingScalars k) * domainIdentityPolynomial F n M params stmt oStmt k) @@ -419,7 +232,7 @@ theorem logupQPolynomial_degreeOf _ ≤ (Finset.univ : Finset (Fin params.numGroups)).sup fun k => MvPolynomial.degreeOf i (helperPolynomial F n M params oStmt k + - lagrangeKernelPolynomial F n stmt.zChallenge * + MvPolynomial.eqPolynomial stmt.zChallenge * MvPolynomial.C (stmt.batchingScalars k) * domainIdentityPolynomial F n M params stmt oStmt k) := by exact MvPolynomial.degreeOf_sum_le i _ _ @@ -430,17 +243,17 @@ theorem logupQPolynomial_degreeOf (helperPolynomial_degreeOf F n M params oStmt k i).trans (by omega) have hProduct : MvPolynomial.degreeOf i - (lagrangeKernelPolynomial F n stmt.zChallenge * + (MvPolynomial.eqPolynomial stmt.zChallenge * MvPolynomial.C (stmt.batchingScalars k) * domainIdentityPolynomial F n M params stmt oStmt k) ≤ M + 3 := by calc _ ≤ MvPolynomial.degreeOf i - (lagrangeKernelPolynomial F n stmt.zChallenge * + (MvPolynomial.eqPolynomial stmt.zChallenge * MvPolynomial.C (stmt.batchingScalars k)) + MvPolynomial.degreeOf i (domainIdentityPolynomial F n M params stmt oStmt k) := by exact MvPolynomial.degreeOf_mul_le i _ _ - _ ≤ (MvPolynomial.degreeOf i (lagrangeKernelPolynomial F n stmt.zChallenge) + + _ ≤ (MvPolynomial.degreeOf i (MvPolynomial.eqPolynomial stmt.zChallenge) + MvPolynomial.degreeOf i (MvPolynomial.C (stmt.batchingScalars k))) + MvPolynomial.degreeOf i (domainIdentityPolynomial F n M params stmt oStmt k) := by @@ -448,66 +261,137 @@ theorem logupQPolynomial_degreeOf exact MvPolynomial.degreeOf_mul_le i _ _ _ ≤ (1 + 0) + (M + 2) := by gcongr - · exact lagrangeKernelPolynomial_degreeOf F n stmt.zChallenge i + · exact MvPolynomial.eqPolynomial_degreeOf (R := F) stmt.zChallenge i · exact (MvPolynomial.degreeOf_C (R := F) (stmt.batchingScalars k) i).le · exact domainIdentityPolynomial_degreeOf F n M params stmt oStmt k i _ = M + 3 := by omega exact (MvPolynomial.degreeOf_add_le i _ _).trans (max_le hHelper hProduct) -private theorem termPhiPolynomial_eval_sign (hs : (-1 : F) ≠ 1) +private theorem termPhiPolynomial_eval_hypercube (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) (i : TermIdx M) (u : Hypercube n) : - MvPolynomial.eval (signPoint F u) (termPhiPolynomial F n M params stmt oStmt i) + MvPolynomial.eval (u : Fin n → F) (termPhiPolynomial F n M params stmt oStmt i) = termPhi (fun idx => oStmt (.input idx)) stmt.xChallenge i u := by rcases h : termToInput i with _ | c <;> simp only [termPhiPolynomial, termPhi, phi, h, inputOraclePolynomial, tableOracle, - columnOracle, map_add, MvPolynomial.eval_C, multilinearOraclePolynomial_eval_sign F n hs] + columnOracle, map_add, MvPolynomial.eval_C, oraclePolynomial_eval_hypercube F n] -private theorem termNumeratorPolynomial_eval_sign (hs : (-1 : F) ≠ 1) +private theorem termNumeratorPolynomial_eval_hypercube (oStmt : ∀ i, OStmtAfterOuter F n M params i) (i : TermIdx M) (u : Hypercube n) : - MvPolynomial.eval (signPoint F u) (termNumeratorPolynomial F n M params oStmt i) + MvPolynomial.eval (u : Fin n → F) (termNumeratorPolynomial F n M params oStmt i) = termNumerator (oStmt .multiplicity) i u := by unfold termNumeratorPolynomial termNumerator numerator cases termToInput i with - | table => simp only [multiplicityPolynomial, multilinearOraclePolynomial_eval_sign F n hs] + | table => simp only [multiplicityPolynomial, oraclePolynomial_eval_hypercube F n] | column c => simp -private theorem domainIdentityPolynomial_eval_sign (hs : (-1 : F) ≠ 1) +private theorem domainIdentityPolynomial_eval_hypercube (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) (k : Fin params.numGroups) (u : Hypercube n) : - MvPolynomial.eval (signPoint F u) (domainIdentityPolynomial F n M params stmt oStmt k) + MvPolynomial.eval (u : Fin n → F) (domainIdentityPolynomial F n M params stmt oStmt k) = domainIdentityTerm (canonicalGroups params) (fun idx => oStmt (.input idx)) (oStmt .multiplicity) (oStmt .helpers) stmt.xChallenge k u := by rw [domainIdentityPolynomial, map_sub, map_mul, map_prod, domainIdentityTerm, denominatorProduct, map_sum] congr 1 · congr 1 - · simp only [helperPolynomial]; exact multilinearOraclePolynomial_eval_sign F n hs _ u + · simp only [helperPolynomial]; exact oraclePolynomial_eval_hypercube F n _ u + · exact Finset.prod_congr rfl + (fun i _ => termPhiPolynomial_eval_hypercube F n M params stmt oStmt i u) + · refine Finset.sum_congr rfl (fun i _ => ?_) + rw [map_mul, map_prod, termNumeratorPolynomial_eval_hypercube F n M params] + congr 1 + exact Finset.prod_congr rfl + (fun j _ => termPhiPolynomial_eval_hypercube F n M params stmt oStmt j u) + +/-- Denominator polynomials evaluated at an arbitrary point match the reconstructed final-query +denominators. -/ +private theorem termPhiPolynomial_eval_point + (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) + (r : Fin n → F) (evals : PointEvaluations F M params.numGroups) + (hEval : logupPointEvaluationsAgree (F := F) (n := n) (M := M) params r oStmt evals) + (i : TermIdx M) : + MvPolynomial.eval r (termPhiPolynomial F n M params stmt oStmt i) + = termPhiAtPoint stmt.xChallenge evals i := by + rcases h : termToInput i with _ | c + · simp only [termPhiPolynomial, termPhiAtPoint, phiAtPoint, h, inputOraclePolynomial, + map_add, MvPolynomial.eval_C] + exact congrArg (fun y => stmt.xChallenge + y) hEval.2.1.symm + · simp only [termPhiPolynomial, termPhiAtPoint, phiAtPoint, h, inputOraclePolynomial, + map_add, MvPolynomial.eval_C] + exact congrArg (fun y => stmt.xChallenge + y) (hEval.2.2.1 c).symm + +/-- Numerator polynomials evaluated at an arbitrary point match the reconstructed final-query +numerators. -/ +private theorem termNumeratorPolynomial_eval_point + (oStmt : ∀ i, OStmtAfterOuter F n M params i) + (r : Fin n → F) (evals : PointEvaluations F M params.numGroups) + (hEval : logupPointEvaluationsAgree (F := F) (n := n) (M := M) params r oStmt evals) + (i : TermIdx M) : + MvPolynomial.eval r (termNumeratorPolynomial F n M params oStmt i) + = termNumeratorAtPoint evals i := by + unfold termNumeratorPolynomial termNumeratorAtPoint numeratorAtPoint + cases h : termToInput i with + | table => + simp only [multiplicityPolynomial] + exact hEval.1.symm + | column c => + simp + +/-- The cleared-denominator identity polynomial evaluated at the final point matches the +reconstructed final-query expression. -/ +private theorem domainIdentityPolynomial_eval_point + (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) + (r : Fin n → F) (evals : PointEvaluations F M params.numGroups) + (hEval : logupPointEvaluationsAgree (F := F) (n := n) (M := M) params r oStmt evals) + (k : Fin params.numGroups) : + MvPolynomial.eval r (domainIdentityPolynomial F n M params stmt oStmt k) + = domainIdentityAtPoint (canonicalGroups params) stmt.xChallenge evals k := by + rw [domainIdentityPolynomial, domainIdentityAtPoint, map_sub, map_mul, map_prod, map_sum] + congr 1 + · congr 1 + · simp only [helperPolynomial] + exact (hEval.2.2.2 k).symm · exact Finset.prod_congr rfl - (fun i _ => termPhiPolynomial_eval_sign F n M params hs stmt oStmt i u) + (fun i _ => termPhiPolynomial_eval_point F n M params stmt oStmt r evals hEval i) · refine Finset.sum_congr rfl (fun i _ => ?_) - rw [map_mul, map_prod, termNumeratorPolynomial_eval_sign F n M params hs] + rw [map_mul, map_prod, termNumeratorPolynomial_eval_point F n M params oStmt r evals hEval] congr 1 exact Finset.prod_congr rfl - (fun j _ => termPhiPolynomial_eval_sign F n M params hs stmt oStmt j u) + (fun j _ => termPhiPolynomial_eval_point F n M params stmt oStmt r evals hEval j) -/-- `logupQPolynomial` restricted to the signed hypercube agrees with `qOnHypercube`. -/ -theorem logupQPolynomial_eval_signPoint (hs : (-1 : F) ≠ 1) +/-- `logupQPolynomial` restricted to the Boolean hypercube agrees with `qOnHypercube`. -/ +theorem logupQPolynomial_eval_hypercube (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) (u : Hypercube n) : - MvPolynomial.eval (signPoint F u) (logupQPolynomial F n M params stmt oStmt) + MvPolynomial.eval (u : Fin n → F) (logupQPolynomial F n M params stmt oStmt) = qOnHypercube (canonicalGroups params) (fun i => oStmt (.input i)) (oStmt .multiplicity) (oStmt .helpers) stmt.xChallenge stmt.zChallenge stmt.batchingScalars u := by rw [logupQPolynomial, qOnHypercube, map_sum] refine Finset.sum_congr rfl (fun k _ => ?_) rw [map_add, map_mul, map_mul, MvPolynomial.eval_C, - show MvPolynomial.eval (signPoint F u) (helperPolynomial F n M params oStmt k) + show MvPolynomial.eval (u : Fin n → F) (helperPolynomial F n M params oStmt k) = evalOnHypercube ((oStmt .helpers) k) u from by - simp only [helperPolynomial]; exact multilinearOraclePolynomial_eval_sign F n hs _ u, - show MvPolynomial.eval (signPoint F u) (lagrangeKernelPolynomial F n stmt.zChallenge) - = lagrangeKernel F u stmt.zChallenge from by rw [lagrangeKernelPolynomial_eval]; rfl, - domainIdentityPolynomial_eval_sign F n M params hs] + simp only [helperPolynomial]; exact oraclePolynomial_eval_hypercube F n _ u, + domainIdentityPolynomial_eval_hypercube F n M params] + +/-- `logupQPolynomial` evaluated at the sumcheck point agrees with the value reconstructed from +the final LogUp oracle-query answers. -/ +theorem logupQPolynomial_eval_point + (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) + (r : Fin n → F) (evals : PointEvaluations F M params.numGroups) + (hEval : logupPointEvaluationsAgree (F := F) (n := n) (M := M) params r oStmt evals) : + MvPolynomial.eval r (logupQPolynomial F n M params stmt oStmt) + = qAtPoint (canonicalGroups params) stmt.xChallenge stmt.zChallenge r + stmt.batchingScalars evals := by + rw [logupQPolynomial, qAtPoint, map_sum] + refine Finset.sum_congr rfl (fun k _ => ?_) + rw [map_add, map_mul, map_mul, MvPolynomial.eval_C, + show MvPolynomial.eval r (helperPolynomial F n M params oStmt k) = evals.helpers k from by + simp only [helperPolynomial] + exact (hEval.2.2.2 k).symm, + domainIdentityPolynomial_eval_point F n M params stmt oStmt r evals hEval] end SumcheckPolynomial diff --git a/ArkLib/ProofSystem/Sumcheck/Spec/General.lean b/ArkLib/ProofSystem/Sumcheck/Spec/General.lean index 7fa223905a..71143438c1 100644 --- a/ArkLib/ProofSystem/Sumcheck/Spec/General.lean +++ b/ArkLib/ProofSystem/Sumcheck/Spec/General.lean @@ -213,6 +213,15 @@ theorem reduction_perfectCompleteness : (R := SingleRound.reduction R n deg D oSpec) (h := fun i => SingleRound.reduction_perfectCompleteness i) +/-- Perfect completeness for the (full) sum-check oracle reduction. -/ +theorem oracleReduction_perfectCompleteness : + (oracleReduction R deg D n oSpec).perfectCompleteness init impl + (relationRound R n deg D 0) (relationRound R n deg D (.last n)) := + OracleReduction.seqCompose_perfectCompleteness + (rel := relationRound R n deg D) + (R := SingleRound.oracleReduction R n deg D oSpec) + (h := fun i => SingleRound.oracleReduction_perfectCompleteness i) + /-- Round-by-round knowledge soundness with error `deg / |R|` per challenge for the (full) sum-check protocol -/ theorem oracleVerifier_rbrKnowledgeSoundness [Fintype R] : From 33bb540ab4f0e0ba5416b84b74bf079313160ef2 Mon Sep 17 00:00:00 2001 From: jCabala Date: Mon, 22 Jun 2026 17:10:18 +0100 Subject: [PATCH 05/12] feat: Finished the Logup completeness proof --- ArkLib/ProofSystem/Logup/Protocol.lean | 10 +- .../Logup/Security/Completeness.lean | 837 ++++++++++++++---- 2 files changed, 686 insertions(+), 161 deletions(-) diff --git a/ArkLib/ProofSystem/Logup/Protocol.lean b/ArkLib/ProofSystem/Logup/Protocol.lean index 3ae0043a98..6b84b73fe1 100644 --- a/ArkLib/ProofSystem/Logup/Protocol.lean +++ b/ArkLib/ProofSystem/Logup/Protocol.lean @@ -420,16 +420,16 @@ variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) variable (params : ProtocolParams M) /-- Query one retained LogUp oracle during the final point check. -/ -private def finalCheckQuery +def finalCheckQuery (i : OuterOracleIdx M) (q : (instOStmtAfterOuterOracleInterface (F := F) (n := n) (params := params) i).Query) : OptionT (OracleComp (oSpec + ([OStmtAfterSumcheck F n M params]ₒ + [finalCheckPSpec.Message]ₒ))) ((instOStmtAfterOuterOracleInterface (F := F) (n := n) (params := params) i).Response q) := - OptionT.lift <| OracleComp.liftComp - (OracleComp.lift <| - OracleSpec.query (show [OStmtAfterSumcheck F n M params]ₒ.Domain from ⟨i, q⟩)) - _ + OptionT.mk <| some <$> OracleComp.lift + (OracleSpec.query + (show (oSpec + ([OStmtAfterSumcheck F n M params]ₒ + + [finalCheckPSpec.Message]ₒ)).Domain from Sum.inr (Sum.inl ⟨i, q⟩))) /-- The verifier's final Protocol 2 check: reconstruct `Q(r)` from retained LogUp oracle evaluations and compare it with the expected value output by sumcheck. -/ diff --git a/ArkLib/ProofSystem/Logup/Security/Completeness.lean b/ArkLib/ProofSystem/Logup/Security/Completeness.lean index 5063503a5d..6a10dccd33 100644 --- a/ArkLib/ProofSystem/Logup/Security/Completeness.lean +++ b/ArkLib/ProofSystem/Logup/Security/Completeness.lean @@ -2,6 +2,7 @@ import ArkLib.OracleReduction.Security.Basic import ArkLib.OracleReduction.Composition.Sequential.Append import ArkLib.ProofSystem.Sumcheck.Spec.General import ArkLib.ProofSystem.Logup.Protocol +import ArkLib.ToVCVio.OracleComp.Coercions.SubSpec /-! # LogUp Completeness @@ -32,11 +33,12 @@ theorem domainIdentityTerm_eq_zero (groups : PartialSumGroups M K) rw [← Finset.mul_prod_erase _ _ hi] field_simp [hφ i hi] +omit [Fintype F] in /-- The honest log-derivative identity (heart of LogUp): with the normalized multiplicity, the table side equals the column side. Needs every column value to occur in the table (`hcols`) and the table counts used by nonzero lookup multiplicities to be nonzero in `F` (`hchar`, from `charLarge`). Holds even at poles, since `x/0 = 0`. -/ -theorem honest_multiplicity_identity (oStmt : ∀ i, OStmtIn F n M i) (x : F) +theorem honest_multiplicity_identity [Finite F] (oStmt : ∀ i, OStmtIn F n M i) (x : F) (hcols : ∀ j : Fin M, ∀ u : Hypercube n, ∃ v : Hypercube n, evalOnHypercube (columnOracle oStmt j) u = evalOnHypercube (tableOracle oStmt) v) (hchar : ∀ a : F, lookupMultiplicityCount oStmt a ≠ 0 → @@ -45,6 +47,7 @@ theorem honest_multiplicity_identity (oStmt : ∀ i, OStmtIn F n M i) (x : F) evalOnHypercube (honestMultiplicity oStmt) u / (x + evalOnHypercube (tableOracle oStmt) u)) = ∑ j : Fin M, ∑ u : Hypercube n, (1 : F) / (x + evalOnHypercube (columnOracle oStmt j) u) := by + letI : Fintype F := Fintype.ofFinite F classical -- per-value cancellation have key : ∀ a : F, @@ -114,6 +117,67 @@ theorem honest_multiplicity_identity (oStmt : ∀ i, OStmtIn F n M i) (x : F) rw [hLHS, hRHS] exact Finset.sum_congr rfl (fun a _ => key a) +/-- If a value is used by a lookup column, then the table contains it often enough that its table +multiplicity is nonzero as an element of `F`. -/ +theorem tableMultiplicityCount_cast_ne_zero_of_lookupMultiplicityCount_ne_zero + (stmt : StmtIn F n M) (oStmt : ∀ i, OStmtIn F n M i) + (hcols : ∀ j : Fin M, ∀ u : Hypercube n, ∃ v : Hypercube n, + evalOnHypercube (columnOracle oStmt j) u = evalOnHypercube (tableOracle oStmt) v) + {a : F} (hlookup : lookupMultiplicityCount oStmt a ≠ 0) : + (tableMultiplicityCount oStmt a : F) ≠ 0 := by + classical + have hlookupCard : + ((Finset.univ : Finset (Fin M × Hypercube n)).filter fun ix => + evalOnHypercube (columnOracle oStmt ix.1) ix.2 = a).card ≠ 0 := by + simpa [lookupMultiplicityCount] using hlookup + obtain ⟨⟨j, u⟩, hju⟩ := Finset.card_ne_zero.mp hlookupCard + simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hju + obtain ⟨v, hv⟩ := hcols j u + have hvTable : evalOnHypercube (tableOracle oStmt) v = a := hv.symm.trans hju + have htablePos : 0 < tableMultiplicityCount oStmt a := by + rw [tableMultiplicityCount, Finset.card_pos] + exact ⟨v, by simp [hvTable]⟩ + have hMpos : 0 < M := lt_of_le_of_lt (Nat.zero_le j.val) j.isLt + have htable_le_card : + tableMultiplicityCount oStmt a ≤ Fintype.card (Hypercube n) := by + rw [tableMultiplicityCount, ← Finset.card_univ] + exact Finset.card_filter_le _ _ + have hcard_hypercube : Fintype.card (Hypercube n) = 2 ^ n := by + simp [Hypercube] + have hpow_le : 2 ^ n ≤ M * 2 ^ n := by + have hMone : 1 ≤ M := Nat.succ_le_of_lt hMpos + nth_rewrite 1 [← Nat.one_mul (2 ^ n)] + exact Nat.mul_le_mul_right (2 ^ n) hMone + have htable_lt_char : tableMultiplicityCount oStmt a < ringChar F := by + calc + tableMultiplicityCount oStmt a ≤ Fintype.card (Hypercube n) := htable_le_card + _ = 2 ^ n := hcard_hypercube + _ ≤ M * 2 ^ n := hpow_le + _ < ringChar F := stmt.charLarge + intro hzero + have hdvd : ringChar F ∣ tableMultiplicityCount oStmt a := + (ringChar.spec F (tableMultiplicityCount oStmt a)).1 hzero + exact (Nat.not_dvd_of_pos_of_lt htablePos htable_lt_char) hdvd + +omit [Fintype F] [DecidableEq F] in +/-- If `x` avoids the table poles, then it avoids all table and lookup-column denominator poles +under the input relation. -/ +theorem termPhi_ne_zero_of_table_poles (oStmt : ∀ i, OStmtIn F n M i) (x : F) + (hcols : ∀ j : Fin M, ∀ u : Hypercube n, ∃ v : Hypercube n, + evalOnHypercube (columnOracle oStmt j) u = evalOnHypercube (tableOracle oStmt) v) + (hNoTablePoles : ∀ u : Hypercube n, + x + evalOnHypercube (tableOracle oStmt) u ≠ 0) : + ∀ (i : TermIdx M) (u : Hypercube n), termPhi oStmt x i u ≠ 0 := by + intro i u + cases hti : termToInput i with + | table => + rw [termPhi, hti] + simpa [phi, tableOracle] using hNoTablePoles u + | column j => + obtain ⟨v, hv⟩ := hcols j u + rw [termPhi, hti, phi] + simpa [hv] using hNoTablePoles v + omit [Fintype F] [DecidableEq F] in /-- The canonical groups partition the term indices `{0,…,M}`, so summing group-by-group equals summing over all terms. -/ @@ -142,10 +206,12 @@ theorem sum_canonicalGroups (params : ProtocolParams M) (g : TermIdx M → F) : · intro h exact ⟨(Nat.le_div_iff_mul_le hℓ).mp (by omega), (Nat.div_lt_iff_lt_mul hℓ).mp (by omega)⟩ +omit [Fintype F] in /-- The honest batched claim sums to zero: `∑ᵤ Q(u) = 0` for the honest multiplicity and helpers, away from poles. Combines `domainIdentityTerm_eq_zero`, `sum_canonicalGroups`, and `honest_multiplicity_identity`. -/ -theorem logupOuterClaim_zero (params : ProtocolParams M) (oStmtIn : ∀ i, OStmtIn F n M i) +theorem logupOuterClaim_zero [Finite F] (params : ProtocolParams M) + (oStmtIn : ∀ i, OStmtIn F n M i) (x : F) (z : Fin n → F) (lam : Fin params.numGroups → F) (hcols : ∀ j : Fin M, ∀ u : Hypercube n, ∃ v : Hypercube n, evalOnHypercube (columnOracle oStmtIn j) u = evalOnHypercube (tableOracle oStmtIn) v) @@ -223,13 +289,6 @@ theorem pole_card_le (oStmt : ∀ i, OStmtIn F n M i) : _ ≤ Fintype.card (Hypercube n) := by rw [← Finset.card_univ]; exact Finset.card_image_le -omit [Fintype F] [DecidableEq F] in -/-- A `LagrangeOracle` query at a Boolean-hypercube point returns the oracle's value there. -/ -theorem lagrange_answer_hypercube (oracle : MultilinearOracle F n) - (a : Hypercube n) : - OracleInterface.answer oracle (a : Fin n → F) = evalOnHypercube oracle a := - lagrangeOracleEval_hypercube oracle a - end OuterAlgebra section Completeness @@ -244,85 +303,129 @@ local instance instOracleCompLawfulMonad' {τ : Type} (spec : OracleSpec τ) : LawfulMonad (OracleComp spec) := OracleComp.instLawfulMonad spec +local instance instOuterChallengeOracleInterface' : + (i : (outerPSpec F n params).ChallengeIdx) → + OracleInterface ((outerPSpec F n params).Challenge i) := + ProtocolSpec.challengeOracleInterface + /-- Completeness error from the current `x`-sampling model: the verifier samples `x` from all of `F`. Following Remark 3 of the LogUp paper, table-pole challenges are treated as bad inputs for the honest handoff rather than rejected by an exponential verifier scan. -/ noncomputable def logupCompletenessError (F : Type) [Fintype F] (n : ℕ) : ℝ≥0 := (Fintype.card (Hypercube n) : ℝ≥0) / (Fintype.card F) -/-- `simulateQ` distributes over a `forIn` loop in `OptionT (OracleComp spec)`: the OptionT -sibling of `simulateQ_list_forIn`, built on `simulateQ_optionT_bind`. -/ -private theorem simulateQ_optionT_list_forIn {ι : Type} {spec : OracleSpec ι} - {nn : Type → Type*} [Monad nn] [LawfulMonad nn] (impl : QueryImpl spec nn) - {α β : Type} (xs : List α) (init : β) - (f : α → β → OptionT (OracleComp spec) (ForInStep β)) : - simulateQ impl (forIn xs init f : OptionT (OracleComp spec) β) - = (forIn xs init (fun a b => simulateQ impl (f a b)) : OptionT nn β) := by - induction xs generalizing init with - | nil => rfl - | cons x xs ih => - rw [List.forIn_cons, List.forIn_cons, simulateQ_optionT_bind] - congr 1 - funext step - cases step with - | done b => rfl - | yield b => exact ih b - -/-- `simulateQ` leaves a `guard` unchanged: `guard` has no queries, so its simulation is itself. -/ -private theorem simulateQ_optionT_guard {ι : Type} {spec : OracleSpec ι} - {nn : Type → Type*} [Monad nn] [LawfulMonad nn] (impl : QueryImpl spec nn) - (P : Prop) [Decidable P] : - simulateQ impl (guard P : OptionT (OracleComp spec) Unit) = (guard P : OptionT nn Unit) := by - change simulateQ impl (if P then pure () else failure : OptionT (OracleComp spec) Unit) - = (if P then pure () else failure : OptionT nn Unit) - by_cases hP : P - · rw [if_pos hP, if_pos hP]; rfl - · rw [if_neg hP, if_neg hP]; rfl - -/-- A list of abort-on-`P` checks can only return successfully when every check avoided `P`. -/ -private theorem guarded_foldlM_succeeds {ι : Type} {spec : OracleSpec ι} - {β γ : Type} (L : List β) (P : β → Prop) [DecidablePred P] (r : γ) : - r ∈ support (List.foldlM (m := OptionT (OracleComp spec)) - (fun (_ : γ) (u : β) => - if P u then failure else pure r) r L) → - ∀ u ∈ L, ¬ P u := by - induction L with - | nil => intro _ u hu; simp at hu - | cons a L' ih => - intro h u hu - rw [List.foldlM_cons] at h - by_cases hPa : P a - · simp [hPa] at h - · simp only [hPa, ↓reduceIte, pure_bind, OptionT.mem_support_iff] at h - rcases List.mem_cons.mp hu with rfl | hu' - · exact hPa - · exact ih h u hu' - -private theorem support_bind_exists {m : Type → Type*} [Monad m] [LawfulMonad m] - [MonadLiftT m SetM] [LawfulMonadLiftT m SetM] - {α β : Type} (x : m α) (f : α → m β) {y : β} - (hy : y ∈ support (x >>= f)) : ∃ a, a ∈ support x ∧ y ∈ support (f a) := by - simpa [mem_support_bind_iff] using hy +private theorem uniform_avoids_table_poles_prob [Inhabited F] + (oStmt : ∀ i, OStmtIn F n M i) : + (1 : ENNReal) - (logupCompletenessError F n : ENNReal) ≤ + Pr[fun x : F => ∀ u : Hypercube n, + x + evalOnHypercube (tableOracle oStmt) u ≠ 0 | $ᵗ F] := by + classical + let bad : F → Prop := + fun x => ∃ u : Hypercube n, x + evalOnHypercube (tableOracle oStmt) u = 0 + have hbad : + Pr[bad | $ᵗ F] ≤ (logupCompletenessError F n : ENNReal) := by + rw [probEvent_uniformSample] + unfold logupCompletenessError + have hcard : + ((Finset.univ.filter bad).card : ENNReal) ≤ + (Fintype.card (Hypercube n) : ENNReal) := by + exact_mod_cast (by simpa [bad] using pole_card_le (F := F) (n := n) (M := M) oStmt) + convert ENNReal.div_le_div_right hcard (Fintype.card F : ENNReal) using 1; norm_num + have hcompl : + Pr[fun x : F => ¬ bad x | $ᵗ F] + Pr[bad | $ᵗ F] = (1 : ENNReal) := by + have h := probEvent_compl ($ᵗ F) (fun x : F => ¬ bad x) + simpa [bad, probFailure_uniformSample, not_not] using h + rw [tsub_le_iff_right] + calc + (1 : ENNReal) = Pr[fun x : F => ¬ bad x | $ᵗ F] + Pr[bad | $ᵗ F] := hcompl.symm + _ ≤ Pr[fun x : F => ¬ bad x | $ᵗ F] + (logupCompletenessError F n : ENNReal) := + add_le_add le_rfl hbad + · apply le_of_eq + congr + funext x + simp [bad] + +private theorem le_probEvent_bind_of_forall_le {m : Type → Type*} [Monad m] [LawfulMonad m] + [MonadLiftT m SPMF] [LawfulMonadLiftT m SPMF] [MonadLiftT m SetM] + [LawfulMonadLiftT m SetM] [EvalDistCompatible m] + {α β : Type} {mx : m α} {my : α → m β} {q : β → Prop} {r : ENNReal} + (hfail : Pr[⊥ | mx] = 0) (h : ∀ x ∈ support mx, r ≤ Pr[q | my x]) : + r ≤ Pr[q | mx >>= my] := by + have htrue : Pr[fun _ : α => True | mx] = (1 : ENNReal) := by + exact probEvent_eq_one (mx := mx) (p := fun _ : α => True) ⟨hfail, by simp⟩ + have hmul := mul_le_probEvent_bind (mx := mx) (my := my) + (p := fun _ : α => True) (q := q) (r := 1) (r' := r) + (by simp [htrue]) (fun x hx _ => h x hx) + simpa using hmul + +omit [Field F] [DecidableEq F] in +private theorem simulateQ_outer_x_challenge [Inhabited F] : + simulateQ + (impl + QueryImpl.liftTarget (StateT σ ProbComp) + (ProtocolSpec.challengeQueryImpl (pSpec := outerPSpec F n params))) + (OracleComp.liftComp + (ProtocolSpec.getChallenge (outerPSpec F n params) + (outerChallengeXIdx F n M params)) + (oSpec + [(outerPSpec F n params).Challenge]ₒ)) + = (StateT.lift ($ᵗ F) : StateT σ ProbComp F) := by + rw [QueryImpl.simulateQ_add_liftComp_right, simulateQ_liftTarget] + have h : + simulateQ (ProtocolSpec.challengeQueryImpl (pSpec := outerPSpec F n params)) + (ProtocolSpec.getChallenge (outerPSpec F n params) + (outerChallengeXIdx F n M params)) + = ($ᵗ F : ProbComp F) := by + unfold ProtocolSpec.getChallenge + change simulateQ (ProtocolSpec.challengeQueryImpl (pSpec := outerPSpec F n params)) + (liftM (OracleSpec.query (spec := [(outerPSpec F n params).Challenge]ₒ) + ⟨outerChallengeXIdx F n M params, ()⟩) : + OracleComp ([(outerPSpec F n params).Challenge]ₒ) F) + = ($ᵗ F : ProbComp F) + rw [simulateQ_query] + change id <$> ($ᵗ F : ProbComp F) = ($ᵗ F : ProbComp F) + simp + rw [h] + rfl + +omit [Field F] [DecidableEq F] in +private theorem simulateQ_outer_batch_challenge [Inhabited F] : + simulateQ + (impl + QueryImpl.liftTarget (StateT σ ProbComp) + (ProtocolSpec.challengeQueryImpl (pSpec := outerPSpec F n params))) + (OracleComp.liftComp + (ProtocolSpec.getChallenge (outerPSpec F n params) + (outerChallengeBatchIdx F n M params)) + (oSpec + [(outerPSpec F n params).Challenge]ₒ)) + = (StateT.lift ($ᵗ BatchingChallenge F n params.numGroups) : + StateT σ ProbComp (BatchingChallenge F n params.numGroups)) := by + rw [QueryImpl.simulateQ_add_liftComp_right, simulateQ_liftTarget] + have h : + simulateQ (ProtocolSpec.challengeQueryImpl (pSpec := outerPSpec F n params)) + (ProtocolSpec.getChallenge (outerPSpec F n params) + (outerChallengeBatchIdx F n M params)) + = ($ᵗ BatchingChallenge F n params.numGroups : + ProbComp (BatchingChallenge F n params.numGroups)) := by + unfold ProtocolSpec.getChallenge + change simulateQ (ProtocolSpec.challengeQueryImpl (pSpec := outerPSpec F n params)) + (liftM (OracleSpec.query (spec := [(outerPSpec F n params).Challenge]ₒ) + ⟨outerChallengeBatchIdx F n M params, ()⟩) : + OracleComp ([(outerPSpec F n params).Challenge]ₒ) + (BatchingChallenge F n params.numGroups)) + = ($ᵗ BatchingChallenge F n params.numGroups : + ProbComp (BatchingChallenge F n params.numGroups)) + rw [simulateQ_query] + change id <$> ($ᵗ BatchingChallenge F n params.numGroups : + ProbComp (BatchingChallenge F n params.numGroups)) = + ($ᵗ BatchingChallenge F n params.numGroups : + ProbComp (BatchingChallenge F n params.numGroups)) + simp + rw [h] + rfl private theorem support_pure_eq {m : Type → Type*} [Monad m] [LawfulMonad m] [MonadLiftT m SetM] [LawfulMonadLiftT m SetM] {α : Type} {x y : α} (h : y ∈ support (pure x : m α)) : y = x := by simpa [mem_support_pure_iff] using h -private theorem mem_support_of_mem_support_liftComp {ι τ α : Type} {spec : OracleSpec ι} - {superSpec : OracleSpec τ} [MonadLiftT (OracleQuery spec) (OracleQuery superSpec)] - (oa : OracleComp spec α) (x : α) : - x ∈ support (oa.liftComp superSpec) → x ∈ support oa := by - intro hx - induction oa using OracleComp.inductionOn generalizing x with - | pure y => - simpa using hx - | query_bind q oa ih => - rw [OracleComp.liftComp_bind, mem_support_bind_iff] at hx - rw [mem_support_bind_iff] - obtain ⟨u, _hu, hx⟩ := hx - exact ⟨u, OracleComp.mem_support_query q u, ih u x hx⟩ - private theorem support_simulateQ_run_fst_subset {ι : Type} {spec : OracleSpec ι} {m : Type → Type*} [Monad m] [LawfulMonad m] [MonadLiftT m SetM] [LawfulMonadLiftT m SetM] {σ α : Type} @@ -333,9 +436,164 @@ private theorem support_simulateQ_run_fst_subset {ι : Type} {spec : OracleSpec rw [StateT.run'_eq, support_map, Set.mem_image] exact ⟨(y, s'), h, rfl⟩) -set_option linter.unusedSectionVars false in +private theorem mem_support_liftM_oracleComp {ι τ : Type} {spec : OracleSpec ι} + {superSpec : OracleSpec τ} {α : Type} + [MonadLift (OracleQuery spec) (OracleQuery superSpec)] + {oa : OracleComp spec α} {x : α} + (h : x ∈ support (liftM oa : OracleComp superSpec α)) : x ∈ support oa := by + rw [← OracleComp.liftComp_eq_liftM (superSpec := superSpec) oa] at h + exact OracleComp.mem_support_of_mem_support_liftComp oa x h + +set_option linter.flexible false in +private theorem reduction_run_prover_mem {ι : Type} {oSpec : OracleSpec ι} + {StmtIn WitIn StmtOut WitOut : Type} {n : ℕ} {pSpec : ProtocolSpec n} + (R : Reduction oSpec StmtIn WitIn StmtOut WitOut pSpec) + (stmt : StmtIn) (wit : WitIn) + (tr : pSpec.FullTranscript) (out : StmtOut) (witOut : WitOut) (verOut : StmtOut) + (h : some ((tr, out, witOut), verOut) ∈ support (R.run stmt wit).run) : + (tr, out, witOut) ∈ support (Prover.run stmt wit R.prover) := by + simp [Reduction.run, OptionT.run_bind] at h + rcases h with ⟨a, a_1, b, hp, hv⟩ + suffices hEq : (a, a_1, b) = (tr, out, witOut) by + simpa [hEq] using hp + simp [Option.elimM] at hv + rcases hv with ⟨i, _hi, hv⟩ + cases i with + | none => simp at hv + | some verCandidate => + simp at hv + rcases hv with ⟨_, rfl, rfl, rfl⟩ + rfl + +set_option linter.flexible false in +private theorem seqCompose_prover_preserves {ι : Type} {oSpec : OracleSpec ι} (m : ℕ) : + ∀ {Stmt : Fin (m + 1) → Type} {O : Type} + {rounds : Fin m → ℕ} {pSpec : ∀ i, ProtocolSpec (rounds i)} + (P : (i : Fin m) → Prover oSpec (Stmt i.castSucc) Unit (Stmt i.succ) Unit (pSpec i)) + (proj : (i : Fin (m + 1)) → Stmt i → O), + (∀ (i : Fin m) (stmt : Stmt i.castSucc) (out : Stmt i.succ) + (tr : (pSpec i).FullTranscript), + (tr, out, ()) ∈ support (Prover.run stmt () (P i)) → + proj i.succ out = proj i.castSucc stmt) → + ∀ (stmt : Stmt 0) (out : Stmt (Fin.last m)) + (tr : (ProtocolSpec.seqCompose pSpec).FullTranscript), + (tr, out, ()) ∈ support (Prover.run stmt () (Prover.seqCompose Stmt (fun _ => Unit) P)) → + proj (Fin.last m) out = proj 0 stmt := by + induction m with + | zero => + intro Stmt O rounds pSpec P proj hP stmt out tr h + rw [Prover.seqCompose_zero] at h + simp [Prover.run, Prover.id, Prover.runToRound] at h + cases h + rfl + | succ m ih => + intro Stmt O rounds pSpec P proj hP stmt out tr h + let tailSpec : ProtocolSpec (Fin.vsum fun i : Fin m => rounds (Fin.succ i)) := + ProtocolSpec.seqCompose (fun i : Fin m => pSpec (Fin.succ i)) + let tail : Prover oSpec (Stmt (Fin.succ 0)) Unit (Stmt (Fin.last (m + 1))) Unit + tailSpec := + Prover.seqCompose (fun i => Stmt i.succ) (fun _ => Unit) + (fun i => P (Fin.succ i)) + let trApp : ((pSpec 0) ++ₚ tailSpec).FullTranscript := tr + have h' : (trApp, out, ()) ∈ support (((do + let ⟨tr₁, stmt₂, wit₂⟩ ← liftM (Prover.run stmt () (P 0)) + let ⟨tr₂, stmt₃, wit₃⟩ ← liftM (Prover.run stmt₂ wit₂ tail) + pure (tr₁ ++ₜ tr₂, stmt₃, wit₃)) : + OracleComp (oSpec + [((pSpec 0) ++ₚ tailSpec).Challenge]ₒ) + (((pSpec 0) ++ₚ tailSpec).FullTranscript × Stmt (Fin.last (m + 1)) × Unit))) := by + rw [← @Prover.append_run ι oSpec (Stmt 0) Unit (Stmt (Fin.succ 0)) Unit + (Stmt (Fin.last (m + 1))) Unit (rounds 0) + (Fin.vsum fun i : Fin m => rounds (Fin.succ i)) + (pSpec 0) tailSpec (P 0) tail stmt ()] + simpa [trApp, tail, tailSpec, Prover.seqCompose_succ] using h + rw [mem_support_bind_iff] at h' + rcases h' with ⟨⟨tr₁, stmt₂, wit₂⟩, h₁, hrest⟩ + cases wit₂ + rw [mem_support_bind_iff] at hrest + rcases hrest with ⟨⟨tr₂, stmt₃, wit₃⟩, h₂, hpure⟩ + cases wit₃ + rw [support_pure, Set.mem_singleton_iff] at hpure + injection hpure with htr hout + have h₁' : (tr₁, stmt₂, ()) ∈ support (Prover.run stmt () (P 0)) := + mem_support_liftM_oracleComp + (superSpec := oSpec + [((pSpec 0) ++ₚ tailSpec).Challenge]ₒ) h₁ + have h₂' : (tr₂, out, ()) ∈ support + (Prover.run stmt₂ () + (Prover.seqCompose (fun i => Stmt i.succ) (fun _ => Unit) + (fun i => P (Fin.succ i)))) := by + cases hout + exact mem_support_liftM_oracleComp + (superSpec := oSpec + [((pSpec 0) ++ₚ tailSpec).Challenge]ₒ) h₂ + calc + proj (Fin.last (m + 1)) out = proj (Fin.succ (Fin.last m)) out := rfl + _ = proj (Fin.succ (0 : Fin (m + 1))) stmt₂ := by + exact ih + (P := fun i => P (Fin.succ i)) + (proj := fun i => proj (Fin.succ i)) + (fun i stmt out tr h => hP (Fin.succ i) stmt out tr h) + stmt₂ out tr₂ h₂' + _ = proj 0 stmt := hP 0 stmt stmt₂ tr₁ h₁' + +private theorem sumcheckSingleRound_prover_preserves_oracleStmt + {R : Type} [CommSemiring R] [DecidableEq R] [SampleableType R] + {n deg : ℕ} {m : ℕ} (D : Fin m ↪ R) {ι : Type} (oSpec : OracleSpec ι) (i : Fin n) + (stmt : Sumcheck.Spec.StatementRound R n i.castSucc × + (∀ j, Sumcheck.Spec.OracleStatement R n deg j)) + (out : Sumcheck.Spec.StatementRound R n i.succ × + (∀ j, Sumcheck.Spec.OracleStatement R n deg j)) + (tr : (Sumcheck.Spec.SingleRound.pSpec R deg).FullTranscript) + (h : (tr, out, ()) ∈ support + (Prover.run stmt () + ((Sumcheck.Spec.SingleRound.oracleReduction R n deg D oSpec i).toReduction.prover))) : + out.2 = stmt.2 := by + rw [Sumcheck.Spec.SingleRound.oracleReduction, OracleReduction.toReduction, + OracleReduction.liftContext, OracleProver.liftContext, Prover.liftContext_run] at h + rw [mem_support_bind_iff] at h + rcases h with ⟨⟨trInner, innerOut, innerWit⟩, _, hout⟩ + rw [support_pure, Set.mem_singleton_iff] at hout + cases hout + rcases stmt with ⟨⟨oldTarget, challenges⟩, oStmt⟩ + rcases innerOut with ⟨⟨newTarget, chal⟩, oStmt'⟩ + rfl + +private theorem sumcheckProver_preserves_oracleStmt + {R : Type} [CommSemiring R] [DecidableEq R] [SampleableType R] + {deg : ℕ} {m : ℕ} (D : Fin m ↪ R) {ι : Type} (oSpec : OracleSpec ι) (n : ℕ) + (stmt : Sumcheck.Spec.StatementRound R n 0 × + (∀ j, Sumcheck.Spec.OracleStatement R n deg j)) + (out : Sumcheck.Spec.StatementRound R n (Fin.last n) × + (∀ j, Sumcheck.Spec.OracleStatement R n deg j)) + (tr : (Sumcheck.Spec.pSpec R deg n).FullTranscript) + (h : (tr, out, ()) ∈ support + (Prover.run stmt () ((Sumcheck.Spec.oracleReduction R deg D n oSpec).toReduction.prover))) : + out.2 = stmt.2 := by + refine @seqCompose_prover_preserves ι oSpec n + (Stmt := fun i => Sumcheck.Spec.StatementRound R n i × + (∀ j, Sumcheck.Spec.OracleStatement R n deg j)) + (O := ∀ j, Sumcheck.Spec.OracleStatement R n deg j) + (rounds := fun _ => 2) + (pSpec := fun _ => Sumcheck.Spec.SingleRound.pSpec R deg) + (P := fun i => (Sumcheck.Spec.SingleRound.oracleReduction R n deg D oSpec i).toReduction.prover) + (proj := fun _ stmt => stmt.2) ?_ stmt out tr ?_ + · intro i stmt out tr h + exact sumcheckSingleRound_prover_preserves_oracleStmt D oSpec i stmt out tr h + · simpa [Sumcheck.Spec.oracleReduction, OracleReduction.seqCompose, OracleProver.seqCompose, + OracleReduction.toReduction] using h + +local macro "peel_sim_map " h:ident " with " pat:rcasesPat : tactic => + `(tactic| + (erw [simulateQ_map, StateT.run_map] at $h:ident + rw [support_map, Set.mem_image] at $h:ident + obtain $pat := $h)) + +local macro "peel_sim_bind " h:ident " with " pat:rcasesPat : tactic => + `(tactic| + (erw [simulateQ_bind, StateT.run_bind] at $h:ident + rw [mem_support_bind_iff] at $h:ident + obtain $pat := $h)) + open OracleComp OracleSpec in -omit [SampleableType F] in +omit σ init impl [DecidableEq F] [SampleableType F] in /-- Simulating the scan-free outer verifier against the honest oracles leaves only the public challenge data packaged as the outer statement. -/ theorem outerVerify_simulateQ_eq (stmt : StmtIn F n M) (oStmt : ∀ i, OStmtIn F n M i) @@ -359,6 +617,8 @@ private theorem Fin.induction_four {motive : Fin 5 → Sort*} {zero : motive 0} Fin.induction (motive := motive) zero succ (Fin.last 4) = succ 3 (succ 2 (succ 1 (succ 0 zero))) := rfl +set_option maxHeartbeats 1000000 in +set_option linter.flexible false in /-- Completeness of the outer LogUp phase: the honest outer prover reaches the zero-sum handoff relation, except with the pole-sampling error. -/ theorem logup_outer_completeness [Inhabited F] : @@ -383,36 +643,18 @@ theorem logup_outer_completeness [Inhabited F] : obtain ⟨s, -, hout⟩ := hout simp only [StateT.run'_eq, support_map, Set.mem_image] at hout obtain ⟨⟨_, s'⟩, hout, rfl⟩ := hout - erw [simulateQ_bind, StateT.run_bind] at hout - rw [mem_support_bind_iff] at hout - obtain ⟨⟨pres, s2⟩, hpres, hver⟩ := hout + peel_sim_bind hout with ⟨⟨pres, s2⟩, hpres, hver⟩ simp only [OptionT.lift, OptionT.mk] at hpres - erw [simulateQ_map, StateT.run_map] at hpres - rw [support_map, Set.mem_image] at hpres - obtain ⟨⟨pval, sp⟩, hpval, hpeq⟩ := hpres - erw [simulateQ_map, StateT.run_map] at hpval - rw [support_map, Set.mem_image] at hpval - obtain ⟨⟨a, sa⟩, ha, hpval_eq⟩ := hpval - erw [simulateQ_bind, StateT.run_bind] at ha - rw [mem_support_bind_iff] at ha - obtain ⟨⟨b, sb⟩, hb, ha3⟩ := ha - erw [simulateQ_map, StateT.run_map] at ha3 - rw [support_map, Set.mem_image] at ha3 - obtain ⟨⟨zlam, szlam⟩, hzlam, ha3eq⟩ := ha3 + peel_sim_map hpres with ⟨⟨pval, sp⟩, hpval, hpeq⟩ + peel_sim_map hpval with ⟨⟨a, sa⟩, ha, hpval_eq⟩ + peel_sim_bind ha with ⟨⟨b, sb⟩, hb, ha3⟩ + peel_sim_map ha3 with ⟨⟨zlam, szlam⟩, hzlam, ha3eq⟩ -- round 1: peel `hb` to reach the `x` challenge query - erw [simulateQ_map, StateT.run_map] at hb - rw [support_map, Set.mem_image] at hb - obtain ⟨⟨c, sc⟩, hc, hbeq⟩ := hb - erw [simulateQ_bind, StateT.run_bind] at hc - rw [mem_support_bind_iff] at hc - obtain ⟨⟨d, sd⟩, hd, hc2⟩ := hc - erw [simulateQ_map, StateT.run_map] at hc2 - rw [support_map, Set.mem_image] at hc2 - obtain ⟨⟨xval, sx⟩, hx, hc2eq⟩ := hc2 + peel_sim_map hb with ⟨⟨c, sc⟩, hc, hbeq⟩ + peel_sim_bind hc with ⟨⟨d, sd⟩, hd, hc2⟩ + peel_sim_map hc2 with ⟨⟨xval, sx⟩, hx, hc2eq⟩ -- round 0 is deterministic (a pure `honestMultiplicity` send) - erw [simulateQ_map, StateT.run_map] at hd - rw [support_map, Set.mem_image] at hd - obtain ⟨⟨e, se⟩, he, hdeq⟩ := hd + peel_sim_map hd with ⟨⟨e, se⟩, he, hdeq⟩ erw [simulateQ_pure, StateT.run_pure] at he rw [support_pure, Set.mem_singleton_iff] at he obtain ⟨rfl, rfl⟩ := Prod.mk.inj he @@ -490,51 +732,12 @@ theorem logup_outer_completeness [Inhabited F] : evalOnHypercube (tableOracle oStmt) v := by simpa [inputRelation] using hIn have hchar : ∀ a : F, lookupMultiplicityCount oStmt a ≠ 0 → - (tableMultiplicityCount oStmt a : F) ≠ 0 := by - intro a hlookup - classical - have hlookupCard : - ((Finset.univ : Finset (Fin M × Hypercube n)).filter fun ix => - evalOnHypercube (columnOracle oStmt ix.1) ix.2 = a).card ≠ 0 := by - simpa [lookupMultiplicityCount] using hlookup - obtain ⟨⟨j, u⟩, hju⟩ := Finset.card_ne_zero.mp hlookupCard - simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hju - obtain ⟨v, hv⟩ := hcols j u - have hvTable : evalOnHypercube (tableOracle oStmt) v = a := hv.symm.trans hju - have htablePos : 0 < tableMultiplicityCount oStmt a := by - rw [tableMultiplicityCount, Finset.card_pos] - exact ⟨v, by simp [hvTable]⟩ - have hMpos : 0 < M := lt_of_le_of_lt (Nat.zero_le j.val) j.isLt - have htable_le_card : - tableMultiplicityCount oStmt a ≤ Fintype.card (Hypercube n) := by - rw [tableMultiplicityCount, ← Finset.card_univ] - exact Finset.card_filter_le _ _ - have hcard_hypercube : Fintype.card (Hypercube n) = 2 ^ n := by - simp [Hypercube] - have hpow_le : 2 ^ n ≤ M * 2 ^ n := by - have hMone : 1 ≤ M := Nat.succ_le_of_lt hMpos - nth_rewrite 1 [← Nat.one_mul (2 ^ n)] - exact Nat.mul_le_mul_right (2 ^ n) hMone - have htable_lt_char : tableMultiplicityCount oStmt a < ringChar F := by - calc - tableMultiplicityCount oStmt a ≤ Fintype.card (Hypercube n) := htable_le_card - _ = 2 ^ n := hcard_hypercube - _ ≤ M * 2 ^ n := hpow_le - _ < ringChar F := stmt.charLarge - intro hzero - have hdvd : ringChar F ∣ tableMultiplicityCount oStmt a := - (ringChar.spec F (tableMultiplicityCount oStmt a)).1 hzero - exact (Nat.not_dvd_of_pos_of_lt htablePos htable_lt_char) hdvd - have hpoles : ∀ (i : TermIdx M) (u : Hypercube n), termPhi oStmt xval i u ≠ 0 := by - intro i u - cases hti : termToInput i with - | table => - rw [termPhi, hti] - simpa [phi, tableOracle] using hNoTablePoles u - | column j => - obtain ⟨v, hv⟩ := hcols j u - rw [termPhi, hti, phi] - simpa [hv] using hNoTablePoles v + (tableMultiplicityCount oStmt a : F) ≠ 0 := + fun _ hlookup => + tableMultiplicityCount_cast_ne_zero_of_lookupMultiplicityCount_ne_zero + stmt oStmt hcols hlookup + have hpoles : ∀ (i : TermIdx M) (u : Hypercube n), termPhi oStmt xval i u ≠ 0 := + termPhi_ne_zero_of_table_poles oStmt xval hcols hNoTablePoles let stmtAfter : StmtAfterOuter F n M params := { xChallenge := xval, zChallenge := zlam.1, batchingScalars := zlam.2 } let oStmtAfter : ∀ i, OStmtAfterOuter F n M params i := @@ -560,9 +763,110 @@ theorem logup_outer_completeness [Inhabited F] : cases i <;> simp [outerMultiplicityMessageIdx, outerHelpersMessageIdx] · -- pole-probability bound `Pr[x avoids all table poles] ≥ 1 - |H|/|F|` - sorry + refine le_trans (uniform_avoids_table_poles_prob (F := F) (n := n) (M := M) oStmt) ?_ + rw [OptionT.mk_bind] + apply le_probEvent_bind_of_forall_le + · simp + · intro s hs + clear hs + simp only [OptionT.run_mk, OptionT.run_bind, OptionT.run_lift, Option.elimM, + map_eq_bind_pure_comp, bind_assoc, pure_bind, bind_pure_comp, simulateQ_bind, + StateT.run'_eq, StateT.run_bind, Function.comp_apply] + erw [simulateQ_bind] + erw [simulateQ_bind] + erw [simulateQ_bind] + erw [simulateQ_bind] + erw [simulateQ_pure] + simp only [pure_bind] + erw [simulateQ_pure] + simp only [pure_bind] + erw [simulateQ_bind] + rw [QueryImpl.simulateQ_add_liftComp_right, simulateQ_liftTarget] + erw [simulateQ_query] + simp only [OracleSpec.query_def, OracleQuery.input_apply, ProtocolSpec.challengeQueryImpl, + OracleQuery.cont_apply, outerPSpec] + simp + · simpa [probEvent_uniformSample] using + (mul_le_probEvent_bind + (mx := (liftM ($ᵗ F : ProbComp F) : OptionT ProbComp F)) + (p := fun x : F => ∀ u : Hypercube n, + x + evalOnHypercube (tableOracle oStmt) u ≠ 0) + (r := Pr[fun x : F => ∀ u : Hypercube n, + x + evalOnHypercube (tableOracle oStmt) u ≠ 0 | $ᵗ F]) + (r' := 1) (by simp) (by + intro x hx hGood + erw [simulateQ_pure] + simp only [StateT.run_pure, liftM_pure, pure_bind] + erw [simulateQ_pure] + simp only [StateT.run_pure, liftM_pure, pure_bind] + rw [one_le_probEvent_iff, probEvent_eq_one_iff] + constructor + · rw [probFailure_bind_eq_zero_iff] + constructor + · simp [OptionT.probFailure_liftM] + · intro batchState hbatch + rw [probFailure_bind_eq_zero_iff] + constructor + · simp [OptionT.probFailure_liftM] + · intro verifiedState hverified + have hSome : ∃ accepted, verifiedState.1 = some accepted := by + cases hfirst : verifiedState.1 with + | none => + exfalso + have hv := hverified + rw [OptionT.support_liftM] at hv + simp only [OracleVerifier.toVerifier] at hv + erw [outerVerify_simulateQ_eq] at hv + erw [simulateQ_bind, StateT.run_bind] at hv + rw [mem_support_bind_iff] at hv + obtain ⟨⟨ver, sver⟩, hverPure, hverRest⟩ := hv + erw [simulateQ_pure, StateT.run_pure] at hverPure + rw [support_pure, Set.mem_singleton_iff] at hverPure + obtain ⟨rfl, rfl⟩ := Prod.mk.inj hverPure + erw [simulateQ_pure, StateT.run_pure] at hverRest + rw [support_pure, Set.mem_singleton_iff] at hverRest + cases hverRest + simp at hfirst + | some accepted => + exact ⟨accepted, rfl⟩ + rcases hSome with ⟨accepted, haccepted⟩ + rw [OptionT.probFailure_eq, OptionT.run_mk, haccepted] + simp + · intro out hout + rw [mem_support_bind_iff] at hout + obtain ⟨batchState, hbatch, hout⟩ := hout + rw [OptionT.support_liftM] at hbatch + erw [simulateQ_bind, StateT.run_bind] at hbatch + rw [mem_support_bind_iff] at hbatch + obtain ⟨⟨batch, sbatch⟩, hbatchSample, hbatchPure⟩ := hbatch + erw [simulateQ_pure, StateT.run_pure] at hbatchPure + rw [support_pure, Set.mem_singleton_iff] at hbatchPure + subst batchState + rw [mem_support_bind_iff] at hout + obtain ⟨verifiedState, hverified, hout⟩ := hout + rw [OptionT.mem_support_iff] at hout + simp only [OptionT.run_mk, support_pure, Set.mem_singleton_iff] at hout + have hverified' := hverified + rw [OptionT.support_liftM] at hverified' + simp only [OracleVerifier.toVerifier] at hverified' + erw [outerVerify_simulateQ_eq] at hverified' + simp [outerChallengeXIdx, outerChallengeBatchIdx, + ProtocolSpec.FullTranscript.challenges, ProtocolSpec.Transcript.concat, + Fin.snoc] at hverified' + erw [simulateQ_bind, StateT.run_bind] at hverified' + rw [mem_support_bind_iff] at hverified' + obtain ⟨⟨verOpt, sver⟩, hverOpt, hverified''⟩ := hverified' + erw [simulateQ_pure, StateT.run_pure] at hverOpt + rw [support_pure, Set.mem_singleton_iff] at hverOpt + obtain ⟨rfl, rfl⟩ := Prod.mk.inj hverOpt + erw [simulateQ_pure, StateT.run_pure] at hverified'' + rw [support_pure, Set.mem_singleton_iff] at hverified'' + subst verifiedState + cases hout + exact hGood)) +set_option linter.flexible false in /-- Lens-completeness for the LogUp→Sumcheck lens: `proj` builds the zero-sum instance, and `lift` retains the outer LogUp data together with sumcheck's final valid point claim. -/ instance logupSumcheckLensComplete : @@ -581,10 +885,17 @@ instance logupSumcheckLensComplete : intro outerStmt outerWit innerStmtOut innerWitOut hCompat _ hRelOut have hOStmt : innerStmtOut.2 = logupSumcheckOracleStmt F n M params outerStmt.1 outerStmt.2 := by - -- The generic sumcheck prover preserves its polynomial oracle statement through every round. - -- A small support-preservation lemma for `Sumcheck.Spec.oracleReduction` should discharge - -- this from `hCompat`. - sorry + simp [Reduction.compatContext] at hCompat + rcases hCompat with ⟨td, vOut, verOStmt, hRun⟩ + have hProver := reduction_run_prover_mem + ((logupConcreteSumcheckOracleReduction oSpec F n M params).toReduction) + ((logupSumcheckContextLens F n M params).toContext.proj (outerStmt, outerWit)).1 + ((logupSumcheckContextLens F n M params).toContext.proj (outerStmt, outerWit)).2 + td innerStmtOut innerWitOut (vOut, verOStmt) hRun + have hPres := sumcheckProver_preserves_oracleStmt (booleanDomain F) oSpec n + ((logupSumcheckContextLens F n M params).toContext.proj (outerStmt, outerWit)).1 + innerStmtOut td hProver + simpa [logupSumcheckContextLens] using hPres cases innerWitOut have hPair : (innerStmtOut.1, logupSumcheckOracleStmt F n M params outerStmt.1 outerStmt.2) = @@ -606,12 +917,226 @@ theorem logupSumcheckPhaseCompleteness : (Sumcheck.Spec.oracleReduction_perfectCompleteness F (logupSumcheckDegree M params) (booleanDomain F) n oSpec) +set_option linter.flexible false in +omit [Fintype F] [SampleableType F] in /-- Completeness of the final LogUp point check: once sumcheck's final claim is valid for the retained LogUp polynomial, the verifier's oracle queries reconstruct the same value. -/ theorem finalCheckCompleteness : (finalCheckOracleReduction oSpec F n M params).completeness init impl (logupAfterSumcheckRelation F n M params) outputRelation 0 := by - sorry + unfold OracleReduction.completeness Reduction.completeness + rintro ⟨stmt, oStmt⟩ ⟨⟩ hRel + have hExpected : + MvPolynomial.eval stmt.finalClaim.challenges + (logupQPolynomial F n M params stmt.outer oStmt) = + stmt.finalClaim.target := by + unfold logupAfterSumcheckRelation Sumcheck.Spec.relationRound at hRel + simp only [Set.mem_setOf_eq, logupSumcheckOracleStmt, logupSumcheckPolynomial] at hRel + have tailSize_zero : n - (Fin.last n : Fin (n + 1)) = 0 := by simp + let tail0 : Fin (n - (Fin.last n : Fin (n + 1))) → F := + fun i => Fin.elim0 (Fin.cast (by simp) i) + have hfinalPoint : + Fin.append stmt.finalClaim.challenges tail0 ∘ + Fin.cast (Sumcheck.Spec.relationRound._proof_1 n (Fin.last n)) = + stmt.finalClaim.challenges := by + funext i + change Fin.append stmt.finalClaim.challenges tail0 + (Fin.cast (Sumcheck.Spec.relationRound._proof_1 n (Fin.last n)) i) = + stmt.finalClaim.challenges i + rw [Fin.append_right_nil stmt.finalClaim.challenges tail0 tailSize_zero] + congr 1 + have hsum : + (∑ x ∈ Fintype.piFinset fun _ : Fin (n - (Fin.last n : Fin (n + 1))) => + Finset.univ.map (booleanDomain F), + MvPolynomial.eval + (Fin.append stmt.finalClaim.challenges x ∘ + Fin.cast (Sumcheck.Spec.relationRound._proof_1 n (Fin.last n))) + (logupQPolynomial F n M params stmt.outer oStmt)) = + MvPolynomial.eval stmt.finalClaim.challenges + (logupQPolynomial F n M params stmt.outer oStmt) := by + rw [Finset.sum_eq_single tail0] + · rw [hfinalPoint] + rfl + · intro b _ hb + exact False.elim (hb (funext fun i => Fin.elim0 (Fin.cast (by simp) i))) + · intro hnot + exact False.elim (hnot (by + rw [Fintype.mem_piFinset] + intro i + exact Fin.elim0 (Fin.cast tailSize_zero i))) + exact hsum ▸ hRel + have hGuard : + qAtPoint (canonicalGroups params) stmt.outer.xChallenge stmt.outer.zChallenge + stmt.finalClaim.challenges stmt.outer.batchingScalars + { multiplicity := lagrangeOracleEval (oStmt .multiplicity) stmt.finalClaim.challenges + table := lagrangeOracleEval (oStmt (.input .table)) stmt.finalClaim.challenges + columns := fun i => + lagrangeOracleEval (oStmt (.input (.column i))) stmt.finalClaim.challenges + helpers := fun k => + lagrangeOracleEval ((oStmt .helpers) k) stmt.finalClaim.challenges } = + stmt.finalClaim.target := by + rw [← hExpected] + exact (logupQPolynomial_eval_point F n M params stmt.outer oStmt + stmt.finalClaim.challenges + { multiplicity := lagrangeOracleEval (oStmt .multiplicity) stmt.finalClaim.challenges + table := lagrangeOracleEval (oStmt (.input .table)) stmt.finalClaim.challenges + columns := fun i => + lagrangeOracleEval (oStmt (.input (.column i))) stmt.finalClaim.challenges + helpers := fun k => + lagrangeOracleEval ((oStmt .helpers) k) stmt.finalClaim.challenges } + (by simp [logupPointEvaluationsAgree])).symm + let qImpl : + QueryImpl + (oSpec + ([OStmtAfterSumcheck F n M params]ₒ + [finalCheckPSpec.Message]ₒ)) + (OracleComp oSpec) := + OracleInterface.simOracle2 (T₁ := OStmtAfterSumcheck F n M params) + (T₂ := finalCheckPSpec.Message) oSpec oStmt + (fun i : finalCheckPSpec.MessageIdx => Fin.elim0 i) + have hquery : + ∀ (i : OuterOracleIdx M) + (q : (instOStmtAfterOuterOracleInterface (F := F) (n := n) (params := params) i).Query), + simulateQ qImpl ((finalCheckQuery oSpec F n M params i q).run) = + (pure (some ((instOStmtAfterOuterOracleInterface + (F := F) (n := n) (params := params) i).answer (oStmt i) q)) : + OracleComp oSpec _) := by + intro i q + simp only [finalCheckQuery, OptionT.run_mk, simulateQ_map, qImpl, + OracleInterface.simOracle2, QueryImpl.addLift_def, simulateQ_query, + QueryImpl.add_apply_inr, QueryImpl.liftTarget_apply, QueryImpl.add, + OracleInterface.simOracle0, OracleInterface.answer, OracleQuery.cont_query, + OracleQuery.input_query] + change some <$> id <$> + (pure (ReaderT.run (OracleInterface.toOC.impl q) (oStmt i)) : + OracleComp oSpec _) = + (pure (some (ReaderT.run (OracleInterface.toOC.impl q) (oStmt i))) : + OracleComp oSpec _) + simp + have hVerify : + simulateQ qImpl + ((finalCheckVerifier oSpec F n M params).verify stmt (fun i => Fin.elim0 i)).run = + (pure (some ()) : OracleComp oSpec (Option StmtOut)) := by + simp only [finalCheckVerifier, OptionT.run_bind, OptionT.run_pure] + erw [simulateQ_bind] + rw [hquery .multiplicity stmt.finalClaim.challenges] + simp only [pure_bind, Option.elim_some] + erw [simulateQ_bind] + rw [hquery (.input .table) stmt.finalClaim.challenges] + simp only [pure_bind, Option.elim_some] + let colValue := fun i : Fin M => + lagrangeOracleEval (oStmt (.input (.column i))) stmt.finalClaim.challenges + have hcols := simulateQ_optionT_mapM_pure qImpl + (fun i : Fin M => + finalCheckQuery oSpec F n M params (.input (.column i)) stmt.finalClaim.challenges) + colValue (Vector.finRange M) (by + intro i + simpa [colValue, OracleInterface.answer, lagrangeOracleEval] using + hquery (.input (.column i)) stmt.finalClaim.challenges) + erw [simulateQ_option_elimM] + erw [hcols] + simp only [pure_bind, Option.elimM, Option.elim_some] + let helperValue := fun k : Fin params.numGroups => + lagrangeOracleEval ((oStmt .helpers) k) stmt.finalClaim.challenges + have hhelpers := simulateQ_optionT_mapM_pure qImpl + (fun k : Fin params.numGroups => + finalCheckQuery oSpec F n M params .helpers ⟨k, stmt.finalClaim.challenges⟩) + helperValue (Vector.finRange params.numGroups) (by + intro k + simpa [helperValue, OracleInterface.answer, lagrangeOracleEval] using + hquery .helpers ⟨k, stmt.finalClaim.challenges⟩) + erw [simulateQ_option_elimM] + erw [hhelpers] + simp only [pure_bind, Option.elimM, Option.elim_some] + have hGuard' : + qAtPoint (canonicalGroups params) stmt.outer.xChallenge stmt.outer.zChallenge + stmt.finalClaim.challenges stmt.outer.batchingScalars + { multiplicity := OracleInterface.answer (oStmt .multiplicity) + stmt.finalClaim.challenges + table := OracleInterface.answer (oStmt (.input .table)) stmt.finalClaim.challenges + columns := colValue + helpers := helperValue } = + stmt.finalClaim.target := by + simpa [OracleInterface.answer, lagrangeOracleEval, colValue, helperValue] using hGuard + erw [simulateQ_option_elimM] + simp [guard, hGuard', OptionT.run_pure, Option.elimM] + have hVerifyDefault : + simulateQ + (OracleInterface.simOracle2 oSpec oStmt + (ProtocolSpec.FullTranscript.messages (default : finalCheckPSpec.FullTranscript))) + (((finalCheckVerifier oSpec F n M params).verify stmt + (ProtocolSpec.FullTranscript.challenges + (default : finalCheckPSpec.FullTranscript))).run) = + (pure (some ()) : OracleComp oSpec (Option StmtOut)) := by + simpa [qImpl, finalCheckPSpec] using hVerify + have hVerifyDefaultT : + OptionT.run + (simulateQ + (OracleInterface.simOracle2 oSpec oStmt + (ProtocolSpec.FullTranscript.messages (default : finalCheckPSpec.FullTranscript))) + ((finalCheckVerifier oSpec F n M params).verify stmt + (ProtocolSpec.FullTranscript.challenges + (default : finalCheckPSpec.FullTranscript)))) = + (pure (some ()) : OracleComp oSpec (Option StmtOut)) := by + simpa [OptionT.run] using hVerifyDefault + have hVerifyDefaultT' : + OptionT.run + (simulateQ + (OracleInterface.simOracle2 oSpec oStmt + (ProtocolSpec.FullTranscript.messages default)) + ((finalCheckVerifier oSpec F n M params).verify stmt + (ProtocolSpec.FullTranscript.challenges default))) = + (pure (some ()) : OracleComp oSpec (Option StmtOut)) := by + simpa [finalCheckPSpec] using hVerifyDefaultT + have hrun : + (finalCheckOracleReduction oSpec F n M params).toReduction.run (stmt, oStmt) () = + (pure ((default, ((), fun i => Fin.elim0 i), ()), ((), fun i => Fin.elim0 i)) : + OptionT (OracleComp _) _) := by + simp [finalCheckOracleReduction, OracleReduction.toReduction, Reduction.run, + finalCheckProver, Prover.run, Verifier.run, Prover.runToRound, + finalCheckPSpec, OracleVerifier.toVerifier] + erw [hVerifyDefaultT'] + simp + congr + funext i + exact Fin.elim0 i + simp only [ENNReal.coe_zero, tsub_zero] + rw [hrun] + rw [ge_iff_le, one_le_probEvent_iff, probEvent_eq_one_iff] + refine ⟨?_, ?_⟩ + · rw [OptionT.probFailure_eq, OptionT.run_mk] + simp only [probFailure_eq_zero, zero_add] + apply probOutput_eq_zero_of_not_mem_support + simp only [support_bind, Set.mem_iUnion, not_exists] + intro s _ hmem + change none ∈ _root_.support + (StateT.run' (simulateQ _ + (pure (some + ((default, ((), fun i => Fin.elim0 i), ()), ((), fun i => Fin.elim0 i))) : + OracleComp _ _)) s) at hmem + rw [simulateQ_pure] at hmem + change none ∈ _root_.support + (Prod.fst <$> (pure (some + ((default, ((), fun i => Fin.elim0 i), ()), ((), fun i => Fin.elim0 i))) : + StateT σ ProbComp _).run s) at hmem + rw [StateT.run_pure] at hmem + simp [map_pure] at hmem + · intro out hout + rw [OptionT.mem_support_iff] at hout + simp only [OptionT.run_mk, support_bind, Set.mem_iUnion] at hout + obtain ⟨s, -, hout⟩ := hout + change some out ∈ _root_.support + (StateT.run' (simulateQ _ + (pure (some + ((default, ((), fun i => Fin.elim0 i), ()), ((), fun i => Fin.elim0 i))) : + OracleComp _ _)) s) at hout + rw [simulateQ_pure] at hout + change some out ∈ _root_.support + (Prod.fst <$> (pure (some + ((default, ((), fun i => Fin.elim0 i), ()), ((), fun i => Fin.elim0 i))) : + StateT σ ProbComp _).run s) at hout + rw [StateT.run_pure] at hout + simp [map_pure, support_pure] at hout + cases hout + exact ⟨Set.mem_univ _, rfl⟩ /-- Main ArkLib completeness theorem for LogUp Protocol 2. -/ theorem logup_completeness : From fadaab65eeb7656a9d8172173f4fc57717cef5a1 Mon Sep 17 00:00:00 2001 From: jCabala Date: Wed, 24 Jun 2026 10:15:34 +0100 Subject: [PATCH 06/12] feat: Added roundtrip lemmas, numGroups lemma and removed division by zewro in soundness --- ArkLib/ProofSystem/Logup/Common.lean | 28 +++++++++++++++++++ ArkLib/ProofSystem/Logup/Protocol.lean | 7 +++-- .../ProofSystem/Logup/Security/Soundness.lean | 15 ++++++++-- 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/ArkLib/ProofSystem/Logup/Common.lean b/ArkLib/ProofSystem/Logup/Common.lean index d55dd6f6a5..2a0b637303 100644 --- a/ArkLib/ProofSystem/Logup/Common.lean +++ b/ArkLib/ProofSystem/Logup/Common.lean @@ -1,5 +1,6 @@ import Mathlib.Algebra.BigOperators.Fin import Mathlib.Algebra.CharP.Defs +import Mathlib.Algebra.Order.Floor.Div import Mathlib.Data.Fin.Basic import Mathlib.Data.Finset.Basic import Mathlib.Data.Fintype.Basic @@ -90,6 +91,23 @@ def inputToTerm {M : ℕ} : InputOracleIdx M → TermIdx M | .table => ⟨0, by omega⟩ | .column i => ⟨i.val + 1, by omega⟩ +/-- `termToInput` is a left inverse of `inputToTerm`: numbering an input label and reading it +back recovers the original label. -/ +@[simp] +theorem termToInput_inputToTerm {M : ℕ} (i : InputOracleIdx M) : + termToInput (inputToTerm i) = i := by + cases i <;> simp [termToInput, inputToTerm] + +/-- `inputToTerm` is a left inverse of `termToInput`: reading a term index as a label and +re-numbering it recovers the original index. -/ +@[simp] +theorem inputToTerm_termToInput {M : ℕ} (i : TermIdx M) : + inputToTerm (termToInput i) = i := by + unfold termToInput + split + · next h => exact Fin.ext h.symm + · next h => apply Fin.ext; simp only [inputToTerm]; omega + /-- Protocol parameter `ℓ`, the chosen partial-sum size from Protocol 2. -/ structure ProtocolParams (M : ℕ) where /-- The partial-sum size `ℓ`. -/ @@ -105,6 +123,16 @@ namespace ProtocolParams def numGroups {M : ℕ} (params : ProtocolParams M) : ℕ := (M + params.sumSize) / params.sumSize +/-- `numGroups` really is the ceiling `⌈(M + 1) / ℓ⌉`: the floor-division formula +`(M + ℓ) / ℓ` agrees with `Nat.ceilDiv` because `ℓ ≥ 1` (`sumSize_pos`). -/ +theorem numGroups_eq_ceilDiv {M : ℕ} (params : ProtocolParams M) : + params.numGroups = (M + 1) ⌈/⌉ params.sumSize := by + have h := params.sumSize_pos + rw [Nat.ceilDiv_eq_add_pred_div] + unfold numGroups + congr 1 + omega + /-- The consecutive interval `Iₖ = [(k - 1)ℓ, kℓ) ∩ [0, M]`, zero-indexed. -/ def group {M : ℕ} (params : ProtocolParams M) (k : Fin params.numGroups) : Finset (TermIdx M) := diff --git a/ArkLib/ProofSystem/Logup/Protocol.lean b/ArkLib/ProofSystem/Logup/Protocol.lean index 6b84b73fe1..2849ea9d6f 100644 --- a/ArkLib/ProofSystem/Logup/Protocol.lean +++ b/ArkLib/ProofSystem/Logup/Protocol.lean @@ -104,7 +104,7 @@ noncomputable instance instOuterPSpecMessageOracleInterface /-- The verifier challenges in the outer LogUp transcript are sampled uniformly from their types. -/ instance instOuterPSpecChallengeSampleable - {F : Type} [Fintype F] [Inhabited F] [SampleableType F] {n M : ℕ} + {F : Type} [Field F] [Fintype F] [SampleableType F] {n M : ℕ} {params : ProtocolParams M} : ∀ i, SampleableType ((outerPSpec F n params).Challenge i) | ⟨0, h0⟩ => by @@ -117,6 +117,9 @@ instance instOuterPSpecChallengeSampleable change Direction.P_to_V = Direction.V_to_P at h2 cases h2 | ⟨3, _⟩ => by + -- `BatchingChallenge` is a product type, and VCVio's `SampleableType (α × β)` instance + -- requires `Inhabited` on each factor; supply it from the field's zero. + letI : Inhabited F := ⟨0⟩ change SampleableType (BatchingChallenge F n params.numGroups) infer_instance @@ -172,7 +175,6 @@ noncomputable instance instPSpecBeforeFinalChallengeSampleable {F : Type} [Field F] [Fintype F] [DecidableEq F] [SampleableType F] {n M : ℕ} {params : ProtocolParams M} : ∀ i, SampleableType ((pSpecBeforeFinal F n M params).Challenge i) := by - letI : Inhabited F := ⟨0⟩ unfold pSpecBeforeFinal exact ProtocolSpec.instSampleableTypeChallengeAppend @@ -181,7 +183,6 @@ noncomputable instance instPSpecChallengeSampleable {F : Type} [Field F] [Fintype F] [DecidableEq F] [SampleableType F] {n M : ℕ} {params : ProtocolParams M} : ∀ i, SampleableType ((pSpec F n M params).Challenge i) := by - letI : Inhabited F := ⟨0⟩ unfold pSpec exact ProtocolSpec.instSampleableTypeChallengeAppend diff --git a/ArkLib/ProofSystem/Logup/Security/Soundness.lean b/ArkLib/ProofSystem/Logup/Security/Soundness.lean index e234a37108..ca75be7f48 100644 --- a/ArkLib/ProofSystem/Logup/Security/Soundness.lean +++ b/ArkLib/ProofSystem/Logup/Security/Soundness.lean @@ -20,7 +20,11 @@ variable (n M : ℕ) variable (params : ProtocolParams M) variable {σ : Type} (init : ProbComp σ) (impl : QueryImpl oSpec (StateT σ ProbComp)) -/-- Paper-shaped soundness error for the LogUp outer checks plus the embedded sumcheck error. -/ +/-- Paper-shaped soundness error for the LogUp outer checks plus the embedded sumcheck error. + +The first term divides by `|F| - |H|`, the number of non-pole field elements a good challenge can +be sampled from. This expression is only meaningful when `|H| < |F|` (see `logup_soundness`); +otherwise the natural-number subtraction truncates to `0` and the term silently vanishes. -/ noncomputable def logupSoundnessError (F : Type) [Fintype F] (n M : ℕ) (params : ProtocolParams M) (sumcheckSoundnessError : ℝ≥0) : ℝ≥0 := ((((M + 1) * Fintype.card (Hypercube n) - 1 : ℕ) : ℝ≥0) / @@ -28,8 +32,13 @@ noncomputable def logupSoundnessError (F : Type) [Fintype F] (n M : ℕ) (params (((params.numGroups + 1 : ℕ) : ℝ≥0) / (Fintype.card F : ℝ≥0)) + sumcheckSoundnessError -/-- Main ArkLib soundness theorem for LogUp Protocol 2. -/ -theorem logup_soundness (sumcheckSoundnessError : ℝ≥0) : +/-- Main ArkLib soundness theorem for LogUp Protocol 2. + +The hypothesis `hcard : |H| < |F|` guarantees there exist non-pole field elements to sample a +challenge from, and makes the `|F| - |H|` denominator of `logupSoundnessError` positive (so the +natural-number subtraction equals the true difference rather than truncating to `0`). -/ +theorem logup_soundness (sumcheckSoundnessError : ℝ≥0) + (hcard : Fintype.card (Hypercube n) < Fintype.card F) : (logupVerifier oSpec F n M params).soundness init impl (inputRelation F n M).language outputRelation.language (logupSoundnessError F n M params sumcheckSoundnessError) := by From 44c9acda2bc392103da8de2289f768438daefcaf Mon Sep 17 00:00:00 2001 From: jCabala Date: Wed, 24 Jun 2026 11:23:56 +0100 Subject: [PATCH 07/12] refactor: Minimized imports and moved polynomial specific types to poly file --- ArkLib/ProofSystem/Logup/Common.lean | 145 ++------- .../Logup/Security/Completeness.lean | 278 +++++++++--------- .../ProofSystem/Logup/Security/Soundness.lean | 6 +- .../Logup/Sumcheck/SumcheckBridge.lean | 77 +---- .../Logup/Sumcheck/SumcheckPolynomial.lean | 68 ++++- 5 files changed, 225 insertions(+), 349 deletions(-) diff --git a/ArkLib/ProofSystem/Logup/Common.lean b/ArkLib/ProofSystem/Logup/Common.lean index 2a0b637303..351a0424f5 100644 --- a/ArkLib/ProofSystem/Logup/Common.lean +++ b/ArkLib/ProofSystem/Logup/Common.lean @@ -1,13 +1,6 @@ -import Mathlib.Algebra.BigOperators.Fin -import Mathlib.Algebra.CharP.Defs import Mathlib.Algebra.Order.Floor.Div -import Mathlib.Data.Fin.Basic -import Mathlib.Data.Finset.Basic -import Mathlib.Data.Fintype.Basic -import Mathlib.Tactic import ArkLib.Data.MvPolynomial.Multilinear -import ArkLib.OracleReduction.Composition.Sequential.Append -import ArkLib.OracleReduction.Basic +import ArkLib.OracleReduction.OracleInterface /-! # LogUp Common Definitions @@ -25,16 +18,11 @@ universe u open scoped BigOperators -/-- The Boolean hypercube with `2^n` points. -/ -@[reducible] -def Hypercube (n : ℕ) : Type := - Fin n → Fin 2 - /-- A Lagrange oracle is a function on `H`; queries evaluate its Boolean MLE. -/ structure LagrangeOracle (F : Type u) (n : ℕ) where - values : Hypercube n → F + values : (Fin n → Fin 2) → F -instance {F : Type u} {n : ℕ} : CoeFun (LagrangeOracle F n) (fun _ => Hypercube n → F) where +instance {F : Type u} {n : ℕ} : CoeFun (LagrangeOracle F n) (fun _ => (Fin n → Fin 2) → F) where coe oracle := oracle.values /-- A multilinear oracle over Boolean row variables, represented in Lagrange form. -/ @@ -42,11 +30,6 @@ instance {F : Type u} {n : ℕ} : CoeFun (LagrangeOracle F n) (fun _ => Hypercub def MultilinearOracle (F : Type u) (n : ℕ) : Type u := LagrangeOracle F n -/-- Evaluate a multilinear oracle on a Boolean-hypercube row. -/ -def evalOnHypercube {F : Type u} {n : ℕ} - (p : MultilinearOracle F n) (u : Hypercube n) : F := - p u - /-- Lagrange-oracle queries evaluate the Boolean multilinear extension at `z`. -/ noncomputable instance instLagrangeOracleInterface {F : Type u} [Field F] {n : ℕ} : OracleInterface (LagrangeOracle F n) where @@ -61,13 +44,6 @@ noncomputable def lagrangeOracleEval {F : Type u} [Field F] {n : ℕ} (oracle : MultilinearOracle F n) (z : Fin n → F) : F := MvPolynomial.eval z (MvPolynomial.MLE oracle.values) -/-- Querying a Boolean-hypercube row returns the stored value at that row. -/ -theorem lagrangeOracleEval_hypercube {F : Type u} [Field F] {n : ℕ} - (oracle : MultilinearOracle F n) (u : Hypercube n) : - lagrangeOracleEval oracle (u : Fin n → F) = evalOnHypercube oracle u := by - change MvPolynomial.eval (u : Fin n → F) (MvPolynomial.MLE oracle.values) = oracle.values u - simp - /-- Input oracle labels for Protocol 2: one table oracle and `M` lookup-column oracles. -/ inductive InputOracleIdx (M : ℕ) where | table : InputOracleIdx M @@ -123,16 +99,6 @@ namespace ProtocolParams def numGroups {M : ℕ} (params : ProtocolParams M) : ℕ := (M + params.sumSize) / params.sumSize -/-- `numGroups` really is the ceiling `⌈(M + 1) / ℓ⌉`: the floor-division formula -`(M + ℓ) / ℓ` agrees with `Nat.ceilDiv` because `ℓ ≥ 1` (`sumSize_pos`). -/ -theorem numGroups_eq_ceilDiv {M : ℕ} (params : ProtocolParams M) : - params.numGroups = (M + 1) ⌈/⌉ params.sumSize := by - have h := params.sumSize_pos - rw [Nat.ceilDiv_eq_add_pred_div] - unfold numGroups - congr 1 - omega - /-- The consecutive interval `Iₖ = [(k - 1)ℓ, kℓ) ∩ [0, M]`, zero-indexed. -/ def group {M : ℕ} (params : ProtocolParams M) (k : Fin params.numGroups) : Finset (TermIdx M) := @@ -174,8 +140,8 @@ def columnOracle {F : Type u} {n M : ℕ} def inputRelation (F : Type u) [Field F] [Fintype F] (n M : ℕ) : Set ((StmtIn F n M × (∀ i, OStmtIn F n M i)) × Unit) := { ⟨⟨_, oStmt⟩, _⟩ | - ∀ i : Fin M, ∀ x : Hypercube n, ∃ y : Hypercube n, - evalOnHypercube (columnOracle oStmt i) x = evalOnHypercube (tableOracle oStmt) y } + ∀ i : Fin M, ∀ x : (Fin n → Fin 2), ∃ y : (Fin n → Fin 2), + (columnOracle oStmt i) x = (tableOracle oStmt) y } /-- The full LogUp protocol returns no additional public data on success. -/ @[reducible, simp] @@ -277,34 +243,20 @@ def PartialSumGroups (M K : ℕ) : Type := def canonicalGroups : PartialSumGroups M params.numGroups := fun k => params.group k -/-- Outer transcript data produced before entering the embedded sumcheck. -/ -structure OuterTranscriptData (F : Type) [Field F] [Fintype F] (n M : ℕ) - (params : ProtocolParams M) where - /-- The multiplicity function `m : H → F`. -/ - multiplicity : MultiplicityMessage F n - /-- The verifier challenge `x : F`. -/ - xChallenge : F - /-- The helper functions `h₁, ..., h_K : H → F`. -/ - helpers : HelperMessages F n params.numGroups - /-- The equality-kernel point `z ∈ Fⁿ`. -/ - zChallenge : Fin n → F - /-- The batching scalars `λ₁, ..., λ_K`. -/ - batchingScalars : Fin params.numGroups → F - /-- Number of table rows with value `a`. -/ def tableMultiplicityCount (oStmt : ∀ i, OStmtIn F n M i) (a : F) : ℕ := - ((Finset.univ : Finset (Hypercube n)).filter fun u => - evalOnHypercube (tableOracle oStmt) u = a).card + ((Finset.univ : Finset (Fin n → Fin 2)).filter fun u => + (tableOracle oStmt) u = a).card /-- Total number of lookup-column entries with value `a`. -/ def lookupMultiplicityCount (oStmt : ∀ i, OStmtIn F n M i) (a : F) : ℕ := - ((Finset.univ : Finset (Fin M × Hypercube n)).filter fun ix => - evalOnHypercube (columnOracle oStmt ix.1) ix.2 = a).card + ((Finset.univ : Finset (Fin M × (Fin n → Fin 2))).filter fun ix => + (columnOracle oStmt ix.1) ix.2 = a).card /-- The normalized multiplicity from paper equation (14), evaluated at one table row. -/ noncomputable def normalizedMultiplicityValue (oStmt : ∀ i, OStmtIn F n M i) - (u : Hypercube n) : F := - let a := evalOnHypercube (tableOracle oStmt) u + (u : (Fin n → Fin 2)) : F := + let a := (tableOracle oStmt) u (lookupMultiplicityCount oStmt a : F) / (tableMultiplicityCount oStmt a : F) /-- The honest multiplicity oracle `m : H → F` from paper equation (14). -/ @@ -314,43 +266,43 @@ noncomputable def honestMultiplicity (oStmt : ∀ i, OStmtIn F n M i) : /-- The denominator term `φᵢ(u)` from Protocol 2. -/ noncomputable def phi (oStmt : ∀ i, OStmtIn F n M i) (xChallenge : F) : - InputOracleIdx M → Hypercube n → F - | .table => fun u => xChallenge + evalOnHypercube (tableOracle oStmt) u - | .column i => fun u => xChallenge + evalOnHypercube (columnOracle oStmt i) u + InputOracleIdx M → (Fin n → Fin 2) → F + | .table => fun u => xChallenge + (tableOracle oStmt) u + | .column i => fun u => xChallenge + (columnOracle oStmt i) u /-- The numerator term `mᵢ(u)` from Protocol 2, with `m₀ = m` and `mᵢ = -1` for columns. -/ noncomputable def numerator (multiplicity : MultilinearOracle F n) : - InputOracleIdx M → Hypercube n → F - | .table => evalOnHypercube multiplicity + InputOracleIdx M → (Fin n → Fin 2) → F + | .table => multiplicity | .column _ => fun _ => -1 /-- The denominator term, indexed as `0, ..., M` as in the paper. -/ noncomputable def termPhi (oStmt : ∀ i, OStmtIn F n M i) (xChallenge : F) - (i : TermIdx M) (u : Hypercube n) : F := + (i : TermIdx M) (u : (Fin n → Fin 2)) : F := phi oStmt xChallenge (termToInput i) u /-- The numerator term, indexed as `0, ..., M` as in the paper. -/ noncomputable def termNumerator (multiplicity : MultilinearOracle F n) - (i : TermIdx M) (u : Hypercube n) : F := + (i : TermIdx M) (u : (Fin n → Fin 2)) : F := numerator multiplicity (termToInput i) u /-- Product of the denominator terms in one partial-sum group. -/ noncomputable def denominatorProduct (groups : PartialSumGroups M K) - (oStmt : ∀ i, OStmtIn F n M i) (xChallenge : F) (k : Fin K) (u : Hypercube n) : F := + (oStmt : ∀ i, OStmtIn F n M i) (xChallenge : F) (k : Fin K) (u : (Fin n → Fin 2)) : F := ∏ i ∈ groups k, termPhi oStmt xChallenge i u /-- The domain-identity expression for one helper function `hₖ`. -/ noncomputable def domainIdentityTerm (groups : PartialSumGroups M K) (oStmt : ∀ i, OStmtIn F n M i) (multiplicity : MultilinearOracle F n) - (helpers : HelperMessages F n K) (xChallenge : F) (k : Fin K) (u : Hypercube n) : F := - evalOnHypercube (helpers k) u * denominatorProduct groups oStmt xChallenge k u - + (helpers : HelperMessages F n K) (xChallenge : F) (k : Fin K) (u : (Fin n → Fin 2)) : F := + (helpers k) u * denominatorProduct groups oStmt xChallenge k u - ∑ i ∈ groups k, termNumerator multiplicity i u * ∏ j ∈ (groups k).erase i, termPhi oStmt xChallenge j u /-- The helper value `hₖ(u) = Σᵢ mᵢ(u)/φᵢ(u)` from paper equation (16). -/ noncomputable def helperValue (groups : PartialSumGroups M K) (oStmt : ∀ i, OStmtIn F n M i) (multiplicity : MultilinearOracle F n) - (xChallenge : F) (k : Fin K) (u : Hypercube n) : F := + (xChallenge : F) (k : Fin K) (u : (Fin n → Fin 2)) : F := ∑ i ∈ groups k, termNumerator multiplicity i u / termPhi oStmt xChallenge i u /-- The honest helper oracle `hₖ : H → F` for one partial-sum group. -/ @@ -365,17 +317,6 @@ noncomputable def honestHelpers (oStmt : ∀ i, OStmtIn F n M i) (xChallenge : F let multiplicity := honestMultiplicity oStmt fun k => helperOracle (canonicalGroups params) oStmt multiplicity xChallenge k -/-- The batched polynomial expression `Q` from paper equation (18), evaluated on a row `u ∈ H`. -/ -noncomputable def qOnHypercube (groups : PartialSumGroups M K) - (oStmt : ∀ i, OStmtIn F n M i) (multiplicity : MultilinearOracle F n) - (helpers : HelperMessages F n K) (xChallenge : F) (zChallenge : Fin n → F) - (batchingScalars : Fin K → F) (u : Hypercube n) : F := - ∑ k : Fin K, ( - evalOnHypercube (helpers k) u + - MvPolynomial.eval (u : Fin n → F) (MvPolynomial.eqPolynomial zChallenge) * - batchingScalars k * - domainIdentityTerm groups oStmt multiplicity helpers xChallenge k u) - /-- Point evaluations queried by the verifier at the final sumcheck point `r`. -/ structure PointEvaluations (F : Type) (M K : ℕ) where /-- Claimed value `m(r)`. -/ @@ -397,48 +338,6 @@ def logupPointEvaluationsAgree (∀ i : Fin M, evals.columns i = lagrangeOracleEval (oStmt (.input (.column i))) r) ∧ ∀ k : Fin params.numGroups, evals.helpers k = lagrangeOracleEval ((oStmt .helpers) k) r -/-- Denominator term at the final sumcheck point, reconstructed from oracle query answers. -/ -def phiAtPoint (xChallenge : F) (evals : PointEvaluations F M K) : - InputOracleIdx M → F - | .table => xChallenge + evals.table - | .column i => xChallenge + evals.columns i - -/-- Numerator term at the final sumcheck point, reconstructed from oracle query answers. -/ -def numeratorAtPoint (evals : PointEvaluations F M K) : InputOracleIdx M → F - | .table => evals.multiplicity - | .column _ => -1 - -/-- Term denominator at the final sumcheck point, indexed by `0, ..., M`. -/ -def termPhiAtPoint (xChallenge : F) (evals : PointEvaluations F M K) (i : TermIdx M) : F := - phiAtPoint xChallenge evals (termToInput i) - -/-- Term numerator at the final sumcheck point, indexed by `0, ..., M`. -/ -def termNumeratorAtPoint (evals : PointEvaluations F M K) (i : TermIdx M) : F := - numeratorAtPoint evals (termToInput i) - -/-- The domain-identity expression at the final sumcheck point `r`. -/ -noncomputable def domainIdentityAtPoint (groups : PartialSumGroups M K) - (xChallenge : F) (evals : PointEvaluations F M K) (k : Fin K) : F := - evals.helpers k * (∏ i ∈ groups k, termPhiAtPoint xChallenge evals i) - - ∑ i ∈ groups k, - termNumeratorAtPoint evals i * - ∏ j ∈ (groups k).erase i, termPhiAtPoint xChallenge evals j - -/-- The verifier's final check value `Q(eq(r,z), m(r), φᵢ(r), hₖ(r))` from paper (19). -/ -noncomputable def qAtPoint (groups : PartialSumGroups M K) (xChallenge : F) - (zChallenge rChallenge : Fin n → F) (batchingScalars : Fin K → F) - (evals : PointEvaluations F M K) : F := - ∑ k : Fin K, ( - evals.helpers k + - MvPolynomial.eval rChallenge (MvPolynomial.eqPolynomial zChallenge) * batchingScalars k * - domainIdentityAtPoint groups xChallenge evals k) - -/-- Predicate for paper step 4: final oracle-query answers match the sumcheck's expected value. -/ -noncomputable def finalQueryCheck (groups : PartialSumGroups M K) (xChallenge : F) - (zChallenge rChallenge : Fin n → F) (batchingScalars : Fin K → F) - (evals : PointEvaluations F M K) (expectedValue : F) : Prop := - qAtPoint groups xChallenge zChallenge rChallenge batchingScalars evals = expectedValue - end ProtocolAlgebra end Logup diff --git a/ArkLib/ProofSystem/Logup/Security/Completeness.lean b/ArkLib/ProofSystem/Logup/Security/Completeness.lean index 6a10dccd33..2d00b36fbd 100644 --- a/ArkLib/ProofSystem/Logup/Security/Completeness.lean +++ b/ArkLib/ProofSystem/Logup/Security/Completeness.lean @@ -24,8 +24,8 @@ omit [Fintype F] [DecidableEq F] in poles (`φᵢ ≠ 0`). -/ theorem domainIdentityTerm_eq_zero (groups : PartialSumGroups M K) (oStmt : ∀ i, OStmtIn F n M i) (mult : MultilinearOracle F n) - (helpers : HelperMessages F n K) (x : F) (k : Fin K) (u : Hypercube n) - (hh : evalOnHypercube (helpers k) u = helperValue groups oStmt mult x k u) + (helpers : HelperMessages F n K) (x : F) (k : Fin K) (u : (Fin n → Fin 2)) + (hh : (helpers k) u = helperValue groups oStmt mult x k u) (hφ : ∀ i ∈ groups k, termPhi oStmt x i u ≠ 0) : domainIdentityTerm groups oStmt mult helpers x k u = 0 := by rw [domainIdentityTerm, denominatorProduct, hh, helperValue, Finset.sum_mul, sub_eq_zero] @@ -39,14 +39,14 @@ side equals the column side. Needs every column value to occur in the table (`hc counts used by nonzero lookup multiplicities to be nonzero in `F` (`hchar`, from `charLarge`). Holds even at poles, since `x/0 = 0`. -/ theorem honest_multiplicity_identity [Finite F] (oStmt : ∀ i, OStmtIn F n M i) (x : F) - (hcols : ∀ j : Fin M, ∀ u : Hypercube n, ∃ v : Hypercube n, - evalOnHypercube (columnOracle oStmt j) u = evalOnHypercube (tableOracle oStmt) v) + (hcols : ∀ j : Fin M, ∀ u : (Fin n → Fin 2), ∃ v : (Fin n → Fin 2), + (columnOracle oStmt j) u = (tableOracle oStmt) v) (hchar : ∀ a : F, lookupMultiplicityCount oStmt a ≠ 0 → (tableMultiplicityCount oStmt a : F) ≠ 0) : - (∑ u : Hypercube n, - evalOnHypercube (honestMultiplicity oStmt) u / (x + evalOnHypercube (tableOracle oStmt) u)) - = ∑ j : Fin M, ∑ u : Hypercube n, - (1 : F) / (x + evalOnHypercube (columnOracle oStmt j) u) := by + (∑ u : (Fin n → Fin 2), + (honestMultiplicity oStmt) u / (x + (tableOracle oStmt) u)) + = ∑ j : Fin M, ∑ u : (Fin n → Fin 2), + (1 : F) / (x + (columnOracle oStmt j) u) := by letI : Fintype F := Fintype.ofFinite F classical -- per-value cancellation @@ -60,11 +60,11 @@ theorem honest_multiplicity_identity [Finite F] (oStmt : ∀ i, OStmtIn F n M i) rw [lookupMultiplicityCount, Finset.card_eq_zero, Finset.filter_eq_empty_iff] rintro ⟨j, u⟩ - hja obtain ⟨v, hv⟩ := hcols j u - have hav : evalOnHypercube (tableOracle oStmt) v = a := hv.symm.trans hja + have hav : (tableOracle oStmt) v = a := hv.symm.trans hja rw [tableMultiplicityCount] at hT exact absurd hT (Finset.card_ne_zero_of_mem (show v ∈ Finset.univ.filter - fun w => evalOnHypercube (tableOracle oStmt) w = a by simp [hav])) + fun w => (tableOracle oStmt) w = a by simp [hav])) simp [hT, hL] · rw [nsmul_eq_mul, nsmul_eq_mul] by_cases hL : lookupMultiplicityCount oStmt a = 0 @@ -73,42 +73,42 @@ theorem honest_multiplicity_identity [Finite F] (oStmt : ∀ i, OStmtIn F n M i) field_simp -- group the table side by table value have hLHS : - (∑ u : Hypercube n, evalOnHypercube (honestMultiplicity oStmt) u / - (x + evalOnHypercube (tableOracle oStmt) u)) + (∑ u : (Fin n → Fin 2), (honestMultiplicity oStmt) u / + (x + (tableOracle oStmt) u)) = ∑ a : F, tableMultiplicityCount oStmt a • ((lookupMultiplicityCount oStmt a : F) / (tableMultiplicityCount oStmt a : F) / (x + a)) := by - rw [← Finset.sum_fiberwise (Finset.univ : Finset (Hypercube n)) - (fun u => evalOnHypercube (tableOracle oStmt) u)] + rw [← Finset.sum_fiberwise (Finset.univ : Finset (Fin n → Fin 2)) + (fun u => (tableOracle oStmt) u)] refine Finset.sum_congr rfl (fun a _ => ?_) have h : ∀ u ∈ Finset.univ.filter - (fun u => evalOnHypercube (tableOracle oStmt) u = a), - evalOnHypercube (honestMultiplicity oStmt) u / - (x + evalOnHypercube (tableOracle oStmt) u) + (fun u => (tableOracle oStmt) u = a), + (honestMultiplicity oStmt) u / + (x + (tableOracle oStmt) u) = (lookupMultiplicityCount oStmt a : F) / (tableMultiplicityCount oStmt a : F) / (x + a) := by intro u hu simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hu - change (lookupMultiplicityCount oStmt (evalOnHypercube (tableOracle oStmt) u) : F) / - (tableMultiplicityCount oStmt (evalOnHypercube (tableOracle oStmt) u) : F) / - (x + evalOnHypercube (tableOracle oStmt) u) + change (lookupMultiplicityCount oStmt ((tableOracle oStmt) u) : F) / + (tableMultiplicityCount oStmt ((tableOracle oStmt) u) : F) / + (x + (tableOracle oStmt) u) = (lookupMultiplicityCount oStmt a : F) / (tableMultiplicityCount oStmt a : F) / (x + a) rw [hu] rw [Finset.sum_congr rfl h, Finset.sum_const] rfl -- group the column side by column value have hRHS : - (∑ j : Fin M, ∑ u : Hypercube n, (1 : F) / - (x + evalOnHypercube (columnOracle oStmt j) u)) + (∑ j : Fin M, ∑ u : (Fin n → Fin 2), (1 : F) / + (x + (columnOracle oStmt j) u)) = ∑ a : F, lookupMultiplicityCount oStmt a • ((1 : F) / (x + a)) := by rw [← Finset.sum_product', Finset.univ_product_univ, - ← Finset.sum_fiberwise (Finset.univ : Finset (Fin M × Hypercube n)) - (fun p => evalOnHypercube (columnOracle oStmt p.1) p.2)] + ← Finset.sum_fiberwise (Finset.univ : Finset (Fin M × (Fin n → Fin 2))) + (fun p => (columnOracle oStmt p.1) p.2)] refine Finset.sum_congr rfl (fun a _ => ?_) have h : ∀ p ∈ Finset.univ.filter - (fun p : Fin M × Hypercube n => - evalOnHypercube (columnOracle oStmt p.1) p.2 = a), - (1 : F) / (x + evalOnHypercube (columnOracle oStmt p.1) p.2) = (1 : F) / (x + a) := by + (fun p : Fin M × (Fin n → Fin 2) => + (columnOracle oStmt p.1) p.2 = a), + (1 : F) / (x + (columnOracle oStmt p.1) p.2) = (1 : F) / (x + a) := by intro p hp simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hp simp only [hp] @@ -121,36 +121,36 @@ theorem honest_multiplicity_identity [Finite F] (oStmt : ∀ i, OStmtIn F n M i) multiplicity is nonzero as an element of `F`. -/ theorem tableMultiplicityCount_cast_ne_zero_of_lookupMultiplicityCount_ne_zero (stmt : StmtIn F n M) (oStmt : ∀ i, OStmtIn F n M i) - (hcols : ∀ j : Fin M, ∀ u : Hypercube n, ∃ v : Hypercube n, - evalOnHypercube (columnOracle oStmt j) u = evalOnHypercube (tableOracle oStmt) v) + (hcols : ∀ j : Fin M, ∀ u : (Fin n → Fin 2), ∃ v : (Fin n → Fin 2), + (columnOracle oStmt j) u = (tableOracle oStmt) v) {a : F} (hlookup : lookupMultiplicityCount oStmt a ≠ 0) : (tableMultiplicityCount oStmt a : F) ≠ 0 := by classical have hlookupCard : - ((Finset.univ : Finset (Fin M × Hypercube n)).filter fun ix => - evalOnHypercube (columnOracle oStmt ix.1) ix.2 = a).card ≠ 0 := by + ((Finset.univ : Finset (Fin M × (Fin n → Fin 2))).filter fun ix => + (columnOracle oStmt ix.1) ix.2 = a).card ≠ 0 := by simpa [lookupMultiplicityCount] using hlookup obtain ⟨⟨j, u⟩, hju⟩ := Finset.card_ne_zero.mp hlookupCard simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hju obtain ⟨v, hv⟩ := hcols j u - have hvTable : evalOnHypercube (tableOracle oStmt) v = a := hv.symm.trans hju + have hvTable : (tableOracle oStmt) v = a := hv.symm.trans hju have htablePos : 0 < tableMultiplicityCount oStmt a := by rw [tableMultiplicityCount, Finset.card_pos] exact ⟨v, by simp [hvTable]⟩ have hMpos : 0 < M := lt_of_le_of_lt (Nat.zero_le j.val) j.isLt have htable_le_card : - tableMultiplicityCount oStmt a ≤ Fintype.card (Hypercube n) := by + tableMultiplicityCount oStmt a ≤ Fintype.card (Fin n → Fin 2) := by rw [tableMultiplicityCount, ← Finset.card_univ] exact Finset.card_filter_le _ _ - have hcard_hypercube : Fintype.card (Hypercube n) = 2 ^ n := by - simp [Hypercube] + have hcard_hypercube : Fintype.card (Fin n → Fin 2) = 2 ^ n := by + simp have hpow_le : 2 ^ n ≤ M * 2 ^ n := by have hMone : 1 ≤ M := Nat.succ_le_of_lt hMpos nth_rewrite 1 [← Nat.one_mul (2 ^ n)] exact Nat.mul_le_mul_right (2 ^ n) hMone have htable_lt_char : tableMultiplicityCount oStmt a < ringChar F := by calc - tableMultiplicityCount oStmt a ≤ Fintype.card (Hypercube n) := htable_le_card + tableMultiplicityCount oStmt a ≤ Fintype.card (Fin n → Fin 2) := htable_le_card _ = 2 ^ n := hcard_hypercube _ ≤ M * 2 ^ n := hpow_le _ < ringChar F := stmt.charLarge @@ -163,11 +163,11 @@ omit [Fintype F] [DecidableEq F] in /-- If `x` avoids the table poles, then it avoids all table and lookup-column denominator poles under the input relation. -/ theorem termPhi_ne_zero_of_table_poles (oStmt : ∀ i, OStmtIn F n M i) (x : F) - (hcols : ∀ j : Fin M, ∀ u : Hypercube n, ∃ v : Hypercube n, - evalOnHypercube (columnOracle oStmt j) u = evalOnHypercube (tableOracle oStmt) v) - (hNoTablePoles : ∀ u : Hypercube n, - x + evalOnHypercube (tableOracle oStmt) u ≠ 0) : - ∀ (i : TermIdx M) (u : Hypercube n), termPhi oStmt x i u ≠ 0 := by + (hcols : ∀ j : Fin M, ∀ u : (Fin n → Fin 2), ∃ v : (Fin n → Fin 2), + (columnOracle oStmt j) u = (tableOracle oStmt) v) + (hNoTablePoles : ∀ u : (Fin n → Fin 2), + x + (tableOracle oStmt) u ≠ 0) : + ∀ (i : TermIdx M) (u : (Fin n → Fin 2)), termPhi oStmt x i u ≠ 0 := by intro i u cases hti : termToInput i with | table => @@ -213,16 +213,16 @@ away from poles. Combines `domainIdentityTerm_eq_zero`, `sum_canonicalGroups`, a theorem logupOuterClaim_zero [Finite F] (params : ProtocolParams M) (oStmtIn : ∀ i, OStmtIn F n M i) (x : F) (z : Fin n → F) (lam : Fin params.numGroups → F) - (hcols : ∀ j : Fin M, ∀ u : Hypercube n, ∃ v : Hypercube n, - evalOnHypercube (columnOracle oStmtIn j) u = evalOnHypercube (tableOracle oStmtIn) v) + (hcols : ∀ j : Fin M, ∀ u : (Fin n → Fin 2), ∃ v : (Fin n → Fin 2), + (columnOracle oStmtIn j) u = (tableOracle oStmtIn) v) (hchar : ∀ a : F, lookupMultiplicityCount oStmtIn a ≠ 0 → (tableMultiplicityCount oStmtIn a : F) ≠ 0) - (hpoles : ∀ (i : TermIdx M) (u : Hypercube n), termPhi oStmtIn x i u ≠ 0) : - (∑ u : Hypercube n, + (hpoles : ∀ (i : TermIdx M) (u : (Fin n → Fin 2)), termPhi oStmtIn x i u ≠ 0) : + (∑ u : (Fin n → Fin 2), qOnHypercube (canonicalGroups params) oStmtIn (honestMultiplicity oStmtIn) (honestHelpers params oStmtIn x) x z lam u) = 0 := by -- honest helpers kill the domain-identity term, leaving `∑ₖ helperValue` - have hq : ∀ u : Hypercube n, + have hq : ∀ u : (Fin n → Fin 2), qOnHypercube (canonicalGroups params) oStmtIn (honestMultiplicity oStmtIn) (honestHelpers params oStmtIn x) x z lam u = ∑ k : Fin params.numGroups, @@ -234,7 +234,7 @@ theorem logupOuterClaim_zero [Finite F] (params : ProtocolParams M) (honestHelpers params oStmtIn x) x k u rfl (fun i _ => hpoles i u), mul_zero, add_zero] rfl -- each helper expands to the per-group term sum, which partitions to the full term sum - have hsum : ∀ u : Hypercube n, + have hsum : ∀ u : (Fin n → Fin 2), (∑ k : Fin params.numGroups, helperValue (canonicalGroups params) oStmtIn (honestMultiplicity oStmtIn) x k u) = ∑ i : TermIdx M, @@ -244,16 +244,16 @@ theorem logupOuterClaim_zero [Finite F] (params : ProtocolParams M) exact sum_canonicalGroups params (fun i => termNumerator (honestMultiplicity oStmtIn) i u / termPhi oStmtIn x i u) -- split the term sum into the table term and the column terms - have hterm : ∀ u : Hypercube n, + have hterm : ∀ u : (Fin n → Fin 2), (∑ i : TermIdx M, termNumerator (honestMultiplicity oStmtIn) i u / termPhi oStmtIn x i u) - = evalOnHypercube (honestMultiplicity oStmtIn) u / - (x + evalOnHypercube (tableOracle oStmtIn) u) - + ∑ j : Fin M, (-1 : F) / (x + evalOnHypercube (columnOracle oStmtIn j) u) := by + = (honestMultiplicity oStmtIn) u / + (x + (tableOracle oStmtIn) u) + + ∑ j : Fin M, (-1 : F) / (x + (columnOracle oStmtIn j) u) := by intro u have hcol : ∀ j : Fin M, termNumerator (honestMultiplicity oStmtIn) (Fin.succ j) u / termPhi oStmtIn x (Fin.succ j) u - = (-1 : F) / (x + evalOnHypercube (columnOracle oStmtIn j) u) := by + = (-1 : F) / (x + (columnOracle oStmtIn j) u) := by intro j have htt : termToInput (Fin.succ j : TermIdx M) = InputOracleIdx.column j := by simp only [termToInput, Fin.val_succ, Nat.succ_ne_zero, ↓reduceDIte] @@ -264,7 +264,7 @@ theorem logupOuterClaim_zero [Finite F] (params : ProtocolParams M) exact Finset.sum_congr rfl (fun j _ => hcol j) simp_rw [hq, hsum, hterm] rw [Finset.sum_add_distrib, honest_multiplicity_identity oStmtIn x hcols hchar, - Finset.sum_comm (f := fun u j => (-1 : F) / (x + evalOnHypercube (columnOracle oStmtIn j) u)), + Finset.sum_comm (f := fun u j => (-1 : F) / (x + (columnOracle oStmtIn j) u)), ← Finset.sum_add_distrib] refine Finset.sum_eq_zero (fun j _ => ?_) rw [← Finset.sum_add_distrib] @@ -273,20 +273,20 @@ theorem logupOuterClaim_zero [Finite F] (params : ProtocolParams M) /-- The table poles `{x : ∃ u, x + t(u) = 0} = {-t(u) : u ∈ H}` number at most `|H|`. This is the counting fact behind the pole-sampling completeness error. -/ theorem pole_card_le (oStmt : ∀ i, OStmtIn F n M i) : - (Finset.univ.filter (fun x : F => ∃ u : Hypercube n, - x + evalOnHypercube (tableOracle oStmt) u = 0)).card - ≤ Fintype.card (Hypercube n) := by + (Finset.univ.filter (fun x : F => ∃ u : (Fin n → Fin 2), + x + (tableOracle oStmt) u = 0)).card + ≤ Fintype.card (Fin n → Fin 2) := by classical - calc (Finset.univ.filter (fun x : F => ∃ u : Hypercube n, - x + evalOnHypercube (tableOracle oStmt) u = 0)).card + calc (Finset.univ.filter (fun x : F => ∃ u : (Fin n → Fin 2), + x + (tableOracle oStmt) u = 0)).card ≤ (Finset.univ.image - (fun u : Hypercube n => -evalOnHypercube (tableOracle oStmt) u)).card := by + (fun u : (Fin n → Fin 2) => -(tableOracle oStmt) u)).card := by apply Finset.card_le_card intro x hx simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hx obtain ⟨u, hu⟩ := hx exact Finset.mem_image.mpr ⟨u, Finset.mem_univ u, (eq_neg_of_add_eq_zero_left hu).symm⟩ - _ ≤ Fintype.card (Hypercube n) := by + _ ≤ Fintype.card (Fin n → Fin 2) := by rw [← Finset.card_univ]; exact Finset.card_image_le end OuterAlgebra @@ -308,27 +308,90 @@ local instance instOuterChallengeOracleInterface' : OracleInterface ((outerPSpec F n params).Challenge i) := ProtocolSpec.challengeOracleInterface +omit [Fintype F] [DecidableEq F] [SampleableType F] in +private theorem sum_piFinset_map_univ_eq_sum_hypercube + (D : Fin 2 ↪ F) (f : (Fin n → F) → F) : + (∑ x ∈ Fintype.piFinset fun _ : Fin n => Finset.univ.map D, f x) = + ∑ u : (Fin n → Fin 2), f (fun j => D (u j)) := by + let e : (Fin n → Fin 2) ↪ (Fin n → F) := Function.Embedding.arrowCongrRight D + change (∑ x ∈ Fintype.piFinset fun _ : Fin n => Finset.univ.map D, f x) = + ∑ u : (Fin n → Fin 2), f (e u) + rw [← Finset.sum_map] + congr 1 + ext x + constructor + · intro hx + rw [Fintype.mem_piFinset] at hx + have hx_coord : ∀ j : Fin n, ∃ b : Fin 2, D b = x j := by + intro j + rcases Finset.mem_map.mp (hx j) with ⟨b, _, hb⟩ + exact ⟨b, hb⟩ + let u : (Fin n → Fin 2) := fun j => Classical.choose (hx_coord j) + exact Finset.mem_map.mpr ⟨u, Finset.mem_univ _, by + funext j + exact Classical.choose_spec (hx_coord j)⟩ + · intro hx + rw [Fintype.mem_piFinset] + intro j + rcases Finset.mem_map.mp hx with ⟨u, _, rfl⟩ + exact Finset.mem_map.mpr ⟨u j, Finset.mem_univ _, rfl⟩ + +omit [Fintype F] [DecidableEq F] [SampleableType F] in +/-- If LogUp's outer algebra proves a zero sum, then the generic Sumcheck input relation is exactly +the claim sent to Sumcheck. -/ +theorem logupSumcheckRelationInput_of_zero + {stmt : StmtAfterOuter F n M params} + {oStmt : ∀ i, OStmtAfterOuter F n M params i} + (hZero : logupOuterSumcheckClaim F n M params stmt oStmt = 0) : + logupSumcheckRelationInput F n M params stmt oStmt := by + unfold logupSumcheckRelationInput Sumcheck.Spec.relationRound + simp only [Fin.coe_ofNat_eq_mod, Nat.zero_mod, Nat.sub_zero, logupInitialSumcheckStatement, + Set.mem_setOf_eq, Fin.elim0_append, logupSumcheckOracleStmt] + change + (∑ x ∈ Fintype.piFinset fun _ : Fin n => Finset.univ.map (booleanDomain F), + MvPolynomial.eval ((x ∘ Fin.cast (by omega)) ∘ Fin.cast (by omega)) + (logupSumcheckPolynomial F n M params stmt oStmt).val) = 0 + rw [sum_piFinset_map_univ_eq_sum_hypercube + (F := F) (n := n) (D := booleanDomain F) + (f := fun x => + MvPolynomial.eval ((x ∘ Fin.cast (by omega)) ∘ Fin.cast (by omega)) + (logupSumcheckPolynomial F n M params stmt oStmt).val)] + calc + (∑ u : (Fin n → Fin 2), + MvPolynomial.eval + ((((fun j => (booleanDomain F) (u j)) ∘ Fin.cast (by omega)) ∘ + Fin.cast (by omega))) + (logupSumcheckPolynomial F n M params stmt oStmt).val) + = + logupOuterSumcheckClaim F n M params stmt oStmt := by + rw [logupOuterSumcheckClaim] + apply Finset.sum_congr rfl + intro u _ + simpa [booleanDomain, logupSumcheckPolynomial] using + logupQPolynomial_eval_hypercube F n M params stmt oStmt u + _ = 0 := hZero + /-- Completeness error from the current `x`-sampling model: the verifier samples `x` from all of `F`. Following Remark 3 of the LogUp paper, table-pole challenges are treated as bad inputs for the honest handoff rather than rejected by an exponential verifier scan. -/ noncomputable def logupCompletenessError (F : Type) [Fintype F] (n : ℕ) : ℝ≥0 := - (Fintype.card (Hypercube n) : ℝ≥0) / (Fintype.card F) + (Fintype.card (Fin n → Fin 2) : ℝ≥0) / (Fintype.card F) private theorem uniform_avoids_table_poles_prob [Inhabited F] (oStmt : ∀ i, OStmtIn F n M i) : (1 : ENNReal) - (logupCompletenessError F n : ENNReal) ≤ - Pr[fun x : F => ∀ u : Hypercube n, - x + evalOnHypercube (tableOracle oStmt) u ≠ 0 | $ᵗ F] := by + Pr[fun x : F => ∀ u : (Fin n → Fin 2), + x + (tableOracle oStmt) u ≠ 0 | $ᵗ F] := by classical let bad : F → Prop := - fun x => ∃ u : Hypercube n, x + evalOnHypercube (tableOracle oStmt) u = 0 + fun x => ∃ u : (Fin n → Fin 2), x + (tableOracle oStmt) u = 0 have hbad : Pr[bad | $ᵗ F] ≤ (logupCompletenessError F n : ENNReal) := by rw [probEvent_uniformSample] unfold logupCompletenessError have hcard : ((Finset.univ.filter bad).card : ENNReal) ≤ - (Fintype.card (Hypercube n) : ENNReal) := by + (Fintype.card (Fin n → Fin 2) : ENNReal) := by exact_mod_cast (by simpa [bad] using pole_card_le (F := F) (n := n) (M := M) oStmt) convert ENNReal.div_le_div_right hcard (Fintype.card F : ENNReal) using 1; norm_num have hcompl : @@ -358,69 +421,6 @@ private theorem le_probEvent_bind_of_forall_le {m : Type → Type*} [Monad m] [L (by simp [htrue]) (fun x hx _ => h x hx) simpa using hmul -omit [Field F] [DecidableEq F] in -private theorem simulateQ_outer_x_challenge [Inhabited F] : - simulateQ - (impl + QueryImpl.liftTarget (StateT σ ProbComp) - (ProtocolSpec.challengeQueryImpl (pSpec := outerPSpec F n params))) - (OracleComp.liftComp - (ProtocolSpec.getChallenge (outerPSpec F n params) - (outerChallengeXIdx F n M params)) - (oSpec + [(outerPSpec F n params).Challenge]ₒ)) - = (StateT.lift ($ᵗ F) : StateT σ ProbComp F) := by - rw [QueryImpl.simulateQ_add_liftComp_right, simulateQ_liftTarget] - have h : - simulateQ (ProtocolSpec.challengeQueryImpl (pSpec := outerPSpec F n params)) - (ProtocolSpec.getChallenge (outerPSpec F n params) - (outerChallengeXIdx F n M params)) - = ($ᵗ F : ProbComp F) := by - unfold ProtocolSpec.getChallenge - change simulateQ (ProtocolSpec.challengeQueryImpl (pSpec := outerPSpec F n params)) - (liftM (OracleSpec.query (spec := [(outerPSpec F n params).Challenge]ₒ) - ⟨outerChallengeXIdx F n M params, ()⟩) : - OracleComp ([(outerPSpec F n params).Challenge]ₒ) F) - = ($ᵗ F : ProbComp F) - rw [simulateQ_query] - change id <$> ($ᵗ F : ProbComp F) = ($ᵗ F : ProbComp F) - simp - rw [h] - rfl - -omit [Field F] [DecidableEq F] in -private theorem simulateQ_outer_batch_challenge [Inhabited F] : - simulateQ - (impl + QueryImpl.liftTarget (StateT σ ProbComp) - (ProtocolSpec.challengeQueryImpl (pSpec := outerPSpec F n params))) - (OracleComp.liftComp - (ProtocolSpec.getChallenge (outerPSpec F n params) - (outerChallengeBatchIdx F n M params)) - (oSpec + [(outerPSpec F n params).Challenge]ₒ)) - = (StateT.lift ($ᵗ BatchingChallenge F n params.numGroups) : - StateT σ ProbComp (BatchingChallenge F n params.numGroups)) := by - rw [QueryImpl.simulateQ_add_liftComp_right, simulateQ_liftTarget] - have h : - simulateQ (ProtocolSpec.challengeQueryImpl (pSpec := outerPSpec F n params)) - (ProtocolSpec.getChallenge (outerPSpec F n params) - (outerChallengeBatchIdx F n M params)) - = ($ᵗ BatchingChallenge F n params.numGroups : - ProbComp (BatchingChallenge F n params.numGroups)) := by - unfold ProtocolSpec.getChallenge - change simulateQ (ProtocolSpec.challengeQueryImpl (pSpec := outerPSpec F n params)) - (liftM (OracleSpec.query (spec := [(outerPSpec F n params).Challenge]ₒ) - ⟨outerChallengeBatchIdx F n M params, ()⟩) : - OracleComp ([(outerPSpec F n params).Challenge]ₒ) - (BatchingChallenge F n params.numGroups)) - = ($ᵗ BatchingChallenge F n params.numGroups : - ProbComp (BatchingChallenge F n params.numGroups)) - rw [simulateQ_query] - change id <$> ($ᵗ BatchingChallenge F n params.numGroups : - ProbComp (BatchingChallenge F n params.numGroups)) = - ($ᵗ BatchingChallenge F n params.numGroups : - ProbComp (BatchingChallenge F n params.numGroups)) - simp - rw [h] - rfl - private theorem support_pure_eq {m : Type → Type*} [Monad m] [LawfulMonad m] [MonadLiftT m SetM] [LawfulMonadLiftT m SetM] {α : Type} {x y : α} (h : y ∈ support (pure x : m α)) : y = x := by @@ -634,8 +634,8 @@ theorem logup_outer_completeness [Inhabited F] : MonadLiftT.monadLift, OracleComp.liftComp_pure, bind_pure_comp, map_pure, QueryImpl.addLift_def] refine ge_trans (probEvent_mono - (p := fun out => ∀ u : Hypercube n, - out.2.1.xChallenge + evalOnHypercube (tableOracle oStmt) u ≠ 0) ?goodOutputs) ?goodProb + (p := fun out => ∀ u : (Fin n → Fin 2), + out.2.1.xChallenge + (tableOracle oStmt) u ≠ 0) ?goodOutputs) ?goodProb · -- every output with a non-pole outer challenge satisfies the success predicate intro out hout hGood rw [OptionT.mem_support_iff] at hout @@ -722,21 +722,21 @@ theorem logup_outer_completeness [Inhabited F] : simp only [Option.some.injEq] at hverifyEq subst vAccepted have hNoTablePoles : - ∀ u : Hypercube n, xval + evalOnHypercube (tableOracle oStmt) u ≠ 0 := by + ∀ u : (Fin n → Fin 2), xval + (tableOracle oStmt) u ≠ 0 := by intro u simpa [outerVerifier, outerChallengeXIdx, outerChallengeBatchIdx, ProtocolSpec.FullTranscript.challenges, ProtocolSpec.Transcript.concat, Fin.snoc] using hGood u - have hcols : ∀ j : Fin M, ∀ u : Hypercube n, ∃ v : Hypercube n, - evalOnHypercube (columnOracle oStmt j) u = - evalOnHypercube (tableOracle oStmt) v := by + have hcols : ∀ j : Fin M, ∀ u : (Fin n → Fin 2), ∃ v : (Fin n → Fin 2), + (columnOracle oStmt j) u = + (tableOracle oStmt) v := by simpa [inputRelation] using hIn have hchar : ∀ a : F, lookupMultiplicityCount oStmt a ≠ 0 → (tableMultiplicityCount oStmt a : F) ≠ 0 := fun _ hlookup => tableMultiplicityCount_cast_ne_zero_of_lookupMultiplicityCount_ne_zero stmt oStmt hcols hlookup - have hpoles : ∀ (i : TermIdx M) (u : Hypercube n), termPhi oStmt xval i u ≠ 0 := + have hpoles : ∀ (i : TermIdx M) (u : (Fin n → Fin 2)), termPhi oStmt xval i u ≠ 0 := termPhi_ne_zero_of_table_poles oStmt xval hcols hNoTablePoles let stmtAfter : StmtAfterOuter F n M params := { xChallenge := xval, zChallenge := zlam.1, batchingScalars := zlam.2 } @@ -789,10 +789,10 @@ theorem logup_outer_completeness [Inhabited F] : · simpa [probEvent_uniformSample] using (mul_le_probEvent_bind (mx := (liftM ($ᵗ F : ProbComp F) : OptionT ProbComp F)) - (p := fun x : F => ∀ u : Hypercube n, - x + evalOnHypercube (tableOracle oStmt) u ≠ 0) - (r := Pr[fun x : F => ∀ u : Hypercube n, - x + evalOnHypercube (tableOracle oStmt) u ≠ 0 | $ᵗ F]) + (p := fun x : F => ∀ u : (Fin n → Fin 2), + x + (tableOracle oStmt) u ≠ 0) + (r := Pr[fun x : F => ∀ u : (Fin n → Fin 2), + x + (tableOracle oStmt) u ≠ 0 | $ᵗ F]) (r' := 1) (by simp) (by intro x hx hGood erw [simulateQ_pure] diff --git a/ArkLib/ProofSystem/Logup/Security/Soundness.lean b/ArkLib/ProofSystem/Logup/Security/Soundness.lean index ca75be7f48..0193010dd8 100644 --- a/ArkLib/ProofSystem/Logup/Security/Soundness.lean +++ b/ArkLib/ProofSystem/Logup/Security/Soundness.lean @@ -27,8 +27,8 @@ be sampled from. This expression is only meaningful when `|H| < |F|` (see `logup otherwise the natural-number subtraction truncates to `0` and the term silently vanishes. -/ noncomputable def logupSoundnessError (F : Type) [Fintype F] (n M : ℕ) (params : ProtocolParams M) (sumcheckSoundnessError : ℝ≥0) : ℝ≥0 := - ((((M + 1) * Fintype.card (Hypercube n) - 1 : ℕ) : ℝ≥0) / - ((Fintype.card F - Fintype.card (Hypercube n) : ℕ) : ℝ≥0)) + + ((((M + 1) * Fintype.card (Fin n → Fin 2) - 1 : ℕ) : ℝ≥0) / + ((Fintype.card F - Fintype.card (Fin n → Fin 2) : ℕ) : ℝ≥0)) + (((params.numGroups + 1 : ℕ) : ℝ≥0) / (Fintype.card F : ℝ≥0)) + sumcheckSoundnessError @@ -38,7 +38,7 @@ The hypothesis `hcard : |H| < |F|` guarantees there exist non-pole field element challenge from, and makes the `|F| - |H|` denominator of `logupSoundnessError` positive (so the natural-number subtraction equals the true difference rather than truncating to `0`). -/ theorem logup_soundness (sumcheckSoundnessError : ℝ≥0) - (hcard : Fintype.card (Hypercube n) < Fintype.card F) : + (hcard : Fintype.card (Fin n → Fin 2) < Fintype.card F) : (logupVerifier oSpec F n M params).soundness init impl (inputRelation F n M).language outputRelation.language (logupSoundnessError F n M params sumcheckSoundnessError) := by diff --git a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean index e11c986f2f..18f4ddbb0f 100644 --- a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean +++ b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean @@ -89,7 +89,7 @@ noncomputable def logupSumcheckOracleStmt noncomputable def logupOuterSumcheckClaim (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) : F := - ∑ u : Hypercube n, + ∑ u : (Fin n → Fin 2), qOnHypercube (canonicalGroups params) (fun i => oStmt (.input i)) (oStmt .multiplicity) (oStmt .helpers) stmt.xChallenge stmt.zChallenge stmt.batchingScalars u @@ -128,33 +128,6 @@ def logupAfterSumcheckRelation : Sumcheck.Spec.relationRound F n (logupSumcheckDegree M params) (booleanDomain F) (Fin.last n) } -private theorem sum_piFinset_map_univ_eq_sum_hypercube - (D : Fin 2 ↪ F) (f : (Fin n → F) → F) : - (∑ x ∈ Fintype.piFinset fun _ : Fin n => Finset.univ.map D, f x) = - ∑ u : Hypercube n, f (fun j => D (u j)) := by - let e : Hypercube n ↪ (Fin n → F) := Function.Embedding.arrowCongrRight D - change (∑ x ∈ Fintype.piFinset fun _ : Fin n => Finset.univ.map D, f x) = - ∑ u : Hypercube n, f (e u) - rw [← Finset.sum_map] - congr 1 - ext x - constructor - · intro hx - rw [Fintype.mem_piFinset] at hx - have hx_coord : ∀ j : Fin n, ∃ b : Fin 2, D b = x j := by - intro j - rcases Finset.mem_map.mp (hx j) with ⟨b, _, hb⟩ - exact ⟨b, hb⟩ - let u : Hypercube n := fun j => Classical.choose (hx_coord j) - exact Finset.mem_map.mpr ⟨u, Finset.mem_univ _, by - funext j - exact Classical.choose_spec (hx_coord j)⟩ - · intro hx - rw [Fintype.mem_piFinset] - intro j - rcases Finset.mem_map.mp hx with ⟨u, _, rfl⟩ - exact Finset.mem_map.mpr ⟨u j, Finset.mem_univ _, rfl⟩ - /-- The initial generic Sumcheck relation induced by a LogUp outer transcript. -/ def logupSumcheckRelationInput (stmt : StmtAfterOuter F n M params) @@ -163,54 +136,6 @@ def logupSumcheckRelationInput ()) ∈ Sumcheck.Spec.relationRound F n (logupSumcheckDegree M params) (booleanDomain F) 0 -/-- If LogUp's outer algebra proves a zero sum, then the generic Sumcheck input relation is exactly -the claim sent to Sumcheck. -/ -theorem logupSumcheckRelationInput_of_zero - {stmt : StmtAfterOuter F n M params} - {oStmt : ∀ i, OStmtAfterOuter F n M params i} - (hZero : logupOuterSumcheckClaim F n M params stmt oStmt = 0) : - logupSumcheckRelationInput F n M params stmt oStmt := by - unfold logupSumcheckRelationInput Sumcheck.Spec.relationRound - simp only [Fin.coe_ofNat_eq_mod, Nat.zero_mod, Nat.sub_zero, logupInitialSumcheckStatement, - Set.mem_setOf_eq, Fin.elim0_append, logupSumcheckOracleStmt] - change - (∑ x ∈ Fintype.piFinset fun _ : Fin n => Finset.univ.map (booleanDomain F), - MvPolynomial.eval ((x ∘ Fin.cast (by omega)) ∘ Fin.cast (by omega)) - (logupSumcheckPolynomial F n M params stmt oStmt).val) = 0 - rw [sum_piFinset_map_univ_eq_sum_hypercube - (F := F) (n := n) (D := booleanDomain F) - (f := fun x => - MvPolynomial.eval ((x ∘ Fin.cast (by omega)) ∘ Fin.cast (by omega)) - (logupSumcheckPolynomial F n M params stmt oStmt).val)] - calc - (∑ u : Hypercube n, - MvPolynomial.eval - ((((fun j => (booleanDomain F) (u j)) ∘ Fin.cast (by omega)) ∘ - Fin.cast (by omega))) - (logupSumcheckPolynomial F n M params stmt oStmt).val) - = - logupOuterSumcheckClaim F n M params stmt oStmt := by - rw [logupOuterSumcheckClaim] - apply Finset.sum_congr rfl - intro u _ - simpa [booleanDomain, logupSumcheckPolynomial] using - logupQPolynomial_eval_hypercube F n M params stmt oStmt u - _ = 0 := hZero - -/-- The obligations needed to replace the current abstract embedded sumcheck by ArkLib's generic -sumcheck plus LogUp's final oracle-query check. -/ -structure LogupSumcheckBridge - (stmt : StmtAfterOuter F n M params) - (oStmt : ∀ i, OStmtAfterOuter F n M params i) where - claimZero : logupOuterSumcheckClaim F n M params stmt oStmt = 0 - finalEval : - ∀ (r : Fin n → F) (evals : PointEvaluations F M params.numGroups), - logupPointEvaluationsAgree (F := F) (n := n) (M := M) params r oStmt evals → - MvPolynomial.eval r (logupSumcheckPolynomial F n M params stmt oStmt).1 = - qAtPoint (canonicalGroups params) stmt.xChallenge stmt.zChallenge r - stmt.batchingScalars evals - - end SumcheckBridge section SumcheckLift diff --git a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean index b9bc27ff66..f4e8b63208 100644 --- a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean +++ b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean @@ -22,16 +22,68 @@ namespace Logup open scoped BigOperators +section QEvaluation + +variable {F : Type} [Field F] [Fintype F] [DecidableEq F] {n M K : ℕ} + +/-- The batched polynomial expression `Q` from paper equation (18), evaluated on a row `u ∈ H`. -/ +noncomputable def qOnHypercube (groups : PartialSumGroups M K) + (oStmt : ∀ i, OStmtIn F n M i) (multiplicity : MultilinearOracle F n) + (helpers : HelperMessages F n K) (xChallenge : F) (zChallenge : Fin n → F) + (batchingScalars : Fin K → F) (u : (Fin n → Fin 2)) : F := + ∑ k : Fin K, ( + (helpers k) u + + MvPolynomial.eval (u : Fin n → F) (MvPolynomial.eqPolynomial zChallenge) * + batchingScalars k * + domainIdentityTerm groups oStmt multiplicity helpers xChallenge k u) + +/-- Denominator term at the final sumcheck point, reconstructed from oracle query answers. -/ +def phiAtPoint (xChallenge : F) (evals : PointEvaluations F M K) : + InputOracleIdx M → F + | .table => xChallenge + evals.table + | .column i => xChallenge + evals.columns i + +/-- Numerator term at the final sumcheck point, reconstructed from oracle query answers. -/ +def numeratorAtPoint (evals : PointEvaluations F M K) : InputOracleIdx M → F + | .table => evals.multiplicity + | .column _ => -1 + +/-- Term denominator at the final sumcheck point, indexed by `0, ..., M`. -/ +def termPhiAtPoint (xChallenge : F) (evals : PointEvaluations F M K) (i : TermIdx M) : F := + phiAtPoint xChallenge evals (termToInput i) + +/-- Term numerator at the final sumcheck point, indexed by `0, ..., M`. -/ +def termNumeratorAtPoint (evals : PointEvaluations F M K) (i : TermIdx M) : F := + numeratorAtPoint evals (termToInput i) + +/-- The domain-identity expression at the final sumcheck point `r`. -/ +noncomputable def domainIdentityAtPoint (groups : PartialSumGroups M K) + (xChallenge : F) (evals : PointEvaluations F M K) (k : Fin K) : F := + evals.helpers k * (∏ i ∈ groups k, termPhiAtPoint xChallenge evals i) - + ∑ i ∈ groups k, + termNumeratorAtPoint evals i * + ∏ j ∈ (groups k).erase i, termPhiAtPoint xChallenge evals j + +/-- The verifier's final check value `Q(eq(r,z), m(r), φᵢ(r), hₖ(r))` from paper (19). -/ +noncomputable def qAtPoint (groups : PartialSumGroups M K) (xChallenge : F) + (zChallenge rChallenge : Fin n → F) (batchingScalars : Fin K → F) + (evals : PointEvaluations F M K) : F := + ∑ k : Fin K, ( + evals.helpers k + + MvPolynomial.eval rChallenge (MvPolynomial.eqPolynomial zChallenge) * batchingScalars k * + domainIdentityAtPoint groups xChallenge evals k) + +end QEvaluation + section SumcheckPolynomial variable (F : Type) [Field F] (n M : ℕ) variable (params : ProtocolParams M) private theorem oraclePolynomial_eval_hypercube - (oracle : MultilinearOracle F n) (u : Hypercube n) : + (oracle : MultilinearOracle F n) (u : (Fin n → Fin 2)) : MvPolynomial.eval (u : Fin n → F) (MvPolynomial.MLE oracle.values) - = evalOnHypercube oracle u := by - change MvPolynomial.eval (u : Fin n → F) (MvPolynomial.MLE oracle.values) = oracle.values u + = oracle u := by simp private noncomputable def inputOraclePolynomial @@ -270,7 +322,7 @@ theorem logupQPolynomial_degreeOf private theorem termPhiPolynomial_eval_hypercube (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) - (i : TermIdx M) (u : Hypercube n) : + (i : TermIdx M) (u : (Fin n → Fin 2)) : MvPolynomial.eval (u : Fin n → F) (termPhiPolynomial F n M params stmt oStmt i) = termPhi (fun idx => oStmt (.input idx)) stmt.xChallenge i u := by rcases h : termToInput i with _ | c <;> @@ -278,7 +330,7 @@ private theorem termPhiPolynomial_eval_hypercube columnOracle, map_add, MvPolynomial.eval_C, oraclePolynomial_eval_hypercube F n] private theorem termNumeratorPolynomial_eval_hypercube - (oStmt : ∀ i, OStmtAfterOuter F n M params i) (i : TermIdx M) (u : Hypercube n) : + (oStmt : ∀ i, OStmtAfterOuter F n M params i) (i : TermIdx M) (u : (Fin n → Fin 2)) : MvPolynomial.eval (u : Fin n → F) (termNumeratorPolynomial F n M params oStmt i) = termNumerator (oStmt .multiplicity) i u := by unfold termNumeratorPolynomial termNumerator numerator @@ -288,7 +340,7 @@ private theorem termNumeratorPolynomial_eval_hypercube private theorem domainIdentityPolynomial_eval_hypercube (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) - (k : Fin params.numGroups) (u : Hypercube n) : + (k : Fin params.numGroups) (u : (Fin n → Fin 2)) : MvPolynomial.eval (u : Fin n → F) (domainIdentityPolynomial F n M params stmt oStmt k) = domainIdentityTerm (canonicalGroups params) (fun idx => oStmt (.input idx)) (oStmt .multiplicity) (oStmt .helpers) stmt.xChallenge k u := by @@ -364,7 +416,7 @@ private theorem domainIdentityPolynomial_eval_point /-- `logupQPolynomial` restricted to the Boolean hypercube agrees with `qOnHypercube`. -/ theorem logupQPolynomial_eval_hypercube (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) - (u : Hypercube n) : + (u : (Fin n → Fin 2)) : MvPolynomial.eval (u : Fin n → F) (logupQPolynomial F n M params stmt oStmt) = qOnHypercube (canonicalGroups params) (fun i => oStmt (.input i)) (oStmt .multiplicity) (oStmt .helpers) stmt.xChallenge stmt.zChallenge stmt.batchingScalars u := by @@ -372,7 +424,7 @@ theorem logupQPolynomial_eval_hypercube refine Finset.sum_congr rfl (fun k _ => ?_) rw [map_add, map_mul, map_mul, MvPolynomial.eval_C, show MvPolynomial.eval (u : Fin n → F) (helperPolynomial F n M params oStmt k) - = evalOnHypercube ((oStmt .helpers) k) u from by + = ((oStmt .helpers) k) u from by simp only [helperPolynomial]; exact oraclePolynomial_eval_hypercube F n _ u, domainIdentityPolynomial_eval_hypercube F n M params] From 26fb404cf683768ac9fff01001244763c1598631 Mon Sep 17 00:00:00 2001 From: jCabala Date: Fri, 26 Jun 2026 21:09:10 +0200 Subject: [PATCH 08/12] refactor: Removed redundand abbreviations and abstractions from the Logup protocol andd restributed theroems and defs between files --- ArkLib.lean | 4 +- ArkLib/ProofSystem/Logup/Algebra.lean | 742 ++++++++++++++++ ArkLib/ProofSystem/Logup/Common.lean | 343 -------- ArkLib/ProofSystem/Logup/Protocol.lean | 819 ++++++++++++------ .../Logup/Security/Completeness.lean | 407 +++------ .../Logup/Sumcheck/SumcheckBridge.lean | 173 ---- .../Logup/Sumcheck/SumcheckPolynomial.lean | 450 ---------- 7 files changed, 1383 insertions(+), 1555 deletions(-) create mode 100644 ArkLib/ProofSystem/Logup/Algebra.lean delete mode 100644 ArkLib/ProofSystem/Logup/Common.lean delete mode 100644 ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean delete mode 100644 ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean diff --git a/ArkLib.lean b/ArkLib.lean index 17b094706a..6d0e2f3f45 100644 --- a/ArkLib.lean +++ b/ArkLib.lean @@ -205,12 +205,10 @@ import ArkLib.ProofSystem.ConstraintSystem.R1CS import ArkLib.ProofSystem.Fri.RoundConsistency import ArkLib.ProofSystem.Fri.Spec.General import ArkLib.ProofSystem.Fri.Spec.SingleRound -import ArkLib.ProofSystem.Logup.Common +import ArkLib.ProofSystem.Logup.Algebra import ArkLib.ProofSystem.Logup.Protocol import ArkLib.ProofSystem.Logup.Security.Completeness import ArkLib.ProofSystem.Logup.Security.Soundness -import ArkLib.ProofSystem.Logup.Sumcheck.SumcheckBridge -import ArkLib.ProofSystem.Logup.Sumcheck.SumcheckPolynomial import ArkLib.ProofSystem.Plonk.Basic import ArkLib.ProofSystem.Spartan.Basic import ArkLib.ProofSystem.Stir.Combine diff --git a/ArkLib/ProofSystem/Logup/Algebra.lean b/ArkLib/ProofSystem/Logup/Algebra.lean new file mode 100644 index 0000000000..03712d2ce3 --- /dev/null +++ b/ArkLib/ProofSystem/Logup/Algebra.lean @@ -0,0 +1,742 @@ +import ArkLib.Data.MvPolynomial.Multilinear +import Mathlib.Algebra.Order.Floor.Div + +/-! +# LogUp algebra and sumcheck polynomial + +This file contains the protocol-independent algebra used to express LogUp as a sumcheck claim. It +does not define statements, transcripts, provers, or verifiers. Instead, it works with plain +hypercube functions `(Fin n → Fin 2) → F`, polynomial inputs `MvPolynomial (Fin n) F`, challenges, +and partial-sum groups. + +The main output is `logupQPolynomial`, the polynomial `Q` that the protocol sends to sumcheck, plus +`logupQPolynomial_degreeOf`, the individual-degree bound needed by sumcheck. The row-level and +point-level definitions in this file are the semantic versions used to connect `Q` back to honest +LogUp data and to the verifier's final point check. + +## Sections + +* **Generic batched polynomial** defines the abstract polynomial shape used by the sumcheck claim: + for each helper group, multiply by the relevant denominators to clear fractions, then batch all + group identities with the equality-polynomial weight and verifier scalars. This section also + proves the generic individual-degree bound, so the later LogUp-specific proof only has to show + that its ingredients are multilinear. +* **Term indexing** gives names to the `M + 1` fractional terms. The table term has input label + `InputIdx.table` and paper index `0`; lookup column `j` has input label `InputIdx.column j` and + paper index `j + 1`. The conversion lemmas here keep later sums over `Fin (M + 1)` aligned with + the table/column oracle labels. +* **Fractional-identity algebra** defines the row-wise LogUp quantities on the Boolean hypercube. + It computes the normalized table multiplicity, forms the logarithmic-derivative terms + `mᵢ(u) / φᵢ(u)` for the table and lookup columns, groups those terms into helper values, clears + denominators inside each group, and combines the cleared identities into the row value + `qOnHypercube`. +* **Final-point reconstructions** defines the verifier-side version of the same expressions at + sumcheck's final point `r`. At this point the verifier has scalar oracle answers `m(r)`, `t(r)`, + `fᵢ(r)`, and `hₖ(r)`, not whole row functions, so this section rebuilds the value that should + equal the final sumcheck claim. +* **The LogUp polynomial** builds the actual multivariate polynomial `Q` from the table, column, + multiplicity, and helper polynomials. It instantiates the generic batched polynomial with LogUp's + concrete denominator and numerator polynomials, then proves the `M + 3` individual-degree bound + required by ArkLib's generic sumcheck protocol. +-/ + +namespace Logup + +open scoped BigOperators + +/-! ## Generic batched polynomial -/ + +section BatchedPolynomial + +variable {F : Type} [Field F] {n T K : ℕ} +variable (groups : Fin K → Finset (Fin T)) +variable (phiPoly numerPoly : Fin T → MvPolynomial (Fin n) F) +variable (helperPoly : Fin K → MvPolynomial (Fin n) F) + +/-- One group's cleared-denominator identity, as a polynomial in abstract per-term denominator +(`phiPoly`) and numerator (`numerPoly`) polynomials and per-group helper polynomials. -/ +noncomputable def batchedDomainIdentity (k : Fin K) : MvPolynomial (Fin n) F := + helperPoly k * (∏ i ∈ groups k, phiPoly i) - + ∑ i ∈ groups k, numerPoly i * ∏ j ∈ (groups k).erase i, phiPoly j + +/-- The generic batched sumcheck polynomial: a `λ`-batched sum of per-group cleared-denominator +identities, weighted by an equality kernel. Independent of any particular protocol. -/ +noncomputable def batchedSumcheckPolynomial + (zChallenge : Fin n → F) (batchingScalars : Fin K → F) : MvPolynomial (Fin n) F := + ∑ k : Fin K, (helperPoly k + + MvPolynomial.eqPolynomial zChallenge * MvPolynomial.C (batchingScalars k) * + batchedDomainIdentity groups phiPoly numerPoly helperPoly k) + +private theorem prod_phiPoly_degreeOf + (hphi : ∀ i v, MvPolynomial.degreeOf v (phiPoly i) ≤ 1) + (s : Finset (Fin T)) (v : Fin n) : + MvPolynomial.degreeOf v (∏ i ∈ s, phiPoly i) ≤ T := by + calc + _ ≤ ∑ i ∈ s, MvPolynomial.degreeOf v (phiPoly i) := MvPolynomial.degreeOf_prod_le v _ _ + _ ≤ ∑ _i ∈ s, 1 := Finset.sum_le_sum (fun i _ => hphi i v) + _ = s.card := by simp + _ ≤ T := le_trans (Finset.card_le_univ s) (by simp) + +/-- Each group identity has individual degree at most `T + 1`. -/ +theorem batchedDomainIdentity_degreeOf + (hphi : ∀ i v, MvPolynomial.degreeOf v (phiPoly i) ≤ 1) + (hnumer : ∀ i v, MvPolynomial.degreeOf v (numerPoly i) ≤ 1) + (hhelper : ∀ k v, MvPolynomial.degreeOf v (helperPoly k) ≤ 1) + (k : Fin K) (v : Fin n) : + MvPolynomial.degreeOf v (batchedDomainIdentity groups phiPoly numerPoly helperPoly k) + ≤ T + 1 := by + unfold batchedDomainIdentity + have hLeft : + MvPolynomial.degreeOf v (helperPoly k * (∏ i ∈ groups k, phiPoly i)) ≤ T + 1 := by + calc + _ ≤ MvPolynomial.degreeOf v (helperPoly k) + + MvPolynomial.degreeOf v (∏ i ∈ groups k, phiPoly i) := MvPolynomial.degreeOf_mul_le v _ _ + _ ≤ 1 + T := by + gcongr + · exact hhelper k v + · exact prod_phiPoly_degreeOf phiPoly hphi (groups k) v + _ = T + 1 := by omega + have hRight : + MvPolynomial.degreeOf v + (∑ i ∈ groups k, numerPoly i * ∏ j ∈ (groups k).erase i, phiPoly j) ≤ T + 1 := by + calc + _ ≤ (groups k).sup fun i => + MvPolynomial.degreeOf v (numerPoly i * ∏ j ∈ (groups k).erase i, phiPoly j) := + MvPolynomial.degreeOf_sum_le v _ _ + _ ≤ T + 1 := by + apply Finset.sup_le + intro i _ + calc + _ ≤ MvPolynomial.degreeOf v (numerPoly i) + + MvPolynomial.degreeOf v (∏ j ∈ (groups k).erase i, phiPoly j) := + MvPolynomial.degreeOf_mul_le v _ _ + _ ≤ 1 + T := by + gcongr + · exact hnumer i v + · exact prod_phiPoly_degreeOf phiPoly hphi ((groups k).erase i) v + _ = T + 1 := by omega + exact (MvPolynomial.degreeOf_sub_le v _ _).trans (max_le hLeft hRight) + +/-- The batched polynomial has individual degree at most `T + 2`. -/ +theorem batchedSumcheckPolynomial_degreeOf + (zChallenge : Fin n → F) (batchingScalars : Fin K → F) + (hphi : ∀ i v, MvPolynomial.degreeOf v (phiPoly i) ≤ 1) + (hnumer : ∀ i v, MvPolynomial.degreeOf v (numerPoly i) ≤ 1) + (hhelper : ∀ k v, MvPolynomial.degreeOf v (helperPoly k) ≤ 1) + (v : Fin n) : + MvPolynomial.degreeOf v + (batchedSumcheckPolynomial groups phiPoly numerPoly helperPoly zChallenge batchingScalars) + ≤ T + 2 := by + unfold batchedSumcheckPolynomial + calc + _ ≤ (Finset.univ : Finset (Fin K)).sup fun k => + MvPolynomial.degreeOf v + (helperPoly k + + MvPolynomial.eqPolynomial zChallenge * MvPolynomial.C (batchingScalars k) * + batchedDomainIdentity groups phiPoly numerPoly helperPoly k) := + MvPolynomial.degreeOf_sum_le v _ _ + _ ≤ T + 2 := by + apply Finset.sup_le + intro k _ + have hHelper : + MvPolynomial.degreeOf v (helperPoly k) ≤ T + 2 := (hhelper k v).trans (by omega) + have hProduct : + MvPolynomial.degreeOf v + (MvPolynomial.eqPolynomial zChallenge * MvPolynomial.C (batchingScalars k) * + batchedDomainIdentity groups phiPoly numerPoly helperPoly k) ≤ T + 2 := by + calc + _ ≤ MvPolynomial.degreeOf v + (MvPolynomial.eqPolynomial zChallenge * MvPolynomial.C (batchingScalars k)) + + MvPolynomial.degreeOf v + (batchedDomainIdentity groups phiPoly numerPoly helperPoly k) := + MvPolynomial.degreeOf_mul_le v _ _ + _ ≤ (MvPolynomial.degreeOf v (MvPolynomial.eqPolynomial zChallenge) + + MvPolynomial.degreeOf v (MvPolynomial.C (batchingScalars k))) + + MvPolynomial.degreeOf v + (batchedDomainIdentity groups phiPoly numerPoly helperPoly k) := by + gcongr + exact MvPolynomial.degreeOf_mul_le v _ _ + _ ≤ (1 + 0) + (T + 1) := by + gcongr + · exact MvPolynomial.eqPolynomial_degreeOf (R := F) zChallenge v + · exact (MvPolynomial.degreeOf_C (R := F) (batchingScalars k) v).le + · exact batchedDomainIdentity_degreeOf groups phiPoly numerPoly helperPoly + hphi hnumer hhelper k v + _ = T + 2 := by omega + exact (MvPolynomial.degreeOf_add_le v _ _).trans (max_le hHelper hProduct) + +end BatchedPolynomial + +/-! ## Term indexing + +LogUp has `M + 1` terms: term `0` is the table, terms `1, …, M` are the lookup columns. -/ + +/-- Labels for the `M + 1` LogUp terms: the table and the `M` lookup columns. -/ +inductive InputIdx (M : ℕ) where + | table : InputIdx M + | column : Fin M → InputIdx M +deriving DecidableEq + +/-- Term labels `0, ..., M` from the paper, with `0` denoting the table term. -/ +@[reducible] +def TermIdx (M : ℕ) : Type := + Fin (M + 1) + +/-- Interpret term index `0` as the table and term indices `1, ..., M` as lookup columns. -/ +def termToInput {M : ℕ} (i : TermIdx M) : InputIdx M := + if h : i.val = 0 then .table else .column ⟨i.val - 1, by omega⟩ + +/-- Interpret a table/column label as its term index `0, ..., M`. -/ +def inputToTerm {M : ℕ} : InputIdx M → TermIdx M + | .table => ⟨0, by omega⟩ + | .column i => ⟨i.val + 1, by omega⟩ + +@[simp] +theorem termToInput_inputToTerm {M : ℕ} (i : InputIdx M) : + termToInput (inputToTerm i) = i := by + cases i <;> simp [termToInput, inputToTerm] + +@[simp] +theorem inputToTerm_termToInput {M : ℕ} (i : TermIdx M) : + inputToTerm (termToInput i) = i := by + unfold termToInput + split + · next h => exact Fin.ext h.symm + · next h => apply Fin.ext; simp only [inputToTerm]; omega + +/-! ## Fractional-identity algebra + +LogUp's logarithmic-derivative construction, evaluated on hypercube rows. The table, columns, +multiplicity, and helper functions are bare hypercube functions `(Fin n → Fin 2) → F`. -/ + +section Algebra + +variable {F : Type} [Field F] {n M K : ℕ} + +/-- Number of table rows with value `a`. -/ +def tableMultiplicityCount [Fintype F] [DecidableEq F] (table : (Fin n → Fin 2) → F) (a : F) : ℕ := + ((Finset.univ : Finset (Fin n → Fin 2)).filter fun u => table u = a).card + +/-- Total number of lookup-column entries with value `a`. -/ +def lookupMultiplicityCount [Fintype F] [DecidableEq F] + (columns : Fin M → (Fin n → Fin 2) → F) (a : F) : ℕ := + ((Finset.univ : Finset (Fin M × (Fin n → Fin 2))).filter fun ix => columns ix.1 ix.2 = a).card + +/-- The normalized multiplicity from paper equation (14), evaluated at one table row. -/ +noncomputable def normalizedMultiplicityValue [Fintype F] [DecidableEq F] + (table : (Fin n → Fin 2) → F) (columns : Fin M → (Fin n → Fin 2) → F) (u : Fin n → Fin 2) : F := + let a := table u + (lookupMultiplicityCount columns a : F) / (tableMultiplicityCount table a : F) + +/-- The denominator term `φᵢ(u)` from Protocol 2. -/ +noncomputable def phi (table : (Fin n → Fin 2) → F) (columns : Fin M → (Fin n → Fin 2) → F) + (xChallenge : F) : InputIdx M → (Fin n → Fin 2) → F + | .table => fun u => xChallenge + table u + | .column i => fun u => xChallenge + columns i u + +/-- The numerator term `mᵢ(u)`, with `m₀ = m` and `mᵢ = -1` for columns. -/ +noncomputable def numerator (multiplicity : (Fin n → Fin 2) → F) : + InputIdx M → (Fin n → Fin 2) → F + | .table => multiplicity + | .column _ => fun _ => -1 + +/-- The denominator term, indexed as `0, ..., M` as in the paper. -/ +noncomputable def termPhi (table : (Fin n → Fin 2) → F) (columns : Fin M → (Fin n → Fin 2) → F) + (xChallenge : F) (i : TermIdx M) (u : Fin n → Fin 2) : F := + phi table columns xChallenge (termToInput i) u + +/-- The numerator term, indexed as `0, ..., M` as in the paper. -/ +noncomputable def termNumerator (multiplicity : (Fin n → Fin 2) → F) + (i : TermIdx M) (u : Fin n → Fin 2) : F := + numerator multiplicity (termToInput i) u + +/-- The domain-identity expression for one helper function `hₖ`. -/ +noncomputable def domainIdentityTerm (groups : Fin K → Finset (TermIdx M)) + (table : (Fin n → Fin 2) → F) (columns : Fin M → (Fin n → Fin 2) → F) + (multiplicity : (Fin n → Fin 2) → F) (helpers : Fin K → (Fin n → Fin 2) → F) + (xChallenge : F) (k : Fin K) (u : Fin n → Fin 2) : F := + helpers k u * (∏ i ∈ groups k, termPhi table columns xChallenge i u) - + ∑ i ∈ groups k, + termNumerator multiplicity i u * ∏ j ∈ (groups k).erase i, termPhi table columns xChallenge j u + +/-- The helper value `hₖ(u) = Σᵢ mᵢ(u)/φᵢ(u)` from paper equation (16). -/ +noncomputable def helperValue (groups : Fin K → Finset (TermIdx M)) + (table : (Fin n → Fin 2) → F) (columns : Fin M → (Fin n → Fin 2) → F) + (multiplicity : (Fin n → Fin 2) → F) (xChallenge : F) (k : Fin K) (u : Fin n → Fin 2) : F := + ∑ i ∈ groups k, termNumerator multiplicity i u / termPhi table columns xChallenge i u + +/-- The batched polynomial expression `Q` from paper equation (18), evaluated on a row `u ∈ H`. -/ +noncomputable def qOnHypercube (groups : Fin K → Finset (TermIdx M)) + (table : (Fin n → Fin 2) → F) (columns : Fin M → (Fin n → Fin 2) → F) + (multiplicity : (Fin n → Fin 2) → F) (helpers : Fin K → (Fin n → Fin 2) → F) + (xChallenge : F) (zChallenge : Fin n → F) (batchingScalars : Fin K → F) + (u : Fin n → Fin 2) : F := + ∑ k : Fin K, ( + helpers k u + + MvPolynomial.eval (u : Fin n → F) (MvPolynomial.eqPolynomial zChallenge) * + batchingScalars k * + domainIdentityTerm groups table columns multiplicity helpers xChallenge k u) + +/-- Honest helper values make the cleared-denominator identity vanish pointwise, away from poles. -/ +theorem domainIdentityTerm_eq_zero (groups : Fin K → Finset (TermIdx M)) + (table : (Fin n → Fin 2) → F) (columns : Fin M → (Fin n → Fin 2) → F) + (multiplicity : (Fin n → Fin 2) → F) (helpers : Fin K → (Fin n → Fin 2) → F) + (xChallenge : F) (k : Fin K) (u : Fin n → Fin 2) + (hh : helpers k u = helperValue groups table columns multiplicity xChallenge k u) + (hφ : ∀ i ∈ groups k, termPhi table columns xChallenge i u ≠ 0) : + domainIdentityTerm groups table columns multiplicity helpers xChallenge k u = 0 := by + rw [domainIdentityTerm, hh, helperValue, Finset.sum_mul, sub_eq_zero] + refine Finset.sum_congr rfl (fun i hi => ?_) + rw [← Finset.mul_prod_erase _ _ hi] + field_simp [hφ i hi] + +/-- The normalized multiplicity makes the table-side logarithmic derivative equal the sum of the +lookup-column logarithmic derivatives. -/ +theorem honest_multiplicity_identity [Fintype F] [DecidableEq F] + (table : (Fin n → Fin 2) → F) (columns : Fin M → (Fin n → Fin 2) → F) (xChallenge : F) + (hcols : ∀ j : Fin M, ∀ u : Fin n → Fin 2, ∃ v : Fin n → Fin 2, + columns j u = table v) + (hchar : ∀ a : F, lookupMultiplicityCount columns a ≠ 0 → + (tableMultiplicityCount table a : F) ≠ 0) : + (∑ u : Fin n → Fin 2, + normalizedMultiplicityValue table columns u / (xChallenge + table u)) + = ∑ j : Fin M, ∑ u : Fin n → Fin 2, + (1 : F) / (xChallenge + columns j u) := by + classical + have key : ∀ a : F, + tableMultiplicityCount table a • + ((lookupMultiplicityCount columns a : F) / + (tableMultiplicityCount table a : F) / (xChallenge + a)) + = lookupMultiplicityCount columns a • ((1 : F) / (xChallenge + a)) := by + intro a + by_cases hT : tableMultiplicityCount table a = 0 + · have hL : lookupMultiplicityCount columns a = 0 := by + rw [lookupMultiplicityCount, Finset.card_eq_zero, Finset.filter_eq_empty_iff] + rintro ⟨j, u⟩ - hja + obtain ⟨v, hv⟩ := hcols j u + have hav : table v = a := hv.symm.trans hja + rw [tableMultiplicityCount] at hT + exact absurd hT (Finset.card_ne_zero_of_mem + (show v ∈ Finset.univ.filter (fun w => table w = a) by simp [hav])) + simp [hT, hL] + · rw [nsmul_eq_mul, nsmul_eq_mul] + by_cases hL : lookupMultiplicityCount columns a = 0 + · simp [hL] + · have hTF := hchar a hL + field_simp + have hLHS : + (∑ u : Fin n → Fin 2, normalizedMultiplicityValue table columns u / + (xChallenge + table u)) + = ∑ a : F, tableMultiplicityCount table a • + ((lookupMultiplicityCount columns a : F) / (tableMultiplicityCount table a : F) + / (xChallenge + a)) := by + rw [← Finset.sum_fiberwise (Finset.univ : Finset (Fin n → Fin 2)) table] + refine Finset.sum_congr rfl (fun a _ => ?_) + have h : ∀ u ∈ Finset.univ.filter (fun u => table u = a), + normalizedMultiplicityValue table columns u / (xChallenge + table u) + = (lookupMultiplicityCount columns a : F) / (tableMultiplicityCount table a : F) + / (xChallenge + a) := by + intro u hu + simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hu + change (lookupMultiplicityCount columns (table u) : F) / + (tableMultiplicityCount table (table u) : F) / + (xChallenge + table u) + = (lookupMultiplicityCount columns a : F) / + (tableMultiplicityCount table a : F) / (xChallenge + a) + rw [hu] + rw [Finset.sum_congr rfl h, Finset.sum_const] + rfl + have hRHS : + (∑ j : Fin M, ∑ u : Fin n → Fin 2, (1 : F) / (xChallenge + columns j u)) + = ∑ a : F, lookupMultiplicityCount columns a • ((1 : F) / (xChallenge + a)) := by + rw [← Finset.sum_product', Finset.univ_product_univ, + ← Finset.sum_fiberwise (Finset.univ : Finset (Fin M × (Fin n → Fin 2))) + (fun p => columns p.1 p.2)] + refine Finset.sum_congr rfl (fun a _ => ?_) + have h : ∀ p ∈ Finset.univ.filter + (fun p : Fin M × (Fin n → Fin 2) => columns p.1 p.2 = a), + (1 : F) / (xChallenge + columns p.1 p.2) = (1 : F) / (xChallenge + a) := by + intro p hp + simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hp + simp only [hp] + rw [Finset.sum_congr rfl h, Finset.sum_const] + rfl + rw [hLHS, hRHS] + exact Finset.sum_congr rfl (fun a _ => key a) + +/-- If a lookup value appears in a column and every lookup value appears in the table, then the +corresponding table multiplicity is nonzero as a field element under the LogUp characteristic +bound. -/ +theorem tableMultiplicityCount_cast_ne_zero_of_lookupMultiplicityCount_ne_zero [Fintype F] + [DecidableEq F] (hcharLarge : M * 2 ^ n < ringChar F) + (table : (Fin n → Fin 2) → F) (columns : Fin M → (Fin n → Fin 2) → F) + (hcols : ∀ j : Fin M, ∀ u : Fin n → Fin 2, ∃ v : Fin n → Fin 2, + columns j u = table v) + {a : F} (hlookup : lookupMultiplicityCount columns a ≠ 0) : + (tableMultiplicityCount table a : F) ≠ 0 := by + classical + have hlookupCard : + ((Finset.univ : Finset (Fin M × (Fin n → Fin 2))).filter fun ix => + columns ix.1 ix.2 = a).card ≠ 0 := by + simpa [lookupMultiplicityCount] using hlookup + obtain ⟨⟨j, u⟩, hju⟩ := Finset.card_ne_zero.mp hlookupCard + simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hju + obtain ⟨v, hv⟩ := hcols j u + have hvTable : table v = a := hv.symm.trans hju + have htablePos : 0 < tableMultiplicityCount table a := by + rw [tableMultiplicityCount, Finset.card_pos] + exact ⟨v, by simp [hvTable]⟩ + have hMpos : 0 < M := lt_of_le_of_lt (Nat.zero_le j.val) j.isLt + have htable_le_card : + tableMultiplicityCount table a ≤ Fintype.card (Fin n → Fin 2) := by + rw [tableMultiplicityCount, ← Finset.card_univ] + exact Finset.card_filter_le _ _ + have hcard_hypercube : Fintype.card (Fin n → Fin 2) = 2 ^ n := by + simp + have hpow_le : 2 ^ n ≤ M * 2 ^ n := by + have hMone : 1 ≤ M := Nat.succ_le_of_lt hMpos + nth_rewrite 1 [← Nat.one_mul (2 ^ n)] + exact Nat.mul_le_mul_right (2 ^ n) hMone + have htable_lt_char : tableMultiplicityCount table a < ringChar F := by + calc + tableMultiplicityCount table a ≤ Fintype.card (Fin n → Fin 2) := htable_le_card + _ = 2 ^ n := hcard_hypercube + _ ≤ M * 2 ^ n := hpow_le + _ < ringChar F := hcharLarge + intro hzero + have hdvd : ringChar F ∣ tableMultiplicityCount table a := + (ringChar.spec F (tableMultiplicityCount table a)).1 hzero + exact (Nat.not_dvd_of_pos_of_lt htablePos htable_lt_char) hdvd + +/-- If `x` avoids the table poles, then it avoids all table and lookup-column denominator poles +under lookup containment. -/ +theorem termPhi_ne_zero_of_table_poles + (table : (Fin n → Fin 2) → F) (columns : Fin M → (Fin n → Fin 2) → F) (xChallenge : F) + (hcols : ∀ j : Fin M, ∀ u : Fin n → Fin 2, ∃ v : Fin n → Fin 2, + columns j u = table v) + (hNoTablePoles : ∀ u : Fin n → Fin 2, xChallenge + table u ≠ 0) : + ∀ (i : TermIdx M) (u : Fin n → Fin 2), termPhi table columns xChallenge i u ≠ 0 := by + intro i u + cases hti : termToInput i with + | table => + rw [termPhi, hti] + simpa [phi] using hNoTablePoles u + | column j => + obtain ⟨v, hv⟩ := hcols j u + rw [termPhi, hti, phi] + simpa [hv] using hNoTablePoles v + +/-- The table-pole set has size at most the Boolean hypercube. -/ +theorem pole_card_le [Fintype F] [DecidableEq F] (table : (Fin n → Fin 2) → F) : + (Finset.univ.filter (fun x : F => ∃ u : Fin n → Fin 2, x + table u = 0)).card + ≤ Fintype.card (Fin n → Fin 2) := by + classical + calc + (Finset.univ.filter (fun x : F => ∃ u : Fin n → Fin 2, x + table u = 0)).card + ≤ (Finset.univ.image (fun u : Fin n → Fin 2 => -table u)).card := by + apply Finset.card_le_card + intro x hx + simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hx + obtain ⟨u, hu⟩ := hx + exact Finset.mem_image.mpr + ⟨u, Finset.mem_univ u, (eq_neg_of_add_eq_zero_left hu).symm⟩ + _ ≤ Fintype.card (Fin n → Fin 2) := by + rw [← Finset.card_univ] + exact Finset.card_image_le + +/-- The honest row-wise LogUp claim sums to zero away from denominator poles. -/ +theorem logupOuterClaim_zero [Fintype F] [DecidableEq F] + (groups : Fin K → Finset (TermIdx M)) + (hgroups : ∀ g : TermIdx M → F, (∑ k : Fin K, ∑ i ∈ groups k, g i) = ∑ i : TermIdx M, g i) + (table : (Fin n → Fin 2) → F) (columns : Fin M → (Fin n → Fin 2) → F) + (xChallenge : F) (zChallenge : Fin n → F) (batchingScalars : Fin K → F) + (hcols : ∀ j : Fin M, ∀ u : Fin n → Fin 2, ∃ v : Fin n → Fin 2, + columns j u = table v) + (hchar : ∀ a : F, lookupMultiplicityCount columns a ≠ 0 → + (tableMultiplicityCount table a : F) ≠ 0) + (hpoles : ∀ (i : TermIdx M) (u : Fin n → Fin 2), + termPhi table columns xChallenge i u ≠ 0) : + (∑ u : Fin n → Fin 2, + qOnHypercube groups table columns (normalizedMultiplicityValue table columns) + (fun k u => helperValue groups table columns + (normalizedMultiplicityValue table columns) xChallenge k u) + xChallenge zChallenge batchingScalars u) = 0 := by + have hq : ∀ u : Fin n → Fin 2, + qOnHypercube groups table columns (normalizedMultiplicityValue table columns) + (fun k u => helperValue groups table columns + (normalizedMultiplicityValue table columns) xChallenge k u) + xChallenge zChallenge batchingScalars u + = ∑ k : Fin K, + helperValue groups table columns + (normalizedMultiplicityValue table columns) xChallenge k u := by + intro u + simp only [qOnHypercube] + refine Finset.sum_congr rfl (fun k _ => ?_) + rw [domainIdentityTerm_eq_zero groups table columns (normalizedMultiplicityValue table columns) + (fun k u => helperValue groups table columns + (normalizedMultiplicityValue table columns) xChallenge k u) + xChallenge k u rfl (fun i _ => hpoles i u), mul_zero, add_zero] + have hsum : ∀ u : Fin n → Fin 2, + (∑ k : Fin K, + helperValue groups table columns + (normalizedMultiplicityValue table columns) xChallenge k u) + = ∑ i : TermIdx M, + termNumerator (normalizedMultiplicityValue table columns) i u / + termPhi table columns xChallenge i u := by + intro u + simp only [helperValue] + exact hgroups + (fun i => termNumerator (normalizedMultiplicityValue table columns) i u / + termPhi table columns xChallenge i u) + have hterm : ∀ u : Fin n → Fin 2, + (∑ i : TermIdx M, + termNumerator (normalizedMultiplicityValue table columns) i u / + termPhi table columns xChallenge i u) + = normalizedMultiplicityValue table columns u / (xChallenge + table u) + + ∑ j : Fin M, (-1 : F) / (xChallenge + columns j u) := by + intro u + have hcol : ∀ j : Fin M, + termNumerator (normalizedMultiplicityValue table columns) (Fin.succ j) u / + termPhi table columns xChallenge (Fin.succ j) u + = (-1 : F) / (xChallenge + columns j u) := by + intro j + have htt : termToInput (Fin.succ j : TermIdx M) = InputIdx.column j := by + simp [termToInput] + simp only [termNumerator, termPhi, htt, numerator, phi] + rw [Fin.sum_univ_succ] + refine congrArg₂ (· + ·) rfl ?_ + exact Finset.sum_congr rfl (fun j _ => hcol j) + simp_rw [hq, hsum, hterm] + rw [Finset.sum_add_distrib, honest_multiplicity_identity table columns xChallenge hcols hchar, + Finset.sum_comm (f := fun u j => (-1 : F) / (xChallenge + columns j u)), + ← Finset.sum_add_distrib] + refine Finset.sum_eq_zero (fun j _ => ?_) + rw [← Finset.sum_add_distrib] + exact Finset.sum_eq_zero (fun u _ => by ring) + +end Algebra + +/-! ## Final-point reconstructions + +The verifier's reconstruction of `Q` at the sumcheck point `r` from the claimed evaluation values +`mVal = m(r)`, `tVal = t(r)`, `colVals i = fᵢ(r)`, `helperVals k = hₖ(r)`. -/ + +section AtPoint + +variable {F : Type} [Field F] {n M K : ℕ} + +/-- Denominator term at the final sumcheck point, from the claimed evaluation values. -/ +def phiAtPoint (xChallenge tVal : F) (colVals : Fin M → F) : InputIdx M → F + | .table => xChallenge + tVal + | .column i => xChallenge + colVals i + +/-- Numerator term at the final sumcheck point, from the claimed multiplicity value. -/ +def numeratorAtPoint (mVal : F) : InputIdx M → F + | .table => mVal + | .column _ => -1 + +/-- Term denominator at the final sumcheck point, indexed by `0, ..., M`. -/ +def termPhiAtPoint (xChallenge tVal : F) (colVals : Fin M → F) (i : TermIdx M) : F := + phiAtPoint xChallenge tVal colVals (termToInput i) + +/-- Term numerator at the final sumcheck point, indexed by `0, ..., M`. -/ +def termNumeratorAtPoint (mVal : F) (i : TermIdx M) : F := + numeratorAtPoint mVal (termToInput i) + +/-- The domain-identity expression at the final sumcheck point `r`. -/ +noncomputable def domainIdentityAtPoint (groups : Fin K → Finset (TermIdx M)) + (xChallenge mVal tVal : F) (colVals : Fin M → F) (helperVals : Fin K → F) (k : Fin K) : F := + helperVals k * (∏ i ∈ groups k, termPhiAtPoint xChallenge tVal colVals i) - + ∑ i ∈ groups k, + termNumeratorAtPoint mVal i * + ∏ j ∈ (groups k).erase i, termPhiAtPoint xChallenge tVal colVals j + +/-- The verifier's final check value `Q(eq(r,z), m(r), φᵢ(r), hₖ(r))` from paper (19). -/ +noncomputable def qAtPoint (groups : Fin K → Finset (TermIdx M)) + (xChallenge : F) (zChallenge rChallenge : Fin n → F) (batchingScalars : Fin K → F) + (mVal tVal : F) (colVals : Fin M → F) (helperVals : Fin K → F) : F := + ∑ k : Fin K, ( + helperVals k + + MvPolynomial.eval rChallenge (MvPolynomial.eqPolynomial zChallenge) * batchingScalars k * + domainIdentityAtPoint groups xChallenge mVal tVal colVals helperVals k) + +end AtPoint + +/-! ## The LogUp polynomial + +The input polynomials (table, columns, multiplicity, helpers) combined into LogUp's instantiation of +the generic batched polynomial. -/ + +section Polynomial + +variable {F : Type} [Field F] {n M K : ℕ} + +/-- Denominator polynomial `φ` for one term: `x + (its oracle polynomial)`. -/ +noncomputable def termPhiPolynomial (table : MvPolynomial (Fin n) F) + (columns : Fin M → MvPolynomial (Fin n) F) (xChallenge : F) (i : TermIdx M) : + MvPolynomial (Fin n) F := + MvPolynomial.C xChallenge + + match termToInput i with + | .table => table + | .column j => columns j + +/-- Numerator polynomial for one term: the multiplicity polynomial for the table term, `-1` else. -/ +noncomputable def termNumeratorPolynomial (multiplicity : MvPolynomial (Fin n) F) (i : TermIdx M) : + MvPolynomial (Fin n) F := + match termToInput i with + | .table => multiplicity + | .column _ => MvPolynomial.C (-1) + +theorem termPhiPolynomial_degreeOf {table : MvPolynomial (Fin n) F} + {columns : Fin M → MvPolynomial (Fin n) F} + (htable : ∀ v, MvPolynomial.degreeOf v table ≤ 1) + (hcolumns : ∀ j v, MvPolynomial.degreeOf v (columns j) ≤ 1) + (xChallenge : F) (j : TermIdx M) (i : Fin n) : + MvPolynomial.degreeOf i (termPhiPolynomial table columns xChallenge j) ≤ 1 := by + unfold termPhiPolynomial + calc + _ ≤ max (MvPolynomial.degreeOf i (MvPolynomial.C xChallenge)) + (MvPolynomial.degreeOf i + (match termToInput j with + | .table => table + | .column c => columns c)) := + MvPolynomial.degreeOf_add_le i _ _ + _ ≤ max 0 1 := by + gcongr + · exact (MvPolynomial.degreeOf_C (R := F) xChallenge i).le + · cases termToInput j with + | table => exact htable i + | column c => exact hcolumns c i + _ = 1 := by omega + +theorem termNumeratorPolynomial_degreeOf {multiplicity : MvPolynomial (Fin n) F} + (hmult : ∀ v, MvPolynomial.degreeOf v multiplicity ≤ 1) (j : TermIdx M) (i : Fin n) : + MvPolynomial.degreeOf i (termNumeratorPolynomial multiplicity j) ≤ 1 := by + unfold termNumeratorPolynomial + cases termToInput j with + | table => exact hmult i + | column c => exact (MvPolynomial.degreeOf_C (R := F) (-1 : F) i).le.trans (by omega) + +/-- The concrete multivariate LogUp sumcheck polynomial `Q`: the generic batched polynomial +instantiated with the oracle polynomials. -/ +noncomputable def logupQPolynomial (groups : Fin K → Finset (TermIdx M)) + (table : MvPolynomial (Fin n) F) (columns : Fin M → MvPolynomial (Fin n) F) + (multiplicity : MvPolynomial (Fin n) F) (helpers : Fin K → MvPolynomial (Fin n) F) + (xChallenge : F) (zChallenge : Fin n → F) (batchingScalars : Fin K → F) : + MvPolynomial (Fin n) F := + batchedSumcheckPolynomial groups (termPhiPolynomial table columns xChallenge) + (termNumeratorPolynomial multiplicity) helpers zChallenge batchingScalars + +/-- `Q` has individual degree at most `M + 3`, given that the oracle polynomials are multilinear. -/ +theorem logupQPolynomial_degreeOf (groups : Fin K → Finset (TermIdx M)) + {table : MvPolynomial (Fin n) F} {columns : Fin M → MvPolynomial (Fin n) F} + {multiplicity : MvPolynomial (Fin n) F} {helpers : Fin K → MvPolynomial (Fin n) F} + (htable : ∀ v, MvPolynomial.degreeOf v table ≤ 1) + (hcolumns : ∀ j v, MvPolynomial.degreeOf v (columns j) ≤ 1) + (hmult : ∀ v, MvPolynomial.degreeOf v multiplicity ≤ 1) + (hhelper : ∀ k v, MvPolynomial.degreeOf v (helpers k) ≤ 1) + (xChallenge : F) (zChallenge : Fin n → F) (batchingScalars : Fin K → F) (i : Fin n) : + MvPolynomial.degreeOf i + (logupQPolynomial groups table columns multiplicity helpers + xChallenge zChallenge batchingScalars) ≤ M + 3 := by + refine le_trans (batchedSumcheckPolynomial_degreeOf groups + (termPhiPolynomial table columns xChallenge) (termNumeratorPolynomial multiplicity) + helpers zChallenge batchingScalars + (fun j v => termPhiPolynomial_degreeOf htable hcolumns xChallenge j v) + (fun j v => termNumeratorPolynomial_degreeOf hmult j v) + hhelper i) (by omega) + +end Polynomial + +/-! ## Polynomial evaluation agreement -/ + +section PolynomialEval + +variable {F : Type} [Field F] {n M K : ℕ} + +/-- On Boolean rows, the concrete polynomial `Q` agrees with the row-wise LogUp expression built +from the Boolean evaluations of its input polynomials. -/ +theorem logupQPolynomial_eval_hypercube (groups : Fin K → Finset (TermIdx M)) + (table : MvPolynomial (Fin n) F) (columns : Fin M → MvPolynomial (Fin n) F) + (multiplicity : MvPolynomial (Fin n) F) (helpers : Fin K → MvPolynomial (Fin n) F) + (xChallenge : F) (zChallenge : Fin n → F) (batchingScalars : Fin K → F) + (u : Fin n → Fin 2) : + MvPolynomial.eval (u : Fin n → F) + (logupQPolynomial groups table columns multiplicity helpers + xChallenge zChallenge batchingScalars) + = + qOnHypercube groups (MvPolynomial.toEvalsZeroOne table) + (fun i => MvPolynomial.toEvalsZeroOne (columns i)) + (MvPolynomial.toEvalsZeroOne multiplicity) + (fun k => MvPolynomial.toEvalsZeroOne (helpers k)) + xChallenge zChallenge batchingScalars u := by + have hphi : ∀ i : TermIdx M, + MvPolynomial.eval (u : Fin n → F) (termPhiPolynomial table columns xChallenge i) = + termPhi (MvPolynomial.toEvalsZeroOne table) + (fun j => MvPolynomial.toEvalsZeroOne (columns j)) xChallenge i u := by + intro i + unfold termPhiPolynomial termPhi phi + cases termToInput i <;> simp [MvPolynomial.toEvalsZeroOne] + have hnum : ∀ i : TermIdx M, + MvPolynomial.eval (u : Fin n → F) (termNumeratorPolynomial multiplicity i) = + termNumerator (MvPolynomial.toEvalsZeroOne multiplicity) i u := by + intro i + unfold termNumeratorPolynomial termNumerator numerator + cases termToInput i <;> simp [MvPolynomial.toEvalsZeroOne] + rw [logupQPolynomial, batchedSumcheckPolynomial, qOnHypercube, map_sum] + refine Finset.sum_congr rfl (fun k _ => ?_) + rw [map_add, map_mul, map_mul, MvPolynomial.eval_C] + congr 2 + rw [batchedDomainIdentity, domainIdentityTerm, map_sub, map_mul, map_prod, map_sum] + congr 1 + · congr 1 + exact Finset.prod_congr rfl (fun i _ => hphi i) + · refine Finset.sum_congr rfl (fun i _ => ?_) + rw [map_mul, map_prod, hnum i] + congr 1 + exact Finset.prod_congr rfl (fun j _ => hphi j) + +/-- At an arbitrary point, the concrete polynomial `Q` agrees with the value reconstructed from +the scalar evaluations of its input polynomials. -/ +theorem logupQPolynomial_eval_point (groups : Fin K → Finset (TermIdx M)) + (table : MvPolynomial (Fin n) F) (columns : Fin M → MvPolynomial (Fin n) F) + (multiplicity : MvPolynomial (Fin n) F) (helpers : Fin K → MvPolynomial (Fin n) F) + (xChallenge : F) (zChallenge rChallenge : Fin n → F) (batchingScalars : Fin K → F) : + MvPolynomial.eval rChallenge + (logupQPolynomial groups table columns multiplicity helpers + xChallenge zChallenge batchingScalars) + = + qAtPoint groups xChallenge zChallenge rChallenge batchingScalars + (MvPolynomial.eval rChallenge multiplicity) + (MvPolynomial.eval rChallenge table) + (fun i => MvPolynomial.eval rChallenge (columns i)) + (fun k => MvPolynomial.eval rChallenge (helpers k)) := by + have hphi : ∀ i : TermIdx M, + MvPolynomial.eval rChallenge (termPhiPolynomial table columns xChallenge i) = + termPhiAtPoint xChallenge (MvPolynomial.eval rChallenge table) + (fun j => MvPolynomial.eval rChallenge (columns j)) i := by + intro i + unfold termPhiPolynomial termPhiAtPoint phiAtPoint + cases termToInput i <;> simp + have hnum : ∀ i : TermIdx M, + MvPolynomial.eval rChallenge (termNumeratorPolynomial multiplicity i) = + termNumeratorAtPoint (MvPolynomial.eval rChallenge multiplicity) i := by + intro i + unfold termNumeratorPolynomial termNumeratorAtPoint numeratorAtPoint + cases termToInput i <;> simp + rw [logupQPolynomial, batchedSumcheckPolynomial, qAtPoint, map_sum] + refine Finset.sum_congr rfl (fun k _ => ?_) + rw [map_add, map_mul, map_mul, MvPolynomial.eval_C] + congr 2 + rw [batchedDomainIdentity, domainIdentityAtPoint, map_sub, map_mul, map_prod, map_sum] + congr 1 + · congr 1 + exact Finset.prod_congr rfl (fun i _ => hphi i) + · refine Finset.sum_congr rfl (fun i _ => ?_) + rw [map_mul, map_prod, hnum i] + congr 1 + exact Finset.prod_congr rfl (fun j _ => hphi j) + +end PolynomialEval + +end Logup diff --git a/ArkLib/ProofSystem/Logup/Common.lean b/ArkLib/ProofSystem/Logup/Common.lean deleted file mode 100644 index 351a0424f5..0000000000 --- a/ArkLib/ProofSystem/Logup/Common.lean +++ /dev/null @@ -1,343 +0,0 @@ -import Mathlib.Algebra.Order.Floor.Div -import ArkLib.Data.MvPolynomial.Multilinear -import ArkLib.OracleReduction.OracleInterface - -/-! -# LogUp Common Definitions - -Shared vocabulary for the LogUp lookup argument formalization, following Protocol 2 of Haböck's -LogUp paper (Cryptology ePrint Archive, Paper 2022/1530, -). - -This file collects the common definitions and algebraic tools reused by the other LogUp files. --/ - -namespace Logup - -universe u - -open scoped BigOperators - -/-- A Lagrange oracle is a function on `H`; queries evaluate its Boolean MLE. -/ -structure LagrangeOracle (F : Type u) (n : ℕ) where - values : (Fin n → Fin 2) → F - -instance {F : Type u} {n : ℕ} : CoeFun (LagrangeOracle F n) (fun _ => (Fin n → Fin 2) → F) where - coe oracle := oracle.values - -/-- A multilinear oracle over Boolean row variables, represented in Lagrange form. -/ -@[reducible] -def MultilinearOracle (F : Type u) (n : ℕ) : Type u := - LagrangeOracle F n - -/-- Lagrange-oracle queries evaluate the Boolean multilinear extension at `z`. -/ -noncomputable instance instLagrangeOracleInterface {F : Type u} [Field F] {n : ℕ} : - OracleInterface (LagrangeOracle F n) where - Query := Fin n → F - toOC.spec := (Fin n → F) →ₒ F - toOC.impl z := do - let oracle ← read - return MvPolynomial.eval z (MvPolynomial.MLE oracle.values) - -/-- The semantic value returned by querying a Lagrange-form multilinear oracle at `z`. -/ -noncomputable def lagrangeOracleEval {F : Type u} [Field F] {n : ℕ} - (oracle : MultilinearOracle F n) (z : Fin n → F) : F := - MvPolynomial.eval z (MvPolynomial.MLE oracle.values) - -/-- Input oracle labels for Protocol 2: one table oracle and `M` lookup-column oracles. -/ -inductive InputOracleIdx (M : ℕ) where - | table : InputOracleIdx M - | column : Fin M → InputOracleIdx M -deriving DecidableEq - -/-- Term labels `0, ..., M` from the paper, with `0` denoting the table term. -/ -@[reducible] -def TermIdx (M : ℕ) : Type := - Fin (M + 1) - -/-- Interpret term index `0` as the table and term indices `1, ..., M` as lookup columns. -/ -def termToInput {M : ℕ} (i : TermIdx M) : InputOracleIdx M := - if h : i.val = 0 then - .table - else - .column ⟨i.val - 1, by omega⟩ - -/-- Interpret input-oracle labels as term indices `0, ..., M`. -/ -def inputToTerm {M : ℕ} : InputOracleIdx M → TermIdx M - | .table => ⟨0, by omega⟩ - | .column i => ⟨i.val + 1, by omega⟩ - -/-- `termToInput` is a left inverse of `inputToTerm`: numbering an input label and reading it -back recovers the original label. -/ -@[simp] -theorem termToInput_inputToTerm {M : ℕ} (i : InputOracleIdx M) : - termToInput (inputToTerm i) = i := by - cases i <;> simp [termToInput, inputToTerm] - -/-- `inputToTerm` is a left inverse of `termToInput`: reading a term index as a label and -re-numbering it recovers the original index. -/ -@[simp] -theorem inputToTerm_termToInput {M : ℕ} (i : TermIdx M) : - inputToTerm (termToInput i) = i := by - unfold termToInput - split - · next h => exact Fin.ext h.symm - · next h => apply Fin.ext; simp only [inputToTerm]; omega - -/-- Protocol parameter `ℓ`, the chosen partial-sum size from Protocol 2. -/ -structure ProtocolParams (M : ℕ) where - /-- The partial-sum size `ℓ`. -/ - sumSize : ℕ - /-- Protocol 2 requires `1 ≤ ℓ`. -/ - sumSize_pos : 0 < sumSize - /-- Protocol 2 requires `ℓ ≤ M + 1`. -/ - sumSize_le : sumSize ≤ M + 1 - -namespace ProtocolParams - -/-- The number of partial-sum groups `K = ceil((M + 1) / ℓ)`. -/ -def numGroups {M : ℕ} (params : ProtocolParams M) : ℕ := - (M + params.sumSize) / params.sumSize - -/-- The consecutive interval `Iₖ = [(k - 1)ℓ, kℓ) ∩ [0, M]`, zero-indexed. -/ -def group {M : ℕ} (params : ProtocolParams M) (k : Fin params.numGroups) : - Finset (TermIdx M) := - Finset.univ.filter fun i : TermIdx M => - k.val * params.sumSize ≤ i.val ∧ i.val < (k.val + 1) * params.sumSize - -end ProtocolParams - -/-- Public parameter assumptions for Protocol 2. -/ -structure StmtIn (F : Type u) [Field F] [Fintype F] (n M : ℕ) : Type u where - /-- The paper's characteristic condition `char(F) > M * 2^n`. -/ - charLarge : M * 2 ^ n < ringChar F - -/-- Input oracle statements: the table `t` and lookup columns `fᵢ`, as multilinear oracles. -/ -@[reducible, simp] -def OStmtIn (F : Type u) (n M : ℕ) : InputOracleIdx M → Type u - | .table => MultilinearOracle F n - | .column _ => MultilinearOracle F n - -/-- Input LogUp oracles are accessed by evaluating their multilinear polynomial extensions. -/ -noncomputable instance instOStmtInOracleInterface {F : Type u} [Field F] {n M : ℕ} : - ∀ i, OracleInterface (OStmtIn F n M i) - | .table => inferInstance - | .column _ => inferInstance - -/-- Accessor for the input table oracle `t`. -/ -@[reducible, simp] -def tableOracle {F : Type u} {n M : ℕ} - (oStmt : ∀ i, OStmtIn F n M i) : MultilinearOracle F n := - oStmt .table - -/-- Accessor for the `i`-th lookup-column oracle `fᵢ`. -/ -@[reducible, simp] -def columnOracle {F : Type u} {n M : ℕ} - (oStmt : ∀ i, OStmtIn F n M i) (i : Fin M) : MultilinearOracle F n := - oStmt (.column i) - -/-- Semantic input relation for Protocol 2: every lookup-column value occurs in the table range. -/ -def inputRelation (F : Type u) [Field F] [Fintype F] (n M : ℕ) : - Set ((StmtIn F n M × (∀ i, OStmtIn F n M i)) × Unit) := - { ⟨⟨_, oStmt⟩, _⟩ | - ∀ i : Fin M, ∀ x : (Fin n → Fin 2), ∃ y : (Fin n → Fin 2), - (columnOracle oStmt i) x = (tableOracle oStmt) y } - -/-- The full LogUp protocol returns no additional public data on success. -/ -@[reducible, simp] -def StmtOut : Type := - Unit - -/-- The full LogUp protocol leaves no output oracle statements. -/ -@[reducible, simp] -def OutputOracleIdx : Type := - Fin 0 - -/-- Output oracle statements for the full LogUp protocol. -/ -@[reducible, simp] -def OStmtOut : OutputOracleIdx → Type := - fun i => Fin.elim0 i - -/-- The full LogUp protocol has no output oracle interfaces to provide. -/ -instance instOStmtOutOracleInterface : - ∀ i, OracleInterface (OStmtOut i) := - fun i => Fin.elim0 i - -/-- The full protocol has a trivial final relation: successful verification returns `Unit`. -/ -def outputRelation : Set ((StmtOut × (∀ i, OStmtOut i)) × Unit) := - Set.univ - -section ProtocolSpec - -/-- The prover's first Protocol 2 message: the multiplicity function `m : H → F`. -/ -@[reducible, simp] -def MultiplicityMessage (F : Type) (n : ℕ) : Type := - MultilinearOracle F n - -/-- The prover's second Protocol 2 message: helper functions `h₁, ..., h_K : H → F`. -/ -@[reducible, simp] -def HelperMessages (F : Type) (n K : ℕ) : Type := - Fin K → MultilinearOracle F n - -/-- The verifier's second outer challenge: `z ∈ Fⁿ` and batching scalars `λ₁, ..., λ_K`. -/ -@[reducible, simp] -def BatchingChallenge (F : Type) (n K : ℕ) : Type := - (Fin n → F) × (Fin K → F) - -end ProtocolSpec - -section OuterIO - -variable (F : Type) [Field F] [Fintype F] (n M : ℕ) (params : ProtocolParams M) - -/-- The outer LogUp statement after the verifier has sampled `x`, `z`, and `λ`. -/ -structure StmtAfterOuter where - /-- The logarithmic-derivative challenge `x`. -/ - xChallenge : F - /-- The equality-kernel point `z ∈ Fⁿ`. -/ - zChallenge : Fin n → F - /-- The batching scalars `λ₁, ..., λ_K`. -/ - batchingScalars : Fin params.numGroups → F - -/-- Oracle labels retained after the outer LogUp phase. -/ -inductive OuterOracleIdx (M : ℕ) where - | input : InputOracleIdx M → OuterOracleIdx M - | multiplicity : OuterOracleIdx M - | helpers : OuterOracleIdx M -deriving DecidableEq - -/-- Oracle statements retained after the outer LogUp phase. -/ -@[reducible, simp] -def OStmtAfterOuter (F : Type) [Field F] (n M : ℕ) (params : ProtocolParams M) : - OuterOracleIdx M → Type - | .input i => OStmtIn F n M i - | .multiplicity => MultiplicityMessage F n - | .helpers => HelperMessages F n params.numGroups - -/-- Oracle interfaces for all oracle statements retained after the outer LogUp phase. -/ -noncomputable instance instOStmtAfterOuterOracleInterface - {F : Type} [Field F] {n M : ℕ} {params : ProtocolParams M} : - ∀ i, OracleInterface (OStmtAfterOuter F n M params i) - | .input i => inferInstanceAs (OracleInterface (OStmtIn F n M i)) - | .multiplicity => inferInstanceAs (OracleInterface (MultiplicityMessage F n)) - | .helpers => inferInstanceAs (OracleInterface (HelperMessages F n params.numGroups)) - -/-- Protocol 2 has no private witness beyond the input oracles at this layer. -/ -@[reducible, simp] -def WitIn (F : Type u) [Field F] [Fintype F] (_n M : ℕ) (_params : ProtocolParams M) : Type := - Unit - -end OuterIO - -section ProtocolAlgebra - -variable {F : Type} [Field F] [Fintype F] [DecidableEq F] {n M K : ℕ} -variable (params : ProtocolParams M) - -/-- A decomposition of the term indices `{0, ..., M}` into the partial-sum groups `Iₖ`. -/ -@[reducible] -def PartialSumGroups (M K : ℕ) : Type := - Fin K → Finset (TermIdx M) - -/-- The paper's canonical consecutive groups `Iₖ`, derived from the parameter `ℓ`. -/ -def canonicalGroups : PartialSumGroups M params.numGroups := - fun k => params.group k - -/-- Number of table rows with value `a`. -/ -def tableMultiplicityCount (oStmt : ∀ i, OStmtIn F n M i) (a : F) : ℕ := - ((Finset.univ : Finset (Fin n → Fin 2)).filter fun u => - (tableOracle oStmt) u = a).card - -/-- Total number of lookup-column entries with value `a`. -/ -def lookupMultiplicityCount (oStmt : ∀ i, OStmtIn F n M i) (a : F) : ℕ := - ((Finset.univ : Finset (Fin M × (Fin n → Fin 2))).filter fun ix => - (columnOracle oStmt ix.1) ix.2 = a).card - -/-- The normalized multiplicity from paper equation (14), evaluated at one table row. -/ -noncomputable def normalizedMultiplicityValue (oStmt : ∀ i, OStmtIn F n M i) - (u : (Fin n → Fin 2)) : F := - let a := (tableOracle oStmt) u - (lookupMultiplicityCount oStmt a : F) / (tableMultiplicityCount oStmt a : F) - -/-- The honest multiplicity oracle `m : H → F` from paper equation (14). -/ -noncomputable def honestMultiplicity (oStmt : ∀ i, OStmtIn F n M i) : - MultiplicityMessage F n := - ⟨fun u => normalizedMultiplicityValue oStmt u⟩ - -/-- The denominator term `φᵢ(u)` from Protocol 2. -/ -noncomputable def phi (oStmt : ∀ i, OStmtIn F n M i) (xChallenge : F) : - InputOracleIdx M → (Fin n → Fin 2) → F - | .table => fun u => xChallenge + (tableOracle oStmt) u - | .column i => fun u => xChallenge + (columnOracle oStmt i) u - -/-- The numerator term `mᵢ(u)` from Protocol 2, with `m₀ = m` and `mᵢ = -1` for columns. -/ -noncomputable def numerator (multiplicity : MultilinearOracle F n) : - InputOracleIdx M → (Fin n → Fin 2) → F - | .table => multiplicity - | .column _ => fun _ => -1 - -/-- The denominator term, indexed as `0, ..., M` as in the paper. -/ -noncomputable def termPhi (oStmt : ∀ i, OStmtIn F n M i) (xChallenge : F) - (i : TermIdx M) (u : (Fin n → Fin 2)) : F := - phi oStmt xChallenge (termToInput i) u - -/-- The numerator term, indexed as `0, ..., M` as in the paper. -/ -noncomputable def termNumerator (multiplicity : MultilinearOracle F n) - (i : TermIdx M) (u : (Fin n → Fin 2)) : F := - numerator multiplicity (termToInput i) u - -/-- Product of the denominator terms in one partial-sum group. -/ -noncomputable def denominatorProduct (groups : PartialSumGroups M K) - (oStmt : ∀ i, OStmtIn F n M i) (xChallenge : F) (k : Fin K) (u : (Fin n → Fin 2)) : F := - ∏ i ∈ groups k, termPhi oStmt xChallenge i u - -/-- The domain-identity expression for one helper function `hₖ`. -/ -noncomputable def domainIdentityTerm (groups : PartialSumGroups M K) - (oStmt : ∀ i, OStmtIn F n M i) (multiplicity : MultilinearOracle F n) - (helpers : HelperMessages F n K) (xChallenge : F) (k : Fin K) (u : (Fin n → Fin 2)) : F := - (helpers k) u * denominatorProduct groups oStmt xChallenge k u - - ∑ i ∈ groups k, - termNumerator multiplicity i u * ∏ j ∈ (groups k).erase i, termPhi oStmt xChallenge j u - -/-- The helper value `hₖ(u) = Σᵢ mᵢ(u)/φᵢ(u)` from paper equation (16). -/ -noncomputable def helperValue (groups : PartialSumGroups M K) - (oStmt : ∀ i, OStmtIn F n M i) (multiplicity : MultilinearOracle F n) - (xChallenge : F) (k : Fin K) (u : (Fin n → Fin 2)) : F := - ∑ i ∈ groups k, termNumerator multiplicity i u / termPhi oStmt xChallenge i u - -/-- The honest helper oracle `hₖ : H → F` for one partial-sum group. -/ -noncomputable def helperOracle (groups : PartialSumGroups M K) - (oStmt : ∀ i, OStmtIn F n M i) (multiplicity : MultilinearOracle F n) - (xChallenge : F) (k : Fin K) : MultilinearOracle F n := - ⟨fun u => helperValue groups oStmt multiplicity xChallenge k u⟩ - -/-- The honest helper-oracle bundle `h₁, ..., h_K` from paper equation (16). -/ -noncomputable def honestHelpers (oStmt : ∀ i, OStmtIn F n M i) (xChallenge : F) : - HelperMessages F n params.numGroups := - let multiplicity := honestMultiplicity oStmt - fun k => helperOracle (canonicalGroups params) oStmt multiplicity xChallenge k - -/-- Point evaluations queried by the verifier at the final sumcheck point `r`. -/ -structure PointEvaluations (F : Type) (M K : ℕ) where - /-- Claimed value `m(r)`. -/ - multiplicity : F - /-- Claimed value `t(r)`. -/ - table : F - /-- Claimed values `fᵢ(r)`. -/ - columns : Fin M → F - /-- Claimed values `hₖ(r)`. -/ - helpers : Fin K → F - -/-- Semantic agreement between final oracle-query answers and the retained LogUp oracles. -/ -def logupPointEvaluationsAgree - (r : Fin n → F) - (oStmt : ∀ i, OStmtAfterOuter F n M params i) - (evals : PointEvaluations F M params.numGroups) : Prop := - evals.multiplicity = lagrangeOracleEval (oStmt .multiplicity) r ∧ - evals.table = lagrangeOracleEval (oStmt (.input .table)) r ∧ - (∀ i : Fin M, evals.columns i = lagrangeOracleEval (oStmt (.input (.column i))) r) ∧ - ∀ k : Fin params.numGroups, evals.helpers k = lagrangeOracleEval ((oStmt .helpers) k) r - -end ProtocolAlgebra - -end Logup diff --git a/ArkLib/ProofSystem/Logup/Protocol.lean b/ArkLib/ProofSystem/Logup/Protocol.lean index 2849ea9d6f..02206d0e7d 100644 --- a/ArkLib/ProofSystem/Logup/Protocol.lean +++ b/ArkLib/ProofSystem/Logup/Protocol.lean @@ -1,5 +1,8 @@ -import ArkLib.ProofSystem.Logup.Common -import ArkLib.ProofSystem.Logup.Sumcheck.SumcheckBridge +import Mathlib.Algebra.Order.Floor.Div +import ArkLib.Data.MvPolynomial.Multilinear +import ArkLib.OracleReduction.OracleInterface +import ArkLib.ProofSystem.Logup.Algebra +import ArkLib.ProofSystem.Sumcheck.Spec.General /-! # LogUp Protocol @@ -13,27 +16,64 @@ oracle. ## Protocol Specification -Protocol 2 is parameterized by: +The LogUp protocol is parameterized by: - a field `F`; -- a row dimension `n`, with rows indexed by the Boolean hypercube; +- a row dimension `n`, with rows indexed by the Boolean hypercube `H = {±1}ⁿ`; - `M` lookup-column oracles `fᵢ : H → F` and one table oracle `t : H → F`; -- a partial-sum size `ell`, stored in `ProtocolParams`, which determines the number `K` of helper - oracles; and +- a partial-sum size `ℓ` (`1 ≤ ℓ ≤ M + 1`), stored in `ProtocolParams`, which determines the number + `K = ⌈(M + 1) / ℓ⌉` of helper oracles; and - the characteristic condition `char(F) > M * 2^n`. -The input relation says that every value appearing in any lookup column also appears somewhere in -the table. The protocol proves this relation as follows. +### The relation and its logarithmic-derivative reformulation -1. The prover sends the normalized multiplicity oracle `m`, which records how often table values are - used by the lookup columns. The verifier samples the logarithmic-derivative challenge `x`. -2. Using `x`, the prover sends helper oracles `h₁, ..., h_K` for the partial-sum groups of the - logarithmic-derivative identity. -3. The verifier samples a point `z : Fin n → F` and batching scalars `λ₁, ..., λ_K`. These data turn - the helper identities into one batched polynomial `Q`, and the prover and verifier run generic - sumcheck on the claim `∑ u ∈ H, Q(u) = 0`. -4. Sumcheck leaves a final point `r` and a claimed value `v = Q(r)`. The verifier queries the - retained LogUp oracles at `r`, reconstructs `Q(r)`, and accepts only if this reconstructed value - equals `v`. +The input relation is the set inclusion `⋃ᵢ {fᵢ(u) : u ∈ H} ⊆ {t(u) : u ∈ H}`: every value +appearing in any lookup column also appears somewhere in the table. Under the characteristic +condition, Lemma 5 of the paper makes this equivalent to the existence of a multiplicity function +`m : H → F` satisfying the rational identity (paper eq. (13)) + + `∑ u ∈ H, ∑ i ∈ [1, M], 1 / (X + fᵢ(u)) = ∑ u ∈ H, m(u) / (X + t(u))` in `F(X)`. + +The canonical witness is the normalized multiplicity (paper eq. (14)), +`m(u) = (∑ᵢ #{y ∈ H : fᵢ(y) = t(u)}) / #{y ∈ H : t(y) = t(u)}`, which counts how often the table +value `t(u)` is used across the lookup columns (and equals the plain count when `t` is injective). + +### The protocol + +1. The prover sends the multiplicity oracle `m`. The verifier samples the logarithmic-derivative + challenge `x ←$ F`. The paper draws `x ∉ {-t(u) : u ∈ H}` by rejection sampling so that every + denominator is nonzero; following Remark 3, this formalization instead samples `x` uniformly from + all of `F` and absorbs the rare bad event (a vanishing denominator) into the completeness error, + avoiding the rejection-sampling constraint. Specializing the identity above at `X = x` and + clearing it to one side gives the scalar claim (paper eq. (15)) + + `∑ u ∈ H, (∑ i ∈ [1, M], 1 / (x + fᵢ(u))) - m(u) / (x + t(u)) = 0`. + + It is convenient to fold the table and columns into `M + 1` indexed terms (paper notation): + `m₀ = m`, `ϕ₀(u) = x + t(u)`, and `mᵢ = -1`, `ϕᵢ(u) = x + fᵢ(u)` for `i = 1, ..., M`, so the + summand is `∑ i ∈ [0, M], mᵢ(u) / ϕᵢ(u)`. + +2. The denominator index set `[0, M]` is partitioned into the `K` consecutive groups + `Iₖ = [(k-1)ℓ, kℓ) ∩ [0, M]`. For each group the prover forms the partial sum (paper eq. (16)) + `hₖ(u) = ∑ i ∈ Iₖ, mᵢ(u) / ϕᵢ(u)` and sends the helper oracles `h₁, ..., h_K`. By construction + the helpers sum to zero, `∑ u ∈ H, (h₁(u) + ... + h_K(u)) = 0`, and each obeys the + cleared-denominator *domain identity* (paper eq. (17)) + + `hₖ(u) * (∏ i ∈ Iₖ, ϕᵢ(u)) = ∑ i ∈ Iₖ, mᵢ(u) * (∏ j ∈ Iₖ \ {i}, ϕⱼ(u))` for all `u ∈ H`. + +3. The verifier samples a point `z : Fin n → F` and batching scalars `λ₁, ..., λ_K`. Multiplying the + domain identities by the Lagrange kernel `L_H(·, z)` turns each "for all `u ∈ H`" identity into a + zero-sum over `H`, and the `λ`-batching collapses the `K` domain identities together with the + helper zero-sum into the single batched polynomial `Q` (paper eq. (18)) + + `Q = ∑ k ∈ [1, K], (hₖ + L * λₖ * (hₖ * ∏ i ∈ Iₖ, ϕᵢ - ∑ i ∈ Iₖ, mᵢ * ∏ j ∈ Iₖ \ {i}, ϕⱼ))`, + + where `L = L_H(u, z)`. Prover and verifier then run generic sumcheck (Protocol 1) on the claim + `∑ u ∈ H, Q(L_H(u, z), m(u), ϕ₀(u), ..., ϕ_M(u), h₁(u), ..., h_K(u)) = 0`. + +4. Sumcheck leaves a final point `r : Fin n → F` and a claimed value `v` for the polynomial of + paper eq. (19) at `r`. The verifier queries the retained LogUp oracles `m, t, f₁, ..., f_M`, + `h₁, ..., h_K` at `r`, computes the kernel value `L_H(r, z)` itself, reconstructs `Q(r)` from + these answers, and accepts only if this reconstructed value equals `v`. ### Formalization @@ -46,17 +86,18 @@ The formalization is split into three ArkLib reductions. * The outer LogUp phase sends the multiplicity oracle `m`, samples the challenge `x`, sends the helper oracles, and samples the batching challenge `(z, lambda)`. At the end of this phase, the verifier has the data needed to state a single zero-sum claim about the LogUp polynomial `Q`. -* The sumcheck phase turns the values retained from the outer phase into the polynomial `Q`, then - runs ArkLib's generic sumcheck protocol on the claim that `Q` sums to zero. The translation tools - and polynomial construction live in the - [`Logup/Sumcheck`](ArkLib/ProofSystem/Logup/Sumcheck/) directory, with the bridge connecting the - LogUp-specific data to the generic sumcheck input and output. +* The sumcheck phase runs ArkLib's generic sumcheck on the claim that `Q` sums to zero. The + *polynomial and all of LogUp's fractional-identity algebra* are protocol-agnostic and live in + [`Algebra`](ArkLib/ProofSystem/Logup/Algebra.lean), parameterized on bare oracle functions. This + file supplies the oracle data from the retained LogUp oracles and connects the two worlds through + the context lens `logupSumcheckContextLens`. * The final zero-round phase queries the retained LogUp oracles at sumcheck's final point and checks that those oracle values really give the value claimed by sumcheck. -This file defines the transcript shapes; the prover-side objects for the outer, sumcheck, -final-check, and full protocol; the matching verifier-side objects; and then the oracle reductions -for each phase and for the composed protocol. +This file holds the LogUp *interactive oracle reduction* itself: the oracle representation types, +the input/output statements and relations, the sumcheck bridge and context lens, the transcript +shapes, the prover/verifier objects, and the oracle reductions for each phase and the composed +protocol. The main artefacts that define the protocol are `pSpec`, `logupProver`, `logupVerifier`, and `logupOracleReduction`. @@ -65,9 +106,147 @@ and `logupOracleReduction`. namespace Logup -open scoped BigOperators +universe u + +open scoped BigOperators MvPolynomial + +/-! # Phase 1 — Outer LogUp + +The input boundary, outer transcript, honest multiplicity/helper messages, outer prover/verifier, +and first oracle reduction. -/ + +section Phase1 + +/-- Protocol parameter `ℓ`, the chosen partial-sum size from Protocol 2. -/ +structure ProtocolParams (M : ℕ) where + /-- The partial-sum size `ℓ`. -/ + sumSize : ℕ + /-- Protocol 2 requires `1 ≤ ℓ`. -/ + sumSize_pos : 0 < sumSize + /-- Protocol 2 requires `ℓ ≤ M + 1`. -/ + sumSize_le : sumSize ≤ M + 1 + +namespace ProtocolParams + +/-- The number of partial-sum groups `K = ceil((M + 1) / ℓ)`. -/ +def numGroups {M : ℕ} (params : ProtocolParams M) : ℕ := + (M + params.sumSize) / params.sumSize + +/-- `numGroups` really is the ceiling `⌈(M + 1) / ℓ⌉`: the floor-division formula +`(M + ℓ) / ℓ` agrees with `Nat.ceilDiv` because `ℓ ≥ 1` (`sumSize_pos`). -/ +theorem numGroups_eq_ceilDiv {M : ℕ} (params : ProtocolParams M) : + params.numGroups = (M + 1) ⌈/⌉ params.sumSize := by + have h := params.sumSize_pos + rw [Nat.ceilDiv_eq_add_pred_div] + unfold numGroups + congr 1 + omega + +/-- The consecutive interval `Iₖ = [(k - 1)ℓ, kℓ) ∩ [0, M]`, zero-indexed. -/ +def group {M : ℕ} (params : ProtocolParams M) (k : Fin params.numGroups) : + Finset (TermIdx M) := + Finset.univ.filter fun i : TermIdx M => + k.val * params.sumSize ≤ i.val ∧ i.val < (k.val + 1) * params.sumSize + +end ProtocolParams + +/-- Input oracle statements: the table `t` and lookup columns `fᵢ`, as multilinear polynomials +(reusing ArkLib's bounded-degree polynomial oracle, queried by evaluation). -/ +@[reducible, simp] +def OStmtIn (F : Type u) [CommSemiring F] (n M : ℕ) : InputIdx M → Type u + | .table => F⦃≤ 1⦄[X (Fin n)] + | .column _ => F⦃≤ 1⦄[X (Fin n)] + +/-- Input LogUp oracles are accessed by evaluating their multilinear polynomial extensions. -/ +noncomputable instance instOStmtInOracleInterface {F : Type u} [Field F] {n M : ℕ} : + ∀ i, OracleInterface (OStmtIn F n M i) + | .table => inferInstance + | .column _ => inferInstance + +/-- The prover's first Protocol 2 message: the multiplicity function `m : H → F`, as a +multilinear polynomial. -/ +@[reducible, simp] +def MultiplicityMessage (F : Type) [CommSemiring F] (n : ℕ) : Type := + F⦃≤ 1⦄[X (Fin n)] + +/-- The prover's second Protocol 2 message: helper functions `h₁, ..., h_K : H → F`, as +multilinear polynomials. -/ +@[reducible, simp] +def HelperMessages (F : Type) [CommSemiring F] (n K : ℕ) : Type := + Fin K → F⦃≤ 1⦄[X (Fin n)] + +/-- The verifier's second outer challenge: `z ∈ Fⁿ` and batching scalars `λ₁, ..., λ_K`. -/ +@[reducible, simp] +def BatchingChallenge (F : Type) (n K : ℕ) : Type := + (Fin n → F) × (Fin K → F) + +variable (F : Type) [Field F] [Fintype F] (n M : ℕ) (params : ProtocolParams M) + +/-- The outer LogUp statement after the verifier has sampled `x`, `z`, and `λ`. -/ +structure StmtAfterOuter where + /-- The logarithmic-derivative challenge `x`. -/ + xChallenge : F + /-- The equality-kernel point `z ∈ Fⁿ`. -/ + zChallenge : Fin n → F + /-- The batching scalars `λ₁, ..., λ_K`. -/ + batchingScalars : Fin params.numGroups → F + +/-- Oracle labels retained after the outer LogUp phase. -/ +inductive OuterOracleIdx (M : ℕ) where + | input : InputIdx M → OuterOracleIdx M + | multiplicity : OuterOracleIdx M + | helpers : OuterOracleIdx M +deriving DecidableEq + +/-- Oracle statements retained after the outer LogUp phase. -/ +@[reducible, simp] +def OStmtAfterOuter (F : Type) [Field F] (n M : ℕ) (params : ProtocolParams M) : + OuterOracleIdx M → Type + | .input i => OStmtIn F n M i + | .multiplicity => MultiplicityMessage F n + | .helpers => HelperMessages F n params.numGroups + +/-- Oracle interfaces for all oracle statements retained after the outer LogUp phase. -/ +noncomputable instance instOStmtAfterOuterOracleInterface + {F : Type} [Field F] {n M : ℕ} {params : ProtocolParams M} : + ∀ i, OracleInterface (OStmtAfterOuter F n M params i) + | .input i => inferInstanceAs (OracleInterface (OStmtIn F n M i)) + | .multiplicity => inferInstanceAs (OracleInterface (MultiplicityMessage F n)) + | .helpers => inferInstanceAs (OracleInterface (HelperMessages F n params.numGroups)) + + +variable {F : Type} [Field F] [Fintype F] [DecidableEq F] {n M : ℕ} +variable (params : ProtocolParams M) + -section ProtocolSpec +/-- The honest multiplicity oracle `m : H → F` from paper equation (14), as the multilinear +extension of the normalized-multiplicity table. -/ +noncomputable def honestMultiplicity (oStmt : ∀ i, OStmtIn F n M i) : MultiplicityMessage F n := + (MvPolynomial.MLEEquiv (R := F) (σ := Fin n)).symm + (normalizedMultiplicityValue (MvPolynomial.toEvalsZeroOne (oStmt .table).1) + (fun i => MvPolynomial.toEvalsZeroOne (oStmt (.column i)).1)) + +/-- The honest helper-oracle bundle `h₁, ..., h_K` from paper equation (16). -/ +noncomputable def honestHelpers (oStmt : ∀ i, OStmtIn F n M i) (xChallenge : F) : + HelperMessages F n params.numGroups := + fun k => + (MvPolynomial.MLEEquiv (R := F) (σ := Fin n)).symm + (helperValue (params.group) (MvPolynomial.toEvalsZeroOne (oStmt .table).1) + (fun i => MvPolynomial.toEvalsZeroOne (oStmt (.column i)).1) + (MvPolynomial.toEvalsZeroOne (honestMultiplicity oStmt).1) xChallenge k) + +/-- Public parameter assumptions for Protocol 2. -/ +structure StmtIn (F : Type u) [Field F] [Fintype F] (n M : ℕ) : Type u where + /-- The paper's characteristic condition `char(F) > M * 2^n`. -/ + charLarge : M * 2 ^ n < ringChar F + +/-- Semantic input relation for Protocol 2: every lookup-column value occurs in the table range. -/ +def inputRelation (F : Type u) [Field F] [Fintype F] (n M : ℕ) : + Set ((StmtIn F n M × (∀ i, OStmtIn F n M i)) × Unit) := + { ⟨⟨_, oStmt⟩, _⟩ | + ∀ i : Fin M, ∀ x : (Fin n → Fin 2), ∃ y : (Fin n → Fin 2), + MvPolynomial.toEvalsZeroOne (oStmt (.column i)).1 x + = MvPolynomial.toEvalsZeroOne (oStmt .table).1 y } open ProtocolSpec @@ -80,7 +259,7 @@ The transcript shape is: 4. `V → P`: challenge `(z, λ)`. -/ @[reducible] -def outerPSpec (F : Type) (n : ℕ) {M : ℕ} (params : ProtocolParams M) : ProtocolSpec 4 := +def outerPSpec (F : Type) [Field F] (n : ℕ) {M : ℕ} (params : ProtocolParams M) : ProtocolSpec 4 := ⟨!v[.P_to_V], !v[MultiplicityMessage F n]⟩ ++ₚ ⟨!v[.V_to_P], !v[F]⟩ ++ₚ ⟨!v[.P_to_V], !v[HelperMessages F n params.numGroups]⟩ ++ₚ @@ -123,73 +302,6 @@ instance instOuterPSpecChallengeSampleable change SampleableType (BatchingChallenge F n params.numGroups) infer_instance -end ProtocolSpec - -section FinalCheckSpec - -/-- The final LogUp point check has no transcript messages; it only queries retained oracles. -/ -@[reducible] -def finalCheckPSpec : ProtocolSpec 0 := - !p[] - -end FinalCheckSpec - -section FullProtocolSpec - -open ProtocolSpec - -/-- Protocol 2 before the final point check: outer LogUp followed by generic sumcheck. -/ -@[reducible] -noncomputable def pSpecBeforeFinal (F : Type) [Field F] [Fintype F] [DecidableEq F] - (n M : ℕ) (params : ProtocolParams M) := - outerPSpec F n params ++ₚ logupSumcheckPSpec F n M params - -/-- Protocol 2 transcript shape: outer LogUp, ArkLib's generic sumcheck, and the final point check. --/ -@[reducible] -noncomputable def pSpec (F : Type) [Field F] [Fintype F] [DecidableEq F] - (n M : ℕ) (params : ProtocolParams M) := - pSpecBeforeFinal F n M params ++ₚ finalCheckPSpec - -/-- The prover messages before the final check are oracle-accessible: outer LogUp followed by the -embedded sumcheck messages. -/ -noncomputable instance instPSpecBeforeFinalMessageOracleInterface - {F : Type} [Field F] [Fintype F] [DecidableEq F] {n M : ℕ} - {params : ProtocolParams M} : - ∀ i, OracleInterface ((pSpecBeforeFinal F n M params).Message i) := by - unfold pSpecBeforeFinal - exact ProtocolSpec.instOracleInterfaceMessageAppend - -/-- The full LogUp prover messages are oracle-accessible: outer LogUp and sumcheck messages; the -final point check has no messages. -/ -noncomputable instance instPSpecMessageOracleInterface - {F : Type} [Field F] [Fintype F] [DecidableEq F] {n M : ℕ} - {params : ProtocolParams M} : - ∀ i, OracleInterface ((pSpec F n M params).Message i) := by - unfold pSpec - exact ProtocolSpec.instOracleInterfaceMessageAppend - -/-- The verifier challenges before the final check are sampleable: outer LogUp followed by the -embedded sumcheck challenges. -/ -noncomputable instance instPSpecBeforeFinalChallengeSampleable - {F : Type} [Field F] [Fintype F] [DecidableEq F] [SampleableType F] {n M : ℕ} - {params : ProtocolParams M} : - ∀ i, SampleableType ((pSpecBeforeFinal F n M params).Challenge i) := by - unfold pSpecBeforeFinal - exact ProtocolSpec.instSampleableTypeChallengeAppend - -/-- The full LogUp verifier challenges are sampleable: the final point check has no challenges. -/ -noncomputable instance instPSpecChallengeSampleable - {F : Type} [Field F] [Fintype F] [DecidableEq F] [SampleableType F] {n M : ℕ} - {params : ProtocolParams M} : - ∀ i, SampleableType ((pSpec F n M params).Challenge i) := by - unfold pSpec - exact ProtocolSpec.instSampleableTypeChallengeAppend - -end FullProtocolSpec - -section OuterProver - variable {ι : Type} (oSpec : OracleSpec ι) variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) variable (params : ProtocolParams M) @@ -204,7 +316,7 @@ def outerProverState : Fin 5 → Type /-- The honest prover for the outer LogUp phase. -/ noncomputable def outerProver : - OracleProver oSpec (StmtIn F n M) (OStmtIn F n M) (WitIn F n M params) + OracleProver oSpec (StmtIn F n M) (OStmtIn F n M) (Unit) (StmtAfterOuter F n M params) (OStmtAfterOuter F n M params) Unit (outerPSpec F n params) where PrvState := outerProverState F n M params @@ -234,109 +346,6 @@ noncomputable def outerProver : | .helpers => honestHelpers params oStmt x), ()) -end OuterProver - -section ConcreteSumcheckReduction - -variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) -variable (params : ProtocolParams M) - -/-- ArkLib's generic sumcheck prover specialized to LogUp's Boolean domain and degree bound. -/ -noncomputable def logupConcreteSumcheckOracleProver [SampleableType F] : - OracleProver oSpec (LogupSumcheckStmtIn F n M params) - (LogupSumcheckOracleStatement F n M params) Unit - (LogupSumcheckStmtOut F n M params) - (LogupSumcheckOracleStatement F n M params) Unit - (logupSumcheckPSpec F n M params) := - (Sumcheck.Spec.oracleReduction F (logupSumcheckDegree M params) - (booleanDomain F) n oSpec).prover - -/-- ArkLib's generic sumcheck verifier specialized to LogUp's Boolean domain and degree bound. -/ -noncomputable def logupConcreteSumcheckOracleVerifier [SampleableType F] : - OracleVerifier oSpec (LogupSumcheckStmtIn F n M params) - (LogupSumcheckOracleStatement F n M params) - (LogupSumcheckStmtOut F n M params) - (LogupSumcheckOracleStatement F n M params) - (logupSumcheckPSpec F n M params) := - Sumcheck.Spec.oracleVerifier F (logupSumcheckDegree M params) - (booleanDomain F) n oSpec - -/-- ArkLib's generic sumcheck reduction specialized to LogUp's Boolean domain and degree bound. -/ -noncomputable def logupConcreteSumcheckOracleReduction [SampleableType F] : - OracleReduction oSpec (LogupSumcheckStmtIn F n M params) - (LogupSumcheckOracleStatement F n M params) Unit - (LogupSumcheckStmtOut F n M params) - (LogupSumcheckOracleStatement F n M params) Unit - (logupSumcheckPSpec F n M params) := - Sumcheck.Spec.oracleReduction F (logupSumcheckDegree M params) - (booleanDomain F) n oSpec - -end ConcreteSumcheckReduction - -section SumcheckProver - -variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [SampleableType F] (n M : ℕ) -variable (params : ProtocolParams M) - -/-- The prover for the embedded sumcheck phase of LogUp Protocol 2. -/ -noncomputable def sumcheckProver : - OracleProver oSpec (StmtAfterOuter F n M params) (OStmtAfterOuter F n M params) Unit - (StmtAfterSumcheck F n M params) (OStmtAfterSumcheck F n M params) Unit - (logupSumcheckPSpec F n M params) := - let lens : - OracleContext.Lens.{0, 0, 0, 0} - (StmtAfterOuter F n M params) (StmtAfterSumcheck F n M params) - (LogupSumcheckStmtIn F n M params) (LogupSumcheckStmtOut F n M params) - (OStmtAfterOuter F n M params) (OStmtAfterSumcheck F n M params) - (LogupSumcheckOracleStatement F n M params) - (LogupSumcheckOracleStatement F n M params) - Unit Unit Unit Unit := - logupSumcheckContextLens F n M params - (logupConcreteSumcheckOracleProver oSpec F n M params).liftContext lens - -end SumcheckProver - -section FinalCheckProver - -variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) -variable (params : ProtocolParams M) - -/-- The prover for the final LogUp point check; there are no messages in this phase. -/ -noncomputable def finalCheckProver : - OracleProver oSpec (StmtAfterSumcheck F n M params) (OStmtAfterSumcheck F n M params) Unit - StmtOut OStmtOut Unit - finalCheckPSpec where - PrvState := fun _ => StmtAfterSumcheck F n M params × - (∀ i, OStmtAfterSumcheck F n M params i) - input := fun ⟨ctx, _⟩ => ctx - sendMessage := fun i => Fin.elim0 i - receiveChallenge := fun i => Fin.elim0 i - output := fun _ => pure ((((), fun i => Fin.elim0 i), ())) - -end FinalCheckProver - -section FullProver - -variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [SampleableType F] (n M : ℕ) -variable (params : ProtocolParams M) - -/-- The full LogUp prover, composed from the outer prover and embedded sumcheck prover. -/ -noncomputable def logupProver : - OracleProver oSpec (StmtIn F n M) (OStmtIn F n M) (WitIn F n M params) - (StmtOut) (OStmtOut) Unit - (pSpec F n M params) := - Prover.append - (Prover.append (outerProver oSpec F n M params) (sumcheckProver oSpec F n M params)) - (finalCheckProver oSpec F n M params) - -end FullProver - -section OuterVerifier - variable {ι : Type} (oSpec : OracleSpec ι) variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) variable (params : ProtocolParams M) @@ -388,9 +397,176 @@ noncomputable def outerVerifier : | multiplicity => rfl | helpers => rfl -end OuterVerifier +variable {ι : Type} (oSpec : OracleSpec ι) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) +variable (params : ProtocolParams M) + +/-- The outer LogUp phase as an ArkLib oracle reduction. -/ +noncomputable def outerOracleReduction : + OracleReduction oSpec (StmtIn F n M) (OStmtIn F n M) (Unit) + (StmtAfterOuter F n M params) (OStmtAfterOuter F n M params) Unit + (outerPSpec F n params) where + prover := outerProver oSpec F n M params + verifier := outerVerifier oSpec F n M params + +end Phase1 + + +/-! # Phase 2 — Sumcheck + +Runs ArkLib's generic sumcheck on the zero-sum claim for LogUp's polynomial `Q`. Contains the +bridge packaging `Q` for generic sumcheck, the context lens, the lifted prover/verifier, and +the assembled oracle reduction. -/ + +section Phase2 + +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) +variable (params : ProtocolParams M) + +/-- Individual-degree bound for LogUp's embedded sumcheck polynomial. -/ +def logupSumcheckDegree (_params : ProtocolParams M) : ℕ := + M + 3 + +/-- LogUp state after the embedded sumcheck, before the final oracle-query check. -/ +structure StmtAfterSumcheck where + /-- The outer transcript data retained for reconstructing `Q(r)`. -/ + outer : StmtAfterOuter F n M params + /-- Sumcheck's final point claim `Q(r) = v`. -/ + finalClaim : (Sumcheck.Spec.StatementRound F n (.last n)) + + +/-- LogUp enters the embedded sumcheck with the zero-sum claim. -/ +def logupInitialSumcheckStatement : (Sumcheck.Spec.StatementRound F n 0) where + target := 0 + challenges := fun i => Fin.elim0 i + +/-- Package `logupQPolynomial` with its degree certificate into ArkLib's oracle statement type. -/ +noncomputable def logupSumcheckPolynomial + (stmt : StmtAfterOuter F n M params) + (oStmt : ∀ i, OStmtAfterOuter F n M params i) : + (Sumcheck.Spec.OracleStatement F n (logupSumcheckDegree M params)) () := by + classical + refine ⟨logupQPolynomial (params.group) (oStmt (.input .table)).1 + (fun i => (oStmt (.input (.column i))).1) (oStmt .multiplicity).1 + (fun k => (oStmt .helpers k).1) stmt.xChallenge stmt.zChallenge stmt.batchingScalars, ?_⟩ + rw [MvPolynomial.mem_restrictDegree_iff_degreeOf_le] + intro i + exact logupQPolynomial_degreeOf (params.group) + ((MvPolynomial.mem_restrictDegree_iff_degreeOf_le _ _).mp (oStmt (.input .table)).2) + (fun j => (MvPolynomial.mem_restrictDegree_iff_degreeOf_le _ _).mp (oStmt (.input (.column j))).2) + ((MvPolynomial.mem_restrictDegree_iff_degreeOf_le _ _).mp (oStmt .multiplicity).2) + (fun k => (MvPolynomial.mem_restrictDegree_iff_degreeOf_le _ _).mp (oStmt .helpers k).2) + stmt.xChallenge stmt.zChallenge stmt.batchingScalars i + +/-- Package the LogUp `Q` polynomial as the single oracle statement expected by Sumcheck. -/ +noncomputable def logupSumcheckOracleStmt + (stmt : StmtAfterOuter F n M params) + (oStmt : ∀ i, OStmtAfterOuter F n M params i) : + ∀ i, Sumcheck.Spec.OracleStatement F n (logupSumcheckDegree M params) i := + fun _ => logupSumcheckPolynomial F n M params stmt oStmt + +/-- The LogUp zero-sum claim that is fed to the generic sumcheck. -/ +noncomputable def logupOuterSumcheckClaim + (stmt : StmtAfterOuter F n M params) + (oStmt : ∀ i, OStmtAfterOuter F n M params i) : F := + ∑ u : (Fin n → Fin 2), + MvPolynomial.eval (u : Fin n → F) + (logupQPolynomial (params.group) (oStmt (.input .table)).1 + (fun i => (oStmt (.input (.column i))).1) (oStmt .multiplicity).1 + (fun k => (oStmt .helpers k).1) stmt.xChallenge stmt.zChallenge stmt.batchingScalars) + +/-- Relation handed from the outer LogUp phase to the embedded sumcheck: the outer algebra produced +a genuine zero-sum claim. -/ +def logupMidRelation : + Set ((StmtAfterOuter F n M params × (∀ i, OStmtAfterOuter F n M params i)) × Unit) := + { x | logupOuterSumcheckClaim F n M params x.1.1 x.1.2 = 0 } + +variable (F : Type) [Field F] (n M : ℕ) +variable (params : ProtocolParams M) + +/-- The Boolean sumcheck domain, packaged in the form expected by `Sumcheck.Spec`. -/ +def booleanDomain : Fin 2 ↪ F where + toFun := fun b => (b : F) + inj' := by + intro a b h + fin_cases a <;> fin_cases b + · rfl + · simp at h + · simp at h + · rfl + +/-- Relation after embedded sumcheck: the final sumcheck point claim is valid for the retained +LogUp polynomial. The final LogUp phase consumes this by querying the retained oracles at that +point and recomputing `Q(r)`. -/ +def logupAfterSumcheckRelation : + Set ((StmtAfterSumcheck F n M params × (∀ i, OStmtAfterOuter F n M params i)) × Unit) := + { x | + ((x.1.1.finalClaim, + logupSumcheckOracleStmt F n M params x.1.1.outer x.1.2), ()) ∈ + Sumcheck.Spec.relationRound F n (logupSumcheckDegree M params) (booleanDomain F) + (Fin.last n) } + +/-- The initial generic Sumcheck relation induced by a LogUp outer transcript. -/ +def logupSumcheckRelationInput + (stmt : StmtAfterOuter F n M params) + (oStmt : ∀ i, OStmtAfterOuter F n M params i) : Prop := + ((logupInitialSumcheckStatement F n, logupSumcheckOracleStmt F n M params stmt oStmt), + ()) ∈ + Sumcheck.Spec.relationRound F n (logupSumcheckDegree M params) (booleanDomain F) 0 + +variable {ι : Type} (oSpec : OracleSpec ι) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) +variable (params : ProtocolParams M) + +/-- Context lens from LogUp's retained outer state to ArkLib's generic Sumcheck state. -section SumcheckVerifier +The projection builds the generic zero-sum claim and its single polynomial oracle. The lift keeps +the outer LogUp data and retained oracles together with Sumcheck's final point claim, so the next +LogUp phase can perform the paper's final oracle-query check. +-/ +noncomputable def logupSumcheckContextLens : + OracleContext.Lens + (StmtAfterOuter F n M params) (StmtAfterSumcheck F n M params) + ((Sumcheck.Spec.StatementRound F n 0)) ((Sumcheck.Spec.StatementRound F n (.last n))) + (OStmtAfterOuter F n M params) (OStmtAfterOuter F n M params) + (Sumcheck.Spec.OracleStatement F n (logupSumcheckDegree M params)) + (Sumcheck.Spec.OracleStatement F n (logupSumcheckDegree M params)) + Unit Unit Unit Unit where + stmt := + ⟨fun ctx => + (logupInitialSumcheckStatement F n, + logupSumcheckOracleStmt F n M params ctx.1 ctx.2), + fun ctx inner => + ({ outer := ctx.1, finalClaim := inner.1 }, ctx.2)⟩ + wit := + ⟨fun _ => (), + fun _ _ => ()⟩ + +variable {ι : Type} (oSpec : OracleSpec ι) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) +variable (params : ProtocolParams M) + +/-- ArkLib's generic sumcheck reduction specialized to LogUp's Boolean domain and degree bound. -/ +noncomputable def logupConcreteSumcheckOracleReduction [SampleableType F] : + OracleReduction oSpec ((Sumcheck.Spec.StatementRound F n 0)) + (Sumcheck.Spec.OracleStatement F n (logupSumcheckDegree M params)) Unit + ((Sumcheck.Spec.StatementRound F n (.last n))) + (Sumcheck.Spec.OracleStatement F n (logupSumcheckDegree M params)) Unit + ((Sumcheck.Spec.pSpec F (logupSumcheckDegree M params) n)) := + Sumcheck.Spec.oracleReduction F (logupSumcheckDegree M params) + (booleanDomain F) n oSpec + +variable {ι : Type} (oSpec : OracleSpec ι) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [SampleableType F] (n M : ℕ) +variable (params : ProtocolParams M) + +/-- The prover for the embedded sumcheck phase of LogUp Protocol 2. -/ +noncomputable def sumcheckProver : + OracleProver oSpec (StmtAfterOuter F n M params) (OStmtAfterOuter F n M params) Unit + (StmtAfterSumcheck F n M params) (OStmtAfterOuter F n M params) Unit + ((Sumcheck.Spec.pSpec F (logupSumcheckDegree M params) n)) := + (logupConcreteSumcheckOracleReduction oSpec F n M params).prover.liftContext + (logupSumcheckContextLens F n M params) variable {ι : Type} (oSpec : OracleSpec ι) variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) @@ -399,22 +575,78 @@ variable (params : ProtocolParams M) /-- The verifier for the embedded sumcheck phase of LogUp Protocol 2. -/ noncomputable def sumcheckVerifier [SampleableType F] : OracleVerifier oSpec (StmtAfterOuter F n M params) (OStmtAfterOuter F n M params) - (StmtAfterSumcheck F n M params) (OStmtAfterSumcheck F n M params) - (logupSumcheckPSpec F n M params) := - let lens : - OracleContext.Lens.{0, 0, 0, 0} - (StmtAfterOuter F n M params) (StmtAfterSumcheck F n M params) - (LogupSumcheckStmtIn F n M params) (LogupSumcheckStmtOut F n M params) - (OStmtAfterOuter F n M params) (OStmtAfterSumcheck F n M params) - (LogupSumcheckOracleStatement F n M params) - (LogupSumcheckOracleStatement F n M params) - Unit Unit Unit Unit := - logupSumcheckContextLens F n M params - (logupConcreteSumcheckOracleVerifier oSpec F n M params).liftContext lens.stmt - -end SumcheckVerifier - -section FinalCheckVerifier + (StmtAfterSumcheck F n M params) (OStmtAfterOuter F n M params) + ((Sumcheck.Spec.pSpec F (logupSumcheckDegree M params) n)) := + (logupConcreteSumcheckOracleReduction oSpec F n M params).verifier.liftContext + (logupSumcheckContextLens F n M params).stmt + +variable {ι : Type} (oSpec : OracleSpec ι) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [SampleableType F] (n M : ℕ) +variable (params : ProtocolParams M) + +/-- The embedded LogUp sumcheck phase, obtained by lifting ArkLib's generic Sumcheck reduction +through the LogUp-to-Sumcheck context lens. -/ +noncomputable def sumcheckOracleReduction : + OracleReduction oSpec (StmtAfterOuter F n M params) (OStmtAfterOuter F n M params) Unit + (StmtAfterSumcheck F n M params) (OStmtAfterOuter F n M params) Unit + ((Sumcheck.Spec.pSpec F (logupSumcheckDegree M params) n)) where + prover := sumcheckProver oSpec F n M params + verifier := sumcheckVerifier oSpec F n M params + +end Phase2 + + +/-! # Phase 3 — Final point check + +The verifier queries the retained LogUp oracles at sumcheck's final +point and checks that they reproduce the claimed value. -/ + +section Phase3 + +/-- The final LogUp point check has no transcript messages; it only queries retained oracles. -/ +@[reducible] +def finalCheckPSpec : ProtocolSpec 0 := + !p[] + +/-- The full LogUp protocol returns no additional public data on success. -/ +@[reducible, simp] +def StmtOut : Type := + Unit + +/-- The full LogUp protocol leaves no output oracle statements. -/ +@[reducible, simp] +def OutputOracleIdx : Type := + Fin 0 + +/-- Output oracle statements for the full LogUp protocol. -/ +@[reducible, simp] +def OStmtOut : OutputOracleIdx → Type := + fun i => Fin.elim0 i + +/-- The full LogUp protocol has no output oracle interfaces to provide. -/ +instance instOStmtOutOracleInterface : + ∀ i, OracleInterface (OStmtOut i) := + fun i => Fin.elim0 i + +/-- The final relation after the point check: accepted executions return `Unit`. -/ +def outputRelation : Set ((StmtOut × (∀ i, OStmtOut i)) × Unit) := + Set.univ + +variable {ι : Type} (oSpec : OracleSpec ι) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) +variable (params : ProtocolParams M) + +/-- The prover for the final LogUp point check; there are no messages in this phase. -/ +noncomputable def finalCheckProver : + OracleProver oSpec (StmtAfterSumcheck F n M params) (OStmtAfterOuter F n M params) Unit + StmtOut OStmtOut Unit + finalCheckPSpec where + PrvState := fun _ => StmtAfterSumcheck F n M params × + (∀ i, OStmtAfterOuter F n M params i) + input := fun ⟨ctx, _⟩ => ctx + sendMessage := fun i => Fin.elim0 i + receiveChallenge := fun i => Fin.elim0 i + output := fun _ => pure ((((), fun i => Fin.elim0 i), ())) variable {ι : Type} (oSpec : OracleSpec ι) variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) @@ -425,17 +657,17 @@ def finalCheckQuery (i : OuterOracleIdx M) (q : (instOStmtAfterOuterOracleInterface (F := F) (n := n) (params := params) i).Query) : OptionT - (OracleComp (oSpec + ([OStmtAfterSumcheck F n M params]ₒ + [finalCheckPSpec.Message]ₒ))) + (OracleComp (oSpec + ([OStmtAfterOuter F n M params]ₒ + [finalCheckPSpec.Message]ₒ))) ((instOStmtAfterOuterOracleInterface (F := F) (n := n) (params := params) i).Response q) := OptionT.mk <| some <$> OracleComp.lift (OracleSpec.query - (show (oSpec + ([OStmtAfterSumcheck F n M params]ₒ + + (show (oSpec + ([OStmtAfterOuter F n M params]ₒ + [finalCheckPSpec.Message]ₒ)).Domain from Sum.inr (Sum.inl ⟨i, q⟩))) /-- The verifier's final Protocol 2 check: reconstruct `Q(r)` from retained LogUp oracle evaluations and compare it with the expected value output by sumcheck. -/ noncomputable def finalCheckVerifier : - OracleVerifier oSpec (StmtAfterSumcheck F n M params) (OStmtAfterSumcheck F n M params) + OracleVerifier oSpec (StmtAfterSumcheck F n M params) (OStmtAfterOuter F n M params) StmtOut OStmtOut finalCheckPSpec where verify := fun stmt _ => do @@ -447,13 +679,9 @@ noncomputable def finalCheckVerifier : (fun i => finalCheckQuery oSpec F n M params (.input (.column i)) r) let helperValues ← (Vector.finRange params.numGroups).mapM (fun k => finalCheckQuery oSpec F n M params .helpers ⟨k, r⟩) - let evals : PointEvaluations F M params.numGroups := - { multiplicity := multiplicity - table := table - columns := fun i => columnValues[i] - helpers := fun k => helperValues[k] } - guard (qAtPoint (canonicalGroups params) stmt.outer.xChallenge stmt.outer.zChallenge r - stmt.outer.batchingScalars evals = expectedValue) + guard (qAtPoint (params.group) stmt.outer.xChallenge stmt.outer.zChallenge r + stmt.outer.batchingScalars multiplicity table (fun i => columnValues[i]) + (fun k => helperValues[k]) = expectedValue) pure () embed := @@ -462,77 +690,105 @@ noncomputable def finalCheckVerifier : hEq := fun i => Fin.elim0 i -end FinalCheckVerifier - -section FullVerifier - variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [SampleableType F] (n M : ℕ) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) variable (params : ProtocolParams M) -/-- The full LogUp verifier, obtained by composing the outer verifier with the embedded sumcheck -verifier and final point check. -/ -noncomputable def logupVerifier : - OracleVerifier oSpec (StmtIn F n M) (OStmtIn F n M) - (StmtOut) (OStmtOut) - (pSpec F n M params) := - OracleVerifier.append - (OracleVerifier.append (outerVerifier oSpec F n M params) - (sumcheckVerifier oSpec F n M params)) - (finalCheckVerifier oSpec F n M params) +/-- The final LogUp point check as an ArkLib oracle reduction. -/ +noncomputable def finalCheckOracleReduction : + OracleReduction oSpec (StmtAfterSumcheck F n M params) (OStmtAfterOuter F n M params) Unit + (StmtOut) (OStmtOut) Unit + finalCheckPSpec where + prover := finalCheckProver oSpec F n M params + verifier := finalCheckVerifier oSpec F n M params -end FullVerifier +end Phase3 -section OuterReduction -variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) -variable (params : ProtocolParams M) +/-! # Full protocol -/-- The outer LogUp phase as an ArkLib oracle reduction. -/ -noncomputable def outerOracleReduction : - OracleReduction oSpec (StmtIn F n M) (OStmtIn F n M) (WitIn F n M params) - (StmtAfterOuter F n M params) (OStmtAfterOuter F n M params) Unit - (outerPSpec F n params) where - prover := outerProver oSpec F n M params - verifier := outerVerifier oSpec F n M params +The three phase reductions composed into the full LogUp transcript, prover, verifier, and oracle +reduction. -/ -end OuterReduction +section FullProtocol -section SumcheckReduction +open ProtocolSpec -variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [SampleableType F] (n M : ℕ) -variable (params : ProtocolParams M) +/-- Protocol 2 before the final point check: outer LogUp followed by generic sumcheck. -/ +@[reducible] +noncomputable def pSpecBeforeFinal (F : Type) [Field F] [Fintype F] [DecidableEq F] + (n M : ℕ) (params : ProtocolParams M) := + outerPSpec F n params ++ₚ (Sumcheck.Spec.pSpec F (logupSumcheckDegree M params) n) -/-- The embedded LogUp sumcheck phase, obtained by lifting ArkLib's generic Sumcheck reduction -through the LogUp-to-Sumcheck context lens. -/ -noncomputable def sumcheckOracleReduction : - OracleReduction oSpec (StmtAfterOuter F n M params) (OStmtAfterOuter F n M params) Unit - (StmtAfterSumcheck F n M params) (OStmtAfterSumcheck F n M params) Unit - (logupSumcheckPSpec F n M params) where - prover := sumcheckProver oSpec F n M params - verifier := sumcheckVerifier oSpec F n M params +/-- Protocol 2 transcript shape: outer LogUp, ArkLib's generic sumcheck, and the final point check. +-/ +@[reducible] +noncomputable def pSpec (F : Type) [Field F] [Fintype F] [DecidableEq F] + (n M : ℕ) (params : ProtocolParams M) := + pSpecBeforeFinal F n M params ++ₚ finalCheckPSpec -end SumcheckReduction +/-- The prover messages before the final check are oracle-accessible: outer LogUp followed by the +embedded sumcheck messages. -/ +noncomputable instance instPSpecBeforeFinalMessageOracleInterface + {F : Type} [Field F] [Fintype F] [DecidableEq F] {n M : ℕ} + {params : ProtocolParams M} : + ∀ i, OracleInterface ((pSpecBeforeFinal F n M params).Message i) := by + unfold pSpecBeforeFinal + exact ProtocolSpec.instOracleInterfaceMessageAppend -section FinalCheckReduction +/-- The full LogUp prover messages are oracle-accessible: outer LogUp and sumcheck messages; the +final point check has no messages. -/ +noncomputable instance instPSpecMessageOracleInterface + {F : Type} [Field F] [Fintype F] [DecidableEq F] {n M : ℕ} + {params : ProtocolParams M} : + ∀ i, OracleInterface ((pSpec F n M params).Message i) := by + unfold pSpec + exact ProtocolSpec.instOracleInterfaceMessageAppend + +/-- The verifier challenges before the final check are sampleable: outer LogUp followed by the +embedded sumcheck challenges. -/ +noncomputable instance instPSpecBeforeFinalChallengeSampleable + {F : Type} [Field F] [Fintype F] [DecidableEq F] [SampleableType F] {n M : ℕ} + {params : ProtocolParams M} : + ∀ i, SampleableType ((pSpecBeforeFinal F n M params).Challenge i) := by + unfold pSpecBeforeFinal + exact ProtocolSpec.instSampleableTypeChallengeAppend + +/-- The full LogUp verifier challenges are sampleable: the final point check has no challenges. -/ +noncomputable instance instPSpecChallengeSampleable + {F : Type} [Field F] [Fintype F] [DecidableEq F] [SampleableType F] {n M : ℕ} + {params : ProtocolParams M} : + ∀ i, SampleableType ((pSpec F n M params).Challenge i) := by + unfold pSpec + exact ProtocolSpec.instSampleableTypeChallengeAppend variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [SampleableType F] (n M : ℕ) variable (params : ProtocolParams M) -/-- The final LogUp point check as an ArkLib oracle reduction. -/ -noncomputable def finalCheckOracleReduction : - OracleReduction oSpec (StmtAfterSumcheck F n M params) (OStmtAfterSumcheck F n M params) Unit +/-- The full LogUp prover, composed from the outer prover and embedded sumcheck prover. -/ +noncomputable def logupProver : + OracleProver oSpec (StmtIn F n M) (OStmtIn F n M) (Unit) (StmtOut) (OStmtOut) Unit - finalCheckPSpec where - prover := finalCheckProver oSpec F n M params - verifier := finalCheckVerifier oSpec F n M params + (pSpec F n M params) := + Prover.append + (Prover.append (outerProver oSpec F n M params) (sumcheckProver oSpec F n M params)) + (finalCheckProver oSpec F n M params) -end FinalCheckReduction +variable {ι : Type} (oSpec : OracleSpec ι) +variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [SampleableType F] (n M : ℕ) +variable (params : ProtocolParams M) -section FullReduction +/-- The full LogUp verifier, obtained by composing the outer verifier with the embedded sumcheck +verifier and final point check. -/ +noncomputable def logupVerifier : + OracleVerifier oSpec (StmtIn F n M) (OStmtIn F n M) + (StmtOut) (OStmtOut) + (pSpec F n M params) := + OracleVerifier.append + (OracleVerifier.append (outerVerifier oSpec F n M params) + (sumcheckVerifier oSpec F n M params)) + (finalCheckVerifier oSpec F n M params) variable {ι : Type} (oSpec : OracleSpec ι) variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [SampleableType F] (n M : ℕ) @@ -540,12 +796,13 @@ variable (params : ProtocolParams M) /-- The full LogUp Protocol as an ArkLib oracle reduction. -/ noncomputable def logupOracleReduction : - OracleReduction oSpec (StmtIn F n M) (OStmtIn F n M) (WitIn F n M params) + OracleReduction oSpec (StmtIn F n M) (OStmtIn F n M) (Unit) (StmtOut) (OStmtOut) Unit (pSpec F n M params) where prover := logupProver oSpec F n M params verifier := logupVerifier oSpec F n M params -end FullReduction +end FullProtocol + end Logup diff --git a/ArkLib/ProofSystem/Logup/Security/Completeness.lean b/ArkLib/ProofSystem/Logup/Security/Completeness.lean index 2d00b36fbd..fcc6c7f7e4 100644 --- a/ArkLib/ProofSystem/Logup/Security/Completeness.lean +++ b/ArkLib/ProofSystem/Logup/Security/Completeness.lean @@ -15,174 +15,13 @@ open scoped NNReal namespace Logup -section OuterAlgebra +section ProtocolAlgebra -variable {F : Type} [Field F] [Fintype F] [DecidableEq F] {n M K : ℕ} +variable {F : Type} [Field F] {M : ℕ} -omit [Fintype F] [DecidableEq F] in -/-- For honest helpers (`hₖ = ∑ᵢ mᵢ/φᵢ`) the domain-identity term vanishes pointwise, away from -poles (`φᵢ ≠ 0`). -/ -theorem domainIdentityTerm_eq_zero (groups : PartialSumGroups M K) - (oStmt : ∀ i, OStmtIn F n M i) (mult : MultilinearOracle F n) - (helpers : HelperMessages F n K) (x : F) (k : Fin K) (u : (Fin n → Fin 2)) - (hh : (helpers k) u = helperValue groups oStmt mult x k u) - (hφ : ∀ i ∈ groups k, termPhi oStmt x i u ≠ 0) : - domainIdentityTerm groups oStmt mult helpers x k u = 0 := by - rw [domainIdentityTerm, denominatorProduct, hh, helperValue, Finset.sum_mul, sub_eq_zero] - refine Finset.sum_congr rfl (fun i hi => ?_) - rw [← Finset.mul_prod_erase _ _ hi] - field_simp [hφ i hi] - -omit [Fintype F] in -/-- The honest log-derivative identity (heart of LogUp): with the normalized multiplicity, the table -side equals the column side. Needs every column value to occur in the table (`hcols`) and the table -counts used by nonzero lookup multiplicities to be nonzero in `F` (`hchar`, from `charLarge`). -Holds even at poles, since `x/0 = 0`. -/ -theorem honest_multiplicity_identity [Finite F] (oStmt : ∀ i, OStmtIn F n M i) (x : F) - (hcols : ∀ j : Fin M, ∀ u : (Fin n → Fin 2), ∃ v : (Fin n → Fin 2), - (columnOracle oStmt j) u = (tableOracle oStmt) v) - (hchar : ∀ a : F, lookupMultiplicityCount oStmt a ≠ 0 → - (tableMultiplicityCount oStmt a : F) ≠ 0) : - (∑ u : (Fin n → Fin 2), - (honestMultiplicity oStmt) u / (x + (tableOracle oStmt) u)) - = ∑ j : Fin M, ∑ u : (Fin n → Fin 2), - (1 : F) / (x + (columnOracle oStmt j) u) := by - letI : Fintype F := Fintype.ofFinite F - classical - -- per-value cancellation - have key : ∀ a : F, - tableMultiplicityCount oStmt a • - ((lookupMultiplicityCount oStmt a : F) / (tableMultiplicityCount oStmt a : F) / (x + a)) - = lookupMultiplicityCount oStmt a • ((1 : F) / (x + a)) := by - intro a - by_cases hT : tableMultiplicityCount oStmt a = 0 - · have hL : lookupMultiplicityCount oStmt a = 0 := by - rw [lookupMultiplicityCount, Finset.card_eq_zero, Finset.filter_eq_empty_iff] - rintro ⟨j, u⟩ - hja - obtain ⟨v, hv⟩ := hcols j u - have hav : (tableOracle oStmt) v = a := hv.symm.trans hja - rw [tableMultiplicityCount] at hT - exact absurd hT (Finset.card_ne_zero_of_mem - (show v ∈ Finset.univ.filter - fun w => (tableOracle oStmt) w = a by simp [hav])) - simp [hT, hL] - · rw [nsmul_eq_mul, nsmul_eq_mul] - by_cases hL : lookupMultiplicityCount oStmt a = 0 - · simp [hL] - · have hTF := hchar a hL - field_simp - -- group the table side by table value - have hLHS : - (∑ u : (Fin n → Fin 2), (honestMultiplicity oStmt) u / - (x + (tableOracle oStmt) u)) - = ∑ a : F, tableMultiplicityCount oStmt a • - ((lookupMultiplicityCount oStmt a : F) / (tableMultiplicityCount oStmt a : F) - / (x + a)) := by - rw [← Finset.sum_fiberwise (Finset.univ : Finset (Fin n → Fin 2)) - (fun u => (tableOracle oStmt) u)] - refine Finset.sum_congr rfl (fun a _ => ?_) - have h : ∀ u ∈ Finset.univ.filter - (fun u => (tableOracle oStmt) u = a), - (honestMultiplicity oStmt) u / - (x + (tableOracle oStmt) u) - = (lookupMultiplicityCount oStmt a : F) / (tableMultiplicityCount oStmt a : F) - / (x + a) := by - intro u hu - simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hu - change (lookupMultiplicityCount oStmt ((tableOracle oStmt) u) : F) / - (tableMultiplicityCount oStmt ((tableOracle oStmt) u) : F) / - (x + (tableOracle oStmt) u) - = (lookupMultiplicityCount oStmt a : F) / (tableMultiplicityCount oStmt a : F) / (x + a) - rw [hu] - rw [Finset.sum_congr rfl h, Finset.sum_const] - rfl - -- group the column side by column value - have hRHS : - (∑ j : Fin M, ∑ u : (Fin n → Fin 2), (1 : F) / - (x + (columnOracle oStmt j) u)) - = ∑ a : F, lookupMultiplicityCount oStmt a • ((1 : F) / (x + a)) := by - rw [← Finset.sum_product', Finset.univ_product_univ, - ← Finset.sum_fiberwise (Finset.univ : Finset (Fin M × (Fin n → Fin 2))) - (fun p => (columnOracle oStmt p.1) p.2)] - refine Finset.sum_congr rfl (fun a _ => ?_) - have h : ∀ p ∈ Finset.univ.filter - (fun p : Fin M × (Fin n → Fin 2) => - (columnOracle oStmt p.1) p.2 = a), - (1 : F) / (x + (columnOracle oStmt p.1) p.2) = (1 : F) / (x + a) := by - intro p hp - simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hp - simp only [hp] - rw [Finset.sum_congr rfl h, Finset.sum_const] - rfl - rw [hLHS, hRHS] - exact Finset.sum_congr rfl (fun a _ => key a) - -/-- If a value is used by a lookup column, then the table contains it often enough that its table -multiplicity is nonzero as an element of `F`. -/ -theorem tableMultiplicityCount_cast_ne_zero_of_lookupMultiplicityCount_ne_zero - (stmt : StmtIn F n M) (oStmt : ∀ i, OStmtIn F n M i) - (hcols : ∀ j : Fin M, ∀ u : (Fin n → Fin 2), ∃ v : (Fin n → Fin 2), - (columnOracle oStmt j) u = (tableOracle oStmt) v) - {a : F} (hlookup : lookupMultiplicityCount oStmt a ≠ 0) : - (tableMultiplicityCount oStmt a : F) ≠ 0 := by - classical - have hlookupCard : - ((Finset.univ : Finset (Fin M × (Fin n → Fin 2))).filter fun ix => - (columnOracle oStmt ix.1) ix.2 = a).card ≠ 0 := by - simpa [lookupMultiplicityCount] using hlookup - obtain ⟨⟨j, u⟩, hju⟩ := Finset.card_ne_zero.mp hlookupCard - simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hju - obtain ⟨v, hv⟩ := hcols j u - have hvTable : (tableOracle oStmt) v = a := hv.symm.trans hju - have htablePos : 0 < tableMultiplicityCount oStmt a := by - rw [tableMultiplicityCount, Finset.card_pos] - exact ⟨v, by simp [hvTable]⟩ - have hMpos : 0 < M := lt_of_le_of_lt (Nat.zero_le j.val) j.isLt - have htable_le_card : - tableMultiplicityCount oStmt a ≤ Fintype.card (Fin n → Fin 2) := by - rw [tableMultiplicityCount, ← Finset.card_univ] - exact Finset.card_filter_le _ _ - have hcard_hypercube : Fintype.card (Fin n → Fin 2) = 2 ^ n := by - simp - have hpow_le : 2 ^ n ≤ M * 2 ^ n := by - have hMone : 1 ≤ M := Nat.succ_le_of_lt hMpos - nth_rewrite 1 [← Nat.one_mul (2 ^ n)] - exact Nat.mul_le_mul_right (2 ^ n) hMone - have htable_lt_char : tableMultiplicityCount oStmt a < ringChar F := by - calc - tableMultiplicityCount oStmt a ≤ Fintype.card (Fin n → Fin 2) := htable_le_card - _ = 2 ^ n := hcard_hypercube - _ ≤ M * 2 ^ n := hpow_le - _ < ringChar F := stmt.charLarge - intro hzero - have hdvd : ringChar F ∣ tableMultiplicityCount oStmt a := - (ringChar.spec F (tableMultiplicityCount oStmt a)).1 hzero - exact (Nat.not_dvd_of_pos_of_lt htablePos htable_lt_char) hdvd - -omit [Fintype F] [DecidableEq F] in -/-- If `x` avoids the table poles, then it avoids all table and lookup-column denominator poles -under the input relation. -/ -theorem termPhi_ne_zero_of_table_poles (oStmt : ∀ i, OStmtIn F n M i) (x : F) - (hcols : ∀ j : Fin M, ∀ u : (Fin n → Fin 2), ∃ v : (Fin n → Fin 2), - (columnOracle oStmt j) u = (tableOracle oStmt) v) - (hNoTablePoles : ∀ u : (Fin n → Fin 2), - x + (tableOracle oStmt) u ≠ 0) : - ∀ (i : TermIdx M) (u : (Fin n → Fin 2)), termPhi oStmt x i u ≠ 0 := by - intro i u - cases hti : termToInput i with - | table => - rw [termPhi, hti] - simpa [phi, tableOracle] using hNoTablePoles u - | column j => - obtain ⟨v, hv⟩ := hcols j u - rw [termPhi, hti, phi] - simpa [hv] using hNoTablePoles v - -omit [Fintype F] [DecidableEq F] in -/-- The canonical groups partition the term indices `{0,…,M}`, so summing group-by-group equals -summing over all terms. -/ -theorem sum_canonicalGroups (params : ProtocolParams M) (g : TermIdx M → F) : - (∑ k : Fin params.numGroups, ∑ i ∈ canonicalGroups params k, g i) = ∑ i : TermIdx M, g i := by +/-- The protocol's concrete partial-sum groups partition the term indices `{0, …, M}`. -/ +theorem sum_protocolGroups (params : ProtocolParams M) (g : TermIdx M → F) : + (∑ k : Fin params.numGroups, ∑ i ∈ params.group k, g i) = ∑ i : TermIdx M, g i := by classical have hℓ := params.sumSize_pos have hidx : ∀ i : TermIdx M, i.val / params.sumSize < params.numGroups := by @@ -196,8 +35,7 @@ theorem sum_canonicalGroups (params : ProtocolParams M) (g : TermIdx M → F) : refine Finset.sum_congr rfl (fun k _ => ?_) congr 1 ext i - simp only [canonicalGroups, ProtocolParams.group, Finset.mem_filter, Finset.mem_univ, true_and, - Fin.ext_iff] + simp only [ProtocolParams.group, Finset.mem_filter, Finset.mem_univ, true_and, Fin.ext_iff] constructor · rintro ⟨h1, h2⟩ have ha : k.val ≤ i.val / params.sumSize := (Nat.le_div_iff_mul_le hℓ).mpr h1 @@ -206,90 +44,7 @@ theorem sum_canonicalGroups (params : ProtocolParams M) (g : TermIdx M → F) : · intro h exact ⟨(Nat.le_div_iff_mul_le hℓ).mp (by omega), (Nat.div_lt_iff_lt_mul hℓ).mp (by omega)⟩ -omit [Fintype F] in -/-- The honest batched claim sums to zero: `∑ᵤ Q(u) = 0` for the honest multiplicity and helpers, -away from poles. Combines `domainIdentityTerm_eq_zero`, `sum_canonicalGroups`, and -`honest_multiplicity_identity`. -/ -theorem logupOuterClaim_zero [Finite F] (params : ProtocolParams M) - (oStmtIn : ∀ i, OStmtIn F n M i) - (x : F) (z : Fin n → F) (lam : Fin params.numGroups → F) - (hcols : ∀ j : Fin M, ∀ u : (Fin n → Fin 2), ∃ v : (Fin n → Fin 2), - (columnOracle oStmtIn j) u = (tableOracle oStmtIn) v) - (hchar : ∀ a : F, lookupMultiplicityCount oStmtIn a ≠ 0 → - (tableMultiplicityCount oStmtIn a : F) ≠ 0) - (hpoles : ∀ (i : TermIdx M) (u : (Fin n → Fin 2)), termPhi oStmtIn x i u ≠ 0) : - (∑ u : (Fin n → Fin 2), - qOnHypercube (canonicalGroups params) oStmtIn (honestMultiplicity oStmtIn) - (honestHelpers params oStmtIn x) x z lam u) = 0 := by - -- honest helpers kill the domain-identity term, leaving `∑ₖ helperValue` - have hq : ∀ u : (Fin n → Fin 2), - qOnHypercube (canonicalGroups params) oStmtIn (honestMultiplicity oStmtIn) - (honestHelpers params oStmtIn x) x z lam u - = ∑ k : Fin params.numGroups, - helperValue (canonicalGroups params) oStmtIn (honestMultiplicity oStmtIn) x k u := by - intro u - simp only [qOnHypercube] - refine Finset.sum_congr rfl (fun k _ => ?_) - rw [domainIdentityTerm_eq_zero (canonicalGroups params) oStmtIn (honestMultiplicity oStmtIn) - (honestHelpers params oStmtIn x) x k u rfl (fun i _ => hpoles i u), mul_zero, add_zero] - rfl - -- each helper expands to the per-group term sum, which partitions to the full term sum - have hsum : ∀ u : (Fin n → Fin 2), - (∑ k : Fin params.numGroups, - helperValue (canonicalGroups params) oStmtIn (honestMultiplicity oStmtIn) x k u) - = ∑ i : TermIdx M, - termNumerator (honestMultiplicity oStmtIn) i u / termPhi oStmtIn x i u := by - intro u - simp only [helperValue] - exact sum_canonicalGroups params - (fun i => termNumerator (honestMultiplicity oStmtIn) i u / termPhi oStmtIn x i u) - -- split the term sum into the table term and the column terms - have hterm : ∀ u : (Fin n → Fin 2), - (∑ i : TermIdx M, termNumerator (honestMultiplicity oStmtIn) i u / termPhi oStmtIn x i u) - = (honestMultiplicity oStmtIn) u / - (x + (tableOracle oStmtIn) u) - + ∑ j : Fin M, (-1 : F) / (x + (columnOracle oStmtIn j) u) := by - intro u - have hcol : ∀ j : Fin M, - termNumerator (honestMultiplicity oStmtIn) (Fin.succ j) u / - termPhi oStmtIn x (Fin.succ j) u - = (-1 : F) / (x + (columnOracle oStmtIn j) u) := by - intro j - have htt : termToInput (Fin.succ j : TermIdx M) = InputOracleIdx.column j := by - simp only [termToInput, Fin.val_succ, Nat.succ_ne_zero, ↓reduceDIte] - congr 1 - simp only [termNumerator, termPhi, htt, numerator, phi] - rw [Fin.sum_univ_succ] - refine congrArg₂ (· + ·) rfl ?_ - exact Finset.sum_congr rfl (fun j _ => hcol j) - simp_rw [hq, hsum, hterm] - rw [Finset.sum_add_distrib, honest_multiplicity_identity oStmtIn x hcols hchar, - Finset.sum_comm (f := fun u j => (-1 : F) / (x + (columnOracle oStmtIn j) u)), - ← Finset.sum_add_distrib] - refine Finset.sum_eq_zero (fun j _ => ?_) - rw [← Finset.sum_add_distrib] - exact Finset.sum_eq_zero (fun u _ => by ring) - -/-- The table poles `{x : ∃ u, x + t(u) = 0} = {-t(u) : u ∈ H}` number at most `|H|`. This is the -counting fact behind the pole-sampling completeness error. -/ -theorem pole_card_le (oStmt : ∀ i, OStmtIn F n M i) : - (Finset.univ.filter (fun x : F => ∃ u : (Fin n → Fin 2), - x + (tableOracle oStmt) u = 0)).card - ≤ Fintype.card (Fin n → Fin 2) := by - classical - calc (Finset.univ.filter (fun x : F => ∃ u : (Fin n → Fin 2), - x + (tableOracle oStmt) u = 0)).card - ≤ (Finset.univ.image - (fun u : (Fin n → Fin 2) => -(tableOracle oStmt) u)).card := by - apply Finset.card_le_card - intro x hx - simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hx - obtain ⟨u, hu⟩ := hx - exact Finset.mem_image.mpr ⟨u, Finset.mem_univ u, (eq_neg_of_add_eq_zero_left hu).symm⟩ - _ ≤ Fintype.card (Fin n → Fin 2) := by - rw [← Finset.card_univ]; exact Finset.card_image_le - -end OuterAlgebra +end ProtocolAlgebra section Completeness @@ -367,8 +122,8 @@ theorem logupSumcheckRelationInput_of_zero rw [logupOuterSumcheckClaim] apply Finset.sum_congr rfl intro u _ - simpa [booleanDomain, logupSumcheckPolynomial] using - logupQPolynomial_eval_hypercube F n M params stmt oStmt u + simp only [booleanDomain, logupSumcheckPolynomial] + congr 1 _ = 0 := hZero /-- Completeness error from the current `x`-sampling model: the verifier samples `x` from all of @@ -377,14 +132,16 @@ the honest handoff rather than rejected by an exponential verifier scan. -/ noncomputable def logupCompletenessError (F : Type) [Fintype F] (n : ℕ) : ℝ≥0 := (Fintype.card (Fin n → Fin 2) : ℝ≥0) / (Fintype.card F) +omit [DecidableEq F] in private theorem uniform_avoids_table_poles_prob [Inhabited F] (oStmt : ∀ i, OStmtIn F n M i) : (1 : ENNReal) - (logupCompletenessError F n : ENNReal) ≤ Pr[fun x : F => ∀ u : (Fin n → Fin 2), - x + (tableOracle oStmt) u ≠ 0 | $ᵗ F] := by + x + MvPolynomial.toEvalsZeroOne (oStmt .table).1 u ≠ 0 | $ᵗ F] := by classical + letI : DecidableEq F := Classical.decEq F let bad : F → Prop := - fun x => ∃ u : (Fin n → Fin 2), x + (tableOracle oStmt) u = 0 + fun x => ∃ u : (Fin n → Fin 2), x + MvPolynomial.toEvalsZeroOne (oStmt .table).1 u = 0 have hbad : Pr[bad | $ᵗ F] ≤ (logupCompletenessError F n : ENNReal) := by rw [probEvent_uniformSample] @@ -392,7 +149,9 @@ private theorem uniform_avoids_table_poles_prob [Inhabited F] have hcard : ((Finset.univ.filter bad).card : ENNReal) ≤ (Fintype.card (Fin n → Fin 2) : ENNReal) := by - exact_mod_cast (by simpa [bad] using pole_card_le (F := F) (n := n) (M := M) oStmt) + exact_mod_cast (by + simpa [bad] using + pole_card_le (F := F) (n := n) (table := MvPolynomial.toEvalsZeroOne (oStmt .table).1)) convert ENNReal.div_le_div_right hcard (Fintype.card F : ENNReal) using 1; norm_num have hcompl : Pr[fun x : F => ¬ bad x | $ᵗ F] + Pr[bad | $ᵗ F] = (1 : ENNReal) := by @@ -412,8 +171,8 @@ private theorem le_probEvent_bind_of_forall_le {m : Type → Type*} [Monad m] [L [MonadLiftT m SPMF] [LawfulMonadLiftT m SPMF] [MonadLiftT m SetM] [LawfulMonadLiftT m SetM] [EvalDistCompatible m] {α β : Type} {mx : m α} {my : α → m β} {q : β → Prop} {r : ENNReal} - (hfail : Pr[⊥ | mx] = 0) (h : ∀ x ∈ support mx, r ≤ Pr[q | my x]) : - r ≤ Pr[q | mx >>= my] := by + (hfail : Pr[⊥ | mx] = 0) (h : ∀ x ∈ support mx, r ≤ Pr[ q | my x]) : + r ≤ Pr[ q | mx >>= my] := by have htrue : Pr[fun _ : α => True | mx] = (1 : ENNReal) := by exact probEvent_eq_one (mx := mx) (p := fun _ : α => True) ⟨hfail, by simp⟩ have hmul := mul_le_probEvent_bind (mx := mx) (my := my) @@ -618,6 +377,8 @@ private theorem Fin.induction_four {motive : Fin 5 → Sort*} {zero : motive 0} = succ 3 (succ 2 (succ 1 (succ 0 zero))) := rfl set_option maxHeartbeats 1000000 in +-- The proof peels the whole four-round outer transcript and reconstructs the handoff relation; +-- the generated support terms are large even though the reasoning is deterministic. set_option linter.flexible false in /-- Completeness of the outer LogUp phase: the honest outer prover reaches the zero-sum handoff relation, except with the pole-sampling error. -/ @@ -635,7 +396,8 @@ theorem logup_outer_completeness [Inhabited F] : QueryImpl.addLift_def] refine ge_trans (probEvent_mono (p := fun out => ∀ u : (Fin n → Fin 2), - out.2.1.xChallenge + (tableOracle oStmt) u ≠ 0) ?goodOutputs) ?goodProb + out.2.1.xChallenge + MvPolynomial.toEvalsZeroOne (oStmt .table).1 u ≠ 0) + ?goodOutputs) ?goodProb · -- every output with a non-pole outer challenge satisfies the success predicate intro out hout hGood rw [OptionT.mem_support_iff] at hout @@ -722,22 +484,30 @@ theorem logup_outer_completeness [Inhabited F] : simp only [Option.some.injEq] at hverifyEq subst vAccepted have hNoTablePoles : - ∀ u : (Fin n → Fin 2), xval + (tableOracle oStmt) u ≠ 0 := by + ∀ u : (Fin n → Fin 2), + xval + MvPolynomial.toEvalsZeroOne (oStmt .table).1 u ≠ 0 := by intro u simpa [outerVerifier, outerChallengeXIdx, outerChallengeBatchIdx, ProtocolSpec.FullTranscript.challenges, ProtocolSpec.Transcript.concat, Fin.snoc] using hGood u have hcols : ∀ j : Fin M, ∀ u : (Fin n → Fin 2), ∃ v : (Fin n → Fin 2), - (columnOracle oStmt j) u = - (tableOracle oStmt) v := by + MvPolynomial.toEvalsZeroOne (oStmt (.column j)).1 u = + MvPolynomial.toEvalsZeroOne (oStmt .table).1 v := by simpa [inputRelation] using hIn - have hchar : ∀ a : F, lookupMultiplicityCount oStmt a ≠ 0 → - (tableMultiplicityCount oStmt a : F) ≠ 0 := + have hchar : ∀ a : F, + lookupMultiplicityCount + (fun j => MvPolynomial.toEvalsZeroOne (oStmt (.column j)).1) a ≠ 0 → + (tableMultiplicityCount (MvPolynomial.toEvalsZeroOne (oStmt .table).1) a : F) ≠ 0 := fun _ hlookup => tableMultiplicityCount_cast_ne_zero_of_lookupMultiplicityCount_ne_zero - stmt oStmt hcols hlookup - have hpoles : ∀ (i : TermIdx M) (u : (Fin n → Fin 2)), termPhi oStmt xval i u ≠ 0 := - termPhi_ne_zero_of_table_poles oStmt xval hcols hNoTablePoles + stmt.charLarge (MvPolynomial.toEvalsZeroOne (oStmt .table).1) + (fun j => MvPolynomial.toEvalsZeroOne (oStmt (.column j)).1) hcols hlookup + have hpoles : ∀ (i : TermIdx M) (u : (Fin n → Fin 2)), + termPhi (MvPolynomial.toEvalsZeroOne (oStmt .table).1) + (fun j => MvPolynomial.toEvalsZeroOne (oStmt (.column j)).1) xval i u ≠ 0 := + termPhi_ne_zero_of_table_poles (MvPolynomial.toEvalsZeroOne (oStmt .table).1) + (fun j => MvPolynomial.toEvalsZeroOne (oStmt (.column j)).1) xval hcols + hNoTablePoles let stmtAfter : StmtAfterOuter F n M params := { xChallenge := xval, zChallenge := zlam.1, batchingScalars := zlam.2 } let oStmtAfter : ∀ i, OStmtAfterOuter F n M params i := @@ -745,10 +515,36 @@ theorem logup_outer_completeness [Inhabited F] : | .input i => oStmt i | .multiplicity => honestMultiplicity oStmt | .helpers => honestHelpers params oStmt xval + have hMultiplicity : + MvPolynomial.toEvalsZeroOne (honestMultiplicity oStmt).1 = + normalizedMultiplicityValue (MvPolynomial.toEvalsZeroOne (oStmt .table).1) + (fun i => MvPolynomial.toEvalsZeroOne (oStmt (.column i)).1) := by + change (MvPolynomial.MLEEquiv (R := F) (σ := Fin n)) (honestMultiplicity oStmt) = _ + simp [honestMultiplicity] + have hHelpers : + (fun k => MvPolynomial.toEvalsZeroOne (honestHelpers params oStmt xval k).1) = + fun k u => + helperValue params.group (MvPolynomial.toEvalsZeroOne (oStmt .table).1) + (fun i => MvPolynomial.toEvalsZeroOne (oStmt (.column i)).1) + (normalizedMultiplicityValue (MvPolynomial.toEvalsZeroOne (oStmt .table).1) + (fun i => MvPolynomial.toEvalsZeroOne (oStmt (.column i)).1)) + xval k u := by + funext k + change (MvPolynomial.MLEEquiv (R := F) (σ := Fin n)) + (honestHelpers params oStmt xval k) = _ + simp [honestHelpers, hMultiplicity] have hmid : ((stmtAfter, oStmtAfter), ()) ∈ logupMidRelation F n M params := by - simpa [stmtAfter, oStmtAfter, logupMidRelation, logupOuterSumcheckClaim] using - logupOuterClaim_zero (F := F) (n := n) (M := M) params oStmt xval - zlam.1 zlam.2 hcols hchar hpoles + unfold logupMidRelation logupOuterSumcheckClaim + rw [← logupOuterClaim_zero + (groups := params.group) (hgroups := sum_protocolGroups (F := F) params) + (table := MvPolynomial.toEvalsZeroOne (oStmt .table).1) + (columns := fun j => MvPolynomial.toEvalsZeroOne (oStmt (.column j)).1) + (xChallenge := xval) (zChallenge := zlam.1) (batchingScalars := zlam.2) + hcols hchar hpoles] + apply Finset.sum_congr rfl + intro u _ + rw [logupQPolynomial_eval_hypercube] + simp [stmtAfter, oStmtAfter, hMultiplicity, hHelpers] simp [outerVerifier, outerChallengeXIdx, outerChallengeBatchIdx, ProtocolSpec.FullTranscript.challenges, ProtocolSpec.FullTranscript.messages, ProtocolSpec.Transcript.concat, Fin.snoc] @@ -790,9 +586,9 @@ theorem logup_outer_completeness [Inhabited F] : (mul_le_probEvent_bind (mx := (liftM ($ᵗ F : ProbComp F) : OptionT ProbComp F)) (p := fun x : F => ∀ u : (Fin n → Fin 2), - x + (tableOracle oStmt) u ≠ 0) + x + MvPolynomial.toEvalsZeroOne (oStmt .table).1 u ≠ 0) (r := Pr[fun x : F => ∀ u : (Fin n → Fin 2), - x + (tableOracle oStmt) u ≠ 0 | $ᵗ F]) + x + MvPolynomial.toEvalsZeroOne (oStmt .table).1 u ≠ 0 | $ᵗ F]) (r' := 1) (by simp) (by intro x hx hGood erw [simulateQ_pure] @@ -928,7 +724,10 @@ theorem finalCheckCompleteness : rintro ⟨stmt, oStmt⟩ ⟨⟩ hRel have hExpected : MvPolynomial.eval stmt.finalClaim.challenges - (logupQPolynomial F n M params stmt.outer oStmt) = + (logupQPolynomial (params.group) (oStmt (.input .table)).1 + (fun i => (oStmt (.input (.column i))).1) (oStmt .multiplicity).1 + (fun k => (oStmt .helpers k).1) stmt.outer.xChallenge stmt.outer.zChallenge + stmt.outer.batchingScalars) = stmt.finalClaim.target := by unfold logupAfterSumcheckRelation Sumcheck.Spec.relationRound at hRel simp only [Set.mem_setOf_eq, logupSumcheckOracleStmt, logupSumcheckPolynomial] at hRel @@ -951,9 +750,15 @@ theorem finalCheckCompleteness : MvPolynomial.eval (Fin.append stmt.finalClaim.challenges x ∘ Fin.cast (Sumcheck.Spec.relationRound._proof_1 n (Fin.last n))) - (logupQPolynomial F n M params stmt.outer oStmt)) = + (logupQPolynomial (params.group) (oStmt (.input .table)).1 + (fun i => (oStmt (.input (.column i))).1) (oStmt .multiplicity).1 + (fun k => (oStmt .helpers k).1) stmt.outer.xChallenge stmt.outer.zChallenge + stmt.outer.batchingScalars)) = MvPolynomial.eval stmt.finalClaim.challenges - (logupQPolynomial F n M params stmt.outer oStmt) := by + (logupQPolynomial (params.group) (oStmt (.input .table)).1 + (fun i => (oStmt (.input (.column i))).1) (oStmt .multiplicity).1 + (fun k => (oStmt .helpers k).1) stmt.outer.xChallenge stmt.outer.zChallenge + stmt.outer.batchingScalars) := by rw [Finset.sum_eq_single tail0] · rw [hfinalPoint] rfl @@ -966,30 +771,23 @@ theorem finalCheckCompleteness : exact Fin.elim0 (Fin.cast tailSize_zero i))) exact hsum ▸ hRel have hGuard : - qAtPoint (canonicalGroups params) stmt.outer.xChallenge stmt.outer.zChallenge + qAtPoint (params.group) stmt.outer.xChallenge stmt.outer.zChallenge stmt.finalClaim.challenges stmt.outer.batchingScalars - { multiplicity := lagrangeOracleEval (oStmt .multiplicity) stmt.finalClaim.challenges - table := lagrangeOracleEval (oStmt (.input .table)) stmt.finalClaim.challenges - columns := fun i => - lagrangeOracleEval (oStmt (.input (.column i))) stmt.finalClaim.challenges - helpers := fun k => - lagrangeOracleEval ((oStmt .helpers) k) stmt.finalClaim.challenges } = + (MvPolynomial.eval stmt.finalClaim.challenges (oStmt .multiplicity).1) + (MvPolynomial.eval stmt.finalClaim.challenges (oStmt (.input .table)).1) + (fun i => MvPolynomial.eval stmt.finalClaim.challenges (oStmt (.input (.column i))).1) + (fun k => MvPolynomial.eval stmt.finalClaim.challenges (oStmt .helpers k).1) = stmt.finalClaim.target := by rw [← hExpected] - exact (logupQPolynomial_eval_point F n M params stmt.outer oStmt - stmt.finalClaim.challenges - { multiplicity := lagrangeOracleEval (oStmt .multiplicity) stmt.finalClaim.challenges - table := lagrangeOracleEval (oStmt (.input .table)) stmt.finalClaim.challenges - columns := fun i => - lagrangeOracleEval (oStmt (.input (.column i))) stmt.finalClaim.challenges - helpers := fun k => - lagrangeOracleEval ((oStmt .helpers) k) stmt.finalClaim.challenges } - (by simp [logupPointEvaluationsAgree])).symm + exact (logupQPolynomial_eval_point (params.group) (oStmt (.input .table)).1 + (fun i => (oStmt (.input (.column i))).1) (oStmt .multiplicity).1 + (fun k => (oStmt .helpers k).1) stmt.outer.xChallenge stmt.outer.zChallenge + stmt.finalClaim.challenges stmt.outer.batchingScalars).symm let qImpl : QueryImpl - (oSpec + ([OStmtAfterSumcheck F n M params]ₒ + [finalCheckPSpec.Message]ₒ)) + (oSpec + ([OStmtAfterOuter F n M params]ₒ + [finalCheckPSpec.Message]ₒ)) (OracleComp oSpec) := - OracleInterface.simOracle2 (T₁ := OStmtAfterSumcheck F n M params) + OracleInterface.simOracle2 (T₁ := OStmtAfterOuter F n M params) (T₂ := finalCheckPSpec.Message) oSpec oStmt (fun i : finalCheckPSpec.MessageIdx => Fin.elim0 i) have hquery : @@ -1023,39 +821,38 @@ theorem finalCheckCompleteness : rw [hquery (.input .table) stmt.finalClaim.challenges] simp only [pure_bind, Option.elim_some] let colValue := fun i : Fin M => - lagrangeOracleEval (oStmt (.input (.column i))) stmt.finalClaim.challenges + MvPolynomial.eval stmt.finalClaim.challenges (oStmt (.input (.column i))).1 have hcols := simulateQ_optionT_mapM_pure qImpl (fun i : Fin M => finalCheckQuery oSpec F n M params (.input (.column i)) stmt.finalClaim.challenges) colValue (Vector.finRange M) (by intro i - simpa [colValue, OracleInterface.answer, lagrangeOracleEval] using + simpa [colValue, OracleInterface.answer] using hquery (.input (.column i)) stmt.finalClaim.challenges) erw [simulateQ_option_elimM] erw [hcols] simp only [pure_bind, Option.elimM, Option.elim_some] let helperValue := fun k : Fin params.numGroups => - lagrangeOracleEval ((oStmt .helpers) k) stmt.finalClaim.challenges + MvPolynomial.eval stmt.finalClaim.challenges (oStmt .helpers k).1 have hhelpers := simulateQ_optionT_mapM_pure qImpl (fun k : Fin params.numGroups => finalCheckQuery oSpec F n M params .helpers ⟨k, stmt.finalClaim.challenges⟩) helperValue (Vector.finRange params.numGroups) (by intro k - simpa [helperValue, OracleInterface.answer, lagrangeOracleEval] using + simpa [helperValue, OracleInterface.answer] using hquery .helpers ⟨k, stmt.finalClaim.challenges⟩) erw [simulateQ_option_elimM] erw [hhelpers] simp only [pure_bind, Option.elimM, Option.elim_some] have hGuard' : - qAtPoint (canonicalGroups params) stmt.outer.xChallenge stmt.outer.zChallenge + qAtPoint (params.group) stmt.outer.xChallenge stmt.outer.zChallenge stmt.finalClaim.challenges stmt.outer.batchingScalars - { multiplicity := OracleInterface.answer (oStmt .multiplicity) - stmt.finalClaim.challenges - table := OracleInterface.answer (oStmt (.input .table)) stmt.finalClaim.challenges - columns := colValue - helpers := helperValue } = + (OracleInterface.answer (oStmt .multiplicity) stmt.finalClaim.challenges) + (OracleInterface.answer (oStmt (.input .table)) stmt.finalClaim.challenges) + colValue + helperValue = stmt.finalClaim.target := by - simpa [OracleInterface.answer, lagrangeOracleEval, colValue, helperValue] using hGuard + simpa [OracleInterface.answer, colValue, helperValue] using hGuard erw [simulateQ_option_elimM] simp [guard, hGuard', OptionT.run_pure, Option.elimM] have hVerifyDefault : diff --git a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean deleted file mode 100644 index 18f4ddbb0f..0000000000 --- a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckBridge.lean +++ /dev/null @@ -1,173 +0,0 @@ -import ArkLib.ProofSystem.Logup.Sumcheck.SumcheckPolynomial -import ArkLib.ProofSystem.Sumcheck.Spec.General - -/-! -# LogUp Sumcheck Bridge - -Adapter between LogUp's outer phase and ArkLib's generic sumcheck protocol, for Protocol 2 of -Haböck's LogUp paper (Cryptology ePrint Archive, Paper 2022/1530, -). - -The outer phase leaves LogUp-specific data: challenges, input oracles, the multiplicity oracle, and -helper oracles. `SumcheckPolynomial.lean` builds the polynomial `Q` from that data. This file -packages `Q` as the single polynomial oracle expected by `Sumcheck.Spec` and states the zero-sum -claim that starts the sumcheck phase. - -The main artefact is `logupSumcheckContextLens`. It takes the output of the outer LogUp phase and presents it to generic sumcheck as -that zero-sum claim for `Q`. When sumcheck finishes, the lens packages the final sumcheck point and -claimed value back together with the retained LogUp data, so the final LogUp verifier can query the -original oracles and check the claim. - -The supporting definitions here describe the sumcheck transcript shape, the packaged `Q` oracle, and -the relations used to state that this translation is correct. --/ - -namespace Logup - -open scoped BigOperators - -section SumcheckInterface - -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) -variable (params : ProtocolParams M) - -/-- Individual-degree bound for LogUp's embedded sumcheck polynomial. -/ -def logupSumcheckDegree (_params : ProtocolParams M) : ℕ := - M + 3 - -/-- The concrete ArkLib Sumcheck transcript shape for LogUp's embedded sumcheck. -/ -abbrev logupSumcheckPSpec : ProtocolSpec (Fin.vsum (fun _ : Fin n => 2)) := - Sumcheck.Spec.pSpec F (logupSumcheckDegree M params) n - -/-- The generic sumcheck input statement used by LogUp: target `0`, no prior challenges. -/ -abbrev LogupSumcheckStmtIn (_params : ProtocolParams M) : Type := - Sumcheck.Spec.StatementRound F n 0 - -/-- The generic sumcheck output statement: the final claim at verifier point `r`. -/ -abbrev LogupSumcheckStmtOut (_params : ProtocolParams M) : Type := - Sumcheck.Spec.StatementRound F n (.last n) - -/-- LogUp state after the embedded sumcheck, before the final oracle-query check. -/ -structure StmtAfterSumcheck where - /-- The outer transcript data retained for reconstructing `Q(r)`. -/ - outer : StmtAfterOuter F n M params - /-- Sumcheck's final point claim `Q(r) = v`. -/ - finalClaim : LogupSumcheckStmtOut F n M params - -/-- Oracle statements retained after sumcheck for the final LogUp point check. -/ -abbrev OStmtAfterSumcheck : OuterOracleIdx M → Type := - OStmtAfterOuter F n M params - -/-- The generic sumcheck oracle statement: LogUp's bounded-degree `Q` polynomial. -/ -abbrev LogupSumcheckOracleStatement : Unit → Type := - Sumcheck.Spec.OracleStatement F n (logupSumcheckDegree M params) - -/-- LogUp enters the embedded sumcheck with the zero-sum claim. -/ -def logupInitialSumcheckStatement : LogupSumcheckStmtIn F n M params where - target := 0 - challenges := fun i => Fin.elim0 i - -/-- Package `logupQPolynomial` with its degree certificate into ArkLib's oracle statement type. -/ -noncomputable def logupSumcheckPolynomial - (stmt : StmtAfterOuter F n M params) - (oStmt : ∀ i, OStmtAfterOuter F n M params i) : - LogupSumcheckOracleStatement F n M params () := by - classical - exact ⟨logupQPolynomial F n M params stmt oStmt, by - rw [MvPolynomial.mem_restrictDegree_iff_degreeOf_le] - intro i - exact logupQPolynomial_degreeOf F n M params stmt oStmt i⟩ - -/-- Package the LogUp `Q` polynomial as the single oracle statement expected by Sumcheck. -/ -noncomputable def logupSumcheckOracleStmt - (stmt : StmtAfterOuter F n M params) - (oStmt : ∀ i, OStmtAfterOuter F n M params i) : - ∀ i, LogupSumcheckOracleStatement F n M params i := - fun _ => logupSumcheckPolynomial F n M params stmt oStmt - -/-- The LogUp zero-sum claim that is fed to the generic sumcheck. -/ -noncomputable def logupOuterSumcheckClaim - (stmt : StmtAfterOuter F n M params) - (oStmt : ∀ i, OStmtAfterOuter F n M params i) : F := - ∑ u : (Fin n → Fin 2), - qOnHypercube (canonicalGroups params) (fun i => oStmt (.input i)) (oStmt .multiplicity) - (oStmt .helpers) stmt.xChallenge stmt.zChallenge stmt.batchingScalars u - -/-- Relation handed from the outer LogUp phase to the embedded sumcheck: the outer algebra produced -a genuine zero-sum claim. -/ -def logupMidRelation : - Set ((StmtAfterOuter F n M params × (∀ i, OStmtAfterOuter F n M params i)) × Unit) := - { x | logupOuterSumcheckClaim F n M params x.1.1 x.1.2 = 0 } - -end SumcheckInterface - -section SumcheckBridge - -variable (F : Type) [Field F] (n M : ℕ) -variable (params : ProtocolParams M) - -/-- The Boolean sumcheck domain, packaged in the form expected by `Sumcheck.Spec`. -/ -def booleanDomain : Fin 2 ↪ F where - toFun := fun b => (b : F) - inj' := by - intro a b h - fin_cases a <;> fin_cases b - · rfl - · simp at h - · simp at h - · rfl - -/-- Relation after embedded sumcheck: the final sumcheck point claim is valid for the retained -LogUp polynomial. The final LogUp phase consumes this by querying the retained oracles at that -point and recomputing `Q(r)`. -/ -def logupAfterSumcheckRelation : - Set ((StmtAfterSumcheck F n M params × (∀ i, OStmtAfterSumcheck F n M params i)) × Unit) := - { x | - ((x.1.1.finalClaim, - logupSumcheckOracleStmt F n M params x.1.1.outer x.1.2), ()) ∈ - Sumcheck.Spec.relationRound F n (logupSumcheckDegree M params) (booleanDomain F) - (Fin.last n) } - -/-- The initial generic Sumcheck relation induced by a LogUp outer transcript. -/ -def logupSumcheckRelationInput - (stmt : StmtAfterOuter F n M params) - (oStmt : ∀ i, OStmtAfterOuter F n M params i) : Prop := - ((logupInitialSumcheckStatement F n M params, logupSumcheckOracleStmt F n M params stmt oStmt), - ()) ∈ - Sumcheck.Spec.relationRound F n (logupSumcheckDegree M params) (booleanDomain F) 0 - -end SumcheckBridge - -section SumcheckLift - -variable {ι : Type} (oSpec : OracleSpec ι) -variable (F : Type) [Field F] [Fintype F] [DecidableEq F] (n M : ℕ) -variable (params : ProtocolParams M) - -/-- Context lens from LogUp's retained outer state to ArkLib's generic Sumcheck state. - -The projection builds the generic zero-sum claim and its single polynomial oracle. The lift keeps -the outer LogUp data and retained oracles together with Sumcheck's final point claim, so the next -LogUp phase can perform the paper's final oracle-query check. --/ -noncomputable def logupSumcheckContextLens : - OracleContext.Lens - (StmtAfterOuter F n M params) (StmtAfterSumcheck F n M params) - (LogupSumcheckStmtIn F n M params) (LogupSumcheckStmtOut F n M params) - (OStmtAfterOuter F n M params) (OStmtAfterSumcheck F n M params) - (LogupSumcheckOracleStatement F n M params) - (LogupSumcheckOracleStatement F n M params) - Unit Unit Unit Unit where - stmt := - ⟨fun ctx => - (logupInitialSumcheckStatement F n M params, - logupSumcheckOracleStmt F n M params ctx.1 ctx.2), - fun ctx inner => - ({ outer := ctx.1, finalClaim := inner.1 }, ctx.2)⟩ - wit := - ⟨fun _ => (), - fun _ _ => ()⟩ - -end SumcheckLift - -end Logup diff --git a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean b/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean deleted file mode 100644 index f4e8b63208..0000000000 --- a/ArkLib/ProofSystem/Logup/Sumcheck/SumcheckPolynomial.lean +++ /dev/null @@ -1,450 +0,0 @@ -import ArkLib.Data.MvPolynomial.Multilinear -import ArkLib.ProofSystem.Logup.Common - -/-! -# LogUp Sumcheck Polynomial - -Construction of the concrete polynomial used by LogUp's embedded sumcheck, following Protocol 2 of -Haböck's LogUp paper (Cryptology ePrint Archive, Paper 2022/1530, -). - -This file defines the polynomial `logupQPolynomial : MvPolynomial (Fin n) F` used as LogUp's -sumcheck instance after the outer phase. - -It proves direct facts about this polynomial, namely: -* its individual-degree bound `degreeOf ≤ M + 3`, -* its agreement with `qOnHypercube` on the -Boolean hypercube, and -* its agreement with the final-query reconstruction at sumcheck's final point. --/ - -namespace Logup - -open scoped BigOperators - -section QEvaluation - -variable {F : Type} [Field F] [Fintype F] [DecidableEq F] {n M K : ℕ} - -/-- The batched polynomial expression `Q` from paper equation (18), evaluated on a row `u ∈ H`. -/ -noncomputable def qOnHypercube (groups : PartialSumGroups M K) - (oStmt : ∀ i, OStmtIn F n M i) (multiplicity : MultilinearOracle F n) - (helpers : HelperMessages F n K) (xChallenge : F) (zChallenge : Fin n → F) - (batchingScalars : Fin K → F) (u : (Fin n → Fin 2)) : F := - ∑ k : Fin K, ( - (helpers k) u + - MvPolynomial.eval (u : Fin n → F) (MvPolynomial.eqPolynomial zChallenge) * - batchingScalars k * - domainIdentityTerm groups oStmt multiplicity helpers xChallenge k u) - -/-- Denominator term at the final sumcheck point, reconstructed from oracle query answers. -/ -def phiAtPoint (xChallenge : F) (evals : PointEvaluations F M K) : - InputOracleIdx M → F - | .table => xChallenge + evals.table - | .column i => xChallenge + evals.columns i - -/-- Numerator term at the final sumcheck point, reconstructed from oracle query answers. -/ -def numeratorAtPoint (evals : PointEvaluations F M K) : InputOracleIdx M → F - | .table => evals.multiplicity - | .column _ => -1 - -/-- Term denominator at the final sumcheck point, indexed by `0, ..., M`. -/ -def termPhiAtPoint (xChallenge : F) (evals : PointEvaluations F M K) (i : TermIdx M) : F := - phiAtPoint xChallenge evals (termToInput i) - -/-- Term numerator at the final sumcheck point, indexed by `0, ..., M`. -/ -def termNumeratorAtPoint (evals : PointEvaluations F M K) (i : TermIdx M) : F := - numeratorAtPoint evals (termToInput i) - -/-- The domain-identity expression at the final sumcheck point `r`. -/ -noncomputable def domainIdentityAtPoint (groups : PartialSumGroups M K) - (xChallenge : F) (evals : PointEvaluations F M K) (k : Fin K) : F := - evals.helpers k * (∏ i ∈ groups k, termPhiAtPoint xChallenge evals i) - - ∑ i ∈ groups k, - termNumeratorAtPoint evals i * - ∏ j ∈ (groups k).erase i, termPhiAtPoint xChallenge evals j - -/-- The verifier's final check value `Q(eq(r,z), m(r), φᵢ(r), hₖ(r))` from paper (19). -/ -noncomputable def qAtPoint (groups : PartialSumGroups M K) (xChallenge : F) - (zChallenge rChallenge : Fin n → F) (batchingScalars : Fin K → F) - (evals : PointEvaluations F M K) : F := - ∑ k : Fin K, ( - evals.helpers k + - MvPolynomial.eval rChallenge (MvPolynomial.eqPolynomial zChallenge) * batchingScalars k * - domainIdentityAtPoint groups xChallenge evals k) - -end QEvaluation - -section SumcheckPolynomial - -variable (F : Type) [Field F] (n M : ℕ) -variable (params : ProtocolParams M) - -private theorem oraclePolynomial_eval_hypercube - (oracle : MultilinearOracle F n) (u : (Fin n → Fin 2)) : - MvPolynomial.eval (u : Fin n → F) (MvPolynomial.MLE oracle.values) - = oracle u := by - simp - -private noncomputable def inputOraclePolynomial - (oStmt : ∀ i, OStmtAfterOuter F n M params i) (idx : InputOracleIdx M) : - MvPolynomial (Fin n) F := - match idx with - | .table => MvPolynomial.MLE (oStmt (.input .table)).values - | .column i => MvPolynomial.MLE (oStmt (.input (.column i))).values - -private noncomputable def multiplicityPolynomial - (oStmt : ∀ i, OStmtAfterOuter F n M params i) : - MvPolynomial (Fin n) F := - MvPolynomial.MLE (oStmt .multiplicity).values - -private noncomputable def helperPolynomial - (oStmt : ∀ i, OStmtAfterOuter F n M params i) (k : Fin params.numGroups) : - MvPolynomial (Fin n) F := - MvPolynomial.MLE ((oStmt .helpers) k).values - -private noncomputable def termPhiPolynomial - (stmt : StmtAfterOuter F n M params) - (oStmt : ∀ i, OStmtAfterOuter F n M params i) (i : TermIdx M) : - MvPolynomial (Fin n) F := - MvPolynomial.C stmt.xChallenge + inputOraclePolynomial F n M params oStmt (termToInput i) - -private noncomputable def termNumeratorPolynomial - (oStmt : ∀ i, OStmtAfterOuter F n M params i) (i : TermIdx M) : - MvPolynomial (Fin n) F := - match termToInput i with - | .table => multiplicityPolynomial F n M params oStmt - | .column _ => MvPolynomial.C (-1) - -private noncomputable def domainIdentityPolynomial - (stmt : StmtAfterOuter F n M params) - (oStmt : ∀ i, OStmtAfterOuter F n M params i) (k : Fin params.numGroups) : - MvPolynomial (Fin n) F := - helperPolynomial F n M params oStmt k * - (∏ i ∈ canonicalGroups params k, termPhiPolynomial F n M params stmt oStmt i) - - ∑ i ∈ canonicalGroups params k, - termNumeratorPolynomial F n M params oStmt i * - ∏ j ∈ (canonicalGroups params k).erase i, - termPhiPolynomial F n M params stmt oStmt j - -private theorem inputOraclePolynomial_degreeOf - (oStmt : ∀ i, OStmtAfterOuter F n M params i) (idx : InputOracleIdx M) - (i : Fin n) : - MvPolynomial.degreeOf i (inputOraclePolynomial F n M params oStmt idx) ≤ 1 := by - cases idx with - | table => - simpa [inputOraclePolynomial] using - (MvPolynomial.MLE_degreeOf (R := F) ((oStmt (.input .table)).values) i) - | column j => - simpa [inputOraclePolynomial] using - (MvPolynomial.MLE_degreeOf (R := F) ((oStmt (.input (.column j))).values) i) - -private theorem multiplicityPolynomial_degreeOf - (oStmt : ∀ i, OStmtAfterOuter F n M params i) (i : Fin n) : - MvPolynomial.degreeOf i (multiplicityPolynomial F n M params oStmt) ≤ 1 := by - simpa [multiplicityPolynomial] using - (MvPolynomial.MLE_degreeOf (R := F) ((oStmt .multiplicity).values) i) - -private theorem helperPolynomial_degreeOf - (oStmt : ∀ i, OStmtAfterOuter F n M params i) (k : Fin params.numGroups) - (i : Fin n) : - MvPolynomial.degreeOf i (helperPolynomial F n M params oStmt k) ≤ 1 := by - simpa [helperPolynomial] using - (MvPolynomial.MLE_degreeOf (R := F) (((oStmt .helpers) k).values) i) - -private theorem termPhiPolynomial_degreeOf - (stmt : StmtAfterOuter F n M params) - (oStmt : ∀ i, OStmtAfterOuter F n M params i) (j : TermIdx M) (i : Fin n) : - MvPolynomial.degreeOf i (termPhiPolynomial F n M params stmt oStmt j) ≤ 1 := by - calc - _ ≤ max (MvPolynomial.degreeOf i (MvPolynomial.C stmt.xChallenge)) - (MvPolynomial.degreeOf i (inputOraclePolynomial F n M params oStmt (termToInput j))) := by - exact MvPolynomial.degreeOf_add_le i _ _ - _ ≤ max 0 1 := by - gcongr - · exact (MvPolynomial.degreeOf_C (R := F) stmt.xChallenge i).le - · exact inputOraclePolynomial_degreeOf F n M params oStmt (termToInput j) i - _ = 1 := by - omega - -private theorem termNumeratorPolynomial_degreeOf - (oStmt : ∀ i, OStmtAfterOuter F n M params i) (j : TermIdx M) (i : Fin n) : - MvPolynomial.degreeOf i (termNumeratorPolynomial F n M params oStmt j) ≤ 1 := by - unfold termNumeratorPolynomial - cases h : termToInput j with - | table => - simpa [h, multiplicityPolynomial] using - multiplicityPolynomial_degreeOf F n M params oStmt i - | column c => - exact (MvPolynomial.degreeOf_C (R := F) (-1 : F) i).le.trans (by omega) - -private theorem finset_card_termIdx_le (s : Finset (TermIdx M)) : - s.card ≤ M + 1 := by - simpa [TermIdx] using Finset.card_le_univ (s := s) - -private theorem termPhiPolynomial_prod_degreeOf - (stmt : StmtAfterOuter F n M params) - (oStmt : ∀ i, OStmtAfterOuter F n M params i) (s : Finset (TermIdx M)) - (i : Fin n) : - MvPolynomial.degreeOf i - (∏ j ∈ s, termPhiPolynomial F n M params stmt oStmt j) ≤ M + 1 := by - calc - _ ≤ ∑ j ∈ s, - MvPolynomial.degreeOf i (termPhiPolynomial F n M params stmt oStmt j) := by - exact MvPolynomial.degreeOf_prod_le i _ _ - _ ≤ ∑ _j ∈ s, 1 := by - apply Finset.sum_le_sum - intro j _ - exact termPhiPolynomial_degreeOf F n M params stmt oStmt j i - _ = s.card := by - simp - _ ≤ M + 1 := finset_card_termIdx_le M s - -private theorem domainIdentityPolynomial_degreeOf - (stmt : StmtAfterOuter F n M params) - (oStmt : ∀ i, OStmtAfterOuter F n M params i) (k : Fin params.numGroups) - (i : Fin n) : - MvPolynomial.degreeOf i (domainIdentityPolynomial F n M params stmt oStmt k) ≤ M + 2 := by - unfold domainIdentityPolynomial - have hProd : - MvPolynomial.degreeOf i - (∏ j ∈ canonicalGroups params k, termPhiPolynomial F n M params stmt oStmt j) ≤ - M + 1 := - termPhiPolynomial_prod_degreeOf F n M params stmt oStmt (canonicalGroups params k) i - have hLeft : - MvPolynomial.degreeOf i - (helperPolynomial F n M params oStmt k * - (∏ j ∈ canonicalGroups params k, termPhiPolynomial F n M params stmt oStmt j)) ≤ - M + 2 := by - calc - _ ≤ MvPolynomial.degreeOf i (helperPolynomial F n M params oStmt k) + - MvPolynomial.degreeOf i - (∏ j ∈ canonicalGroups params k, - termPhiPolynomial F n M params stmt oStmt j) := by - exact MvPolynomial.degreeOf_mul_le i _ _ - _ ≤ 1 + (M + 1) := by - gcongr - exact helperPolynomial_degreeOf F n M params oStmt k i - _ = M + 2 := by - omega - have hRight : - MvPolynomial.degreeOf i - (∑ j ∈ canonicalGroups params k, - termNumeratorPolynomial F n M params oStmt j * - ∏ l ∈ (canonicalGroups params k).erase j, - termPhiPolynomial F n M params stmt oStmt l) ≤ - M + 2 := by - calc - _ ≤ (canonicalGroups params k).sup fun j => - MvPolynomial.degreeOf i - (termNumeratorPolynomial F n M params oStmt j * - ∏ l ∈ (canonicalGroups params k).erase j, - termPhiPolynomial F n M params stmt oStmt l) := by - exact MvPolynomial.degreeOf_sum_le i _ _ - _ ≤ M + 2 := by - apply Finset.sup_le - intro j _ - have hEraseProd : - MvPolynomial.degreeOf i - (∏ l ∈ (canonicalGroups params k).erase j, - termPhiPolynomial F n M params stmt oStmt l) ≤ M + 1 := - termPhiPolynomial_prod_degreeOf F n M params stmt oStmt - ((canonicalGroups params k).erase j) i - calc - _ ≤ MvPolynomial.degreeOf i (termNumeratorPolynomial F n M params oStmt j) + - MvPolynomial.degreeOf i - (∏ l ∈ (canonicalGroups params k).erase j, - termPhiPolynomial F n M params stmt oStmt l) := by - exact MvPolynomial.degreeOf_mul_le i _ _ - _ ≤ 1 + (M + 1) := by - gcongr - exact termNumeratorPolynomial_degreeOf F n M params oStmt j i - _ = M + 2 := by - omega - exact (MvPolynomial.degreeOf_sub_le i _ _).trans (max_le hLeft hRight) - -/-- The concrete multivariate LogUp sumcheck polynomial before packaging with its degree proof. -/ -noncomputable def logupQPolynomial - (stmt : StmtAfterOuter F n M params) - (oStmt : ∀ i, OStmtAfterOuter F n M params i) : - MvPolynomial (Fin n) F := - ∑ k : Fin params.numGroups, ( - helperPolynomial F n M params oStmt k + - MvPolynomial.eqPolynomial stmt.zChallenge * - MvPolynomial.C (stmt.batchingScalars k) * - domainIdentityPolynomial F n M params stmt oStmt k) - -theorem logupQPolynomial_degreeOf - (stmt : StmtAfterOuter F n M params) - (oStmt : ∀ i, OStmtAfterOuter F n M params i) (i : Fin n) : - MvPolynomial.degreeOf i (logupQPolynomial F n M params stmt oStmt) ≤ M + 3 := by - classical - unfold logupQPolynomial - calc - _ ≤ (Finset.univ : Finset (Fin params.numGroups)).sup fun k => - MvPolynomial.degreeOf i - (helperPolynomial F n M params oStmt k + - MvPolynomial.eqPolynomial stmt.zChallenge * - MvPolynomial.C (stmt.batchingScalars k) * - domainIdentityPolynomial F n M params stmt oStmt k) := by - exact MvPolynomial.degreeOf_sum_le i _ _ - _ ≤ M + 3 := by - apply Finset.sup_le - intro k _ - have hHelper : MvPolynomial.degreeOf i (helperPolynomial F n M params oStmt k) ≤ M + 3 := - (helperPolynomial_degreeOf F n M params oStmt k i).trans (by omega) - have hProduct : - MvPolynomial.degreeOf i - (MvPolynomial.eqPolynomial stmt.zChallenge * - MvPolynomial.C (stmt.batchingScalars k) * - domainIdentityPolynomial F n M params stmt oStmt k) ≤ M + 3 := by - calc - _ ≤ MvPolynomial.degreeOf i - (MvPolynomial.eqPolynomial stmt.zChallenge * - MvPolynomial.C (stmt.batchingScalars k)) + - MvPolynomial.degreeOf i - (domainIdentityPolynomial F n M params stmt oStmt k) := by - exact MvPolynomial.degreeOf_mul_le i _ _ - _ ≤ (MvPolynomial.degreeOf i (MvPolynomial.eqPolynomial stmt.zChallenge) + - MvPolynomial.degreeOf i (MvPolynomial.C (stmt.batchingScalars k))) + - MvPolynomial.degreeOf i - (domainIdentityPolynomial F n M params stmt oStmt k) := by - gcongr - exact MvPolynomial.degreeOf_mul_le i _ _ - _ ≤ (1 + 0) + (M + 2) := by - gcongr - · exact MvPolynomial.eqPolynomial_degreeOf (R := F) stmt.zChallenge i - · exact (MvPolynomial.degreeOf_C (R := F) (stmt.batchingScalars k) i).le - · exact domainIdentityPolynomial_degreeOf F n M params stmt oStmt k i - _ = M + 3 := by - omega - exact (MvPolynomial.degreeOf_add_le i _ _).trans (max_le hHelper hProduct) - -private theorem termPhiPolynomial_eval_hypercube - (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) - (i : TermIdx M) (u : (Fin n → Fin 2)) : - MvPolynomial.eval (u : Fin n → F) (termPhiPolynomial F n M params stmt oStmt i) - = termPhi (fun idx => oStmt (.input idx)) stmt.xChallenge i u := by - rcases h : termToInput i with _ | c <;> - simp only [termPhiPolynomial, termPhi, phi, h, inputOraclePolynomial, tableOracle, - columnOracle, map_add, MvPolynomial.eval_C, oraclePolynomial_eval_hypercube F n] - -private theorem termNumeratorPolynomial_eval_hypercube - (oStmt : ∀ i, OStmtAfterOuter F n M params i) (i : TermIdx M) (u : (Fin n → Fin 2)) : - MvPolynomial.eval (u : Fin n → F) (termNumeratorPolynomial F n M params oStmt i) - = termNumerator (oStmt .multiplicity) i u := by - unfold termNumeratorPolynomial termNumerator numerator - cases termToInput i with - | table => simp only [multiplicityPolynomial, oraclePolynomial_eval_hypercube F n] - | column c => simp - -private theorem domainIdentityPolynomial_eval_hypercube - (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) - (k : Fin params.numGroups) (u : (Fin n → Fin 2)) : - MvPolynomial.eval (u : Fin n → F) (domainIdentityPolynomial F n M params stmt oStmt k) - = domainIdentityTerm (canonicalGroups params) (fun idx => oStmt (.input idx)) - (oStmt .multiplicity) (oStmt .helpers) stmt.xChallenge k u := by - rw [domainIdentityPolynomial, map_sub, map_mul, map_prod, domainIdentityTerm, - denominatorProduct, map_sum] - congr 1 - · congr 1 - · simp only [helperPolynomial]; exact oraclePolynomial_eval_hypercube F n _ u - · exact Finset.prod_congr rfl - (fun i _ => termPhiPolynomial_eval_hypercube F n M params stmt oStmt i u) - · refine Finset.sum_congr rfl (fun i _ => ?_) - rw [map_mul, map_prod, termNumeratorPolynomial_eval_hypercube F n M params] - congr 1 - exact Finset.prod_congr rfl - (fun j _ => termPhiPolynomial_eval_hypercube F n M params stmt oStmt j u) - -/-- Denominator polynomials evaluated at an arbitrary point match the reconstructed final-query -denominators. -/ -private theorem termPhiPolynomial_eval_point - (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) - (r : Fin n → F) (evals : PointEvaluations F M params.numGroups) - (hEval : logupPointEvaluationsAgree (F := F) (n := n) (M := M) params r oStmt evals) - (i : TermIdx M) : - MvPolynomial.eval r (termPhiPolynomial F n M params stmt oStmt i) - = termPhiAtPoint stmt.xChallenge evals i := by - rcases h : termToInput i with _ | c - · simp only [termPhiPolynomial, termPhiAtPoint, phiAtPoint, h, inputOraclePolynomial, - map_add, MvPolynomial.eval_C] - exact congrArg (fun y => stmt.xChallenge + y) hEval.2.1.symm - · simp only [termPhiPolynomial, termPhiAtPoint, phiAtPoint, h, inputOraclePolynomial, - map_add, MvPolynomial.eval_C] - exact congrArg (fun y => stmt.xChallenge + y) (hEval.2.2.1 c).symm - -/-- Numerator polynomials evaluated at an arbitrary point match the reconstructed final-query -numerators. -/ -private theorem termNumeratorPolynomial_eval_point - (oStmt : ∀ i, OStmtAfterOuter F n M params i) - (r : Fin n → F) (evals : PointEvaluations F M params.numGroups) - (hEval : logupPointEvaluationsAgree (F := F) (n := n) (M := M) params r oStmt evals) - (i : TermIdx M) : - MvPolynomial.eval r (termNumeratorPolynomial F n M params oStmt i) - = termNumeratorAtPoint evals i := by - unfold termNumeratorPolynomial termNumeratorAtPoint numeratorAtPoint - cases h : termToInput i with - | table => - simp only [multiplicityPolynomial] - exact hEval.1.symm - | column c => - simp - -/-- The cleared-denominator identity polynomial evaluated at the final point matches the -reconstructed final-query expression. -/ -private theorem domainIdentityPolynomial_eval_point - (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) - (r : Fin n → F) (evals : PointEvaluations F M params.numGroups) - (hEval : logupPointEvaluationsAgree (F := F) (n := n) (M := M) params r oStmt evals) - (k : Fin params.numGroups) : - MvPolynomial.eval r (domainIdentityPolynomial F n M params stmt oStmt k) - = domainIdentityAtPoint (canonicalGroups params) stmt.xChallenge evals k := by - rw [domainIdentityPolynomial, domainIdentityAtPoint, map_sub, map_mul, map_prod, map_sum] - congr 1 - · congr 1 - · simp only [helperPolynomial] - exact (hEval.2.2.2 k).symm - · exact Finset.prod_congr rfl - (fun i _ => termPhiPolynomial_eval_point F n M params stmt oStmt r evals hEval i) - · refine Finset.sum_congr rfl (fun i _ => ?_) - rw [map_mul, map_prod, termNumeratorPolynomial_eval_point F n M params oStmt r evals hEval] - congr 1 - exact Finset.prod_congr rfl - (fun j _ => termPhiPolynomial_eval_point F n M params stmt oStmt r evals hEval j) - -/-- `logupQPolynomial` restricted to the Boolean hypercube agrees with `qOnHypercube`. -/ -theorem logupQPolynomial_eval_hypercube - (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) - (u : (Fin n → Fin 2)) : - MvPolynomial.eval (u : Fin n → F) (logupQPolynomial F n M params stmt oStmt) - = qOnHypercube (canonicalGroups params) (fun i => oStmt (.input i)) (oStmt .multiplicity) - (oStmt .helpers) stmt.xChallenge stmt.zChallenge stmt.batchingScalars u := by - rw [logupQPolynomial, qOnHypercube, map_sum] - refine Finset.sum_congr rfl (fun k _ => ?_) - rw [map_add, map_mul, map_mul, MvPolynomial.eval_C, - show MvPolynomial.eval (u : Fin n → F) (helperPolynomial F n M params oStmt k) - = ((oStmt .helpers) k) u from by - simp only [helperPolynomial]; exact oraclePolynomial_eval_hypercube F n _ u, - domainIdentityPolynomial_eval_hypercube F n M params] - -/-- `logupQPolynomial` evaluated at the sumcheck point agrees with the value reconstructed from -the final LogUp oracle-query answers. -/ -theorem logupQPolynomial_eval_point - (stmt : StmtAfterOuter F n M params) (oStmt : ∀ i, OStmtAfterOuter F n M params i) - (r : Fin n → F) (evals : PointEvaluations F M params.numGroups) - (hEval : logupPointEvaluationsAgree (F := F) (n := n) (M := M) params r oStmt evals) : - MvPolynomial.eval r (logupQPolynomial F n M params stmt oStmt) - = qAtPoint (canonicalGroups params) stmt.xChallenge stmt.zChallenge r - stmt.batchingScalars evals := by - rw [logupQPolynomial, qAtPoint, map_sum] - refine Finset.sum_congr rfl (fun k _ => ?_) - rw [map_add, map_mul, map_mul, MvPolynomial.eval_C, - show MvPolynomial.eval r (helperPolynomial F n M params oStmt k) = evals.helpers k from by - simp only [helperPolynomial] - exact (hEval.2.2.2 k).symm, - domainIdentityPolynomial_eval_point F n M params stmt oStmt r evals hEval] - -end SumcheckPolynomial - -end Logup From c0092f89e5c64bbef543016911204133bde970b1 Mon Sep 17 00:00:00 2001 From: jCabala Date: Fri, 26 Jun 2026 22:36:56 +0200 Subject: [PATCH 09/12] feat: Added lemmas 4 and 5 --- ArkLib/ProofSystem/Logup/Algebra.lean | 170 +++++++++++++++++++++++++- 1 file changed, 164 insertions(+), 6 deletions(-) diff --git a/ArkLib/ProofSystem/Logup/Algebra.lean b/ArkLib/ProofSystem/Logup/Algebra.lean index 03712d2ce3..95bc174bef 100644 --- a/ArkLib/ProofSystem/Logup/Algebra.lean +++ b/ArkLib/ProofSystem/Logup/Algebra.lean @@ -4,10 +4,9 @@ import Mathlib.Algebra.Order.Floor.Div /-! # LogUp algebra and sumcheck polynomial -This file contains the protocol-independent algebra used to express LogUp as a sumcheck claim. It -does not define statements, transcripts, provers, or verifiers. Instead, it works with plain -hypercube functions `(Fin n → Fin 2) → F`, polynomial inputs `MvPolynomial (Fin n) F`, challenges, -and partial-sum groups. +This file contains the algebra used to express LogUp as a sumcheck claim and tries to be decoupled +from the protocol types. It works with plain hypercube functions `(Fin n → Fin 2) → F`, polynomial +inputs `MvPolynomial (Fin n) F`, challenges, and partial-sum groups. The main output is `logupQPolynomial`, the polynomial `Q` that the protocol sends to sumcheck, plus `logupQPolynomial_degreeOf`, the individual-degree bound needed by sumcheck. The row-level and @@ -204,6 +203,165 @@ theorem inputToTerm_termToInput {M : ℕ} (i : TermIdx M) : · next h => exact Fin.ext h.symm · next h => apply Fin.ext; simp only [inputToTerm]; omega +/-! ## Fractional decompositions in `F[X]` (paper Section 2.3) + +Paper-shaped statements over the polynomial ring `F[X]`, in cleared-denominator form: +`clearedDecomp` is the numerator left after multiplying the formal `∑_z m(z)/(X + z)` by the common +denominator `∏_{w∈F}(X + w)`. Working in `F[X]` (rather than `RatFunc F`) keeps these formal and +dependency-light. We prove `clearedDecomp`'s uniqueness (Lemma 4), the "collecting" bridge +`clearedSum_eq_clearedDecomp`, and the set-inclusion criterion (Lemma 5). -/ + +section FractionalDecomposition + +open Polynomial + +variable {F : Type} [Field F] [Fintype F] [DecidableEq F] + +/-- The cleared-denominator decomposition `∑_{z ∈ F} m(z) · ∏_{w ≠ z}(X + w)` in `F[X]`: the +numerator left after multiplying the formal `∑_z m(z)/(X + z)` by `∏_{w∈F}(X + w)`. -/ +noncomputable def clearedDecomp (m : F → F) : F[X] := + ∑ z : F, C (m z) * ∏ w ∈ Finset.univ.erase z, (X + C w) + +/-- **Lemma 4** (uniqueness of fractional decompositions). Over a finite field the coefficient map +`m ↦ clearedDecomp m` is injective: two cleared decompositions coincide in `F[X]` iff their +coefficient functions agree everywhere. -/ +theorem clearedDecomp_injective {m₁ m₂ : F → F} : + clearedDecomp m₁ = clearedDecomp m₂ ↔ m₁ = m₂ := by + refine ⟨fun H => ?_, fun H => by rw [H]⟩ + funext w + -- The coefficient-difference decomposition is the zero polynomial. + have hp0 : (∑ z : F, C (m₁ z - m₂ z) * ∏ u ∈ Finset.univ.erase z, (X + C u)) = 0 := by + have hsub : clearedDecomp m₁ - clearedDecomp m₂ = 0 := by rw [H, sub_self] + rw [clearedDecomp, clearedDecomp, ← Finset.sum_sub_distrib] at hsub + rw [← hsub] + exact Finset.sum_congr rfl (fun z _ => by rw [map_sub, sub_mul]) + -- Evaluating at `-w` isolates the `w`-th coefficient. + have heval := congrArg (Polynomial.eval (-w)) hp0 + simp only [Polynomial.eval_finsetSum, Polynomial.eval_mul, Polynomial.eval_C, + Polynomial.eval_prod, Polynomial.eval_add, Polynomial.eval_X, Polynomial.eval_zero] at heval + have hsingle : (∑ z : F, (m₁ z - m₂ z) * ∏ u ∈ Finset.univ.erase z, (-w + u)) + = (m₁ w - m₂ w) * ∏ u ∈ Finset.univ.erase w, (-w + u) := by + refine Finset.sum_eq_single w (fun z _ hzw => ?_) + (fun h => absurd (Finset.mem_univ w) h) + have hwmem : w ∈ Finset.univ.erase z := + Finset.mem_erase.mpr ⟨fun hwz => hzw hwz.symm, Finset.mem_univ w⟩ + exact mul_eq_zero_of_right _ (Finset.prod_eq_zero hwmem (neg_add_cancel w)) + rw [hsingle] at heval + have hprodne : (∏ u ∈ Finset.univ.erase w, (-w + u)) ≠ 0 := by + rw [Finset.prod_ne_zero_iff] + intro u hu hzero + rw [Finset.mem_erase] at hu + exact hu.1 (neg_injective (add_eq_zero_iff_eq_neg.mp hzero)).symm + exact sub_eq_zero.mp ((mul_eq_zero.mp heval).resolve_right hprodne) + +/-- "Collecting": an indexed sum of cleared single terms `∑ i, c i · ∏_{w ≠ a i}(X + w)` equals the +cleared decomposition whose coefficient at `z` is the total weight `∑_{a i = z} c i`. This is the +bridge between sequence-indexed sums (the protocol side) and `clearedDecomp` (Lemmas 4/5). -/ +theorem clearedSum_eq_clearedDecomp {ι : Type*} [Fintype ι] (a c : ι → F) : + (∑ i, C (c i) * ∏ w ∈ Finset.univ.erase (a i), (X + C w)) + = clearedDecomp (fun z => ∑ i ∈ Finset.univ.filter (fun i => a i = z), c i) := by + unfold clearedDecomp + rw [← Finset.sum_fiberwise (Finset.univ : Finset ι) a + (fun i => C (c i) * ∏ w ∈ Finset.univ.erase (a i), (X + C w))] + refine Finset.sum_congr rfl (fun z _ => ?_) + rw [map_sum, Finset.sum_mul] + refine Finset.sum_congr rfl (fun i hi => ?_) + rw [Finset.mem_filter] at hi + rw [hi.2] + +/-- The multiplicity of a value `z` in a finite sequence `a : ι → F`. -/ +def seqMultiplicity {ι : Type*} [Fintype ι] (a : ι → F) (z : F) : ℕ := + (Finset.univ.filter fun i => a i = z).card + +/-- **Lemma 5** (set inclusion), cleared-denominator form. When the field characteristic exceeds the +sizes of the index types, the set underlying `a` is contained in that of `b` iff there exist +multiplicities `m` making the cleared logarithmic-derivative identity hold in `F[X]` (the cleared +form of `∑_i 1/(X + a i) = ∑_j m j/(X + b j)`). The honest witness is the normalized multiplicity +`m j = ma(b j)/mb(b j)`. Generalizes the paper's equal-length sequences to arbitrary index types. -/ +theorem setInclusion_iff_cleared {ι κ : Type*} [Fintype ι] [Fintype κ] + (hNa : Fintype.card ι < ringChar F) (hNb : Fintype.card κ < ringChar F) + (a : ι → F) (b : κ → F) : + (∀ i, ∃ j, a i = b j) ↔ + ∃ m : κ → F, + (∑ i, ∏ w ∈ Finset.univ.erase (a i), (X + C w)) + = ∑ j, C (m j) * ∏ w ∈ Finset.univ.erase (b j), (X + C w) := by + -- Nonzero-cast of multiplicities: any count below `ringChar F` survives. + have hcastne : ∀ k : ℕ, 0 < k → k < ringChar F → (k : F) ≠ 0 := by + intro k hk hkr hzero + exact (Nat.not_dvd_of_pos_of_lt hk hkr) ((ringChar.spec F k).1 hzero) + have hleA : ∀ w : F, seqMultiplicity a w ≤ Fintype.card ι := by + intro w + calc seqMultiplicity a w ≤ (Finset.univ : Finset ι).card := Finset.card_filter_le _ _ + _ = Fintype.card ι := Finset.card_univ + have hleB : ∀ w : F, seqMultiplicity b w ≤ Fintype.card κ := by + intro w + calc seqMultiplicity b w ≤ (Finset.univ : Finset κ).card := Finset.card_filter_le _ _ + _ = Fintype.card κ := Finset.card_univ + have hcast : ∀ z : F, (∑ _i ∈ Finset.univ.filter (fun i => a i = z), (1 : F)) + = (seqMultiplicity a z : F) := by + intro z; rw [Finset.sum_const, nsmul_eq_mul, mul_one]; rfl + -- Collecting turns each side into a `clearedDecomp`. + have hL : (∑ i, ∏ w ∈ Finset.univ.erase (a i), (X + C w)) + = clearedDecomp (fun z => (seqMultiplicity a z : F)) := by + have h1 : (∑ i, ∏ w ∈ Finset.univ.erase (a i), (X + C w)) + = ∑ i, C (1 : F) * ∏ w ∈ Finset.univ.erase (a i), (X + C w) := by simp + rw [h1, clearedSum_eq_clearedDecomp a (fun _ => 1)] + exact congrArg clearedDecomp (funext (fun z => hcast z)) + have hR : ∀ m : κ → F, (∑ j, C (m j) * ∏ w ∈ Finset.univ.erase (b j), (X + C w)) + = clearedDecomp (fun z => ∑ j ∈ Finset.univ.filter (fun j => b j = z), m j) := + fun m => clearedSum_eq_clearedDecomp b m + -- By Lemma 4 the identity is exactly fiberwise equality of total weights. + have key : ∀ m : κ → F, + ((∑ i, ∏ w ∈ Finset.univ.erase (a i), (X + C w)) + = ∑ j, C (m j) * ∏ w ∈ Finset.univ.erase (b j), (X + C w)) + ↔ ∀ z : F, (seqMultiplicity a z : F) + = ∑ j ∈ Finset.univ.filter (fun j => b j = z), m j := by + intro m + rw [hL, hR m, clearedDecomp_injective] + exact funext_iff + constructor + · -- Forward: the normalized multiplicity is a valid witness. + intro hinc + refine ⟨fun j => (seqMultiplicity a (b j) : F) / (seqMultiplicity b (b j) : F), (key _).2 ?_⟩ + intro z + by_cases hz : seqMultiplicity a z = 0 + · rw [hz, Nat.cast_zero] + symm + refine Finset.sum_eq_zero (fun j hj => ?_) + rw [Finset.mem_filter] at hj + rw [hj.2, hz, Nat.cast_zero, zero_div] + · obtain ⟨i₀, hi₀⟩ := + Finset.card_ne_zero.mp (show (Finset.univ.filter (fun i => a i = z)).card ≠ 0 from hz) + rw [Finset.mem_filter] at hi₀ + obtain ⟨j₀, hj₀⟩ := hinc i₀ + have hbz_ne : seqMultiplicity b z ≠ 0 := by + refine Finset.card_ne_zero.mpr ⟨j₀, Finset.mem_filter.mpr ⟨Finset.mem_univ j₀, ?_⟩⟩ + rw [hj₀.symm.trans hi₀.2] + have hconst : ∀ j ∈ Finset.univ.filter (fun j => b j = z), + (seqMultiplicity a (b j) : F) / (seqMultiplicity b (b j) : F) + = (seqMultiplicity a z : F) / (seqMultiplicity b z : F) := by + intro j hj; rw [Finset.mem_filter] at hj; rw [hj.2] + have hcard : ((Finset.univ.filter (fun j => b j = z)).card : F) = (seqMultiplicity b z : F) := + rfl + rw [Finset.sum_congr rfl hconst, Finset.sum_const, nsmul_eq_mul, hcard] + have hbzF : (seqMultiplicity b z : F) ≠ 0 := + hcastne _ (Nat.pos_of_ne_zero hbz_ne) (lt_of_le_of_lt (hleB z) hNb) + field_simp + · -- Converse: a value of `a` has nonzero multiplicity, so its fiber in `b` is nonempty. + rintro ⟨m, hm⟩ i + have hcoef := (key m).1 hm (a i) + by_contra hcon + simp only [not_exists] at hcon + have hempty : Finset.univ.filter (fun j => b j = a i) = ∅ := by + rw [Finset.filter_eq_empty_iff] + exact fun j _ hbj => hcon j hbj.symm + rw [hempty, Finset.sum_empty] at hcoef + have hpos : 0 < seqMultiplicity a (a i) := + Finset.card_pos.mpr ⟨i, Finset.mem_filter.mpr ⟨Finset.mem_univ i, rfl⟩⟩ + exact hcastne _ hpos (lt_of_le_of_lt (hleA (a i)) hNa) hcoef + +end FractionalDecomposition + /-! ## Fractional-identity algebra LogUp's logarithmic-derivative construction, evaluated on hypercube rows. The table, columns, @@ -256,8 +414,8 @@ noncomputable def domainIdentityTerm (groups : Fin K → Finset (TermIdx M)) (multiplicity : (Fin n → Fin 2) → F) (helpers : Fin K → (Fin n → Fin 2) → F) (xChallenge : F) (k : Fin K) (u : Fin n → Fin 2) : F := helpers k u * (∏ i ∈ groups k, termPhi table columns xChallenge i u) - - ∑ i ∈ groups k, - termNumerator multiplicity i u * ∏ j ∈ (groups k).erase i, termPhi table columns xChallenge j u + ∑ i ∈ groups k, termNumerator multiplicity i u * ∏ j + ∈ (groups k).erase i, termPhi table columns xChallenge j u /-- The helper value `hₖ(u) = Σᵢ mᵢ(u)/φᵢ(u)` from paper equation (16). -/ noncomputable def helperValue (groups : Fin K → Finset (TermIdx M)) From 403ad647111dbc7fffe82525d3ac0f8c66362b48 Mon Sep 17 00:00:00 2001 From: jCabala Date: Sun, 28 Jun 2026 19:49:07 +0200 Subject: [PATCH 10/12] feat: Wired lemma 5 into completeness proof --- ArkLib/ProofSystem/Logup/Algebra.lean | 129 +++++++++++--------------- 1 file changed, 54 insertions(+), 75 deletions(-) diff --git a/ArkLib/ProofSystem/Logup/Algebra.lean b/ArkLib/ProofSystem/Logup/Algebra.lean index 95bc174bef..de5ea3655d 100644 --- a/ArkLib/ProofSystem/Logup/Algebra.lean +++ b/ArkLib/ProofSystem/Logup/Algebra.lean @@ -360,6 +360,50 @@ theorem setInclusion_iff_cleared {ι κ : Type*} [Fintype ι] [Fintype κ] Finset.card_pos.mpr ⟨i, Finset.mem_filter.mpr ⟨Finset.mem_univ i, rfl⟩⟩ exact hcastne _ hpos (lt_of_le_of_lt (hleA (a i)) hNa) hcoef +/-- **Lemma 5**, forward direction evaluated at a point `x` (paper eq. (15)). If every value of `a` +occurs among the values of `b`, then the logarithmic-derivative identity holds at `x` with the +normalized multiplicity `seqMultiplicity a (b j) / seqMultiplicity b (b j)` as witness. This is the +evaluated specialization that the completeness proof consumes; the formal `F[X]` statement is +`setInclusion_iff_cleared`. The hypothesis `hchar` (that a nonzero `a`-multiplicity forces a nonzero +`b`-multiplicity in `F`) packages set inclusion together with the characteristic bound, exactly as in +the protocol. Lean's `_ / 0 = 0` convention makes pole hypotheses unnecessary. -/ +theorem setInclusion_eval_forward {ι κ : Type*} [Fintype ι] [Fintype κ] + (a : ι → F) (b : κ → F) (x : F) + (hchar : ∀ z : F, seqMultiplicity a z ≠ 0 → (seqMultiplicity b z : F) ≠ 0) : + (∑ i, (1 : F) / (x + a i)) + = ∑ j, (seqMultiplicity a (b j) : F) / (seqMultiplicity b (b j) : F) / (x + b j) := by + have key : ∀ z : F, + seqMultiplicity b z • ((seqMultiplicity a z : F) / (seqMultiplicity b z : F) / (x + z)) + = seqMultiplicity a z • ((1 : F) / (x + z)) := by + intro z + by_cases ha : seqMultiplicity a z = 0 + · simp [ha] + · rw [nsmul_eq_mul, nsmul_eq_mul] + have hbF : (seqMultiplicity b z : F) ≠ 0 := hchar z ha + field_simp + have hLHS : (∑ i, (1 : F) / (x + a i)) + = ∑ z : F, seqMultiplicity a z • ((1 : F) / (x + z)) := by + rw [← Finset.sum_fiberwise (Finset.univ : Finset ι) a] + refine Finset.sum_congr rfl (fun z _ => ?_) + have h : ∀ i ∈ Finset.univ.filter (fun i => a i = z), + (1 : F) / (x + a i) = (1 : F) / (x + z) := + fun i hi => by rw [Finset.mem_filter] at hi; rw [hi.2] + rw [Finset.sum_congr rfl h, Finset.sum_const] + rfl + have hRHS : (∑ j, (seqMultiplicity a (b j) : F) / (seqMultiplicity b (b j) : F) / (x + b j)) + = ∑ z : F, seqMultiplicity b z • + ((seqMultiplicity a z : F) / (seqMultiplicity b z : F) / (x + z)) := by + rw [← Finset.sum_fiberwise (Finset.univ : Finset κ) b] + refine Finset.sum_congr rfl (fun z _ => ?_) + have h : ∀ j ∈ Finset.univ.filter (fun j => b j = z), + (seqMultiplicity a (b j) : F) / (seqMultiplicity b (b j) : F) / (x + b j) + = (seqMultiplicity a z : F) / (seqMultiplicity b z : F) / (x + z) := + fun j hj => by rw [Finset.mem_filter] at hj; rw [hj.2] + rw [Finset.sum_congr rfl h, Finset.sum_const] + rfl + rw [hLHS, hRHS] + exact Finset.sum_congr rfl (fun z _ => (key z).symm) + end FractionalDecomposition /-! ## Fractional-identity algebra @@ -448,80 +492,6 @@ theorem domainIdentityTerm_eq_zero (groups : Fin K → Finset (TermIdx M)) rw [← Finset.mul_prod_erase _ _ hi] field_simp [hφ i hi] -/-- The normalized multiplicity makes the table-side logarithmic derivative equal the sum of the -lookup-column logarithmic derivatives. -/ -theorem honest_multiplicity_identity [Fintype F] [DecidableEq F] - (table : (Fin n → Fin 2) → F) (columns : Fin M → (Fin n → Fin 2) → F) (xChallenge : F) - (hcols : ∀ j : Fin M, ∀ u : Fin n → Fin 2, ∃ v : Fin n → Fin 2, - columns j u = table v) - (hchar : ∀ a : F, lookupMultiplicityCount columns a ≠ 0 → - (tableMultiplicityCount table a : F) ≠ 0) : - (∑ u : Fin n → Fin 2, - normalizedMultiplicityValue table columns u / (xChallenge + table u)) - = ∑ j : Fin M, ∑ u : Fin n → Fin 2, - (1 : F) / (xChallenge + columns j u) := by - classical - have key : ∀ a : F, - tableMultiplicityCount table a • - ((lookupMultiplicityCount columns a : F) / - (tableMultiplicityCount table a : F) / (xChallenge + a)) - = lookupMultiplicityCount columns a • ((1 : F) / (xChallenge + a)) := by - intro a - by_cases hT : tableMultiplicityCount table a = 0 - · have hL : lookupMultiplicityCount columns a = 0 := by - rw [lookupMultiplicityCount, Finset.card_eq_zero, Finset.filter_eq_empty_iff] - rintro ⟨j, u⟩ - hja - obtain ⟨v, hv⟩ := hcols j u - have hav : table v = a := hv.symm.trans hja - rw [tableMultiplicityCount] at hT - exact absurd hT (Finset.card_ne_zero_of_mem - (show v ∈ Finset.univ.filter (fun w => table w = a) by simp [hav])) - simp [hT, hL] - · rw [nsmul_eq_mul, nsmul_eq_mul] - by_cases hL : lookupMultiplicityCount columns a = 0 - · simp [hL] - · have hTF := hchar a hL - field_simp - have hLHS : - (∑ u : Fin n → Fin 2, normalizedMultiplicityValue table columns u / - (xChallenge + table u)) - = ∑ a : F, tableMultiplicityCount table a • - ((lookupMultiplicityCount columns a : F) / (tableMultiplicityCount table a : F) - / (xChallenge + a)) := by - rw [← Finset.sum_fiberwise (Finset.univ : Finset (Fin n → Fin 2)) table] - refine Finset.sum_congr rfl (fun a _ => ?_) - have h : ∀ u ∈ Finset.univ.filter (fun u => table u = a), - normalizedMultiplicityValue table columns u / (xChallenge + table u) - = (lookupMultiplicityCount columns a : F) / (tableMultiplicityCount table a : F) - / (xChallenge + a) := by - intro u hu - simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hu - change (lookupMultiplicityCount columns (table u) : F) / - (tableMultiplicityCount table (table u) : F) / - (xChallenge + table u) - = (lookupMultiplicityCount columns a : F) / - (tableMultiplicityCount table a : F) / (xChallenge + a) - rw [hu] - rw [Finset.sum_congr rfl h, Finset.sum_const] - rfl - have hRHS : - (∑ j : Fin M, ∑ u : Fin n → Fin 2, (1 : F) / (xChallenge + columns j u)) - = ∑ a : F, lookupMultiplicityCount columns a • ((1 : F) / (xChallenge + a)) := by - rw [← Finset.sum_product', Finset.univ_product_univ, - ← Finset.sum_fiberwise (Finset.univ : Finset (Fin M × (Fin n → Fin 2))) - (fun p => columns p.1 p.2)] - refine Finset.sum_congr rfl (fun a _ => ?_) - have h : ∀ p ∈ Finset.univ.filter - (fun p : Fin M × (Fin n → Fin 2) => columns p.1 p.2 = a), - (1 : F) / (xChallenge + columns p.1 p.2) = (1 : F) / (xChallenge + a) := by - intro p hp - simp only [Finset.mem_filter, Finset.mem_univ, true_and] at hp - simp only [hp] - rw [Finset.sum_congr rfl h, Finset.sum_const] - rfl - rw [hLHS, hRHS] - exact Finset.sum_congr rfl (fun a _ => key a) - /-- If a lookup value appears in a column and every lookup value appears in the table, then the corresponding table multiplicity is nonzero as a field element under the LogUp characteristic bound. -/ @@ -664,8 +634,17 @@ theorem logupOuterClaim_zero [Fintype F] [DecidableEq F] rw [Fin.sum_univ_succ] refine congrArg₂ (· + ·) rfl ?_ exact Finset.sum_congr rfl (fun j _ => hcol j) + -- Paper eq. (15): the honest identity at `xChallenge`, as the evaluated forward of Lemma 5 + -- (`setInclusion_eval_forward`) with the lookup columns as `a` and the table as `b`. + have hmi : (∑ u : Fin n → Fin 2, + normalizedMultiplicityValue table columns u / (xChallenge + table u)) + = ∑ j : Fin M, ∑ u : Fin n → Fin 2, (1 : F) / (xChallenge + columns j u) := by + have h := setInclusion_eval_forward (fun p : Fin M × (Fin n → Fin 2) => columns p.1 p.2) + table xChallenge hchar + rw [Fintype.sum_prod_type] at h + exact h.symm simp_rw [hq, hsum, hterm] - rw [Finset.sum_add_distrib, honest_multiplicity_identity table columns xChallenge hcols hchar, + rw [Finset.sum_add_distrib, hmi, Finset.sum_comm (f := fun u j => (-1 : F) / (xChallenge + columns j u)), ← Finset.sum_add_distrib] refine Finset.sum_eq_zero (fun j _ => ?_) From 23297730d4a735e111945858493b229d95a59570 Mon Sep 17 00:00:00 2001 From: jCabala Date: Sun, 28 Jun 2026 20:15:02 +0200 Subject: [PATCH 11/12] fix: FIxed flexible linter warnigns and updated algebra comments --- ArkLib/ProofSystem/Logup/Algebra.lean | 44 ++++++++----------- .../Logup/Security/Completeness.lean | 25 ++++++----- 2 files changed, 33 insertions(+), 36 deletions(-) diff --git a/ArkLib/ProofSystem/Logup/Algebra.lean b/ArkLib/ProofSystem/Logup/Algebra.lean index de5ea3655d..d625e76d59 100644 --- a/ArkLib/ProofSystem/Logup/Algebra.lean +++ b/ArkLib/ProofSystem/Logup/Algebra.lean @@ -15,28 +15,23 @@ LogUp data and to the verifier's final point check. ## Sections -* **Generic batched polynomial** defines the abstract polynomial shape used by the sumcheck claim: - for each helper group, multiply by the relevant denominators to clear fractions, then batch all - group identities with the equality-polynomial weight and verifier scalars. This section also - proves the generic individual-degree bound, so the later LogUp-specific proof only has to show - that its ingredients are multilinear. -* **Term indexing** gives names to the `M + 1` fractional terms. The table term has input label - `InputIdx.table` and paper index `0`; lookup column `j` has input label `InputIdx.column j` and - paper index `j + 1`. The conversion lemmas here keep later sums over `Fin (M + 1)` aligned with - the table/column oracle labels. -* **Fractional-identity algebra** defines the row-wise LogUp quantities on the Boolean hypercube. - It computes the normalized table multiplicity, forms the logarithmic-derivative terms - `mᵢ(u) / φᵢ(u)` for the table and lookup columns, groups those terms into helper values, clears - denominators inside each group, and combines the cleared identities into the row value +* **Generic batched polynomial** is the abstract polynomial shape used by the sumcheck claim, with + its generic individual-degree bound. The later LogUp proof only has to show its ingredients are + multilinear. +* **Term indexing** names the `M + 1` fractional terms and relates the table/column oracle labels to + paper indices `0, …, M`. +* **Fractional decompositions in `F[X]`** are the protocol-independent, paper-shaped statements over + the polynomial ring (cleared-denominator form): uniqueness of fractional decompositions + (Lemma 4), the collecting bridge, and the set-inclusion criterion (Lemma 5). +* **Fractional-identity algebra** builds the row-wise LogUp quantities on the Boolean hypercube: + normalized multiplicities, logarithmic-derivative terms, helper groups, and the row value `qOnHypercube`. -* **Final-point reconstructions** defines the verifier-side version of the same expressions at - sumcheck's final point `r`. At this point the verifier has scalar oracle answers `m(r)`, `t(r)`, - `fᵢ(r)`, and `hₖ(r)`, not whole row functions, so this section rebuilds the value that should - equal the final sumcheck claim. -* **The LogUp polynomial** builds the actual multivariate polynomial `Q` from the table, column, - multiplicity, and helper polynomials. It instantiates the generic batched polynomial with LogUp's - concrete denominator and numerator polynomials, then proves the `M + 3` individual-degree bound - required by ArkLib's generic sumcheck protocol. +* **Final-point reconstructions** rebuilds the same value from the verifier's scalar oracle answers + at sumcheck's final point `r`. +* **The LogUp polynomial** assembles the concrete polynomial `Q` from the input polynomials and + proves the individual-degree bound required by ArkLib's generic sumcheck protocol. +* **Polynomial evaluation agreement** shows `Q` agrees on Boolean rows with the row-wise expression + built from its inputs' Boolean evaluations. -/ namespace Logup @@ -360,13 +355,14 @@ theorem setInclusion_iff_cleared {ι κ : Type*} [Fintype ι] [Fintype κ] Finset.card_pos.mpr ⟨i, Finset.mem_filter.mpr ⟨Finset.mem_univ i, rfl⟩⟩ exact hcastne _ hpos (lt_of_le_of_lt (hleA (a i)) hNa) hcoef +set_option linter.unusedFintypeInType false in /-- **Lemma 5**, forward direction evaluated at a point `x` (paper eq. (15)). If every value of `a` occurs among the values of `b`, then the logarithmic-derivative identity holds at `x` with the normalized multiplicity `seqMultiplicity a (b j) / seqMultiplicity b (b j)` as witness. This is the evaluated specialization that the completeness proof consumes; the formal `F[X]` statement is `setInclusion_iff_cleared`. The hypothesis `hchar` (that a nonzero `a`-multiplicity forces a nonzero -`b`-multiplicity in `F`) packages set inclusion together with the characteristic bound, exactly as in -the protocol. Lean's `_ / 0 = 0` convention makes pole hypotheses unnecessary. -/ +`b`-multiplicity in `F`) packages set inclusion together with the characteristic bound, exactly as +in the protocol. Lean's `_ / 0 = 0` convention makes pole hypotheses unnecessary. -/ theorem setInclusion_eval_forward {ι κ : Type*} [Fintype ι] [Fintype κ] (a : ι → F) (b : κ → F) (x : F) (hchar : ∀ z : F, seqMultiplicity a z ≠ 0 → (seqMultiplicity b z : F) ≠ 0) : @@ -578,8 +574,6 @@ theorem logupOuterClaim_zero [Fintype F] [DecidableEq F] (hgroups : ∀ g : TermIdx M → F, (∑ k : Fin K, ∑ i ∈ groups k, g i) = ∑ i : TermIdx M, g i) (table : (Fin n → Fin 2) → F) (columns : Fin M → (Fin n → Fin 2) → F) (xChallenge : F) (zChallenge : Fin n → F) (batchingScalars : Fin K → F) - (hcols : ∀ j : Fin M, ∀ u : Fin n → Fin 2, ∃ v : Fin n → Fin 2, - columns j u = table v) (hchar : ∀ a : F, lookupMultiplicityCount columns a ≠ 0 → (tableMultiplicityCount table a : F) ≠ 0) (hpoles : ∀ (i : TermIdx M) (u : Fin n → Fin 2), diff --git a/ArkLib/ProofSystem/Logup/Security/Completeness.lean b/ArkLib/ProofSystem/Logup/Security/Completeness.lean index fcc6c7f7e4..c9628c5556 100644 --- a/ArkLib/ProofSystem/Logup/Security/Completeness.lean +++ b/ArkLib/ProofSystem/Logup/Security/Completeness.lean @@ -203,7 +203,7 @@ private theorem mem_support_liftM_oracleComp {ι τ : Type} {spec : OracleSpec rw [← OracleComp.liftComp_eq_liftM (superSpec := superSpec) oa] at h exact OracleComp.mem_support_of_mem_support_liftComp oa x h -set_option linter.flexible false in + private theorem reduction_run_prover_mem {ι : Type} {oSpec : OracleSpec ι} {StmtIn WitIn StmtOut WitOut : Type} {n : ℕ} {pSpec : ProtocolSpec n} (R : Reduction oSpec StmtIn WitIn StmtOut WitOut pSpec) @@ -211,20 +211,23 @@ private theorem reduction_run_prover_mem {ι : Type} {oSpec : OracleSpec ι} (tr : pSpec.FullTranscript) (out : StmtOut) (witOut : WitOut) (verOut : StmtOut) (h : some ((tr, out, witOut), verOut) ∈ support (R.run stmt wit).run) : (tr, out, witOut) ∈ support (Prover.run stmt wit R.prover) := by - simp [Reduction.run, OptionT.run_bind] at h + simp only [ProtocolSpec.ChallengeIdx, ProtocolSpec.Challenge, Reduction.run, bind_pure_comp, + OptionT.run_bind, OptionT.run_monadLift, monadLift_self, + OracleSpec.ProgrammingPolicy.empty_apply, OptionT.run_map, Option.elimM_map, Option.elim_some, + support_bind, Set.mem_iUnion, exists_prop, Prod.exists] at h rcases h with ⟨a, a_1, b, hp, hv⟩ suffices hEq : (a, a_1, b) = (tr, out, witOut) by simpa [hEq] using hp - simp [Option.elimM] at hv + simp only [Option.elimM, support_bind, Set.mem_iUnion, exists_prop] at hv rcases hv with ⟨i, _hi, hv⟩ cases i with | none => simp at hv | some verCandidate => - simp at hv + simp only [Option.elim_some, support_map, Set.mem_image, Option.map_eq_some_iff, + Prod.mk.injEq, exists_eq_right_right, ↓existsAndEq, true_and] at hv rcases hv with ⟨_, rfl, rfl, rfl⟩ rfl -set_option linter.flexible false in private theorem seqCompose_prover_preserves {ι : Type} {oSpec : OracleSpec ι} (m : ℕ) : ∀ {Stmt : Fin (m + 1) → Type} {O : Type} {rounds : Fin m → ℕ} {pSpec : ∀ i, ProtocolSpec (rounds i)} @@ -242,7 +245,9 @@ private theorem seqCompose_prover_preserves {ι : Type} {oSpec : OracleSpec ι} | zero => intro Stmt O rounds pSpec P proj hP stmt out tr h rw [Prover.seqCompose_zero] at h - simp [Prover.run, Prover.id, Prover.runToRound] at h + simp only [Fin.vsum_zero, Fin.reduceLast, Nat.reduceAdd, ProtocolSpec.ChallengeIdx, + ProtocolSpec.Challenge, Prover.run, Fin.isValue, Prover.id, ProtocolSpec.MessageIdx, + ProtocolSpec.Message, Prover.runToRound, id_eq, Fin.induction_zero] at h cases h rfl | succ m ih => @@ -379,7 +384,7 @@ private theorem Fin.induction_four {motive : Fin 5 → Sort*} {zero : motive 0} set_option maxHeartbeats 1000000 in -- The proof peels the whole four-round outer transcript and reconstructs the handoff relation; -- the generated support terms are large even though the reasoning is deterministic. -set_option linter.flexible false in + /-- Completeness of the outer LogUp phase: the honest outer prover reaches the zero-sum handoff relation, except with the pole-sampling error. -/ theorem logup_outer_completeness [Inhabited F] : @@ -540,7 +545,7 @@ theorem logup_outer_completeness [Inhabited F] : (table := MvPolynomial.toEvalsZeroOne (oStmt .table).1) (columns := fun j => MvPolynomial.toEvalsZeroOne (oStmt (.column j)).1) (xChallenge := xval) (zChallenge := zlam.1) (batchingScalars := zlam.2) - hcols hchar hpoles] + hchar hpoles] apply Finset.sum_congr rfl intro u _ rw [logupQPolynomial_eval_hypercube] @@ -661,8 +666,6 @@ theorem logup_outer_completeness [Inhabited F] : cases hout exact hGood)) - -set_option linter.flexible false in /-- Lens-completeness for the LogUp→Sumcheck lens: `proj` builds the zero-sum instance, and `lift` retains the outer LogUp data together with sumcheck's final valid point claim. -/ instance logupSumcheckLensComplete : @@ -713,7 +716,7 @@ theorem logupSumcheckPhaseCompleteness : (Sumcheck.Spec.oracleReduction_perfectCompleteness F (logupSumcheckDegree M params) (booleanDomain F) n oSpec) -set_option linter.flexible false in + omit [Fintype F] [SampleableType F] in /-- Completeness of the final LogUp point check: once sumcheck's final claim is valid for the retained LogUp polynomial, the verifier's oracle queries reconstruct the same value. -/ From 99ecd6c246a79d5ce743d02bf658f3d15f49623a Mon Sep 17 00:00:00 2001 From: jCabala Date: Sun, 28 Jun 2026 20:41:55 +0200 Subject: [PATCH 12/12] refactor: Reused sumcheck lens and removed more linter problems --- ArkLib/ProofSystem/Logup/Protocol.lean | 18 ++++++++++-------- .../Logup/Security/Completeness.lean | 7 +++++-- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/ArkLib/ProofSystem/Logup/Protocol.lean b/ArkLib/ProofSystem/Logup/Protocol.lean index 02206d0e7d..3df8e07c87 100644 --- a/ArkLib/ProofSystem/Logup/Protocol.lean +++ b/ArkLib/ProofSystem/Logup/Protocol.lean @@ -453,10 +453,10 @@ noncomputable def logupSumcheckPolynomial intro i exact logupQPolynomial_degreeOf (params.group) ((MvPolynomial.mem_restrictDegree_iff_degreeOf_le _ _).mp (oStmt (.input .table)).2) - (fun j => (MvPolynomial.mem_restrictDegree_iff_degreeOf_le _ _).mp (oStmt (.input (.column j))).2) - ((MvPolynomial.mem_restrictDegree_iff_degreeOf_le _ _).mp (oStmt .multiplicity).2) - (fun k => (MvPolynomial.mem_restrictDegree_iff_degreeOf_le _ _).mp (oStmt .helpers k).2) - stmt.xChallenge stmt.zChallenge stmt.batchingScalars i + (fun j => (MvPolynomial.mem_restrictDegree_iff_degreeOf_le _ _).mp + (oStmt (.input (.column j))).2) ((MvPolynomial.mem_restrictDegree_iff_degreeOf_le _ _).mp + (oStmt .multiplicity).2) (fun k => (MvPolynomial.mem_restrictDegree_iff_degreeOf_le _ _).mp + (oStmt .helpers k).2) stmt.xChallenge stmt.zChallenge stmt.batchingScalars i /-- Package the LogUp `Q` polynomial as the single oracle statement expected by Sumcheck. -/ noncomputable def logupSumcheckOracleStmt @@ -585,13 +585,15 @@ variable (F : Type) [Field F] [Fintype F] [DecidableEq F] [SampleableType F] (n variable (params : ProtocolParams M) /-- The embedded LogUp sumcheck phase, obtained by lifting ArkLib's generic Sumcheck reduction -through the LogUp-to-Sumcheck context lens. -/ +through the LogUp-to-Sumcheck context lens with the generic `OracleReduction.liftContext`. Its +prover and verifier are defeq to `sumcheckProver`/`sumcheckVerifier`, which the full-protocol +appends consume directly. -/ noncomputable def sumcheckOracleReduction : OracleReduction oSpec (StmtAfterOuter F n M params) (OStmtAfterOuter F n M params) Unit (StmtAfterSumcheck F n M params) (OStmtAfterOuter F n M params) Unit - ((Sumcheck.Spec.pSpec F (logupSumcheckDegree M params) n)) where - prover := sumcheckProver oSpec F n M params - verifier := sumcheckVerifier oSpec F n M params + ((Sumcheck.Spec.pSpec F (logupSumcheckDegree M params) n)) := + (logupConcreteSumcheckOracleReduction oSpec F n M params).liftContext + (logupSumcheckContextLens F n M params) end Phase2 diff --git a/ArkLib/ProofSystem/Logup/Security/Completeness.lean b/ArkLib/ProofSystem/Logup/Security/Completeness.lean index c9628c5556..564d210859 100644 --- a/ArkLib/ProofSystem/Logup/Security/Completeness.lean +++ b/ArkLib/ProofSystem/Logup/Security/Completeness.lean @@ -550,7 +550,7 @@ theorem logup_outer_completeness [Inhabited F] : intro u _ rw [logupQPolynomial_eval_hypercube] simp [stmtAfter, oStmtAfter, hMultiplicity, hHelpers] - simp [outerVerifier, outerChallengeXIdx, outerChallengeBatchIdx, + simp? [outerVerifier, outerChallengeXIdx, outerChallengeBatchIdx, ProtocolSpec.FullTranscript.challenges, ProtocolSpec.FullTranscript.messages, ProtocolSpec.Transcript.concat, Fin.snoc] constructor @@ -684,7 +684,10 @@ instance logupSumcheckLensComplete : intro outerStmt outerWit innerStmtOut innerWitOut hCompat _ hRelOut have hOStmt : innerStmtOut.2 = logupSumcheckOracleStmt F n M params outerStmt.1 outerStmt.2 := by - simp [Reduction.compatContext] at hCompat + simp only [Reduction.compatContext, Function.comp_apply, ProtocolSpec.ChallengeIdx, + ProtocolSpec.Challenge, OStmtAfterOuter, OStmtIn, MultiplicityMessage, HelperMessages, + Set.mem_image, OptionT.mem_support_iff, Prod.exists, exists_and_right, + exists_eq_right] at hCompat rcases hCompat with ⟨td, vOut, verOStmt, hRun⟩ have hProver := reduction_run_prover_mem ((logupConcreteSumcheckOracleReduction oSpec F n M params).toReduction)