From 44a5e0be1bf06973c454eb2f28e60d506bd9715c Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Mon, 10 May 2021 12:58:58 -0700 Subject: [PATCH 1/4] Some cleanup related to the two polynomial APIs Porting over some code from `old_polynomial`, and changing `ListPolynomialCommitment` to use the newer API. There's one remaining use of `old_polynomial` for long division; I think that can eventually go away when we switch to doing values-only FRI (unless another use comes up). --- src/polynomial/commitment.rs | 29 +++++------ src/polynomial/polynomial.rs | 96 ++++++++++++++++++++++++++++++++---- 2 files changed, 101 insertions(+), 24 deletions(-) diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 610fbc55..7f9b44bd 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -4,7 +4,6 @@ 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_with_powers; -use crate::polynomial::old_polynomial::Polynomial; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::proof::{FriProof, Hash}; use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; @@ -84,8 +83,9 @@ impl ListPolynomialCommitment { .polynomials .iter() .rev() - .map(|p| p.clone().into()) - .fold(Polynomial::empty(), |acc, p| acc.scalar_mul(alpha).add(&p)); + .fold(PolynomialCoeffs::zero(self.degree), |acc, p| { + &(&acc * alpha) + &p + }); // Scale evaluations by `alpha`. let composition_evals = evaluations .iter() @@ -118,7 +118,11 @@ 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], evals: &[F], poly: &Polynomial) -> Polynomial { + fn compute_quotient( + points: &[F], + evals: &[F], + poly: &PolynomialCoeffs, + ) -> PolynomialCoeffs { let pairs = points .iter() .zip(evals) @@ -126,18 +130,15 @@ impl ListPolynomialCommitment { .collect::>(); debug_assert!(pairs.iter().all(|&(x, e)| poly.eval(x) == e)); - let interpolant: Polynomial = interpolant(&pairs).into(); - let denominator = points - .iter() - .fold(Polynomial::from(vec![F::ONE]), |acc, &x| { - acc.mul(&vec![-x, F::ONE].into()) - }); - let numerator = poly.add(&interpolant.neg()); - let (mut quotient, rem) = numerator.polynomial_division(&denominator); + let interpolant = interpolant(&pairs); + let denominator = points.iter().fold(PolynomialCoeffs::one(), |acc, &x| { + &acc * &PolynomialCoeffs::new(vec![-x, F::ONE]) + }); + let numerator = poly - &interpolant; + let (mut quotient, rem) = numerator.div_rem(&denominator); debug_assert!(rem.is_zero()); - quotient.pad((quotient.degree() + 1).next_power_of_two()); - quotient + quotient.padded(quotient.degree_plus_one().next_power_of_two()) } } diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index 113a6d2a..a0cdeb8b 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -1,7 +1,10 @@ +use std::cmp::max; +use std::ops::{Add, Mul, Sub}; + use crate::field::fft::{fft, ifft}; use crate::field::field::Field; +use crate::polynomial::old_polynomial::Polynomial; use crate::util::log2_strict; -use std::slice::Iter; /// A polynomial in point-value form. /// @@ -65,6 +68,14 @@ impl PolynomialCoeffs { Self::new(vec![F::ZERO; len]) } + pub(crate) fn one() -> Self { + Self::new(vec![F::ONE]) + } + + pub(crate) fn is_zero(&self) -> bool { + self.coeffs.iter().all(|x| x.is_zero()) + } + /// The number of coefficients. This does not filter out any zero coefficients, so it is not /// necessarily related to the degree. pub(crate) fn len(&self) -> usize { @@ -93,13 +104,13 @@ impl PolynomialCoeffs { polys.into_iter().map(|p| p.lde(rate_bits)).collect() } - pub(crate) fn lde(self, rate_bits: usize) -> Self { - let original_size = self.len(); - let lde_size = original_size << rate_bits; - let Self { mut coeffs } = self; - for _ in 0..(lde_size - original_size) { - coeffs.push(F::ZERO); - } + pub(crate) fn lde(&self, rate_bits: usize) -> Self { + self.padded(self.len() << rate_bits) + } + + pub(crate) fn padded(&self, new_len: usize) -> Self { + let mut coeffs = self.coeffs.clone(); + coeffs.resize(new_len, F::ZERO); Self { coeffs } } @@ -109,13 +120,20 @@ impl PolynomialCoeffs { } /// Degree of the polynomial + 1. - fn degree_plus_one(&self) -> usize { + pub(crate) fn degree_plus_one(&self) -> usize { (0usize..self.len()) .rev() .find(|&i| self.coeffs[i].is_nonzero()) .map_or(0, |i| i + 1) } + pub(crate) fn div_rem(&self, rhs: &Self) -> (Self, Self) { + let lhs = Polynomial::from(self.clone()); + let rhs = Polynomial::from(rhs.clone()); + let (q, r) = lhs.polynomial_long_division(&rhs); + (q.into(), r.into()) + } + pub fn fft(self) -> PolynomialValues { fft(self) } @@ -137,11 +155,69 @@ impl From> for PolynomialCoeffs { } } +impl Add for &PolynomialCoeffs { + type Output = PolynomialCoeffs; + + fn add(self, rhs: Self) -> Self::Output { + let len = max(self.len(), rhs.len()); + let mut coeffs = self.coeffs.clone(); + coeffs.resize(len, F::ZERO); + for (i, &c) in rhs.coeffs.iter().enumerate() { + coeffs[i] += c; + } + PolynomialCoeffs::new(coeffs) + } +} + +impl Sub for &PolynomialCoeffs { + type Output = PolynomialCoeffs; + + fn sub(self, rhs: Self) -> Self::Output { + let len = max(self.len(), rhs.len()); + let mut coeffs = self.coeffs.clone(); + coeffs.resize(len, F::ZERO); + for (i, &c) in rhs.coeffs.iter().enumerate() { + coeffs[i] -= c; + } + PolynomialCoeffs::new(coeffs) + } +} + +impl Mul for &PolynomialCoeffs { + type Output = PolynomialCoeffs; + + fn mul(self, rhs: F) -> Self::Output { + let coeffs = self.coeffs.iter().map(|&x| rhs * x).collect(); + PolynomialCoeffs::new(coeffs) + } +} + +impl Mul for &PolynomialCoeffs { + type Output = PolynomialCoeffs; + + fn mul(self, rhs: Self) -> Self::Output { + let new_len = (self.len() + rhs.len()).next_power_of_two(); + let a = self.padded(new_len); + let b = rhs.padded(new_len); + let a_evals = a.fft(); + let b_evals = b.fft(); + + let mul_evals: Vec = a_evals + .values + .into_iter() + .zip(b_evals.values) + .map(|(pa, pb)| pa * pb) + .collect(); + ifft(mul_evals.into()) + } +} + #[cfg(test)] mod tests { - use super::*; use crate::field::crandall_field::CrandallField; + use super::*; + #[test] fn test_coset_fft() { type F = CrandallField; From 8b309fef41f08f92620d5af1b0c3e632ac54e313 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Mon, 10 May 2021 14:30:18 -0700 Subject: [PATCH 2/4] Tweak --- src/polynomial/polynomial.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index a0cdeb8b..86cba130 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -160,11 +160,9 @@ impl Add for &PolynomialCoeffs { fn add(self, rhs: Self) -> Self::Output { let len = max(self.len(), rhs.len()); - let mut coeffs = self.coeffs.clone(); - coeffs.resize(len, F::ZERO); - for (i, &c) in rhs.coeffs.iter().enumerate() { - coeffs[i] += c; - } + let a = self.padded(len).coeffs; + let b = rhs.padded(len).coeffs; + let coeffs = a.into_iter().zip(b).map(|(x, y)| x + y).collect(); PolynomialCoeffs::new(coeffs) } } From 4d5ea833250c89db08b68f64c0aaa9e1de01041c Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Mon, 10 May 2021 14:32:17 -0700 Subject: [PATCH 3/4] polynomial_long_division -> polynomial_division --- src/polynomial/polynomial.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index 86cba130..79576430 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -130,7 +130,7 @@ impl PolynomialCoeffs { pub(crate) fn div_rem(&self, rhs: &Self) -> (Self, Self) { let lhs = Polynomial::from(self.clone()); let rhs = Polynomial::from(rhs.clone()); - let (q, r) = lhs.polynomial_long_division(&rhs); + let (q, r) = lhs.polynomial_division(&rhs); (q.into(), r.into()) } From 22a625e86db15fdf3baa09b0879ac724a358eb9b Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Wed, 12 May 2021 10:33:36 -0700 Subject: [PATCH 4/4] trim b --- src/polynomial/old_polynomial.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/polynomial/old_polynomial.rs b/src/polynomial/old_polynomial.rs index 2e3dfc04..d011fff7 100644 --- a/src/polynomial/old_polynomial.rs +++ b/src/polynomial/old_polynomial.rs @@ -11,6 +11,8 @@ use std::ops::{Index, IndexMut, RangeBounds}; use std::slice::{Iter, IterMut, SliceIndex}; /// Polynomial struct holding a polynomial in coefficient form. +// TODO: Finish merging this with `PolynomialCoeffs`. +#[deprecated] #[derive(Debug, Clone)] pub struct Polynomial(Vec); @@ -258,6 +260,10 @@ impl Polynomial { /// Returns `(q,r)` the quotient and remainder of the polynomial division of `a` by `b`. /// Generally slower that the equivalent function `Polynomial::polynomial_division`. pub fn polynomial_long_division(&self, b: &Self) -> (Self, Self) { + // Trim b. + let mut b = b.clone(); + b.trim(); + let (a_degree, b_degree) = (self.degree(), b.degree()); if self.is_zero() { (Self::zero(1), Self::empty())