From f27620ca901c55f4b5ec71bb8e36476d998802d7 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 19:40:41 +0200 Subject: [PATCH 1/8] First impl --- src/fri/verifier.rs | 44 ++++++++++++++++++++++++-------------------- src/proof.rs | 4 ++-- src/util/mod.rs | 1 + src/util/scaling.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 22 deletions(-) create mode 100644 src/util/scaling.rs diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index b5a650bb..2ce33bd0 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -9,6 +9,7 @@ use crate::merkle_proofs::verify_merkle_proof; use crate::plonk_challenger::Challenger; use crate::plonk_common::reduce_with_iter; use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, Hash, OpeningSet}; +use crate::util::scaling::ScalingFactor; use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; /// Computes P'(x^arity) from {P(x*g^i)}_(i=0..arity), where g is a `arity`-th root of unity @@ -151,7 +152,7 @@ fn fri_combine_initial, const D: usize>( assert!(D > 1, "Not implemented for D=1."); let degree_log = proof.evals_proofs[0].1.siblings.len() - config.rate_bits; let subgroup_x = F::Extension::from_basefield(subgroup_x); - let mut alpha_powers = alpha.powers(); + let mut alpha = ScalingFactor::new(alpha); let mut sum = F::Extension::ZERO; // We will add three terms to `sum`: @@ -160,53 +161,56 @@ fn fri_combine_initial, const D: usize>( // - one for wire polynomials, which are opened at `x` and its conjugate // Polynomials opened at `x`, i.e., the constants, sigmas and quotient polynomials. - let single_evals = [0, 1, 4] - .iter() - .flat_map(|&i| proof.unsalted_evals(i, config)) - .map(|&e| F::Extension::from_basefield(e)); + let single_evals = &proof + .unsalted_evals(0, config) + .into_iter() + .chain(proof.unsalted_evals(1, config)) + .chain(proof.unsalted_evals(4, config)) + .map(|e| F::Extension::from_basefield(e)); let single_openings = os .constants - .iter() - .chain(&os.plonk_s_sigmas) - .chain(&os.quotient_polys); - let single_diffs = single_evals.zip(single_openings).map(|(e, &o)| e - o); - let single_numerator = reduce_with_iter(single_diffs, &mut alpha_powers); + .clone() + .into_iter() + .chain(os.plonk_s_sigmas.clone()) + .chain(os.quotient_polys.clone()); + let single_diffs = single_evals.zip(single_openings).map(|(e, o)| e - o); + dbg!(single_diffs.rev()); + let single_numerator = alpha.scale(single_diffs); let single_denominator = subgroup_x - zeta; sum += single_numerator / single_denominator; + alpha.shift(sum); // Polynomials opened at `x` and `g x`, i.e., the Zs polynomials. let zs_evals = proof .unsalted_evals(3, config) .iter() .map(|&e| F::Extension::from_basefield(e)); - let zs_composition_eval = reduce_with_iter(zs_evals, alpha_powers.clone()); + let zs_composition_eval = alpha.clone().scale(zs_evals); 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), - ), + (zeta, alpha.clone().scale(&os.plonk_zs)), + (zeta_right, alpha.scale(&os.plonk_zs_right)), ]); let zs_numerator = zs_composition_eval - zs_interpol.eval(subgroup_x); let zs_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); sum += zs_numerator / zs_denominator; + alpha.shift(sum); // Polynomials opened at `x` and `x.frobenius()`, i.e., the wires polynomials. let wire_evals = proof .unsalted_evals(2, config) .iter() .map(|&e| F::Extension::from_basefield(e)); - let wire_composition_eval = reduce_with_iter(wire_evals, alpha_powers.clone()); + let wire_composition_eval = alpha.clone().scale(wire_evals); let zeta_frob = zeta.frobenius(); - let wire_eval = reduce_with_iter(&os.wires, alpha_powers.clone()); + let wire_eval = alpha.clone().scale(&os.wires); // We want to compute `sum a^i*phi(w_i)`, where `phi` denotes the Frobenius automorphism. // Since `phi^D=id` and `phi` is a field automorphism, we have the following equalities: // `sum a^i*phi(w_i) = sum phi(phi^(D-1)(a^i)*w_i) = phi(sum phi^(D-1)(a)^i*w_i)` // So we can compute the original sum using only one call to the `D-1`-repeated Frobenius of alpha, // 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 mut alpha_frob = alpha.repeated_frobenius(D - 1); + let wire_eval_frob = alpha_frob.scale(&os.wires).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_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); diff --git a/src/proof.rs b/src/proof.rs index e05e4093..a029114a 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -99,9 +99,9 @@ pub struct FriInitialTreeProof { } impl FriInitialTreeProof { - pub(crate) fn unsalted_evals(&self, i: usize, config: &FriConfig) -> &[F] { + pub(crate) fn unsalted_evals(&self, i: usize, config: &FriConfig) -> Vec { let evals = &self.evals_proofs[i].0; - &evals[..evals.len() - config.salt_size(i)] + evals[..evals.len() - config.salt_size(i)].to_vec() } } diff --git a/src/util/mod.rs b/src/util/mod.rs index 09bd4e72..f901b0af 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,3 +1,4 @@ +pub mod scaling; pub(crate) mod timing; use crate::field::field::Field; diff --git a/src/util/scaling.rs b/src/util/scaling.rs new file mode 100644 index 00000000..36148d85 --- /dev/null +++ b/src/util/scaling.rs @@ -0,0 +1,41 @@ +use std::borrow::Borrow; + +use crate::field::extension_field::Frobenius; +use crate::field::field::Field; + +#[derive(Copy, Clone)] +pub struct ScalingFactor { + base: F, + count: u64, +} + +impl ScalingFactor { + pub fn new(base: F) -> Self { + Self { base, count: 0 } + } + + pub fn mul(&mut self, x: F) -> F { + self.count += 1; + self.base * x + } + + pub fn scale(&mut self, iter: impl DoubleEndedIterator>) -> F { + iter.rev().fold(F::ZERO, |acc, x| self.mul(acc) + x) + } + + pub fn shift(&mut self, x: F) -> F { + let tmp = self.base.exp(self.count) * x; + self.count = 0; + tmp + } + + pub fn repeated_frobenius(&self, count: usize) -> Self + where + F: Frobenius, + { + Self { + base: self.base.repeated_frobenius(count), + count: self.count, + } + } +} From fe9cd3f76b2da36fd4a8bfd84d830fe91f72d618 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 21:34:04 +0200 Subject: [PATCH 2/8] Working commitments and verifier --- src/fri/verifier.rs | 26 +++++++++++-------- src/polynomial/commitment.rs | 48 +++++++++++++++++++++++------------- src/util/scaling.rs | 32 +++++++++++++++++++++--- 3 files changed, 76 insertions(+), 30 deletions(-) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index f27070b2..50537a9e 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -168,17 +168,22 @@ fn fri_combine_initial, const D: usize>( ] .iter() .flat_map(|&p| proof.unsalted_evals(p)) - .map(|&e| F::Extension::from_basefield(e)); + .map(|&e| F::Extension::from_basefield(e)) + .collect::>(); let single_openings = os .constants .iter() .chain(&os.plonk_s_sigmas) - .chain(&os.quotient_polys); - let single_diffs = single_evals.zip(single_openings).map(|(e, &o)| e - o); - let single_numerator = reduce_with_iter(single_diffs, &mut alpha_powers); + .chain(&os.quotient_polys) + .collect::>(); + let single_diffs = single_evals + .into_iter() + .zip(single_openings) + .map(|(e, &o)| e - o); + let single_numerator = alpha.scale(single_diffs); let single_denominator = subgroup_x - zeta; sum += single_numerator / single_denominator; - alpha.shift(sum); + alpha.reset(); // Polynomials opened at `x` and `g x`, i.e., the Zs polynomials. let zs_evals = proof @@ -188,13 +193,13 @@ fn fri_combine_initial, const D: usize>( let zs_composition_eval = alpha.clone().scale(zs_evals); let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; let zs_interpol = interpolant(&[ - (zeta, alpha.clone().scale(&os.plonk_zs)), - (zeta_right, alpha.scale(&os.plonk_zs_right)), + (zeta, alpha.clone().scale(os.plonk_zs.iter())), + (zeta_right, alpha.scale(os.plonk_zs_right.iter())), ]); let zs_numerator = zs_composition_eval - zs_interpol.eval(subgroup_x); let zs_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); + sum = alpha.shift(sum); sum += zs_numerator / zs_denominator; - alpha.shift(sum); // Polynomials opened at `x` and `x.frobenius()`, i.e., the wires polynomials. let wire_evals = proof @@ -203,17 +208,18 @@ fn fri_combine_initial, const D: usize>( .map(|&e| F::Extension::from_basefield(e)); let wire_composition_eval = alpha.clone().scale(wire_evals); let zeta_frob = zeta.frobenius(); - let wire_eval = alpha.clone().scale(&os.wires); + let wire_eval = alpha.clone().scale(os.wires.iter()); // We want to compute `sum a^i*phi(w_i)`, where `phi` denotes the Frobenius automorphism. // Since `phi^D=id` and `phi` is a field automorphism, we have the following equalities: // `sum a^i*phi(w_i) = sum phi(phi^(D-1)(a^i)*w_i) = phi(sum phi^(D-1)(a)^i*w_i)` // So we can compute the original sum using only one call to the `D-1`-repeated Frobenius of alpha, // and one call at the end of the sum. let mut alpha_frob = alpha.repeated_frobenius(D - 1); - let wire_eval_frob = alpha_frob.scale(&os.wires).frobenius(); + let wire_eval_frob = alpha_frob.scale(os.wires.iter()).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_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); + sum = alpha_frob.shift(sum); sum += wire_numerator / wire_denominator; sum diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index caa61553..9fa7bc01 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -8,10 +8,11 @@ use crate::field::lagrange::interpolant; 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, reduce_with_iter, PlonkPolynomials}; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; +use crate::util::scaling::ScalingFactor; use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; pub const SALT_SIZE: usize = 2; @@ -110,20 +111,24 @@ impl ListPolynomialCommitment { challenger.observe_opening_set(&os); let alpha = challenger.get_extension_challenge(); - let mut alpha_powers = alpha.powers(); + let mut alpha = ScalingFactor::new(alpha); // Final low-degree polynomial that goes into FRI. let mut final_poly = PolynomialCoeffs::empty(); // Polynomials opened at a single point. - let single_polys = [0, 1, 4] - .iter() - .flat_map(|&i| &commitments[i].polynomials) - .map(|p| p.to_extension()); + let single_polys = [ + PlonkPolynomials::CONSTANTS, + PlonkPolynomials::SIGMAS, + PlonkPolynomials::QUOTIENT, + ] + .iter() + .flat_map(|&p| &commitments[p.index].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, alpha_powers.clone()); - let single_composition_eval = reduce_with_iter(single_evals, &mut alpha_powers); + let single_composition_poly = alpha.clone().scale_polys(single_polys); + let single_composition_eval = alpha.scale(single_evals); let single_quotient = Self::compute_quotient( &[zeta], @@ -131,13 +136,17 @@ impl ListPolynomialCommitment { &single_composition_poly, ); final_poly = &final_poly + &single_quotient; + alpha.reset(); // 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_polys = commitments[PlonkPolynomials::ZS.index] + .polynomials + .iter() + .map(|p| p.to_extension()); + let zs_composition_poly = alpha.clone().scale_polys(zs_polys); let zs_composition_evals = [ - reduce_with_iter(&os.plonk_zs, alpha_powers.clone()), - reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), + alpha.clone().scale(os.plonk_zs.iter()), + alpha.scale(os.plonk_zs_right.iter()), ]; let zs_quotient = Self::compute_quotient( @@ -145,17 +154,21 @@ impl ListPolynomialCommitment { &zs_composition_evals, &zs_composition_poly, ); + final_poly = alpha.shift_poly(final_poly); final_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_polys = commitments[PlonkPolynomials::WIRES.index] + .polynomials + .iter() + .map(|p| p.to_extension()); + let wire_composition_poly = alpha.clone().scale_polys(wire_polys); + let mut alpha_frob = alpha.repeated_frobenius(D - 1); let wire_composition_evals = [ - reduce_with_iter(&os.wires, alpha_powers.clone()), - reduce_with_iter(&wire_evals_frob, alpha_powers), + alpha.clone().scale(os.wires.iter()), + alpha_frob.scale(os.wires.iter()).frobenius(), ]; let wires_quotient = Self::compute_quotient( @@ -163,6 +176,7 @@ impl ListPolynomialCommitment { &wire_composition_evals, &wire_composition_poly, ); + final_poly = alpha_frob.shift_poly(final_poly); final_poly = &final_poly + &wires_quotient; let lde_final_poly = final_poly.lde(config.rate_bits); diff --git a/src/util/scaling.rs b/src/util/scaling.rs index 36148d85..e29edf0e 100644 --- a/src/util/scaling.rs +++ b/src/util/scaling.rs @@ -2,8 +2,9 @@ use std::borrow::Borrow; use crate::field::extension_field::Frobenius; use crate::field::field::Field; +use crate::polynomial::polynomial::PolynomialCoeffs; -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub struct ScalingFactor { base: F, count: u64, @@ -14,13 +15,28 @@ impl ScalingFactor { Self { base, count: 0 } } - pub fn mul(&mut self, x: F) -> F { + fn mul(&mut self, x: F) -> F { self.count += 1; self.base * x } + fn mul_poly(&mut self, p: PolynomialCoeffs) -> PolynomialCoeffs { + self.count += 1; + &p * self.base + } + pub fn scale(&mut self, iter: impl DoubleEndedIterator>) -> F { - iter.rev().fold(F::ZERO, |acc, x| self.mul(acc) + x) + iter.rev() + .fold(F::ZERO, |acc, x| self.mul(acc) + *x.borrow()) + } + + pub fn scale_polys( + &mut self, + polys: impl DoubleEndedIterator>>, + ) -> PolynomialCoeffs { + polys.rev().fold(PolynomialCoeffs::empty(), |acc, x| { + &self.mul_poly(acc) + x.borrow() + }) } pub fn shift(&mut self, x: F) -> F { @@ -29,6 +45,16 @@ impl ScalingFactor { tmp } + pub fn shift_poly(&mut self, p: PolynomialCoeffs) -> PolynomialCoeffs { + let tmp = &p * self.base.exp(self.count); + self.count = 0; + tmp + } + + pub fn reset(&mut self) { + self.count = 0; + } + pub fn repeated_frobenius(&self, count: usize) -> Self where F: Frobenius, From 92e0f60c2318c4f7f92ce750229e540b5845604c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 21:57:31 +0200 Subject: [PATCH 3/8] Clippy --- src/fri/verifier.rs | 2 +- src/polynomial/commitment.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 50537a9e..b94807f1 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -7,7 +7,7 @@ use crate::fri::FriConfig; use crate::hash::hash_n_to_1; use crate::merkle_proofs::verify_merkle_proof; use crate::plonk_challenger::Challenger; -use crate::plonk_common::{reduce_with_iter, PlonkPolynomials}; +use crate::plonk_common::PlonkPolynomials; use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, Hash, OpeningSet}; use crate::util::scaling::ScalingFactor; use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 9fa7bc01..c09e1e20 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -8,7 +8,7 @@ use crate::field::lagrange::interpolant; 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, PlonkPolynomials}; +use crate::plonk_common::PlonkPolynomials; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; From 9db7dce738ded406ba23d99356c3a70be4e67bd9 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 22:06:53 +0200 Subject: [PATCH 4/8] scale -> reduce --- src/fri/verifier.rs | 18 +++++++++--------- src/polynomial/commitment.rs | 20 ++++++++++---------- src/util/scaling.rs | 8 ++++---- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index b94807f1..e2c8568f 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -9,7 +9,7 @@ use crate::merkle_proofs::verify_merkle_proof; use crate::plonk_challenger::Challenger; use crate::plonk_common::PlonkPolynomials; use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, Hash, OpeningSet}; -use crate::util::scaling::ScalingFactor; +use crate::util::scaling::ReducingFactor; use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; /// Computes P'(x^arity) from {P(x*g^i)}_(i=0..arity), where g is a `arity`-th root of unity @@ -152,7 +152,7 @@ fn fri_combine_initial, const D: usize>( assert!(D > 1, "Not implemented for D=1."); let degree_log = proof.evals_proofs[0].1.siblings.len() - config.rate_bits; let subgroup_x = F::Extension::from_basefield(subgroup_x); - let mut alpha = ScalingFactor::new(alpha); + let mut alpha = ReducingFactor::new(alpha); let mut sum = F::Extension::ZERO; // We will add three terms to `sum`: @@ -180,7 +180,7 @@ fn fri_combine_initial, const D: usize>( .into_iter() .zip(single_openings) .map(|(e, &o)| e - o); - let single_numerator = alpha.scale(single_diffs); + let single_numerator = alpha.reduce(single_diffs); let single_denominator = subgroup_x - zeta; sum += single_numerator / single_denominator; alpha.reset(); @@ -190,11 +190,11 @@ fn fri_combine_initial, const D: usize>( .unsalted_evals(PlonkPolynomials::ZS) .iter() .map(|&e| F::Extension::from_basefield(e)); - let zs_composition_eval = alpha.clone().scale(zs_evals); + let zs_composition_eval = alpha.clone().reduce(zs_evals); let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; let zs_interpol = interpolant(&[ - (zeta, alpha.clone().scale(os.plonk_zs.iter())), - (zeta_right, alpha.scale(os.plonk_zs_right.iter())), + (zeta, alpha.clone().reduce(os.plonk_zs.iter())), + (zeta_right, alpha.reduce(os.plonk_zs_right.iter())), ]); let zs_numerator = zs_composition_eval - zs_interpol.eval(subgroup_x); let zs_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); @@ -206,16 +206,16 @@ fn fri_combine_initial, const D: usize>( .unsalted_evals(PlonkPolynomials::WIRES) .iter() .map(|&e| F::Extension::from_basefield(e)); - let wire_composition_eval = alpha.clone().scale(wire_evals); + let wire_composition_eval = alpha.clone().reduce(wire_evals); let zeta_frob = zeta.frobenius(); - let wire_eval = alpha.clone().scale(os.wires.iter()); + let wire_eval = alpha.clone().reduce(os.wires.iter()); // We want to compute `sum a^i*phi(w_i)`, where `phi` denotes the Frobenius automorphism. // Since `phi^D=id` and `phi` is a field automorphism, we have the following equalities: // `sum a^i*phi(w_i) = sum phi(phi^(D-1)(a^i)*w_i) = phi(sum phi^(D-1)(a)^i*w_i)` // So we can compute the original sum using only one call to the `D-1`-repeated Frobenius of alpha, // and one call at the end of the sum. let mut alpha_frob = alpha.repeated_frobenius(D - 1); - let wire_eval_frob = alpha_frob.scale(os.wires.iter()).frobenius(); + let wire_eval_frob = alpha_frob.reduce(os.wires.iter()).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_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index c09e1e20..9d07975e 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -12,7 +12,7 @@ use crate::plonk_common::PlonkPolynomials; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; -use crate::util::scaling::ScalingFactor; +use crate::util::scaling::ReducingFactor; use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; pub const SALT_SIZE: usize = 2; @@ -111,7 +111,7 @@ impl ListPolynomialCommitment { challenger.observe_opening_set(&os); let alpha = challenger.get_extension_challenge(); - let mut alpha = ScalingFactor::new(alpha); + let mut alpha = ReducingFactor::new(alpha); // Final low-degree polynomial that goes into FRI. let mut final_poly = PolynomialCoeffs::empty(); @@ -127,8 +127,8 @@ 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 = alpha.clone().scale_polys(single_polys); - let single_composition_eval = alpha.scale(single_evals); + let single_composition_poly = alpha.clone().reduce_polys(single_polys); + let single_composition_eval = alpha.reduce(single_evals); let single_quotient = Self::compute_quotient( &[zeta], @@ -143,10 +143,10 @@ impl ListPolynomialCommitment { .polynomials .iter() .map(|p| p.to_extension()); - let zs_composition_poly = alpha.clone().scale_polys(zs_polys); + let zs_composition_poly = alpha.clone().reduce_polys(zs_polys); let zs_composition_evals = [ - alpha.clone().scale(os.plonk_zs.iter()), - alpha.scale(os.plonk_zs_right.iter()), + alpha.clone().reduce(os.plonk_zs.iter()), + alpha.reduce(os.plonk_zs_right.iter()), ]; let zs_quotient = Self::compute_quotient( @@ -164,11 +164,11 @@ impl ListPolynomialCommitment { .polynomials .iter() .map(|p| p.to_extension()); - let wire_composition_poly = alpha.clone().scale_polys(wire_polys); + let wire_composition_poly = alpha.clone().reduce_polys(wire_polys); let mut alpha_frob = alpha.repeated_frobenius(D - 1); let wire_composition_evals = [ - alpha.clone().scale(os.wires.iter()), - alpha_frob.scale(os.wires.iter()).frobenius(), + alpha.clone().reduce(os.wires.iter()), + alpha_frob.reduce(os.wires.iter()).frobenius(), ]; let wires_quotient = Self::compute_quotient( diff --git a/src/util/scaling.rs b/src/util/scaling.rs index e29edf0e..7155cb37 100644 --- a/src/util/scaling.rs +++ b/src/util/scaling.rs @@ -5,12 +5,12 @@ use crate::field::field::Field; use crate::polynomial::polynomial::PolynomialCoeffs; #[derive(Debug, Copy, Clone)] -pub struct ScalingFactor { +pub struct ReducingFactor { base: F, count: u64, } -impl ScalingFactor { +impl ReducingFactor { pub fn new(base: F) -> Self { Self { base, count: 0 } } @@ -25,12 +25,12 @@ impl ScalingFactor { &p * self.base } - pub fn scale(&mut self, iter: impl DoubleEndedIterator>) -> F { + pub fn reduce(&mut self, iter: impl DoubleEndedIterator>) -> F { iter.rev() .fold(F::ZERO, |acc, x| self.mul(acc) + *x.borrow()) } - pub fn scale_polys( + pub fn reduce_polys( &mut self, polys: impl DoubleEndedIterator>>, ) -> PolynomialCoeffs { From 01053ab96ae09293bc9b0a024d59181550727f88 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 11:30:57 +0200 Subject: [PATCH 5/8] Fix bug --- src/fri/verifier.rs | 25 ++++++----- src/polynomial/commitment.rs | 82 +++++++++++++----------------------- 2 files changed, 43 insertions(+), 64 deletions(-) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index e2c8568f..0e7c7c4d 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::interpolation::{barycentric_weights, interpolate, interpolate2}; use crate::fri::FriConfig; use crate::hash::hash_n_to_1; use crate::merkle_proofs::verify_merkle_proof; @@ -192,11 +192,14 @@ fn fri_combine_initial, const D: usize>( .map(|&e| F::Extension::from_basefield(e)); let zs_composition_eval = alpha.clone().reduce(zs_evals); let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; - let zs_interpol = interpolant(&[ - (zeta, alpha.clone().reduce(os.plonk_zs.iter())), - (zeta_right, alpha.reduce(os.plonk_zs_right.iter())), - ]); - let zs_numerator = zs_composition_eval - zs_interpol.eval(subgroup_x); + let zs_interpol = interpolate2( + [ + (zeta, alpha.clone().reduce(os.plonk_zs.iter())), + (zeta_right, alpha.reduce(os.plonk_zs_right.iter())), + ], + subgroup_x, + ); + let zs_numerator = zs_composition_eval - zs_interpol; let zs_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); sum = alpha.shift(sum); sum += zs_numerator / zs_denominator; @@ -208,18 +211,18 @@ fn fri_combine_initial, const D: usize>( .map(|&e| F::Extension::from_basefield(e)); let wire_composition_eval = alpha.clone().reduce(wire_evals); let zeta_frob = zeta.frobenius(); - let wire_eval = alpha.clone().reduce(os.wires.iter()); + let mut alpha_frob = alpha.repeated_frobenius(D - 1); + let wire_eval = alpha.reduce(os.wires.iter()); // We want to compute `sum a^i*phi(w_i)`, where `phi` denotes the Frobenius automorphism. // Since `phi^D=id` and `phi` is a field automorphism, we have the following equalities: // `sum a^i*phi(w_i) = sum phi(phi^(D-1)(a^i)*w_i) = phi(sum phi^(D-1)(a)^i*w_i)` // So we can compute the original sum using only one call to the `D-1`-repeated Frobenius of alpha, // and one call at the end of the sum. - let mut alpha_frob = alpha.repeated_frobenius(D - 1); let wire_eval_frob = alpha_frob.reduce(os.wires.iter()).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 = alpha_frob.shift(sum); + sum = alpha.shift(sum); sum += wire_numerator / wire_denominator; sum diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 9d07975e..df92a31b 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::interpolation::interpolate2; use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; @@ -125,17 +125,10 @@ impl ListPolynomialCommitment { .iter() .flat_map(|&p| &commitments[p.index].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 = alpha.clone().reduce_polys(single_polys); - let single_composition_eval = alpha.reduce(single_evals); + let single_composition_poly = alpha.reduce_polys(single_polys); - let single_quotient = Self::compute_quotient( - &[zeta], - &[single_composition_eval], - &single_composition_poly, - ); - final_poly = &final_poly + &single_quotient; + let single_quotient = Self::compute_quotient([zeta], single_composition_poly); + final_poly += single_quotient; alpha.reset(); // Zs polynomials are opened at `zeta` and `g*zeta`. @@ -143,19 +136,11 @@ impl ListPolynomialCommitment { .polynomials .iter() .map(|p| p.to_extension()); - let zs_composition_poly = alpha.clone().reduce_polys(zs_polys); - let zs_composition_evals = [ - alpha.clone().reduce(os.plonk_zs.iter()), - alpha.reduce(os.plonk_zs_right.iter()), - ]; + let zs_composition_poly = alpha.reduce_polys(zs_polys); - let zs_quotient = Self::compute_quotient( - &[zeta, g * zeta], - &zs_composition_evals, - &zs_composition_poly, - ); + let zs_quotient = Self::compute_quotient([zeta, g * zeta], zs_composition_poly); final_poly = alpha.shift_poly(final_poly); - final_poly = &final_poly + &zs_quotient; + 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 @@ -164,20 +149,12 @@ impl ListPolynomialCommitment { .polynomials .iter() .map(|p| p.to_extension()); - let wire_composition_poly = alpha.clone().reduce_polys(wire_polys); - let mut alpha_frob = alpha.repeated_frobenius(D - 1); - let wire_composition_evals = [ - alpha.clone().reduce(os.wires.iter()), - alpha_frob.reduce(os.wires.iter()).frobenius(), - ]; + let wire_composition_poly = alpha.reduce_polys(wire_polys); - let wires_quotient = Self::compute_quotient( - &[zeta, zeta.frobenius()], - &wire_composition_evals, - &wire_composition_poly, - ); - final_poly = alpha_frob.shift_poly(final_poly); - final_poly = &final_poly + &wires_quotient; + let wires_quotient = + Self::compute_quotient([zeta, zeta.frobenius()], wire_composition_poly); + final_poly = alpha.shift_poly(final_poly); + final_poly += wires_quotient; let lde_final_poly = final_poly.lde(config.rate_bits); let lde_final_values = lde_final_poly @@ -208,28 +185,27 @@ 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, + fn compute_quotient( + points: [F::Extension; N], + poly: PolynomialCoeffs, ) -> PolynomialCoeffs where F: Extendable, { - let pairs = points - .iter() - .zip(evals) - .map(|(&x, &e)| (x, e)) - .collect::>(); - 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 (quotient, rem) = numerator.div_rem(&denominator); - debug_assert!(rem.is_zero()); + 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], + 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 26e669ddeca3a440bbee65f3a85249a1478cd55e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 11:33:50 +0200 Subject: [PATCH 6/8] 2 `collect`s -> 1 `collect` --- src/fri/verifier.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 0e7c7c4d..d88e8e0b 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -168,19 +168,18 @@ fn fri_combine_initial, const D: usize>( ] .iter() .flat_map(|&p| proof.unsalted_evals(p)) - .map(|&e| F::Extension::from_basefield(e)) - .collect::>(); + .map(|&e| F::Extension::from_basefield(e)); let single_openings = os .constants .iter() .chain(&os.plonk_s_sigmas) - .chain(&os.quotient_polys) - .collect::>(); + .chain(&os.quotient_polys); let single_diffs = single_evals .into_iter() .zip(single_openings) - .map(|(e, &o)| e - o); - let single_numerator = alpha.reduce(single_diffs); + .map(|(e, &o)| e - o) + .collect::>(); + let single_numerator = alpha.reduce(single_diffs.iter()); let single_denominator = subgroup_x - zeta; sum += single_numerator / single_denominator; alpha.reset(); From 492b04843e026ed8a8d1bae048ebf50b72431025 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 11:41:32 +0200 Subject: [PATCH 7/8] Optimize some polynomial operations to avoid cloning. --- src/polynomial/commitment.rs | 5 ++--- src/polynomial/polynomial.rs | 28 +++++++++++++++++++++++++++- src/util/scaling.rs | 15 ++++++++------- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index df92a31b..4b2bf872 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -4,7 +4,6 @@ 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::interpolate2; use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; @@ -139,7 +138,7 @@ impl ListPolynomialCommitment { let zs_composition_poly = alpha.reduce_polys(zs_polys); let zs_quotient = Self::compute_quotient([zeta, g * zeta], zs_composition_poly); - final_poly = alpha.shift_poly(final_poly); + alpha.shift_poly(&mut final_poly); final_poly += zs_quotient; // When working in an extension field, need to check that wires are in the base field. @@ -153,7 +152,7 @@ impl ListPolynomialCommitment { let wires_quotient = Self::compute_quotient([zeta, zeta.frobenius()], wire_composition_poly); - final_poly = alpha.shift_poly(final_poly); + alpha.shift_poly(&mut final_poly); final_poly += wires_quotient; let lde_final_poly = final_poly.lde(config.rate_bits); diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index aefcc0c6..02f66684 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, AddAssign, Mul, Sub, SubAssign}; +use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; use crate::field::extension_field::Extendable; use crate::field::fft::{fft, ifft}; @@ -253,6 +253,16 @@ impl AddAssign for PolynomialCoeffs { } } +impl AddAssign<&Self> 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()); @@ -263,6 +273,16 @@ impl SubAssign for PolynomialCoeffs { } } +impl SubAssign<&Self> 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; @@ -272,6 +292,12 @@ impl Mul for &PolynomialCoeffs { } } +impl MulAssign for PolynomialCoeffs { + fn mul_assign(&mut self, rhs: F) { + self.coeffs.iter_mut().for_each(|x| *x *= rhs); + } +} + impl Mul for &PolynomialCoeffs { type Output = PolynomialCoeffs; diff --git a/src/util/scaling.rs b/src/util/scaling.rs index 7155cb37..25da24c7 100644 --- a/src/util/scaling.rs +++ b/src/util/scaling.rs @@ -20,9 +20,9 @@ impl ReducingFactor { self.base * x } - fn mul_poly(&mut self, p: PolynomialCoeffs) -> PolynomialCoeffs { + fn mul_poly(&mut self, p: &mut PolynomialCoeffs) { self.count += 1; - &p * self.base + *p *= self.base; } pub fn reduce(&mut self, iter: impl DoubleEndedIterator>) -> F { @@ -34,8 +34,10 @@ impl ReducingFactor { &mut self, polys: impl DoubleEndedIterator>>, ) -> PolynomialCoeffs { - polys.rev().fold(PolynomialCoeffs::empty(), |acc, x| { - &self.mul_poly(acc) + x.borrow() + polys.rev().fold(PolynomialCoeffs::empty(), |mut acc, x| { + self.mul_poly(&mut acc); + acc += x.borrow(); + acc }) } @@ -45,10 +47,9 @@ impl ReducingFactor { tmp } - pub fn shift_poly(&mut self, p: PolynomialCoeffs) -> PolynomialCoeffs { - let tmp = &p * self.base.exp(self.count); + pub fn shift_poly(&mut self, p: &mut PolynomialCoeffs) { + *p *= self.base.exp(self.count); self.count = 0; - tmp } pub fn reset(&mut self) { From 517c75abe2f442613d6d260ea21b294e8f0d8bcc Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 11:51:16 +0200 Subject: [PATCH 8/8] Add comment for `ReducingFactor` --- src/util/scaling.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/util/scaling.rs b/src/util/scaling.rs index 25da24c7..cea86195 100644 --- a/src/util/scaling.rs +++ b/src/util/scaling.rs @@ -4,6 +4,13 @@ use crate::field::extension_field::Frobenius; use crate::field::field::Field; use crate::polynomial::polynomial::PolynomialCoeffs; +/// When verifying the composition polynomial in FRI we have to compute sums of the form +/// `(sum_0^k a^i * x_i)/d_0 + (sum_k^r a^i * y_i)/d_1` +/// The most efficient way to do this is to compute both quotient separately using Horner's method, +/// scale the second one by `a^(r-1-k)`, and add them up. +/// This struct abstract away these operations by implementing Horner's method and keeping track +/// of the number of multiplications by `a` to compute the scaling factor. +/// See https://github.com/mir-protocol/plonky2/pull/69 for more details and discussions. #[derive(Debug, Copy, Clone)] pub struct ReducingFactor { base: F,