From 4f8ef2e17820ca5a1ce75c505fcf3e7859ee33e5 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 18 Jun 2021 11:10:33 +0200 Subject: [PATCH 1/7] Optimize some polynomial operations --- src/field/lagrange.rs | 41 ++++++++++++++++++- src/polynomial/commitment.rs | 77 +++++++++++++++++++----------------- src/polynomial/division.rs | 67 ++++++++++++++++++++++++++++++- src/polynomial/polynomial.rs | 22 ++++++++++- 4 files changed, 168 insertions(+), 39 deletions(-) diff --git a/src/field/lagrange.rs b/src/field/lagrange.rs index 8911feb0..97d6fb27 100644 --- a/src/field/lagrange.rs +++ b/src/field/lagrange.rs @@ -65,11 +65,35 @@ pub fn barycentric_weights(points: &[(F, F)]) -> Vec { ) } +/// Interpolate the linear polynomial passing through `points` on `x`. +pub fn interpolate2(points: [(F, F); 2], x: F) -> F { + // a0 -> a1 + // b0 -> b1 + // x -> a1 + (x-a0)*(b1-a1)/(b0-a0) + let (a0, a1) = points[0]; + let (b0, b1) = points[1]; + assert_ne!(a0, b0); + a1 + (x - a0) * (b1 - a1) / (b0 - a0) +} + +/// Returns the linear polynomial passing through `points`. +pub fn interpolant2(points: [(F, F); 2]) -> PolynomialCoeffs { + // a0 -> a1 + // b0 -> b1 + // x -> a1 + (x-a0)*(b1-a1)/(b0-a0) + let (a0, a1) = points[0]; + let (b0, b1) = points[1]; + assert_ne!(a0, b0); + let mult = (b1 - a1) / (b0 - a0); + vec![a1 - a0 * mult, mult].into() +} + #[cfg(test)] mod tests { + use super::*; use crate::field::crandall_field::CrandallField; + use crate::field::extension_field::quartic::QuarticCrandallField; use crate::field::field::Field; - use crate::field::lagrange::interpolant; use crate::polynomial::polynomial::PolynomialCoeffs; #[test] @@ -120,4 +144,19 @@ mod tests { fn eval_naive(coeffs: &PolynomialCoeffs, domain: &[F]) -> Vec<(F, F)> { domain.iter().map(|&x| (x, coeffs.eval(x))).collect() } + + #[test] + fn test_interpolant2() { + type F = QuarticCrandallField; + let points = [(F::rand(), F::rand()), (F::rand(), F::rand())]; + let x = F::rand(); + + let intepol0 = interpolant(&points); + let intepol1 = interpolant2(points); + assert_eq!(intepol0.trimmed(), intepol1.trimmed()); + + let ev0 = interpolate(&points, x, &barycentric_weights(&points)); + let ev1 = interpolate2(points, x); + assert_eq!(ev0, ev1); + } } diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index caa61553..45251e77 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -4,7 +4,7 @@ use rayon::prelude::*; use crate::field::extension_field::Extendable; use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; -use crate::field::lagrange::interpolant; +use crate::field::lagrange::{interpolant, interpolant2}; use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; @@ -122,15 +122,10 @@ impl ListPolynomialCommitment { .map(|p| p.to_extension()); let single_os = [&os.constants, &os.plonk_s_sigmas, &os.quotient_polys]; let single_evals = single_os.iter().flat_map(|v| v.iter()); - let single_composition_poly = reduce_polys_with_iter(single_polys, alpha_powers.clone()); - let single_composition_eval = reduce_with_iter(single_evals, &mut alpha_powers); + let single_composition_poly = reduce_polys_with_iter(single_polys, &mut alpha_powers); - let single_quotient = Self::compute_quotient( - &[zeta], - &[single_composition_eval], - &single_composition_poly, - ); - final_poly = &final_poly + &single_quotient; + let single_quotient = Self::compute_quotient1(zeta, single_composition_poly); + final_poly += single_quotient; // Zs polynomials are opened at `zeta` and `g*zeta`. let zs_polys = commitments[3].polynomials.iter().map(|p| p.to_extension()); @@ -140,12 +135,9 @@ impl ListPolynomialCommitment { reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), ]; - let zs_quotient = Self::compute_quotient( - &[zeta, g * zeta], - &zs_composition_evals, - &zs_composition_poly, - ); - final_poly = &final_poly + &zs_quotient; + let zs_quotient = + Self::compute_quotient2([zeta, g * zeta], zs_composition_evals, zs_composition_poly); + final_poly += zs_quotient; // When working in an extension field, need to check that wires are in the base field. // Check this by opening the wires polynomials at `zeta` and `zeta.frobenius()` and using the fact that @@ -158,12 +150,12 @@ impl ListPolynomialCommitment { reduce_with_iter(&wire_evals_frob, alpha_powers), ]; - let wires_quotient = Self::compute_quotient( - &[zeta, zeta.frobenius()], - &wire_composition_evals, - &wire_composition_poly, + let wires_quotient = Self::compute_quotient2( + [zeta, zeta.frobenius()], + wire_composition_evals, + wire_composition_poly, ); - final_poly = &final_poly + &wires_quotient; + final_poly += wires_quotient; let lde_final_poly = final_poly.lde(config.rate_bits); let lde_final_values = lde_final_poly @@ -192,28 +184,41 @@ impl ListPolynomialCommitment { ) } - /// Given `points=(x_i)`, `evals=(y_i)` and `poly=P` with `P(x_i)=y_i`, computes the polynomial - /// `Q=(P-I)/Z` where `I` interpolates `(x_i, y_i)` and `Z` is the vanishing polynomial on `(x_i)`. - fn compute_quotient( - points: &[F::Extension], - evals: &[F::Extension], - poly: &PolynomialCoeffs, + /// Given `x` and `poly=P(X)`, computes the polynomial `Q=(P-P(x))/(X-x)`. + fn compute_quotient1( + point: F::Extension, + poly: PolynomialCoeffs, ) -> PolynomialCoeffs where F: Extendable, { - let pairs = points - .iter() - .zip(evals) - .map(|(&x, &e)| (x, e)) - .collect::>(); + let (quotient, _ev) = poly.divide_by_linear(point); + quotient.padded(quotient.degree_plus_one().next_power_of_two()) + } + + /// Given `points=(x_i)`, `evals=(y_i)` and `poly=P` with `P(x_i)=y_i`, computes the polynomial + /// `Q=(P-I)/Z` where `I` interpolates `(x_i, y_i)` and `Z` is the vanishing polynomial on `(x_i)`. + fn compute_quotient2( + points: [F::Extension; 2], + evals: [F::Extension; 2], + poly: PolynomialCoeffs, + ) -> PolynomialCoeffs + where + F: Extendable, + { + let pairs = [(points[0], evals[0]), (points[1], evals[1])]; debug_assert!(pairs.iter().all(|&(x, e)| poly.eval(x) == e)); - let interpolant = interpolant(&pairs); - let denominator = points.iter().fold(PolynomialCoeffs::one(), |acc, &x| { - &acc * &PolynomialCoeffs::new(vec![-x, F::Extension::ONE]) - }); - let numerator = poly - &interpolant; + let interpolant = interpolant2(pairs); + let denominator = vec![ + points[0] * points[1], + -points[0] - points[1], + F::Extension::ONE, + ] + .into(); + + let mut numerator = poly; + numerator -= interpolant; let (quotient, rem) = numerator.div_rem(&denominator); debug_assert!(rem.is_zero()); diff --git a/src/polynomial/division.rs b/src/polynomial/division.rs index b74fd00f..db1606af 100644 --- a/src/polynomial/division.rs +++ b/src/polynomial/division.rs @@ -125,8 +125,25 @@ impl PolynomialCoeffs { p } + /// Let `self=p(X)`, this returns `(p(X)-p(z))/(X-z)` and `p(z)`. + /// See https://en.wikipedia.org/wiki/Horner%27s_method + pub(crate) fn divide_by_linear(&self, z: F) -> (PolynomialCoeffs, F) { + let mut bs = self + .coeffs + .iter() + .rev() + .scan(F::ZERO, |acc, &c| { + *acc = *acc * z + c; + Some(*acc) + }) + .collect::>(); + let ev = bs.pop().unwrap_or(F::ZERO); + bs.reverse(); + (Self { coeffs: bs }, ev) + } + /// Computes the inverse of `self` modulo `x^n`. - pub(crate) fn inv_mod_xn(&self, n: usize) -> Self { + pub fn inv_mod_xn(&self, n: usize) -> Self { assert!(self.coeffs[0].is_nonzero(), "Inverse doesn't exist."); let h = if self.len() < n { @@ -166,7 +183,10 @@ impl PolynomialCoeffs { #[cfg(test)] mod tests { + use std::time::Instant; + use crate::field::crandall_field::CrandallField; + use crate::field::extension_field::quartic::QuarticCrandallField; use crate::field::field::Field; use crate::polynomial::polynomial::PolynomialCoeffs; @@ -199,4 +219,49 @@ mod tests { let computed_q = a.divide_by_z_h(4); assert_eq!(computed_q, q); } + + #[test] + #[ignore] + fn test_division_by_linear() { + type F = QuarticCrandallField; + let n = 1_000_000; + let poly = PolynomialCoeffs::new(F::rand_vec(n)); + let z = F::rand(); + let ev = poly.eval(z); + + let timer = Instant::now(); + let (quotient, ev2) = poly.div_rem(&PolynomialCoeffs::new(vec![-z, F::ONE])); + println!("{:.3}s for usual", timer.elapsed().as_secs_f32()); + assert_eq!(ev2.trimmed().coeffs, vec![ev]); + + let timer = Instant::now(); + let (quotient, ev3) = poly.div_rem_long_division(&PolynomialCoeffs::new(vec![-z, F::ONE])); + println!("{:.3}s for long division", timer.elapsed().as_secs_f32()); + assert_eq!(ev3.trimmed().coeffs, vec![ev]); + + let timer = Instant::now(); + let horn = poly.divide_by_linear(z); + println!("{:.3}s for Horner", timer.elapsed().as_secs_f32()); + assert_eq!((quotient, ev), horn); + } + + #[test] + #[ignore] + fn test_division_by_quadratic() { + type F = QuarticCrandallField; + let n = 1_000_000; + let poly = PolynomialCoeffs::new(F::rand_vec(n)); + let quad = PolynomialCoeffs::new(F::rand_vec(2)); + + let timer = Instant::now(); + let (quotient0, rem0) = poly.div_rem(&quad); + println!("{:.3}s for usual", timer.elapsed().as_secs_f32()); + + let timer = Instant::now(); + let (quotient1, rem1) = poly.div_rem_long_division(&quad); + println!("{:.3}s for long division", timer.elapsed().as_secs_f32()); + + assert_eq!(quotient0.trimmed(), quotient1.trimmed()); + assert_eq!(rem0.trimmed(), rem1.trimmed()); + } } diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index 9f605051..aefcc0c6 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -1,6 +1,6 @@ use std::cmp::max; use std::iter::Sum; -use std::ops::{Add, Mul, Sub}; +use std::ops::{Add, AddAssign, Mul, Sub, SubAssign}; use crate::field::extension_field::Extendable; use crate::field::fft::{fft, ifft}; @@ -243,6 +243,26 @@ impl Sub for &PolynomialCoeffs { } } +impl AddAssign for PolynomialCoeffs { + fn add_assign(&mut self, rhs: Self) { + let len = max(self.len(), rhs.len()); + self.coeffs.resize(len, F::ZERO); + for (l, r) in self.coeffs.iter_mut().zip(rhs.coeffs) { + *l += r; + } + } +} + +impl SubAssign for PolynomialCoeffs { + fn sub_assign(&mut self, rhs: Self) { + let len = max(self.len(), rhs.len()); + self.coeffs.resize(len, F::ZERO); + for (l, r) in self.coeffs.iter_mut().zip(rhs.coeffs) { + *l -= r; + } + } +} + impl Mul for &PolynomialCoeffs { type Output = PolynomialCoeffs; From 5a8d951590072207c8fc5420ab4232a03a416558 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 18 Jun 2021 11:16:22 +0200 Subject: [PATCH 2/7] Use `interpolate2` in the FRI verifier --- src/fri/verifier.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index d100797e..486e7174 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -2,7 +2,7 @@ use anyhow::{ensure, Result}; use crate::field::extension_field::{flatten, Extendable, FieldExtension, Frobenius}; use crate::field::field::Field; -use crate::field::lagrange::{barycentric_weights, interpolant, interpolate}; +use crate::field::lagrange::{barycentric_weights, interpolant, interpolate, interpolate2}; use crate::fri::FriConfig; use crate::hash::hash_n_to_1; use crate::merkle_proofs::verify_merkle_proof; @@ -185,14 +185,17 @@ fn fri_combine_initial, const D: usize>( .map(|&e| F::Extension::from_basefield(e)); let zs_composition_eval = reduce_with_iter(zs_evals, alpha_powers.clone()); let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; - let zs_interpol = interpolant(&[ - (zeta, reduce_with_iter(&os.plonk_zs, alpha_powers.clone())), - ( - zeta_right, - reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), - ), - ]); - let zs_numerator = zs_composition_eval - zs_interpol.eval(subgroup_x); + let zs_interpol = interpolate2( + [ + (zeta, reduce_with_iter(&os.plonk_zs, alpha_powers.clone())), + ( + zeta_right, + reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), + ), + ], + subgroup_x, + ); + let zs_numerator = zs_composition_eval - zs_interpol; let zs_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); sum += zs_numerator / zs_denominator; @@ -211,8 +214,8 @@ fn fri_combine_initial, const D: usize>( // and one call at the end of the sum. let alpha_powers_frob = alpha_powers.repeated_frobenius(D - 1); let wire_eval_frob = reduce_with_iter(&os.wires, alpha_powers_frob).frobenius(); - let wire_interpol = interpolant(&[(zeta, wire_eval), (zeta_frob, wire_eval_frob)]); - let wire_numerator = wire_composition_eval - wire_interpol.eval(subgroup_x); + let wire_interpol = interpolate2([(zeta, wire_eval), (zeta_frob, wire_eval_frob)], subgroup_x); + let wire_numerator = wire_composition_eval - wire_interpol; let wire_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); sum += wire_numerator / wire_denominator; From 621c046fe43d23c2f02d10636b8327718d11145d Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 18 Jun 2021 11:17:15 +0200 Subject: [PATCH 3/7] Use long division when dividing by quadratic polynomial --- src/polynomial/commitment.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 45251e77..1b7d611b 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -219,7 +219,7 @@ impl ListPolynomialCommitment { let mut numerator = poly; numerator -= interpolant; - let (quotient, rem) = numerator.div_rem(&denominator); + let (quotient, rem) = numerator.div_rem_long_division(&denominator); debug_assert!(rem.is_zero()); quotient.padded(quotient.degree_plus_one().next_power_of_two()) From 7d4e79f069aa5e043042b1f73bfb6f8d3717867d Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 18 Jun 2021 11:22:38 +0200 Subject: [PATCH 4/7] Clippy --- src/fri/verifier.rs | 2 +- src/polynomial/commitment.rs | 4 +--- src/polynomial/division.rs | 7 +++---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 486e7174..6320505e 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -2,7 +2,7 @@ use anyhow::{ensure, Result}; use crate::field::extension_field::{flatten, Extendable, FieldExtension, Frobenius}; use crate::field::field::Field; -use crate::field::lagrange::{barycentric_weights, interpolant, interpolate, interpolate2}; +use crate::field::lagrange::{barycentric_weights, interpolate, interpolate2}; use crate::fri::FriConfig; use crate::hash::hash_n_to_1; use crate::merkle_proofs::verify_merkle_proof; diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 1b7d611b..ce978d96 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -4,7 +4,7 @@ use rayon::prelude::*; use crate::field::extension_field::Extendable; use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; -use crate::field::lagrange::{interpolant, interpolant2}; +use crate::field::lagrange::interpolant2; use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; @@ -120,8 +120,6 @@ impl ListPolynomialCommitment { .iter() .flat_map(|&i| &commitments[i].polynomials) .map(|p| p.to_extension()); - let single_os = [&os.constants, &os.plonk_s_sigmas, &os.quotient_polys]; - let single_evals = single_os.iter().flat_map(|v| v.iter()); let single_composition_poly = reduce_polys_with_iter(single_polys, &mut alpha_powers); let single_quotient = Self::compute_quotient1(zeta, single_composition_poly); diff --git a/src/polynomial/division.rs b/src/polynomial/division.rs index db1606af..50e1f8a6 100644 --- a/src/polynomial/division.rs +++ b/src/polynomial/division.rs @@ -26,7 +26,7 @@ impl PolynomialCoeffs { .to_vec() .into(); let mut q = rev_q.rev(); - let mut qb = &q * b; + let qb = &q * b; let mut r = self - &qb; q.trim(); r.trim(); @@ -59,8 +59,7 @@ impl PolynomialCoeffs { quotient.coeffs[cur_q_degree] = cur_q_coeff; for (i, &div_coeff) in b.coeffs.iter().enumerate() { - remainder.coeffs[cur_q_degree + i] = - remainder.coeffs[cur_q_degree + i] - (cur_q_coeff * div_coeff); + remainder.coeffs[cur_q_degree + i] -= cur_q_coeff * div_coeff; } remainder.trim(); } @@ -97,7 +96,7 @@ impl PolynomialCoeffs { let denominators = (0..a_eval.len()) .map(|i| { if i != 0 { - root_pow = root_pow * root_n; + root_pow *= root_n; } denominator_g * root_pow - F::ONE }) From a4c86a6b081dfb540579fa16217d78d9d16e6448 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 18 Jun 2021 11:44:06 +0200 Subject: [PATCH 5/7] `lagrange.rs` -> `interpolation.rs` --- src/field/{lagrange.rs => interpolation.rs} | 0 src/field/mod.rs | 2 +- src/fri/verifier.rs | 2 +- src/gadgets/interpolation.rs | 8 +++++--- src/gates/interpolation.rs | 2 +- src/polynomial/commitment.rs | 2 +- 6 files changed, 9 insertions(+), 7 deletions(-) rename src/field/{lagrange.rs => interpolation.rs} (100%) diff --git a/src/field/lagrange.rs b/src/field/interpolation.rs similarity index 100% rename from src/field/lagrange.rs rename to src/field/interpolation.rs diff --git a/src/field/mod.rs b/src/field/mod.rs index 179fb10d..15efe280 100644 --- a/src/field/mod.rs +++ b/src/field/mod.rs @@ -3,7 +3,7 @@ pub mod crandall_field; pub mod extension_field; pub mod fft; pub mod field; -pub(crate) mod lagrange; +pub(crate) mod interpolation; #[cfg(test)] mod field_testing; diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 6320505e..4c8f4d8c 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -2,7 +2,7 @@ use anyhow::{ensure, Result}; use crate::field::extension_field::{flatten, Extendable, FieldExtension, Frobenius}; use crate::field::field::Field; -use crate::field::lagrange::{barycentric_weights, interpolate, interpolate2}; +use crate::field::interpolation::{barycentric_weights, interpolate, interpolate2}; use crate::fri::FriConfig; use crate::hash::hash_n_to_1; use crate::merkle_proofs::verify_merkle_proof; diff --git a/src/gadgets/interpolation.rs b/src/gadgets/interpolation.rs index a94eb582..40b91c1e 100644 --- a/src/gadgets/interpolation.rs +++ b/src/gadgets/interpolation.rs @@ -1,9 +1,10 @@ +use std::marker::PhantomData; + use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::gates::interpolation::InterpolationGate; use crate::target::Target; -use std::marker::PhantomData; impl, const D: usize> CircuitBuilder { /// Interpolate two points. No need for an `InterpolationGate` since the coefficients @@ -56,15 +57,16 @@ impl, const D: usize> CircuitBuilder { #[cfg(test)] mod tests { + use std::convert::TryInto; + use super::*; use crate::circuit_data::CircuitConfig; use crate::field::crandall_field::CrandallField; use crate::field::extension_field::quartic::QuarticCrandallField; use crate::field::extension_field::FieldExtension; use crate::field::field::Field; - use crate::field::lagrange::{interpolant, interpolate}; + use crate::field::interpolation::{interpolant, interpolate}; use crate::witness::PartialWitness; - use std::convert::TryInto; #[test] fn test_interpolate() { diff --git a/src/gates/interpolation.rs b/src/gates/interpolation.rs index ac2ca49f..ccf8d57d 100644 --- a/src/gates/interpolation.rs +++ b/src/gates/interpolation.rs @@ -6,7 +6,7 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::algebra::PolynomialCoeffsAlgebra; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; -use crate::field::lagrange::interpolant; +use crate::field::interpolation::interpolant; use crate::gadgets::polynomial::PolynomialCoeffsExtAlgebraTarget; use crate::gates::gate::{Gate, GateRef}; use crate::generator::{SimpleGenerator, WitnessGenerator}; diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index ce978d96..c67bed2f 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -4,7 +4,7 @@ use rayon::prelude::*; use crate::field::extension_field::Extendable; use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; -use crate::field::lagrange::interpolant2; +use crate::field::interpolation::interpolant2; use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; From 37171505c74a8db76991ead8fef7a7650bc0bd15 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 18 Jun 2021 12:49:40 +0200 Subject: [PATCH 6/7] Remove useless interpolation from `open_plonk` --- src/field/interpolation.rs | 23 +++--------- src/polynomial/commitment.rs | 73 +++++++++++------------------------- 2 files changed, 27 insertions(+), 69 deletions(-) diff --git a/src/field/interpolation.rs b/src/field/interpolation.rs index 97d6fb27..968eeca2 100644 --- a/src/field/interpolation.rs +++ b/src/field/interpolation.rs @@ -76,18 +76,6 @@ pub fn interpolate2(points: [(F, F); 2], x: F) -> F { a1 + (x - a0) * (b1 - a1) / (b0 - a0) } -/// Returns the linear polynomial passing through `points`. -pub fn interpolant2(points: [(F, F); 2]) -> PolynomialCoeffs { - // a0 -> a1 - // b0 -> b1 - // x -> a1 + (x-a0)*(b1-a1)/(b0-a0) - let (a0, a1) = points[0]; - let (b0, b1) = points[1]; - assert_ne!(a0, b0); - let mult = (b1 - a1) / (b0 - a0); - vec![a1 - a0 * mult, mult].into() -} - #[cfg(test)] mod tests { use super::*; @@ -146,17 +134,16 @@ mod tests { } #[test] - fn test_interpolant2() { + fn test_interpolate2() { type F = QuarticCrandallField; let points = [(F::rand(), F::rand()), (F::rand(), F::rand())]; let x = F::rand(); - let intepol0 = interpolant(&points); - let intepol1 = interpolant2(points); - assert_eq!(intepol0.trimmed(), intepol1.trimmed()); + let ev0 = interpolant(&points).eval(x); + let ev1 = interpolate(&points, x, &barycentric_weights(&points)); + let ev2 = interpolate2(points, x); - let ev0 = interpolate(&points, x, &barycentric_weights(&points)); - let ev1 = interpolate2(points, x); assert_eq!(ev0, ev1); + assert_eq!(ev0, ev2); } } diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index c67bed2f..1e4ba1b4 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -4,11 +4,10 @@ use rayon::prelude::*; use crate::field::extension_field::Extendable; use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; -use crate::field::interpolation::interpolant2; use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; -use crate::plonk_common::{reduce_polys_with_iter, reduce_with_iter}; +use crate::plonk_common::reduce_polys_with_iter; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; @@ -122,37 +121,24 @@ impl ListPolynomialCommitment { .map(|p| p.to_extension()); let single_composition_poly = reduce_polys_with_iter(single_polys, &mut alpha_powers); - let single_quotient = Self::compute_quotient1(zeta, single_composition_poly); + let single_quotient = Self::compute_quotient([zeta], single_composition_poly); final_poly += single_quotient; // Zs polynomials are opened at `zeta` and `g*zeta`. let zs_polys = commitments[3].polynomials.iter().map(|p| p.to_extension()); - let zs_composition_poly = reduce_polys_with_iter(zs_polys, alpha_powers.clone()); - let zs_composition_evals = [ - reduce_with_iter(&os.plonk_zs, alpha_powers.clone()), - reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), - ]; + let zs_composition_poly = reduce_polys_with_iter(zs_polys, &mut alpha_powers); - let zs_quotient = - Self::compute_quotient2([zeta, g * zeta], zs_composition_evals, zs_composition_poly); + let zs_quotient = Self::compute_quotient([zeta, g * zeta], zs_composition_poly); final_poly += zs_quotient; // When working in an extension field, need to check that wires are in the base field. // Check this by opening the wires polynomials at `zeta` and `zeta.frobenius()` and using the fact that // a polynomial `f` is over the base field iff `f(z).frobenius()=f(z.frobenius())` with high probability. let wire_polys = commitments[2].polynomials.iter().map(|p| p.to_extension()); - let wire_composition_poly = reduce_polys_with_iter(wire_polys, alpha_powers.clone()); - let wire_evals_frob = os.wires.iter().map(|e| e.frobenius()).collect::>(); - let wire_composition_evals = [ - reduce_with_iter(&os.wires, alpha_powers.clone()), - reduce_with_iter(&wire_evals_frob, alpha_powers), - ]; + let wire_composition_poly = reduce_polys_with_iter(wire_polys, &mut alpha_powers); - let wires_quotient = Self::compute_quotient2( - [zeta, zeta.frobenius()], - wire_composition_evals, - wire_composition_poly, - ); + let wires_quotient = + Self::compute_quotient([zeta, zeta.frobenius()], wire_composition_poly); final_poly += wires_quotient; let lde_final_poly = final_poly.lde(config.rate_bits); @@ -182,43 +168,28 @@ impl ListPolynomialCommitment { ) } - /// Given `x` and `poly=P(X)`, computes the polynomial `Q=(P-P(x))/(X-x)`. - fn compute_quotient1( - point: F::Extension, - poly: PolynomialCoeffs, - ) -> PolynomialCoeffs - where - F: Extendable, - { - let (quotient, _ev) = poly.divide_by_linear(point); - quotient.padded(quotient.degree_plus_one().next_power_of_two()) - } - /// Given `points=(x_i)`, `evals=(y_i)` and `poly=P` with `P(x_i)=y_i`, computes the polynomial /// `Q=(P-I)/Z` where `I` interpolates `(x_i, y_i)` and `Z` is the vanishing polynomial on `(x_i)`. - fn compute_quotient2( - points: [F::Extension; 2], - evals: [F::Extension; 2], + fn compute_quotient( + points: [F::Extension; N], poly: PolynomialCoeffs, ) -> PolynomialCoeffs where F: Extendable, { - let pairs = [(points[0], evals[0]), (points[1], evals[1])]; - debug_assert!(pairs.iter().all(|&(x, e)| poly.eval(x) == e)); - - let interpolant = interpolant2(pairs); - let denominator = vec![ - points[0] * points[1], - -points[0] - points[1], - F::Extension::ONE, - ] - .into(); - - let mut numerator = poly; - numerator -= interpolant; - let (quotient, rem) = numerator.div_rem_long_division(&denominator); - debug_assert!(rem.is_zero()); + let quotient = if N == 1 { + poly.divide_by_linear(points[0]).0 + } else if N == 2 { + let denominator = vec![ + points[0] * points[1], + -points[0] - points[1], + F::Extension::ONE, + ] + .into(); + poly.div_rem_long_division(&denominator).0 // Could also use `divide_by_linear` twice. + } else { + unreachable!("This shouldn't happen. Plonk should open polynomials at 1 or 2 points.") + }; quotient.padded(quotient.degree_plus_one().next_power_of_two()) } From 15922d25189ff6af519a24777b8457bb68275d36 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 21 Jun 2021 10:32:32 +0200 Subject: [PATCH 7/7] Add comment for denominator polynomial --- src/polynomial/commitment.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 1e4ba1b4..bc17f3f3 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -180,6 +180,7 @@ impl ListPolynomialCommitment { let quotient = if N == 1 { poly.divide_by_linear(points[0]).0 } else if N == 2 { + // The denominator is `(X - p0)(X - p1) = p0 p1 - (p0 + p1) X + X^2`. let denominator = vec![ points[0] * points[1], -points[0] - points[1],