Skip to content

Commit 2098d79

Browse files
feat: soundness of binarybasefold
1 parent 4accb34 commit 2098d79

File tree

25 files changed

+9737
-1330
lines changed

25 files changed

+9737
-1330
lines changed

ArkLib.lean

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ import ArkLib.ProofSystem.Binius.BinaryBasefold.General
161161
import ArkLib.ProofSystem.Binius.BinaryBasefold.Prelude
162162
import ArkLib.ProofSystem.Binius.BinaryBasefold.QueryPhase
163163
import ArkLib.ProofSystem.Binius.BinaryBasefold.ReductionLogic
164+
import ArkLib.ProofSystem.Binius.BinaryBasefold.Soundness
164165
import ArkLib.ProofSystem.Binius.BinaryBasefold.Spec
165166
import ArkLib.ProofSystem.Binius.BinaryBasefold.Steps
166167
import ArkLib.ProofSystem.Binius.FRIBinius.CoreInteractionPhase
@@ -213,6 +214,7 @@ import ArkLib.ToMathlib.Data.IndexedBinaryTree.Equiv
213214
import ArkLib.ToMathlib.Data.IndexedBinaryTree.Lemmas
214215
import ArkLib.ToMathlib.Finset.Basic
215216
import ArkLib.ToMathlib.Finsupp.Fin
217+
import ArkLib.ToMathlib.MvPolynomial.Conversions
216218
import ArkLib.ToMathlib.MvPolynomial.Equiv
217219
import ArkLib.ToMathlib.NumberTheory.PrattCertificate
218220
import ArkLib.ToMathlib.UInt.Equiv

ArkLib/Data/Misc/Basic.lean

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ def subtypeSumComplEquiv {α : Type*} {p : α → Prop} [DecidablePred p] :
2828

2929
lemma fun_eta_expansion {α β : Type*} (f : α → β) : f = (fun x => f x) := rfl
3030

31+
lemma fun_eta_expansion_apply {α β : Type*} (f : α → β) (x : α) : (f x) = (fun x => f x) x := rfl
32+
3133
/-- Casting a function equals the function that casts its argument. -/
3234
lemma cast_fun_eq_fun_cast_arg.{u, v} {A B : Type u} {C : Type v} (h : A = B) (f : A → C) :
3335
cast (congrArg (· → C) h) f = fun x => f (cast h.symm x) := by

ArkLib/Data/Nat/Bitwise.lean

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ lemma ENNReal.coe_le_of_NNRat {a b : ℚ≥0} : ((a : ENNReal)) ≤ (b) ↔ a
8080
lemma ENNReal.coe_NNRat_coe_NNReal (x : ℚ≥0) : (x : ENNReal) = ((x : ℝ≥0) : ENNReal) := by rfl
8181
-- We can use `NNRat.cast_div` or so after `ENNReal.coe_NNRat_coe_NNReal`
8282

83+
lemma ENNReal.coe_div_of_NNReal {a b : NNReal} :
84+
((a : ENNReal) / (b : ENNReal)) = (((a / b) : NNReal): ENNReal) := by
85+
sorry
86+
8387
lemma ENNReal.coe_div_of_NNRat {a b : ℚ≥0} (hb : b ≠ 0) :
8488
((a : ENNReal) / (b : ENNReal)) = (((a / b) : ℚ≥0) : ENNReal) := by
8589
rw [ENNReal.coe_NNRat_coe_NNReal, ENNReal.coe_NNRat_coe_NNReal]

ArkLib/Data/Probability/Instances.lean

Lines changed: 237 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ import ArkLib.Data.Probability.Notation
99
import ArkLib.Data.Fin.BigOperators
1010
import ArkLib.Data.Nat.Bitwise
1111
import Mathlib.Algebra.MvPolynomial.SchwartzZippel
12-
12+
import ArkLib.Data.MvPolynomial.Notation
13+
import ArkLib.ToMathlib.MvPolynomial.Equiv
14+
import VCVio.EvalDist.Basic
1315
open ProbabilityTheory Filter
1416
open NNReal Finset Function
15-
open scoped BigOperators ProbabilityTheory
17+
open scoped BigOperators ProbabilityTheory Polynomial
1618
open Real
1719

