From 3de8d36c3afa0747f3f0cb7a7af03ddf1d505b33 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Mon, 3 Jan 2022 08:34:44 -0800 Subject: [PATCH] Use single-point opening expressions (#416) I.e. instead of opening `Z` at `zeta` and `g zeta` by running FRI on a quotient involving an interpolant, we just run FRI on two separate opening expressions, one for `zeta` and one for `g zeta`. A few motivations for this: - I think this will make it slightly easier to generalize our FRI code to work with STARKs. I.e. if we have an object representing the structure of polynomial openings in an IOP, that object will be slightly simpler. - It's less code. We could potentially remove some more code, e.g. the generality of `compute_quotient` is no longer needed, but I left it for now. - It saves 3 gates! --- plonky2/src/fri/commitment.rs | 25 ++++++----------- plonky2/src/fri/recursive_verifier.rs | 39 ++++++++------------------ plonky2/src/fri/verifier.rs | 40 ++++++++------------------- 3 files changed, 31 insertions(+), 73 deletions(-) diff --git a/plonky2/src/fri/commitment.rs b/plonky2/src/fri/commitment.rs index 9d0fd82e..9d7ecf43 100644 --- a/plonky2/src/fri/commitment.rs +++ b/plonky2/src/fri/commitment.rs @@ -172,21 +172,15 @@ impl, C: GenericConfig, const D: usize> // Final low-degree polynomial that goes into FRI. let mut final_poly = PolynomialCoeffs::empty(); - let mut zs_polys = commitments[PlonkPolynomials::ZS_PARTIAL_PRODUCTS.index] - .polynomials - .iter() - .collect::>(); - let partial_products_polys = zs_polys.split_off(common_data.zs_range().end); - - // Polynomials opened at a single point. + // All polynomials are opened at `zeta`. let single_polys = [ PlonkPolynomials::CONSTANTS_SIGMAS, PlonkPolynomials::WIRES, + PlonkPolynomials::ZS_PARTIAL_PRODUCTS, PlonkPolynomials::QUOTIENT, ] .iter() - .flat_map(|&p| &commitments[p.index].polynomials) - .chain(partial_products_polys); + .flat_map(|&p| &commitments[p.index].polynomials); let single_composition_poly = timed!( timing, "reduce single polys", @@ -197,14 +191,13 @@ impl, C: GenericConfig, const D: usize> final_poly += single_quotient; alpha.reset(); - // Zs polynomials are opened at `zeta` and `g*zeta`. - let zs_composition_poly = timed!( - timing, - "reduce Z polys", - alpha.reduce_polys_base(zs_polys.into_iter()) - ); + // Z polynomials have an additional opening at `g zeta`. + let zs_polys = &commitments[PlonkPolynomials::ZS_PARTIAL_PRODUCTS.index].polynomials + [common_data.zs_range()]; + let zs_composition_poly = + timed!(timing, "reduce Z polys", alpha.reduce_polys_base(zs_polys)); - let zs_quotient = Self::compute_quotient([zeta, g * zeta], zs_composition_poly); + let zs_quotient = Self::compute_quotient([g * zeta], zs_composition_poly); alpha.shift_poly(&mut final_poly); final_poly += zs_quotient; diff --git a/plonky2/src/fri/recursive_verifier.rs b/plonky2/src/fri/recursive_verifier.rs index 9d5fae4d..276adc2c 100644 --- a/plonky2/src/fri/recursive_verifier.rs +++ b/plonky2/src/fri/recursive_verifier.rs @@ -274,22 +274,16 @@ impl, const D: usize> CircuitBuilder { let mut alpha = ReducingFactorTarget::new(alpha); let mut sum = self.zero_extension(); - // We will add three terms to `sum`: - // - one for polynomials opened at `x` only - // - one for polynomials opened at `x` and `g x` - - // Polynomials opened at `x`, i.e., the constants-sigmas, wires, quotient and partial products polynomials. + // We will add two terms to `sum`: one for openings at `x`, and one for openings at `g x`. + // All polynomials are opened at `x`. let single_evals = [ PlonkPolynomials::CONSTANTS_SIGMAS, PlonkPolynomials::WIRES, + PlonkPolynomials::ZS_PARTIAL_PRODUCTS, PlonkPolynomials::QUOTIENT, ] .iter() .flat_map(|&p| proof.unsalted_evals(p, config.zero_knowledge)) - .chain( - &proof.unsalted_evals(PlonkPolynomials::ZS_PARTIAL_PRODUCTS, config.zero_knowledge) - [common_data.partial_products_range()], - ) .copied() .collect::>(); let single_composition_eval = alpha.reduce_base(&single_evals, self); @@ -307,16 +301,10 @@ impl, const D: usize> CircuitBuilder { .collect::>(); let zs_composition_eval = alpha.reduce_base(&zs_evals, self); - let interpol_val = self.mul_add_extension( - vanish_zeta, - precomputed_reduced_evals.slope, - precomputed_reduced_evals.zs, - ); - let zs_numerator = self.sub_extension(zs_composition_eval, interpol_val); - let vanish_zeta_right = - self.sub_extension(subgroup_x, precomputed_reduced_evals.zeta_right); - sum = alpha.shift(sum, self); - let zs_denominator = self.mul_extension(vanish_zeta, vanish_zeta_right); + let zs_numerator = + self.sub_extension(zs_composition_eval, precomputed_reduced_evals.zs_right); + let zs_denominator = self.sub_extension(subgroup_x, precomputed_reduced_evals.zeta_right); + sum = alpha.shift(sum, self); // TODO: alpha^count could be precomputed. sum = self.div_add_extension(zs_numerator, zs_denominator, sum); sum @@ -470,9 +458,7 @@ impl, const D: usize> CircuitBuilder { #[derive(Copy, Clone)] struct PrecomputedReducedEvalsTarget { pub single: ExtensionTarget, - pub zs: ExtensionTarget, - /// Slope of the line from `(zeta, zs)` to `(zeta_right, zs_right)`. - pub slope: ExtensionTarget, + pub zs_right: ExtensionTarget, pub zeta_right: ExtensionTarget, } @@ -490,24 +476,21 @@ impl PrecomputedReducedEvalsTarget { .iter() .chain(&os.plonk_sigmas) .chain(&os.wires) - .chain(&os.quotient_polys) + .chain(&os.plonk_zs) .chain(&os.partial_products) + .chain(&os.quotient_polys) .copied() .collect::>(), builder, ); - let zs = alpha.reduce(&os.plonk_zs, builder); let zs_right = alpha.reduce(&os.plonk_zs_right, builder); let g = builder.constant_extension(F::Extension::primitive_root_of_unity(degree_log)); let zeta_right = builder.mul_extension(g, zeta); - let numerator = builder.sub_extension(zs_right, zs); - let denominator = builder.sub_extension(zeta_right, zeta); Self { single, - zs, - slope: builder.div_extension(numerator, denominator), + zs_right, zeta_right, } } diff --git a/plonky2/src/fri/verifier.rs b/plonky2/src/fri/verifier.rs index d374ff68..08bcb12c 100644 --- a/plonky2/src/fri/verifier.rs +++ b/plonky2/src/fri/verifier.rs @@ -1,7 +1,7 @@ use anyhow::{ensure, Result}; use plonky2_field::extension_field::{flatten, Extendable, FieldExtension}; use plonky2_field::field_types::Field; -use plonky2_field::interpolation::{barycentric_weights, interpolate, interpolate2}; +use plonky2_field::interpolation::{barycentric_weights, interpolate}; use plonky2_util::{log2_strict, reverse_index_bits_in_place}; use crate::fri::proof::{FriInitialTreeProof, FriProof, FriQueryRound}; @@ -127,7 +127,6 @@ fn fri_verify_initial_proof>( #[derive(Copy, Clone, Debug)] pub(crate) struct PrecomputedReducedEvals, const D: usize> { pub single: F::Extension, - pub zs: F::Extension, pub zs_right: F::Extension, } @@ -139,17 +138,13 @@ impl, const D: usize> PrecomputedReducedEvals .iter() .chain(&os.plonk_sigmas) .chain(&os.wires) - .chain(&os.quotient_polys) - .chain(&os.partial_products), + .chain(&os.plonk_zs) + .chain(&os.partial_products) + .chain(&os.quotient_polys), ); - let zs = alpha.reduce(os.plonk_zs.iter()); let zs_right = alpha.reduce(os.plonk_zs_right.iter()); - Self { - single, - zs, - zs_right, - } + Self { single, zs_right } } } @@ -172,22 +167,16 @@ pub(crate) fn fri_combine_initial< let mut alpha = ReducingFactor::new(alpha); let mut sum = F::Extension::ZERO; - // We will add three terms to `sum`: - // - one for various polynomials which are opened at a single point `x` - // - one for Zs, which are opened at `x` and `g x` - - // Polynomials opened at `x`, i.e., the constants-sigmas, wires, quotient and partial products polynomials. + // We will add two terms to `sum`: one for openings at `x`, and one for openings at `g x`. + // All polynomials are opened at `x`. let single_evals = [ PlonkPolynomials::CONSTANTS_SIGMAS, PlonkPolynomials::WIRES, + PlonkPolynomials::ZS_PARTIAL_PRODUCTS, PlonkPolynomials::QUOTIENT, ] .iter() .flat_map(|&p| proof.unsalted_evals(p, config.zero_knowledge)) - .chain( - &proof.unsalted_evals(PlonkPolynomials::ZS_PARTIAL_PRODUCTS, config.zero_knowledge) - [common_data.partial_products_range()], - ) .map(|&e| F::Extension::from_basefield(e)); let single_composition_eval = alpha.reduce(single_evals); let single_numerator = single_composition_eval - precomputed_reduced_evals.single; @@ -195,7 +184,7 @@ pub(crate) fn fri_combine_initial< sum += single_numerator / single_denominator; alpha.reset(); - // Polynomials opened at `x` and `g x`, i.e., the Zs polynomials. + // Z polynomials have an additional opening at `g x`. let zs_evals = proof .unsalted_evals(PlonkPolynomials::ZS_PARTIAL_PRODUCTS, config.zero_knowledge) .iter() @@ -203,15 +192,8 @@ pub(crate) fn fri_combine_initial< .take(common_data.zs_range().end); let zs_composition_eval = alpha.reduce(zs_evals); let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; - let zs_interpol = interpolate2( - [ - (zeta, precomputed_reduced_evals.zs), - (zeta_right, precomputed_reduced_evals.zs_right), - ], - subgroup_x, - ); - let zs_numerator = zs_composition_eval - zs_interpol; - let zs_denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); + let zs_numerator = zs_composition_eval - precomputed_reduced_evals.zs_right; + let zs_denominator = subgroup_x - zeta_right; sum = alpha.shift(sum); sum += zs_numerator / zs_denominator;