1820
section
@@ -373,6 +375,26 @@ theorem Pr_le_Pr_of_implies {α : Type} (D : PMF α)
373375
-- 5. Prove the factor `D r` is non-negative
374376
· exact zero_le (D r) -- Probabilities are always non-negative
375377

378+
alias prob_mono := Pr_le_Pr_of_implies
379+
380+
/-- **Union bound**: Pr[A ∨ B] ≤ Pr[A] + Pr[B]. -/
381+
theorem Pr_or_le {α : Type} (D : PMF α)
382+
(f g : α → Prop) [DecidablePred f] [DecidablePred g] [DecidablePred (fun r => f r ∨ g r)] :
383+
Pr_{ let r ← D }[ f r ∨ g r ] ≤ Pr_{ let r ← D }[ f r ] + Pr_{ let r ← D }[ g r ] := by
384+
rw [prob_tsum_form_singleton D (fun r => f r ∨ g r),
385+
prob_tsum_form_singleton D f, prob_tsum_form_singleton D g]
386+
trans ∑' r, (D r * (if f r then 1 else 0) + D r * (if g r then 1 else 0))
387+
· apply ENNReal.tsum_le_tsum
388+
intro r
389+
by_cases hf : f r
390+
· by_cases hg : g r
391+
· simp only [hf, hg, or_true, ↓reduceIte, mul_one]; exact le_add_of_nonneg_right (zero_le (D r))
392+
· simp only [hf, hg, or_true, true_or, ↓reduceIte, mul_one, mul_zero, add_zero]; exact le_refl (D r)
393+
· by_cases hg : g r
394+
· simp only [hf, hg, true_or, or_true, ↓reduceIte, mul_one, mul_zero, zero_add]; exact le_refl (D r)
395+
· simp only [hf, hg, false_or, ↓reduceIte, mul_zero, zero_add]; exact le_refl 0
396+
· rw [ENNReal.tsum_add];
397+
376398
theorem Pr_multi_let_equiv_single_let {α β : Type}
377399
(D₁ : PMF α) (D₂ : PMF β) -- Assuming D₂ is independent for simplicity
378400
(P : α → β → Prop) [∀ x, DecidablePred (P x)] :
@@ -454,6 +476,98 @@ lemma Pr_congr {α : Type} {D : PMF α} {P Q : α → Prop}
454476
congr 2; funext x;
455477
congr 1; exact propext (h x)
456478

479+
section ProbabilitySplitting
480+
variable {A : Type} [Fintype A] [Nonempty A]
481+
482+
/-- Helper: Probability over functions can be split into iterated product.
483+
For uniform sampling over `Fin n → A`, if the predicate factors as a conjunction
484+
over each index, the probability equals the product of individual probabilities.
485+
486+
This is the key lemma for showing that independent repetitions multiply their error rates. -/
487+
theorem prob_pow_of_forall_finFun
488+
(n : ℕ) (P : A → Prop) [DecidablePred P]
489+
[DecidablePred (fun (f : Fin n → A) => ∀ i, P (f i))] :
490+
Pr_{ let f ← $ᵖ (Fin n → A) }[ ∀ i, P (f i) ] =
491+
(Pr_{ let a ← $ᵖ A }[ P a ])^n := by
492+
induction n with
493+
| zero =>
494+
simp only [IsEmpty.forall_iff, PMF.monad_pure_eq_pure, PMF.monad_bind_eq_bind, PMF.bind_const,
495+
PMF.pure_apply, ↓reduceIte, PMF.bind_apply, PMF.uniformOfFintype_apply, eq_iff_iff, true_iff,
496+
mul_ite, mul_one, mul_zero, pow_zero]
497+
| succ n ih =>
498+
-- Shorter equivalence proof
499+
have h_eqv (f : Fin (n + 1) → A) : (∀ i, P (f i)) ↔ P (f (Fin.last n)) ∧ ∀ (i : Fin n), P (f i.castSucc) := by
500+
constructor
501+
· intro h; exact ⟨h _, fun i => h _⟩
502+
· rintro ⟨h_last, h_init⟩ ⟨i, hi⟩
503+
by_cases h : i < n
504+
· exact h_init ⟨i, h⟩
505+
· have : i = n := by omega
506+
simp only [this]
507+
exact h_last
508+
rw [Pr_congr (h := h_eqv)]
509+
-- Chain the splitting and independence results
510+
calc Pr_{ let f ← $ᵖ (Fin (n + 1) → A) }[ P (f (Fin.last n)) ∧ ∀ (i : Fin n), P (f i.castSucc) ]
511+
_ = Pr_{ let a ← $ᵖ A; let f_init ← $ᵖ (Fin n → A) }[ P a ∧ ∀ (i : Fin n), P (f_init i) ] := by
512+
have h := prob_split_last_uniform_sampling_of_finFun (ϑ := n)
513+
(P := fun a (f_init : Fin n → A) => P a ∧ ∀ (i : Fin n), P (f_init i))
514+
simpa using h
515+
_ = Pr_{ let a ← $ᵖ A }[ P a ] * Pr_{ let f_init ← $ᵖ (Fin n → A) }[ ∀ (i : Fin n), P (f_init i) ] := by
516+
-- Convert sequential bind to single uniform over product
517+
have h_prod : Pr_{ let a ← $ᵖ A; let f_init ← $ᵖ (Fin n → A) }[ P a ∧ ∀ (i : Fin n), P (f_init i) ] =
518+
Pr_{ let p ← $ᵖ (A × (Fin n → A)) }[ P p.1 ∧ ∀ (i : Fin n), P (p.2 i) ] := by
519+
rw [prob_split_uniform_sampling_of_prod]
520+
rw [h_prod]
521+
-- Use counting formula for product and components
522+
rw [prob_uniform_eq_card_filter_div_card, prob_uniform_eq_card_filter_div_card,
523+
prob_uniform_eq_card_filter_div_card]
524+
simp only [Fintype.card_prod, ENNReal.div_eq_inv_mul]
525+
-- Filter cardinality multiplies
526+
have h_filter :
527+
(Finset.filter (fun (p : A × (Fin n → A)) => P p.1 ∧ ∀ (i : Fin n), P (p.2 i)) Finset.univ).card =
528+
(Finset.filter (fun (a : A) => P a) Finset.univ).card *
529+
(Finset.filter (fun (f : Fin n → A) => ∀ (i : Fin n), P (f i)) Finset.univ).card := by
530+
have : Finset.filter (fun (p : A × (Fin n → A)) => P p.1 ∧ ∀ (i : Fin n), P (p.2 i)) Finset.univ =
531+
(Finset.filter (fun (a : A) => P a) Finset.univ) ×ˢ
532+
(Finset.filter (fun (f : Fin n → A) => ∀ (i : Fin n), P (f i)) Finset.univ) := by
533+
ext ⟨a, f⟩; simp
534+
rw [this, Finset.card_product]
535+
rw [h_filter]
536+
simp only [Fintype.card_pi, Finset.prod_const, Finset.card_univ, Fintype.card_fin,
537+
Nat.cast_mul, Nat.cast_pow, ENNReal.coe_mul, ENNReal.coe_natCast, ENNReal.coe_pow]
538+
-- (↑(card A))^n = ↑((card A)^n) so lemma pattern (↑a * ↑b)⁻¹ matches
539+
rw [← Nat.cast_pow]
540+
rw [← ENNReal.mul_inv_rev_ENNReal (ha := Fintype.card_ne_zero)
541+
(hb := ne_of_gt (Nat.pow_pos Fintype.card_pos))]
542+
have h_eq : (Finset.filter (fun a => P a) Finset.univ) = Finset.filter P Finset.univ :=
543+
Finset.filter_congr (fun a _ => by rfl)
544+
rw [← h_eq]
545+
conv_lhs =>
546+
rw [←mul_assoc];
547+
rw [mul_assoc (c := ((Finset.filter (fun a => P a) Finset.univ).card : ENNReal))]
548+
rw [mul_comm (b := ((Finset.filter (fun a => P a) Finset.univ).card : ENNReal))]
549+
rw [←mul_assoc]
550+
rw [mul_assoc (a := ((Fintype.card A):ENNReal)⁻¹ * (Finset.filter (fun a => P a) Finset.univ).card)]
551+
_ = (Pr_{ let a ← $ᵖ A }[ P a ]) ^ (n + 1) := by
552+
rw [ih, pow_succ', mul_comm]
553+
554+
/-- Specialization: Probability bound for failing all proximity checks.
555+
When each repetition independently bounds bad events by ε, running n repetitions
556+
has cumulative bound ε^n (product rule for independent events). -/
557+
theorem prob_pow_bound_of_forall
558+
(n : ℕ) (P : A → Prop) [DecidablePred P]
559+
[DecidablePred (fun (f : Fin n → A) => ∀ i, P (f i))]
560+
(ε : ENNReal) (h_bound : Pr_{ let a ← $ᵖ A}[P a] ≤ ε) :
561+
Pr_{ let f ← $ᵖ (Fin n → A) }[ ∀ i, P (f i) ] ≤ ε^n := by
562+
calc Pr_{ let f ← $ᵖ (Fin n → A) }[ ∀ i, P (f i) ]
563+
= (Pr_{ let a ← $ᵖ A }[ P a ])^n := prob_pow_of_forall_finFun n P
564+
_ ≤ ε^n := by
565+
-- Use the fact that x ≤ y implies x^n ≤ y^n for ENNReal
566+
apply pow_le_pow_left'
567+
· exact h_bound
568+
569+
end ProbabilitySplitting
570+
457571
/-- **Schwartz-Zippel Lemma** (Probability Form):
458572
For a non-zero multivariate polynomial `P` of total degree at most `d` over a finite field `L`,
459573
the probability that `P(r)` evaluates to 0 for a uniformly random `r` is at most `d / |L|`. -/
@@ -493,4 +607,125 @@ lemma prob_schwartz_zippel_mv_polynomial {R : Type} [CommRing R] [IsDomain R] [F
493607
rw [Nat.cast_pow] at sz_bound_ENNReal
494608
exact sz_bound_ENNReal
495609

610+
/-- **Schwartz-Zippel for univariate (Fin 1) polynomials with arbitrary degree bound**.
611+
For a non-zero `P : MvPolynomial (Fin 1) R` with `P.totalDegree ≤ d`, the probability that
612+
`P(r)` is 0 for uniform `r : Fin 1 → R` is at most `d / |R|`. -/
613+
lemma prob_schwartz_zippel_univariate_deg {R : Type} [CommRing R] [IsDomain R] [Fintype R]
614+
[DecidableEq R] (d : ℕ) (P : MvPolynomial (Fin 1) R) (h_nonzero : P ≠ 0)
615+
(h_deg : P.totalDegree ≤ d) :
616+
Pr_{ let r ←$ᵖ (Fin 1 → R) }[ MvPolynomial.eval r P = 0 ] ≤
617+
(d : ℝ≥0) / (Fintype.card R : ℝ≥0) := by
618+
rw [prob_uniform_eq_card_filter_div_card]
619+
push_cast
620+
have sz_bound := MvPolynomial.schwartz_zippel_totalDegree (R := R) (n := 1)
621+
(p := P) (hp := h_nonzero) (S := Finset.univ)
622+
simp only [Fintype.piFinset_univ, card_univ] at sz_bound
623+
have sz_bound_le_d_div_card_R : ((#{f | (MvPolynomial.eval f) P = 0}) : ℚ≥0)
624+
/ ((Fintype.card R ^ 1)) ≤ (d : ℚ≥0) / ((#(Finset.univ : Finset R)) : ℚ≥0) := by
625+
calc
626+
_ ≤ (P.totalDegree : ℚ≥0) / ((#(Finset.univ : Finset R)) : ℚ≥0) := sz_bound
627+
_ ≤ (d : ℚ≥0) / ((#(Finset.univ : Finset R)) : ℚ≥0) := by
628+
simp only [card_univ]
629+
apply div_le_of_le_mul₀ (hb := by simp only [zero_le]) (hc := by simp only [zero_le])
630+
rw [div_mul_cancel₀ (h := by simp only [ne_eq, Nat.cast_eq_zero, Fintype.card_ne_zero,
631+
not_false_eq_true])]
632+
exact Nat.cast_le.mpr h_deg
633+
have sz_bound_le_d_div_card_R' : ((#{f | (MvPolynomial.eval f) P = 0}) : ℚ≥0)
634+
/ (Fintype.card R : ℚ≥0) ≤ (d : ℚ≥0) / (Fintype.card R : ℚ≥0) := by
635+
rw [pow_one, card_univ] at sz_bound_le_d_div_card_R
636+
exact sz_bound_le_d_div_card_R
637+
have sz_bound_ENNReal : ((#{f | (MvPolynomial.eval f) P = 0}) : ENNReal)
638+
/ (Fintype.card R : ENNReal) ≤ (d : ENNReal) / (Fintype.card R : ENNReal) := by
639+
simp_rw [ENNReal.coe_Nat_coe_NNRat]
640+
conv_lhs => rw [ENNReal.coe_div_of_NNRat (hb := by
641+
simp only [pow_one, ne_eq, Nat.cast_eq_zero, Fintype.card_ne_zero, not_false_eq_true])]
642+
conv_rhs => rw [ENNReal.coe_div_of_NNRat (hb := by simp only [ne_eq, Nat.cast_eq_zero,
643+
Fintype.card_ne_zero, not_false_eq_true])]
644+
rw [ENNReal.coe_le_of_NNRat]
645+
exact sz_bound_le_d_div_card_R'
646+
simp only [Fintype.card_pi, prod_const, card_univ, Fintype.card_fin, pow_one, ge_iff_le]
647+
exact sz_bound_ENNReal
648+
649+
/-- **Schwartz-Zippel for degree-1 univariate polynomials**.
650+
For two distinct degree-1 univariate polynomials over a commutative ring, the probability
651+
that they agree at a random point is at most `1 / |R|`. -/
652+
lemma prob_poly_agreement_degree_one {R : Type} [CommRing R] [IsDomain R] [Fintype R]
653+
[DecidableEq R]
654+
(p q : R⦃≤ 1⦄[X])
655+
(h_ne : p ≠ q) :
656+
Pr_{ let r ←$ᵖ R }[ p.val.eval r = q.val.eval r ] ≤
657+
(1 : ℝ≥0) / (Fintype.card R : ℝ≥0) := by
658+
-- 1. Setup the multivariate polynomial P = p - q
659+
let P := (p.val - q.val).toMvPolynomial (σ := Fin 1) 0
660+
-- 2. Prove P is non-zero (immediate from p ≠ q)
661+
have h_nz : P ≠ 0 := by
662+
rw [Polynomial.toMvPolynomial_ne_zero_iff, sub_ne_zero]
663+
exact fun h => h_ne (Subtype.eq h)
664+
have h_p_deg : p.val.degree ≤ 1 :=
665+
Polynomial.mem_degreeLE (f := p.val) (n := 1).mp (by simp only [SetLike.coe_mem])
666+
have h_q_deg : q.val.degree ≤ 1 :=
667+
Polynomial.mem_degreeLE (f := q.val) (n := 1).mp (by simp only [SetLike.coe_mem])
668+
-- 3. Prove totalDegree P ≤ 1
669+
have h_deg : P.totalDegree ≤ 1 := by
670+
apply (Polynomial.toMvPolynomial_totalDegree_le _ _).trans
671+
apply (Polynomial.natDegree_sub_le _ _).trans
672+
-- Use the fact that p, q are in R{≤1}[X] directly
673+
simp only [max_le_iff]
674+
constructor <;> apply Polynomial.natDegree_le_of_degree_le <;>
675+
first | exact h_p_deg | exact h_q_deg
676+
-- 4. Apply Schwartz-Zippel
677+
calc Pr_{ let r ←$ᵖ R }[ p.val.eval r = q.val.eval r ]
678+
_ = Pr_{ let r ←$ᵖ R }[ (p.val - q.val).eval r = 0 ] := by
679+
apply Pr_congr; simp [sub_eq_zero]
680+
_ = Pr_{ let r ←$ᵖ R }[ MvPolynomial.eval (fun _ ↦ r) P = 0 ] := by
681+
apply Pr_congr; intro; simp [P, MvPolynomial.eval_toMvPolynomial]
682+
_ = Pr_{ let f ←$ᵖ (Fin 1 → R) }[ MvPolynomial.eval f P = 0 ] := by
683+
-- Move to function space (Fin 1 → R)
684+
rw [← prob_uniform_singleton_finFun_eq]
685+
congr; funext f
686+
-- Collapse: eval f (toMv p) = eval (f 0) p
687+
simp [P, MvPolynomial.eval_toMvPolynomial]
688+
_ ≤ _ := by
689+
have h := prob_schwartz_zippel_mv_polynomial P h_nz h_deg
690+
simp only [Nat.cast_one] at h
691+
exact h
692+
693+
alias prob_schwartz_zippel_univariate_poly := prob_poly_agreement_degree_one
694+
695+
/-- **Schwartz-Zippel for degree-2 univariate polynomials**.
696+
For two distinct degree-2 univariate polynomials over a commutative ring, the probability
697+
that they agree at a random point is at most `2 / |R|`. -/
698+
lemma prob_poly_agreement_degree_two {R : Type} [CommRing R] [IsDomain R] [Fintype R]
699+
[DecidableEq R]
700+
(p q : R⦃≤ 2⦄[X])
701+
(h_ne : p ≠ q) :
702+
Pr_{ let r ←$ᵖ R }[ p.val.eval r = q.val.eval r ] ≤
703+
(2 : ℝ≥0) / (Fintype.card R : ℝ≥0) := by
704+
let P := (p.val - q.val).toMvPolynomial (σ := Fin 1) 0
705+
have h_nz : P ≠ 0 := by
706+
rw [Polynomial.toMvPolynomial_ne_zero_iff, sub_ne_zero]
707+
exact fun h => h_ne (Subtype.eq h)
708+
have h_p_deg : p.val.degree ≤ 2 :=
709+
Polynomial.mem_degreeLE (f := p.val) (n := 2).mp (by simp only [SetLike.coe_mem])
710+
have h_q_deg : q.val.degree ≤ 2 :=
711+
Polynomial.mem_degreeLE (f := q.val) (n := 2).mp (by simp only [SetLike.coe_mem])
712+
have h_deg : P.totalDegree ≤ 2 := by
713+
apply (Polynomial.toMvPolynomial_totalDegree_le _ _).trans
714+
apply (Polynomial.natDegree_sub_le _ _).trans
715+
simp only [max_le_iff]
716+
constructor <;> apply Polynomial.natDegree_le_of_degree_le <;>
717+
first | exact h_p_deg | exact h_q_deg
718+
calc Pr_{ let r ←$ᵖ R }[ p.val.eval r = q.val.eval r ]
719+
_ = Pr_{ let r ←$ᵖ R }[ (p.val - q.val).eval r = 0 ] := by apply Pr_congr; simp [sub_eq_zero]
720+
_ = Pr_{ let r ←$ᵖ R }[ MvPolynomial.eval (fun _ ↦ r) P = 0 ] := by
721+
apply Pr_congr; intro; simp [P, MvPolynomial.eval_toMvPolynomial]
722+
_ = Pr_{ let f ←$ᵖ (Fin 1 → R) }[ MvPolynomial.eval f P = 0 ] := by
723+
rw [← prob_uniform_singleton_finFun_eq]
724+
congr; funext f
725+
simp [P, MvPolynomial.eval_toMvPolynomial]
726+
_ ≤ _ := by
727+
-- The lemma returns (P.totalDegree / card R), which is ≤ (2 / card R)
728+
have h := prob_schwartz_zippel_univariate_deg 2 P h_nz h_deg
729+
exact h
730+
496731
end ProbabilityTools

0 commit comments

Comments
 (0)