From a6acd14dfaadbb3a742ff8ee8a1d199fd04ffdc0 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 16 Jun 2021 17:43:41 +0200 Subject: [PATCH 01/20] Minor rewrites and optimizations --- src/circuit_builder.rs | 15 ++++++++------- src/circuit_data.rs | 2 ++ src/field/fft.rs | 13 +++++++------ src/field/field.rs | 14 +++++++------- src/field/lagrange.rs | 7 ++----- src/permutation_argument.rs | 4 ++-- src/prover.rs | 21 ++++++++++++++++++++- 7 files changed, 48 insertions(+), 28 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 4150bb68..1916eba6 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -236,7 +236,7 @@ impl, const D: usize> CircuitBuilder { .collect() } - fn sigma_vecs(&self, k_is: &[F]) -> Vec> { + fn sigma_vecs(&self, k_is: &[F], subgroup: &[F]) -> Vec> { let degree = self.gate_instances.len(); let degree_log = log2_strict(degree); let mut target_partitions = TargetPartitions::new(); @@ -256,7 +256,7 @@ impl, const D: usize> CircuitBuilder { } let wire_partitions = target_partitions.to_wire_partitions(); - wire_partitions.get_sigma_polys(degree_log, k_is) + wire_partitions.get_sigma_polys(degree_log, k_is, subgroup) } /// Builds a "full circuit", with both prover and verifier data. @@ -270,6 +270,9 @@ impl, const D: usize> CircuitBuilder { let degree = self.gate_instances.len(); info!("degree after blinding & padding: {}", degree); + let degree_bits = log2_strict(degree); + let subgroup = F::two_adic_subgroup(degree_bits); + let constant_vecs = self.constant_polys(); let constants_commitment = ListPolynomialCommitment::new( constant_vecs.into_iter().map(|v| v.ifft()).collect(), @@ -278,7 +281,7 @@ impl, const D: usize> CircuitBuilder { ); let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires); - let sigma_vecs = self.sigma_vecs(&k_is); + let sigma_vecs = self.sigma_vecs(&k_is, &subgroup); let sigmas_commitment = ListPolynomialCommitment::new( sigma_vecs.into_iter().map(|v| v.ifft()).collect(), self.config.fri_config.rate_bits, @@ -292,11 +295,11 @@ impl, const D: usize> CircuitBuilder { sigmas_root, }; - let generators = self.generators; let prover_only = ProverOnlyCircuitData { - generators, + generators: self.generators, constants_commitment, sigmas_commitment, + subgroup, }; // The HashSet of gates will have a non-deterministic order. When converting to a Vec, we @@ -310,8 +313,6 @@ impl, const D: usize> CircuitBuilder { .max() .expect("No gates?"); - let degree_bits = log2_strict(degree); - // TODO: This should also include an encoding of gate constraints. let circuit_digest_parts = [constants_root.elements, sigmas_root.elements]; let circuit_digest = hash_n_to_hash(circuit_digest_parts.concat(), false); diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 4d9a7110..6cc38522 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -104,6 +104,8 @@ pub(crate) struct ProverOnlyCircuitData { pub constants_commitment: ListPolynomialCommitment, /// Commitments to the sigma polynomial. pub sigmas_commitment: ListPolynomialCommitment, + /// Subgroup of order `degree`. + pub subgroup: Vec, } /// Circuit data required by the verifier, but not the prover. diff --git a/src/field/fft.rs b/src/field/fft.rs index 8bcde967..abce58d6 100644 --- a/src/field/fft.rs +++ b/src/field/fft.rs @@ -44,12 +44,14 @@ pub(crate) fn fft_precompute(degree: usize) -> FftPrecomputation { let degree_log = log2_ceil(degree); let mut subgroups_rev = Vec::new(); - for i in 0..=degree_log { - let g_i = F::primitive_root_of_unity(i); - let subgroup = F::cyclic_subgroup_known_order(g_i, 1 << i); + let mut subgroup = F::two_adic_subgroup(degree_log); + for _i in 0..=degree_log { + let subsubgroup = subgroup.iter().step_by(2).copied().collect(); let subgroup_rev = reverse_index_bits(subgroup); subgroups_rev.push(subgroup_rev); + subgroup = subsubgroup; } + subgroups_rev.reverse(); FftPrecomputation { subgroups_rev } } @@ -200,10 +202,9 @@ mod tests { let degree = coefficients.len(); let degree_log = log2_strict(degree); - let g = F::primitive_root_of_unity(degree_log); - let powers_of_g = F::cyclic_subgroup_known_order(g, degree); + let subgroup = F::two_adic_subgroup(degree_log); - let values = powers_of_g + let values = subgroup .into_iter() .map(|x| evaluate_at_naive(&coefficients, x)) .collect(); diff --git a/src/field/field.rs b/src/field/field.rs index 3c6f0e47..b637c72b 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -111,13 +111,13 @@ pub trait Field: /// Computes a multiplicative subgroup whose order is known in advance. fn cyclic_subgroup_known_order(generator: Self, order: usize) -> Vec { - let mut subgroup = Vec::with_capacity(order); - let mut current = Self::ONE; - for _i in 0..order { - subgroup.push(current); - current *= generator; - } - subgroup + generator.powers().take(order).collect() + } + + /// Computes the subgroup generated by the root of unity of a given order generated by `Self::primitive_root_of_unity`. + fn two_adic_subgroup(n_log: usize) -> Vec { + let generator = Self::primitive_root_of_unity(n_log); + generator.powers().take(1 << n_log).collect() } fn cyclic_subgroup_unknown_order(generator: Self) -> Vec { diff --git a/src/field/lagrange.rs b/src/field/lagrange.rs index 8911feb0..4b75651b 100644 --- a/src/field/lagrange.rs +++ b/src/field/lagrange.rs @@ -10,10 +10,8 @@ use crate::util::log2_ceil; pub(crate) fn interpolant(points: &[(F, F)]) -> PolynomialCoeffs { let n = points.len(); let n_log = log2_ceil(n); - let n_padded = 1 << n_log; - let g = F::primitive_root_of_unity(n_log); - let subgroup = F::cyclic_subgroup_known_order(g, n_padded); + let subgroup = F::two_adic_subgroup(n_log); let barycentric_weights = barycentric_weights(points); let subgroup_evals = subgroup .into_iter() @@ -92,8 +90,7 @@ mod tests { for deg_log in 0..4 { let deg = 1 << deg_log; - let g = F::primitive_root_of_unity(deg_log); - let domain = F::cyclic_subgroup_known_order(g, deg); + let domain = F::two_adic_subgroup(deg_log); let coeffs = F::rand_vec(deg); let coeffs = PolynomialCoeffs { coeffs }; diff --git a/src/permutation_argument.rs b/src/permutation_argument.rs index 62ee63d4..54436ecb 100644 --- a/src/permutation_argument.rs +++ b/src/permutation_argument.rs @@ -110,9 +110,9 @@ impl WirePartitions { &self, degree_log: usize, k_is: &[F], + subgroup: &[F], ) -> Vec> { let degree = 1 << degree_log; - let subgroup_generator = F::primitive_root_of_unity(degree_log); let sigma = self.get_sigma_map(degree); sigma @@ -120,7 +120,7 @@ impl WirePartitions { .map(|chunk| { let values = chunk .par_iter() - .map(|&x| k_is[x / degree] * subgroup_generator.exp((x % degree) as u64)) + .map(|&x| k_is[x / degree] * subgroup[x % degree]) .collect::>(); PolynomialValues::new(values) }) diff --git a/src/prover.rs b/src/prover.rs index 7a492eaa..0731d2b4 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -157,7 +157,26 @@ fn compute_z, const D: usize>( common_data: &CommonCircuitData, _i: usize, ) -> PolynomialCoeffs { - PolynomialCoeffs::zero(common_data.degree()) // TODO + todo!() + // let subgroup = + // let mut plonk_z_points = vec![F::ONE]; + // let k_is = common_data.k_is; + // for i in 1..common_data.degree() { + // let x = subgroup[i - 1]; + // let mut numerator = F::ONE; + // let mut denominator = F::ONE; + // for j in 0..NUM_ROUTED_WIRES { + // let wire_value = witness.get_indices(i - 1, j); + // let k_i = k_is[j]; + // let s_id = k_i * x; + // let s_sigma = sigma_values[j][8 * (i - 1)]; + // numerator = numerator * (wire_value + beta * s_id + gamma); + // denominator = denominator * (wire_value + beta * s_sigma + gamma); + // } + // let last = *plonk_z_points.last().unwrap(); + // plonk_z_points.push(last * numerator / denominator); + // } + // plonk_z_points } fn compute_vanishing_polys, const D: usize>( From a71909ba1555005f953753678359789f02039d5c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 15:49:21 +0200 Subject: [PATCH 02/20] Implement `compute_z` and rewrite of `compute_vanishing_polys` --- src/circuit_builder.rs | 15 ++-- src/circuit_data.rs | 5 +- src/polynomial/commitment.rs | 55 +++++++++++--- src/prover.rs | 139 +++++++++++++++++++---------------- src/witness.rs | 24 +++++- 5 files changed, 154 insertions(+), 84 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 02d34fba..6f10c726 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -292,19 +292,13 @@ impl, const D: usize> CircuitBuilder { let subgroup = F::two_adic_subgroup(degree_bits); let constant_vecs = self.constant_polys(); - let constants_commitment = ListPolynomialCommitment::new( - constant_vecs.into_iter().map(|v| v.ifft()).collect(), - self.config.fri_config.rate_bits, - false, - ); + let constants_commitment = + ListPolynomialCommitment::new(constant_vecs, self.config.fri_config.rate_bits, false); let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires); let sigma_vecs = self.sigma_vecs(&k_is, &subgroup); - let sigmas_commitment = ListPolynomialCommitment::new( - sigma_vecs.into_iter().map(|v| v.ifft()).collect(), - self.config.fri_config.rate_bits, - false, - ); + let sigmas_commitment = + ListPolynomialCommitment::new(sigma_vecs, self.config.fri_config.rate_bits, false); let constants_root = constants_commitment.merkle_tree.root; let sigmas_root = sigmas_commitment.merkle_tree.root; @@ -339,6 +333,7 @@ impl, const D: usize> CircuitBuilder { config: self.config, degree_bits, gates, + max_filtered_constraint_degree_bits: 3, // TODO: compute this correctly once filters land. num_gate_constraints, k_is, circuit_digest, diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 28dfbfac..3dc2d64d 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -143,6 +143,9 @@ pub(crate) struct CommonCircuitData, const D: usize> { /// The types of gates used in this circuit. pub(crate) gates: Vec>, + /// The largest number of constraints imposed by any gate. + pub(crate) max_filtered_constraint_degree_bits: usize, + /// The largest number of constraints imposed by any gate. pub(crate) num_gate_constraints: usize, @@ -176,7 +179,7 @@ impl, const D: usize> CommonCircuitData { } pub fn quotient_degree(&self) -> usize { - self.constraint_degree() - 1 + 1 << self.max_filtered_constraint_degree_bits - 1 } pub fn total_constraints(&self) -> usize { diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index c1fd08eb..b11e1ca2 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -9,37 +9,73 @@ 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::polynomial::polynomial::PolynomialCoeffs; +use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; -use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; +use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place, transpose}; pub const SALT_SIZE: usize = 2; pub struct ListPolynomialCommitment { pub polynomials: Vec>, + pub values: Vec>, pub merkle_tree: MerkleTree, pub degree: usize, + pub degree_log: usize, pub rate_bits: usize, pub blinding: bool, } impl ListPolynomialCommitment { - pub fn new(polynomials: Vec>, rate_bits: usize, blinding: bool) -> Self { - let degree = polynomials[0].len(); + /// Creates a list polynomial commitment for the polynomials interpolating the values in `values`. + pub fn new(values: Vec>, rate_bits: usize, blinding: bool) -> Self { + let degree = values[0].len(); + let polynomials = values.iter().map(|v| v.clone().ifft()).collect::>(); let lde_values = timed!( Self::lde_values(&polynomials, rate_bits, blinding), "to compute LDE" ); + Self::new_from_data(polynomials, values, lde_values, degree, rate_bits, blinding) + } + + /// Creates a list polynomial commitment for the polynomials `polynomials`. + pub fn new_from_polys( + polynomials: Vec>, + rate_bits: usize, + blinding: bool, + ) -> Self { + let degree = polynomials[0].len(); + let values = polynomials + .iter() + .map(|v| v.clone().fft()) + .collect::>(); + let lde_values = timed!( + Self::lde_values(&polynomials, rate_bits, blinding), + "to compute LDE" + ); + + Self::new_from_data(polynomials, values, lde_values, degree, rate_bits, blinding) + } + + fn new_from_data( + polynomials: Vec>, + values: Vec>, + lde_values: Vec>, + degree: usize, + rate_bits: usize, + blinding: bool, + ) -> Self { let mut leaves = timed!(transpose(&lde_values), "to transpose LDEs"); reverse_index_bits_in_place(&mut leaves); let merkle_tree = timed!(MerkleTree::new(leaves, false), "to build Merkle tree"); Self { polynomials, + values, merkle_tree, degree, + degree_log: log2_strict(degree), rate_bits, blinding, } @@ -71,9 +107,8 @@ impl ListPolynomialCommitment { .collect() } - pub fn leaf(&self, index: usize) -> &[F] { - let leaf = &self.merkle_tree.leaves[index]; - &leaf[0..leaf.len() - if self.blinding { SALT_SIZE } else { 0 }] + pub fn original_value(&self, index: usize) -> Vec { + self.values.iter().map(|v| v.values[index]).collect() } /// Takes the commitments to the constants - sigmas - wires - zs - quotient — polynomials, @@ -88,7 +123,7 @@ impl ListPolynomialCommitment { F: Extendable, { assert!(D > 1, "Not implemented for D=1."); - let degree_log = log2_strict(commitments[0].degree); + let degree_log = commitments[0].degree_log; let g = F::Extension::primitive_root_of_unity(degree_log); for p in &[zeta, g * zeta] { assert_ne!( @@ -267,11 +302,11 @@ mod tests { fn gen_random_test_case, const D: usize>( k: usize, degree_log: usize, - ) -> Vec> { + ) -> Vec> { let degree = 1 << degree_log; (0..k) - .map(|_| PolynomialCoeffs::new(F::rand_vec(degree))) + .map(|_| PolynomialValues::new(F::rand_vec(degree))) .collect() } diff --git a/src/prover.rs b/src/prover.rs index 116a6b24..08362ac3 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -17,7 +17,7 @@ use crate::timed; use crate::util::transpose; use crate::vars::EvaluationVarsBase; use crate::wire::Wire; -use crate::witness::PartialWitness; +use crate::witness::{PartialWitness, Witness}; /// Corresponds to constants - sigmas - wires - zs - quotient — polynomial commitments. pub const PLONK_BLINDING: [bool; 5] = [false, false, true, true, true]; @@ -31,10 +31,10 @@ pub(crate) fn prove, const D: usize>( let start_proof_gen = Instant::now(); - let mut witness = inputs; + let mut partial_witness = inputs; info!("Running {} generators", prover_data.generators.len()); timed!( - generate_partial_witness(&mut witness, &prover_data.generators), + generate_partial_witness(&mut partial_witness, &prover_data.generators), "to generate witness" ); @@ -42,12 +42,15 @@ pub(crate) fn prove, const D: usize>( let num_wires = config.num_wires; let num_challenges = config.num_challenges; let quotient_degree = common_data.quotient_degree(); - let degree = common_data.degree(); - let wires_polynomials: Vec> = timed!( - (0..num_wires) - .into_par_iter() - .map(|i| compute_wire_polynomial(i, &witness, degree)) + + let witness = partial_witness.full_witness(degree, num_wires); + + let wires_values: Vec> = timed!( + witness + .wire_values + .iter() + .map(|column| PolynomialValues::new(column.clone())) .collect(), "to compute wire polynomials" ); @@ -55,7 +58,7 @@ pub(crate) fn prove, const D: usize>( // TODO: Could try parallelizing the transpose, or not doing it explicitly, instead having // merkle_root_bit_rev_order do it implicitly. let wires_commitment = timed!( - ListPolynomialCommitment::new(wires_polynomials, fri_config.rate_bits, true), + ListPolynomialCommitment::new(wires_values, fri_config.rate_bits, true), "to compute wires commitment" ); @@ -68,7 +71,10 @@ pub(crate) fn prove, const D: usize>( let betas = challenger.get_n_challenges(num_challenges); let gammas = challenger.get_n_challenges(num_challenges); - let plonk_z_vecs = timed!(compute_zs(&common_data), "to compute Z's"); + let plonk_z_vecs = timed!( + compute_zs(&witness, &betas, &gammas, &prover_data, &common_data), + "to compute Z's" + ); let plonk_zs_commitment = timed!( ListPolynomialCommitment::new(plonk_z_vecs, fri_config.rate_bits, true), @@ -107,7 +113,11 @@ pub(crate) fn prove, const D: usize>( ); let quotient_polys_commitment = timed!( - ListPolynomialCommitment::new(all_quotient_poly_chunks, fri_config.rate_bits, true), + ListPolynomialCommitment::new_from_polys( + all_quotient_poly_chunks, + fri_config.rate_bits, + true + ), "to commit to quotient polys" ); @@ -146,37 +156,44 @@ pub(crate) fn prove, const D: usize>( } fn compute_zs, const D: usize>( + witness: &Witness, + betas: &[F], + gammas: &[F], + prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, -) -> Vec> { +) -> Vec> { (0..common_data.config.num_challenges) - .map(|i| compute_z(common_data, i)) + .map(|i| compute_z(witness, betas[i], gammas[i], prover_data, common_data)) .collect() } fn compute_z, const D: usize>( + witness: &Witness, + beta: F, + gamma: F, + prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, - _i: usize, -) -> PolynomialCoeffs { - todo!() - // let subgroup = - // let mut plonk_z_points = vec![F::ONE]; - // let k_is = common_data.k_is; - // for i in 1..common_data.degree() { - // let x = subgroup[i - 1]; - // let mut numerator = F::ONE; - // let mut denominator = F::ONE; - // for j in 0..NUM_ROUTED_WIRES { - // let wire_value = witness.get_indices(i - 1, j); - // let k_i = k_is[j]; - // let s_id = k_i * x; - // let s_sigma = sigma_values[j][8 * (i - 1)]; - // numerator = numerator * (wire_value + beta * s_id + gamma); - // denominator = denominator * (wire_value + beta * s_sigma + gamma); - // } - // let last = *plonk_z_points.last().unwrap(); - // plonk_z_points.push(last * numerator / denominator); - // } - // plonk_z_points +) -> PolynomialValues { + let subgroup = &prover_data.subgroup; + let mut plonk_z_points = vec![F::ONE]; + let k_is = &common_data.k_is; + for i in 1..common_data.degree() { + let x = subgroup[i - 1]; + let mut numerator = F::ONE; + let mut denominator = F::ONE; + let s_sigmas = prover_data.sigmas_commitment.original_value(i - 1); + for j in 0..common_data.config.num_routed_wires { + let wire_value = witness.get_wire(i - 1, j); + let k_i = k_is[j]; + let s_id = k_i * x; + let s_sigma = s_sigmas[j]; + numerator = numerator * (wire_value + beta * s_id + gamma); + denominator = denominator * (wire_value + beta * s_sigma + gamma); + } + let last = *plonk_z_points.last().unwrap(); + plonk_z_points.push(last * numerator / denominator); + } + plonk_z_points.into() } fn compute_vanishing_polys, const D: usize>( @@ -188,21 +205,38 @@ fn compute_vanishing_polys, const D: usize>( gammas: &[F], alphas: &[F], ) -> Vec> { - let lde_size = common_data.lde_size(); - let lde_gen = common_data.lde_generator(); let num_challenges = common_data.config.num_challenges; + let points = F::two_adic_subgroup( + common_data.degree_bits + common_data.max_filtered_constraint_degree_bits, + ); + let lde_size = points.len(); + + let commitment_to_lde = |comm: &ListPolynomialCommitment| -> Vec> { + comm.polynomials + .iter() + .map(|p| p.lde(common_data.max_filtered_constraint_degree_bits).fft()) + .collect() + }; + + let constants_lde = commitment_to_lde(&prover_data.constants_commitment); + let sigmas_lde = commitment_to_lde(&prover_data.sigmas_commitment); + let wires_lde = commitment_to_lde(wires_commitment); + let zs_lde = commitment_to_lde(plonk_zs_commitment); + + let get_at_index = |ldes: &[PolynomialValues], i: usize| { + ldes.iter().map(|l| l.values[i]).collect::>() + }; - let points = F::cyclic_subgroup_known_order(lde_gen, lde_size); let values: Vec> = points .into_par_iter() .enumerate() .map(|(i, x)| { let i_next = (i + 1) % lde_size; - let local_wires = wires_commitment.leaf(i); - let local_constants = prover_data.constants_commitment.leaf(i); - let local_plonk_zs = plonk_zs_commitment.leaf(i); - let next_plonk_zs = plonk_zs_commitment.leaf(i_next); - let s_sigmas = prover_data.sigmas_commitment.leaf(i); + let local_constants = &get_at_index(&constants_lde, i); + let s_sigmas = &get_at_index(&sigmas_lde, i); + let local_wires = &get_at_index(&wires_lde, i); + let local_plonk_zs = &get_at_index(&zs_lde, i); + let next_plonk_zs = &get_at_index(&zs_lde, i_next); debug_assert_eq!(local_wires.len(), common_data.config.num_wires); debug_assert_eq!(local_plonk_zs.len(), num_challenges); @@ -230,22 +264,3 @@ fn compute_vanishing_polys, const D: usize>( .map(PolynomialValues::new) .collect() } - -fn compute_wire_polynomial( - input: usize, - witness: &PartialWitness, - degree: usize, -) -> PolynomialCoeffs { - let wire_values = (0..degree) - // Some gates do not use all wires, and we do not require that generators populate unused - // wires, so some wire values will not be set. We can set these to any value; here we - // arbitrary pick zero. Ideally we would verify that no constraints operate on these unset - // wires, but that isn't trivial. - .map(|gate| { - witness - .try_get_wire(Wire { gate, input }) - .unwrap_or(F::ZERO) - }) - .collect(); - PolynomialValues::new(wire_values).ifft() -} diff --git a/src/witness.rs b/src/witness.rs index e71b1cfb..a870059b 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -1,11 +1,23 @@ use std::collections::HashMap; +use std::convert::TryInto; +use crate::circuit_data::{CircuitConfig, CommonCircuitData}; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; use crate::target::Target; use crate::wire::Wire; -use std::convert::TryInto; + +#[derive(Clone, Debug)] +pub struct Witness { + pub(crate) wire_values: Vec>, +} + +impl Witness { + pub fn get_wire(&self, gate: usize, input: usize) -> F { + self.wire_values[input][gate] + } +} #[derive(Clone, Debug)] pub struct PartialWitness { @@ -121,6 +133,16 @@ impl PartialWitness { self.set_target(target, value); } } + + pub fn full_witness(self, degree: usize, num_wires: usize) -> Witness { + let mut wire_values = vec![vec![F::ZERO; degree]; num_wires]; + self.target_values.into_iter().for_each(|(t, v)| { + if let Target::Wire(Wire { gate, input }) = t { + wire_values[input][gate] = v; + } + }); + Witness { wire_values } + } } impl Default for PartialWitness { From ad5c18b4997451f27cca9647a2f0eafb548013ef Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 16:23:15 +0200 Subject: [PATCH 03/20] Comments and Clippy --- src/circuit_data.rs | 2 +- src/field/extension_field/quadratic.rs | 2 +- src/polynomial/commitment.rs | 4 ++-- src/polynomial/polynomial.rs | 7 ++++++- src/prover.rs | 20 ++++++++++++++------ src/witness.rs | 1 - 6 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 3dc2d64d..aa734650 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -179,7 +179,7 @@ impl, const D: usize> CommonCircuitData { } pub fn quotient_degree(&self) -> usize { - 1 << self.max_filtered_constraint_degree_bits - 1 + ((1 << self.max_filtered_constraint_degree_bits) - 1) * self.degree() } pub fn total_constraints(&self) -> usize { diff --git a/src/field/extension_field/quadratic.rs b/src/field/extension_field/quadratic.rs index 4dc712af..af21ad60 100644 --- a/src/field/extension_field/quadratic.rs +++ b/src/field/extension_field/quadratic.rs @@ -194,7 +194,7 @@ impl DivAssign for QuadraticCrandallField { #[cfg(test)] mod tests { use crate::field::extension_field::quadratic::QuadraticCrandallField; - use crate::field::extension_field::{FieldExtension, Frobenius, OEF}; + use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; #[test] diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index b11e1ca2..5cd1f219 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -12,7 +12,7 @@ use crate::plonk_common::{reduce_polys_with_iter, reduce_with_iter}; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; -use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place, transpose}; +use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; pub const SALT_SIZE: usize = 2; @@ -107,7 +107,7 @@ impl ListPolynomialCommitment { .collect() } - pub fn original_value(&self, index: usize) -> Vec { + pub fn original_values(&self, index: usize) -> Vec { self.values.iter().map(|v| v.values[index]).collect() } diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index 9f605051..0660be1a 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -128,7 +128,12 @@ impl PolynomialCoeffs { } pub(crate) fn padded(&self, new_len: usize) -> Self { - assert!(new_len >= self.len()); + assert!( + new_len >= self.len(), + "Trying to pad a polynomial of length {} to a length of {}.", + self.len(), + new_len + ); let mut coeffs = self.coeffs.clone(); coeffs.resize(new_len, F::ZERO); Self { coeffs } diff --git a/src/prover.rs b/src/prover.rs index 08362ac3..6371d1fd 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -6,17 +6,15 @@ use rayon::prelude::*; use crate::circuit_data::{CommonCircuitData, ProverOnlyCircuitData}; use crate::field::extension_field::Extendable; use crate::field::fft::ifft; -use crate::field::field::Field; use crate::generator::generate_partial_witness; use crate::plonk_challenger::Challenger; use crate::plonk_common::eval_vanishing_poly_base; use crate::polynomial::commitment::ListPolynomialCommitment; -use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; +use crate::polynomial::polynomial::PolynomialValues; use crate::proof::Proof; use crate::timed; use crate::util::transpose; use crate::vars::EvaluationVarsBase; -use crate::wire::Wire; use crate::witness::{PartialWitness, Witness}; /// Corresponds to constants - sigmas - wires - zs - quotient — polynomial commitments. @@ -104,7 +102,15 @@ pub(crate) fn prove, const D: usize>( .into_par_iter() .flat_map(|vanishing_poly| { let vanishing_poly_coeff = ifft(vanishing_poly); + // TODO: run `padded` when the division works. let quotient_poly_coeff = vanishing_poly_coeff.divide_by_z_h(degree); + let x = F::rand(); + assert!( + quotient_poly_coeff.eval(x) * (x.exp(degree as u64) - F::ONE) + != vanishing_poly_coeff.eval(x), + "That's good news, this should fail! The division by z_h doesn't work yet,\ + most likely because compute_vanishing_polys isn't complete (doesn't use filters for example)." + ); // Split t into degree-n chunks. quotient_poly_coeff.chunks(degree) }) @@ -181,14 +187,14 @@ fn compute_z, const D: usize>( let x = subgroup[i - 1]; let mut numerator = F::ONE; let mut denominator = F::ONE; - let s_sigmas = prover_data.sigmas_commitment.original_value(i - 1); + let s_sigmas = prover_data.sigmas_commitment.original_values(i - 1); for j in 0..common_data.config.num_routed_wires { let wire_value = witness.get_wire(i - 1, j); let k_i = k_is[j]; let s_id = k_i * x; let s_sigma = s_sigmas[j]; - numerator = numerator * (wire_value + beta * s_id + gamma); - denominator = denominator * (wire_value + beta * s_sigma + gamma); + numerator *= wire_value + beta * s_id + gamma; + denominator *= wire_value + beta * s_sigma + gamma; } let last = *plonk_z_points.last().unwrap(); plonk_z_points.push(last * numerator / denominator); @@ -211,6 +217,7 @@ fn compute_vanishing_polys, const D: usize>( ); let lde_size = points.len(); + // Low-degree extend the polynomials commited in `comm` to the subgroup of size `lde_size`. let commitment_to_lde = |comm: &ListPolynomialCommitment| -> Vec> { comm.polynomials .iter() @@ -223,6 +230,7 @@ fn compute_vanishing_polys, const D: usize>( let wires_lde = commitment_to_lde(wires_commitment); let zs_lde = commitment_to_lde(plonk_zs_commitment); + // Retrieve the polynomial values at index `i`. let get_at_index = |ldes: &[PolynomialValues], i: usize| { ldes.iter().map(|l| l.values[i]).collect::>() }; diff --git a/src/witness.rs b/src/witness.rs index a870059b..ad810af8 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use std::convert::TryInto; -use crate::circuit_data::{CircuitConfig, CommonCircuitData}; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; From 79e99148ef4882e548d695f0b139fccaad79fc93 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 17 Jun 2021 16:31:24 +0200 Subject: [PATCH 04/20] Minor --- src/circuit_data.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/circuit_data.rs b/src/circuit_data.rs index aa734650..736eb933 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -143,7 +143,7 @@ pub(crate) struct CommonCircuitData, const D: usize> { /// The types of gates used in this circuit. pub(crate) gates: Vec>, - /// The largest number of constraints imposed by any gate. + /// The maximum degree of a filter times a constraint by any gate. pub(crate) max_filtered_constraint_degree_bits: usize, /// The largest number of constraints imposed by any gate. From 4ee70e449b705b2b56bfd9946f28a43a6f515782 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 11:00:47 +0200 Subject: [PATCH 05/20] Fix type errors and move copy constraints check to `Witness` --- src/prover.rs | 22 +++++++++--------- src/witness.rs | 60 +++++++++++++++++++++++++++++--------------------- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/prover.rs b/src/prover.rs index 7f0e5399..16f63fdb 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -23,6 +23,11 @@ pub(crate) fn prove, const D: usize>( inputs: PartialWitness, ) -> Proof { let fri_config = &common_data.config.fri_config; + let config = &common_data.config; + let num_wires = config.num_wires; + let num_challenges = config.num_challenges; + let quotient_degree = common_data.quotient_degree(); + let degree = common_data.degree(); let start_proof_gen = Instant::now(); @@ -33,6 +38,11 @@ pub(crate) fn prove, const D: usize>( "to generate witness" ); + let witness = timed!( + partial_witness.full_witness(degree, num_wires), + "to compute full witness" + ); + timed!( witness .check_copy_constraints(&prover_data.copy_constraints, &prover_data.gate_instances) @@ -40,14 +50,6 @@ pub(crate) fn prove, const D: usize>( "to check copy constraints" ); - let config = &common_data.config; - let num_wires = config.num_wires; - let num_challenges = config.num_challenges; - let quotient_degree = common_data.quotient_degree(); - let degree = common_data.degree(); - - let witness = partial_witness.full_witness(degree, num_wires); - let wires_values: Vec> = timed!( witness .wire_values @@ -169,7 +171,7 @@ fn compute_zs, const D: usize>( witness: &Witness, betas: &[F], gammas: &[F], - prover_data: &ProverOnlyCircuitData, + prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, ) -> Vec> { (0..common_data.config.num_challenges) @@ -181,7 +183,7 @@ fn compute_z, const D: usize>( witness: &Witness, beta: F, gamma: F, - prover_data: &ProverOnlyCircuitData, + prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, ) -> PolynomialValues { let subgroup = &prover_data.subgroup; diff --git a/src/witness.rs b/src/witness.rs index a0e29268..7294f6e4 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -19,6 +19,41 @@ impl Witness { pub fn get_wire(&self, gate: usize, input: usize) -> F { self.wire_values[input][gate] } + + /// Checks that the copy constraints are satisfied in the witness. + pub fn check_copy_constraints( + &self, + copy_constraints: &[(Target, Target)], + gate_instances: &[GateInstance], + ) -> Result<()> + where + F: Extendable, + { + for &(a, b) in copy_constraints { + // TODO: Take care of public inputs once they land. + if let ( + Target::Wire(Wire { + gate: a_gate, + input: a_input, + }), + Target::Wire(Wire { + gate: b_gate, + input: b_input, + }), + ) = (a, b) + { + let va = self.get_wire(a_gate, a_input); + let vb = self.get_wire(b_gate, b_input); + ensure!( + va == vb, + "Copy constraint between wire {} of gate #{} (`{}`) and wire {} of gate #{} (`{}`) is not satisfied. \ + Got values of {} and {} respectively.", + a_input, a_gate, gate_instances[a_gate].gate_type.0.id(), b_input, b_gate, + gate_instances[b_gate].gate_type.0.id(), va, vb); + } + } + Ok(()) + } } #[derive(Clone, Debug)] @@ -145,31 +180,6 @@ impl PartialWitness { }); Witness { wire_values } } - - /// Checks that the copy constraints are satisfied in the witness. - pub fn check_copy_constraints( - &self, - copy_constraints: &[(Target, Target)], - gate_instances: &[GateInstance], - ) -> Result<()> - where - F: Extendable, - { - for &(a, b) in copy_constraints { - // TODO: Take care of public inputs once they land. - if let (Target::Wire(wa), Target::Wire(wb)) = (a, b) { - let va = self.target_values.get(&a).copied().unwrap_or(F::ZERO); - let vb = self.target_values.get(&b).copied().unwrap_or(F::ZERO); - ensure!( - va == vb, - "Copy constraint between wire {} of gate #{} (`{}`) and wire {} of gate #{} (`{}`) is not satisfied. \ - Got values of {} and {} respectively.", - wa.input, wa.gate, gate_instances[wa.gate].gate_type.0.id(), wb.input, wb.gate, - gate_instances[wb.gate].gate_type.0.id(), va, vb); - } - } - Ok(()) - } } impl Default for PartialWitness { From f215dffa9d357c4c9b64ed468ad321582a7e0909 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 11:45:16 +0200 Subject: [PATCH 06/20] Compute quotient directly --- src/polynomial/commitment.rs | 7 ++- src/polynomial/polynomial.rs | 24 +++++++-- src/prover.rs | 94 ++++++++++++++++++------------------ 3 files changed, 73 insertions(+), 52 deletions(-) diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 19b3f4b9..e403cd36 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -13,7 +13,7 @@ use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; use crate::util::scaling::ReducingFactor; -use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; +use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place, transpose}; pub const SALT_SIZE: usize = 2; @@ -111,6 +111,11 @@ impl ListPolynomialCommitment { pub fn original_values(&self, index: usize) -> Vec { self.values.iter().map(|v| v.values[index]).collect() } + pub fn get_lde_values(&self, mut index: usize) -> &[F] { + reverse_bits(index, self.degree_log + self.rate_bits); + let slice = &self.merkle_tree.leaves[index]; + &slice[..slice.len() - if self.blinding { SALT_SIZE } else { 0 }] + } /// Takes the commitments to the constants - sigmas - wires - zs - quotient — polynomials, /// and an opening point `zeta` and produces a batched opening proof + opening set. diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index 54c96c7e..e176aaf8 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -34,6 +34,18 @@ impl PolynomialValues { ifft(self) } + pub fn coset_ifft(self, shift: F) -> PolynomialCoeffs { + let mut shifted_coeffs = self.ifft(); + shifted_coeffs + .coeffs + .iter_mut() + .zip(shift.inverse().powers()) + .for_each(|(c, r)| { + *c *= r; + }); + shifted_coeffs + } + pub fn lde_multiple(polys: Vec, rate_bits: usize) -> Vec { polys.into_iter().map(|p| p.lde(rate_bits)).collect() } @@ -127,16 +139,20 @@ impl PolynomialCoeffs { self.padded(self.len() << rate_bits) } - pub(crate) fn padded(&self, new_len: usize) -> Self { + pub(crate) fn pad(&mut self, new_len: usize) { assert!( new_len >= self.len(), "Trying to pad a polynomial of length {} to a length of {}.", self.len(), new_len ); - let mut coeffs = self.coeffs.clone(); - coeffs.resize(new_len, F::ZERO); - Self { coeffs } + self.coeffs.resize(new_len, F::ZERO); + } + + pub(crate) fn padded(&self, new_len: usize) -> Self { + let mut poly = self.clone(); + poly.pad(new_len); + poly } /// Removes leading zero coefficients. diff --git a/src/prover.rs b/src/prover.rs index 16f63fdb..cd03f6e7 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -10,7 +10,7 @@ use crate::generator::generate_partial_witness; use crate::plonk_challenger::Challenger; use crate::plonk_common::eval_vanishing_poly_base; use crate::polynomial::commitment::ListPolynomialCommitment; -use crate::polynomial::polynomial::PolynomialValues; +use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::Proof; use crate::timed; use crate::util::transpose; @@ -89,8 +89,8 @@ pub(crate) fn prove, const D: usize>( let alphas = challenger.get_n_challenges(num_challenges); - let vanishing_polys = timed!( - compute_vanishing_polys( + let quotient_polys = timed!( + compute_quotient_polys( common_data, prover_data, &wires_commitment, @@ -104,21 +104,13 @@ pub(crate) fn prove, const D: usize>( // Compute the quotient polynomials, aka `t` in the Plonk paper. let all_quotient_poly_chunks = timed!( - vanishing_polys + quotient_polys .into_par_iter() - .flat_map(|vanishing_poly| { - let vanishing_poly_coeff = ifft(vanishing_poly); - // TODO: run `padded` when the division works. - let quotient_poly_coeff = vanishing_poly_coeff.divide_by_z_h(degree); - let x = F::rand(); - assert!( - quotient_poly_coeff.eval(x) * (x.exp(degree as u64) - F::ONE) - != vanishing_poly_coeff.eval(x), - "That's good news, this should fail! The division by z_h doesn't work yet,\ - most likely because compute_vanishing_polys isn't complete (doesn't use filters for example)." - ); + .flat_map(|mut quotient_poly| { + quotient_poly.trim(); + quotient_poly.pad(quotient_degree); // Split t into degree-n chunks. - quotient_poly_coeff.chunks(degree) + quotient_poly.chunks(degree) }) .collect(), "to compute quotient polys" @@ -208,49 +200,50 @@ fn compute_z, const D: usize>( plonk_z_points.into() } -fn compute_vanishing_polys, const D: usize>( +fn compute_quotient_polys<'a, F: Extendable, const D: usize>( common_data: &CommonCircuitData, - prover_data: &ProverOnlyCircuitData, - wires_commitment: &ListPolynomialCommitment, - plonk_zs_commitment: &ListPolynomialCommitment, + prover_data: &'a ProverOnlyCircuitData, + wires_commitment: &'a ListPolynomialCommitment, + plonk_zs_commitment: &'a ListPolynomialCommitment, betas: &[F], gammas: &[F], alphas: &[F], -) -> Vec> { +) -> Vec> { let num_challenges = common_data.config.num_challenges; + assert!( + common_data.max_filtered_constraint_degree_bits <= common_data.config.rate_bits, + "Having constraints of degree higher than the rate is not supported yet. \ + If we need this in the future, we can precompute the larger LDE before computing the `ListPolynomialCommitment`s." + ); + + // We reuse the LDE computed in `ListPolynomialCommitment` and extract every `step` points to get + // an LDE matching `max_filtered_constraint_degree`. + let step = + 1 << (common_data.config.rate_bits - common_data.max_filtered_constraint_degree_bits); + // When opening the `Z`s polys at the "next" point in Plonk, need to look at the point `next_step` + // steps away since we work on an LDE of degree `max_filtered_constraint_degree`. + let next_step = 1 << common_data.max_filtered_constraint_degree_bits; + let points = F::two_adic_subgroup( common_data.degree_bits + common_data.max_filtered_constraint_degree_bits, ); let lde_size = points.len(); - // Low-degree extend the polynomials commited in `comm` to the subgroup of size `lde_size`. - let commitment_to_lde = |comm: &ListPolynomialCommitment| -> Vec> { - comm.polynomials - .iter() - .map(|p| p.lde(common_data.max_filtered_constraint_degree_bits).fft()) - .collect() + // Retrieve the LDE values at index `i`. + let get_at_index = |comm: &'a ListPolynomialCommitment, i: usize| -> &'a [F] { + comm.get_lde_values(i * step) }; - let constants_lde = commitment_to_lde(&prover_data.constants_commitment); - let sigmas_lde = commitment_to_lde(&prover_data.sigmas_commitment); - let wires_lde = commitment_to_lde(wires_commitment); - let zs_lde = commitment_to_lde(plonk_zs_commitment); - - // Retrieve the polynomial values at index `i`. - let get_at_index = |ldes: &[PolynomialValues], i: usize| { - ldes.iter().map(|l| l.values[i]).collect::>() - }; - - let values: Vec> = points + let quotient_values: Vec> = points .into_par_iter() .enumerate() .map(|(i, x)| { - let i_next = (i + 1) % lde_size; - let local_constants = &get_at_index(&constants_lde, i); - let s_sigmas = &get_at_index(&sigmas_lde, i); - let local_wires = &get_at_index(&wires_lde, i); - let local_plonk_zs = &get_at_index(&zs_lde, i); - let next_plonk_zs = &get_at_index(&zs_lde, i_next); + let i_next = (i + next_step) % lde_size; + let local_constants = get_at_index(&prover_data.constants_commitment, i); + let s_sigmas = get_at_index(&prover_data.sigmas_commitment, i); + let local_wires = get_at_index(&wires_commitment, i); + let local_plonk_zs = get_at_index(&plonk_zs_commitment, i); + let next_plonk_zs = get_at_index(&plonk_zs_commitment, i_next); debug_assert_eq!(local_wires.len(), common_data.config.num_wires); debug_assert_eq!(local_plonk_zs.len(), num_challenges); @@ -259,7 +252,7 @@ fn compute_vanishing_polys, const D: usize>( local_constants, local_wires, }; - eval_vanishing_poly_base( + let mut quotient_values = eval_vanishing_poly_base( common_data, x, vars, @@ -269,12 +262,19 @@ fn compute_vanishing_polys, const D: usize>( betas, gammas, alphas, - ) + ); + // TODO: We can avoid computing the exp. + let denominator_inv = x.exp(common_data.degree() as u64).inverse(); + quotient_values + .iter_mut() + .for_each(|v| *v *= denominator_inv); + quotient_values }) .collect(); - transpose(&values) + transpose("ient_values) .into_iter() .map(PolynomialValues::new) + .map(|values| values.coset_ifft(F::MULTIPLICATIVE_GROUP_GENERATOR)) .collect() } From 31f4eee367fc8f6aa3210385aa52c22bceccda61 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 14:11:47 +0200 Subject: [PATCH 07/20] Fix bug with shifted `x` --- src/field/extension_field/mod.rs | 3 ++- src/field/field.rs | 4 ++++ src/gates/gate.rs | 7 +++++++ src/polynomial/commitment.rs | 9 +++------ src/prover.rs | 7 ++++--- src/vars.rs | 4 ++-- 6 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/field/extension_field/mod.rs b/src/field/extension_field/mod.rs index 9caa7dc8..2a176fe9 100644 --- a/src/field/extension_field/mod.rs +++ b/src/field/extension_field/mod.rs @@ -1,6 +1,7 @@ -use crate::field::field::Field; use std::convert::TryInto; +use crate::field::field::Field; + pub mod algebra; pub mod quadratic; pub mod quartic; diff --git a/src/field/field.rs b/src/field/field.rs index b19f175e..3156d2c3 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -266,6 +266,10 @@ pub trait Field: fn rand_vec(n: usize) -> Vec { (0..n).map(|_| Self::rand()).collect() } + + fn coset_shift() -> Self { + Self::MULTIPLICATIVE_GROUP_GENERATOR + } } /// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`. diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 1765191e..cb8decef 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -1,9 +1,16 @@ +use std::borrow::Borrow; +use std::collections::HashMap; +use std::fmt::{Debug, Error, Formatter}; use std::hash::{Hash, Hasher}; +use std::iter::FromIterator; +use std::ops::Index; use std::sync::Arc; use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; +use crate::field::field::Field; +use crate::gates::gate_tree::Tree; use crate::generator::WitnessGenerator; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index e403cd36..aa70fd68 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -92,10 +92,7 @@ impl ListPolynomialCommitment { .par_iter() .map(|p| { assert_eq!(p.len(), degree, "Polynomial degree invalid."); - p.clone() - .lde(rate_bits) - .coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR) - .values + p.clone().lde(rate_bits).coset_fft(F::coset_shift()).values }) .chain(if blinding { // If blinding, salt with two random elements to each leaf vector. @@ -111,8 +108,8 @@ impl ListPolynomialCommitment { pub fn original_values(&self, index: usize) -> Vec { self.values.iter().map(|v| v.values[index]).collect() } - pub fn get_lde_values(&self, mut index: usize) -> &[F] { - reverse_bits(index, self.degree_log + self.rate_bits); + pub fn get_lde_values(&self, index: usize) -> &[F] { + let index = reverse_bits(index, self.degree_log + self.rate_bits); let slice = &self.merkle_tree.leaves[index]; &slice[..slice.len() - if self.blinding { SALT_SIZE } else { 0 }] } diff --git a/src/prover.rs b/src/prover.rs index cd03f6e7..c0cc3d75 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -238,6 +238,7 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( .into_par_iter() .enumerate() .map(|(i, x)| { + let shifted_x = F::coset_shift() * x; let i_next = (i + next_step) % lde_size; let local_constants = get_at_index(&prover_data.constants_commitment, i); let s_sigmas = get_at_index(&prover_data.sigmas_commitment, i); @@ -254,7 +255,7 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( }; let mut quotient_values = eval_vanishing_poly_base( common_data, - x, + shifted_x, vars, local_plonk_zs, next_plonk_zs, @@ -264,7 +265,7 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( alphas, ); // TODO: We can avoid computing the exp. - let denominator_inv = x.exp(common_data.degree() as u64).inverse(); + let denominator_inv = (shifted_x.exp(common_data.degree() as u64) - F::ONE).inverse(); quotient_values .iter_mut() .for_each(|v| *v *= denominator_inv); @@ -275,6 +276,6 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( transpose("ient_values) .into_iter() .map(PolynomialValues::new) - .map(|values| values.coset_ifft(F::MULTIPLICATIVE_GROUP_GENERATOR)) + .map(|values| values.coset_ifft(F::coset_shift())) .collect() } diff --git a/src/vars.rs b/src/vars.rs index 74f15f23..9815dcce 100644 --- a/src/vars.rs +++ b/src/vars.rs @@ -6,13 +6,13 @@ use crate::field::extension_field::target::{ExtensionAlgebraTarget, ExtensionTar use crate::field::extension_field::Extendable; use crate::field::field::Field; -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub struct EvaluationVars<'a, F: Extendable, const D: usize> { pub(crate) local_constants: &'a [F::Extension], pub(crate) local_wires: &'a [F::Extension], } -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub struct EvaluationVarsBase<'a, F: Field> { pub(crate) local_constants: &'a [F], pub(crate) local_wires: &'a [F], From b0550979a62697b5199212d08eea444a073ecd9d Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 15:42:29 +0200 Subject: [PATCH 08/20] Optimize evaluation of `Z_H` on coset. --- src/field/field.rs | 13 ++++++++---- src/gates/gate.rs | 4 ---- src/plonk_common.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++- src/prover.rs | 12 ++++++++--- 4 files changed, 66 insertions(+), 12 deletions(-) diff --git a/src/field/field.rs b/src/field/field.rs index 3156d2c3..081423c8 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -104,10 +104,7 @@ pub trait Field: fn primitive_root_of_unity(n_log: usize) -> Self { assert!(n_log <= Self::TWO_ADICITY); let mut base = Self::POWER_OF_TWO_GENERATOR; - for _ in n_log..Self::TWO_ADICITY { - base = base.square(); - } - base + base.exp_power_of_2(Self::TWO_ADICITY - n_log) } /// Computes a multiplicative subgroup whose order is known in advance. @@ -158,6 +155,14 @@ pub trait Field: bits_u64(self.to_canonical_u64()) } + fn exp_power_of_2(&self, power_log: usize) -> Self { + let mut res = *self; + for _ in 0..power_log { + res = res.square(); + } + res + } + fn exp(&self, power: u64) -> Self { let mut current = *self; let mut product = Self::ONE; diff --git a/src/gates/gate.rs b/src/gates/gate.rs index cb8decef..83b799be 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -1,6 +1,3 @@ -use std::borrow::Borrow; -use std::collections::HashMap; -use std::fmt::{Debug, Error, Formatter}; use std::hash::{Hash, Hasher}; use std::iter::FromIterator; use std::ops::Index; @@ -10,7 +7,6 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; -use crate::gates::gate_tree::Tree; use crate::generator::WitnessGenerator; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; diff --git a/src/plonk_common.rs b/src/plonk_common.rs index c9f11b74..b92a4c57 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -116,6 +116,7 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( /// Like `eval_vanishing_poly`, but specialized for base field points. pub(crate) fn eval_vanishing_poly_base, const D: usize>( common_data: &CommonCircuitData, + i: usize, x: F, vars: EvaluationVarsBase, local_plonk_zs: &[F], @@ -124,6 +125,7 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( betas: &[F], gammas: &[F], alphas: &[F], + z_h_on_coset: &ZeroPolyOnCoset, ) -> Vec { let constraint_terms = evaluate_gate_constraints_base(&common_data.gates, common_data.num_gate_constraints, vars); @@ -136,7 +138,7 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( for i in 0..common_data.config.num_challenges { let z_x = local_plonk_zs[i]; let z_gz = next_plonk_zs[i]; - vanishing_z_1_terms.push(eval_l_1(common_data.degree(), x) * (z_x - F::ONE)); + vanishing_z_1_terms.push(z_h_on_coset.eval_l1(i, x) * (z_x - F::ONE)); let mut f_prime = F::ONE; let mut g_prime = F::ONE; @@ -226,6 +228,51 @@ pub(crate) fn eval_zero_poly(n: usize, x: F) -> F { x.exp(n as u64) - F::ONE } +/// Precomputations of the evaluation of `Z_H(X) = X^n - 1` on a coset `gK` with `H <= K`. +pub(crate) struct ZeroPolyOnCoset { + /// `n = |H|`. + n: F, + /// `rate = |K|/|H|`. + rate: usize, + /// Holds `g^n * (w^n)^i - 1 = g^n * v^i - 1` for `i in 0..rate`, with `w` a generator of `K` and `v` a + /// `rate`-primitive root of unity. + evals: Vec, + /// Holds the multiplicative inverses of `evals`. + inverses: Vec, +} +impl ZeroPolyOnCoset { + pub fn new(n_log: usize, rate_bits: usize) -> Self { + let g_pow_n = F::coset_shift().exp_power_of_2(n_log); + let evals = F::two_adic_subgroup(rate_bits) + .into_iter() + .map(|x| g_pow_n * x - F::ONE) + .collect::>(); + let inverses = F::batch_multiplicative_inverse(&evals); + Self { + n: F::from_canonical_usize(1 << n_log), + rate: 1 << rate_bits, + evals, + inverses, + } + } + + /// Returns `Z_H(g * w^i)`. + pub fn eval(&self, i: usize) -> F { + self.evals[i % self.rate] + } + + /// Returns `Z_H(g * w^i)`. + pub fn eval_inverse(&self, i: usize) -> F { + self.inverses[i % self.rate] + } + + /// Returns `L_1(x) = Z_H(x)/(n * (x - 1))` with `x = w^i`. + pub fn eval_l1(&self, i: usize, x: F) -> F { + // Could also precompute the inverses using Montgomery. + self.eval(i) * (self.n * (x - F::ONE)).inverse() + } +} + /// Evaluate the Lagrange basis `L_1` with `L_1(1) = 1`, and `L_1(x) = 0` for other members of an /// order `n` multiplicative subgroup. pub(crate) fn eval_l_1(n: usize, x: F) -> F { diff --git a/src/prover.rs b/src/prover.rs index c0cc3d75..06a215e5 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -8,7 +8,7 @@ use crate::field::extension_field::Extendable; use crate::field::fft::ifft; use crate::generator::generate_partial_witness; use crate::plonk_challenger::Challenger; -use crate::plonk_common::eval_vanishing_poly_base; +use crate::plonk_common::{eval_vanishing_poly_base, ZeroPolyOnCoset}; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::Proof; @@ -234,6 +234,11 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( comm.get_lde_values(i * step) }; + let z_h_on_coset = ZeroPolyOnCoset::new( + common_data.degree_bits, + common_data.max_filtered_constraint_degree_bits, + ); + let quotient_values: Vec> = points .into_par_iter() .enumerate() @@ -255,6 +260,7 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( }; let mut quotient_values = eval_vanishing_poly_base( common_data, + i, shifted_x, vars, local_plonk_zs, @@ -263,9 +269,9 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( betas, gammas, alphas, + &z_h_on_coset, ); - // TODO: We can avoid computing the exp. - let denominator_inv = (shifted_x.exp(common_data.degree() as u64) - F::ONE).inverse(); + let denominator_inv = z_h_on_coset.eval_inverse(i); quotient_values .iter_mut() .for_each(|v| *v *= denominator_inv); From 35f73a505f77ac3df36bf2d68ed3c2e485a63d7a Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 15:49:36 +0200 Subject: [PATCH 09/20] Clippy --- src/gates/gate.rs | 3 --- src/generator.rs | 1 - src/plonk_common.rs | 4 ++-- src/polynomial/commitment.rs | 1 - src/prover.rs | 9 ++++----- 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 83b799be..1765191e 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -1,12 +1,9 @@ use std::hash::{Hash, Hasher}; -use std::iter::FromIterator; -use std::ops::Index; use std::sync::Arc; use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; -use crate::field::field::Field; use crate::generator::WitnessGenerator; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; diff --git a/src/generator.rs b/src/generator.rs index db81172f..d760df05 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -2,7 +2,6 @@ use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use crate::field::field::Field; -use crate::permutation_argument::TargetPartitions; use crate::target::Target; use crate::witness::PartialWitness; diff --git a/src/plonk_common.rs b/src/plonk_common.rs index b92a4c57..731d5741 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -116,7 +116,7 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( /// Like `eval_vanishing_poly`, but specialized for base field points. pub(crate) fn eval_vanishing_poly_base, const D: usize>( common_data: &CommonCircuitData, - i: usize, + index: usize, x: F, vars: EvaluationVarsBase, local_plonk_zs: &[F], @@ -138,7 +138,7 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( for i in 0..common_data.config.num_challenges { let z_x = local_plonk_zs[i]; let z_gz = next_plonk_zs[i]; - vanishing_z_1_terms.push(z_h_on_coset.eval_l1(i, x) * (z_x - F::ONE)); + vanishing_z_1_terms.push(z_h_on_coset.eval_l1(index, x) * (z_x - F::ONE)); let mut f_prime = F::ONE; let mut g_prime = F::ONE; diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index aa70fd68..5ace678c 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -8,7 +8,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::PlonkPolynomials; -use crate::plonk_common::{reduce_polys_with_iter, reduce_with_iter}; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; diff --git a/src/prover.rs b/src/prover.rs index 06a215e5..6ea0b8fc 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -5,7 +5,6 @@ use rayon::prelude::*; use crate::circuit_data::{CommonCircuitData, ProverOnlyCircuitData}; use crate::field::extension_field::Extendable; -use crate::field::fft::ifft; use crate::generator::generate_partial_witness; use crate::plonk_challenger::Challenger; use crate::plonk_common::{eval_vanishing_poly_base, ZeroPolyOnCoset}; @@ -76,7 +75,7 @@ pub(crate) fn prove, const D: usize>( let gammas = challenger.get_n_challenges(num_challenges); let plonk_z_vecs = timed!( - compute_zs(&witness, &betas, &gammas, &prover_data, &common_data), + compute_zs(&witness, &betas, &gammas, prover_data, common_data), "to compute Z's" ); @@ -247,9 +246,9 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( let i_next = (i + next_step) % lde_size; let local_constants = get_at_index(&prover_data.constants_commitment, i); let s_sigmas = get_at_index(&prover_data.sigmas_commitment, i); - let local_wires = get_at_index(&wires_commitment, i); - let local_plonk_zs = get_at_index(&plonk_zs_commitment, i); - let next_plonk_zs = get_at_index(&plonk_zs_commitment, i_next); + let local_wires = get_at_index(wires_commitment, i); + let local_plonk_zs = get_at_index(plonk_zs_commitment, i); + let next_plonk_zs = get_at_index(plonk_zs_commitment, i_next); debug_assert_eq!(local_wires.len(), common_data.config.num_wires); debug_assert_eq!(local_plonk_zs.len(), num_challenges); From 6605ca9d8950c50b09a249a3e63e50401c9d49bd Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 21:05:59 +0200 Subject: [PATCH 10/20] Add comment for `coset_shift` --- src/field/field.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/field/field.rs b/src/field/field.rs index 081423c8..516012d2 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -272,6 +272,7 @@ pub trait Field: (0..n).map(|_| Self::rand()).collect() } + /// Representative `g` of the coset used in FRI, so that LDEs in FRI are done over `gH`. fn coset_shift() -> Self { Self::MULTIPLICATIVE_GROUP_GENERATOR } From 19e7cb3942e86c372ba9d414fd453181e03e730c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 22:32:52 +0200 Subject: [PATCH 11/20] `into_iter` -> `into_par_iter` --- src/prover.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prover.rs b/src/prover.rs index 6ea0b8fc..67139128 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -279,7 +279,7 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( .collect(); transpose("ient_values) - .into_iter() + .into_par_iter() .map(PolynomialValues::new) .map(|values| values.coset_ifft(F::coset_shift())) .collect() From 54a15c012c1b936b634d5a0d564eb3f684ba185e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 22:38:40 +0200 Subject: [PATCH 12/20] Fixed bug and add division test in the base field. --- src/circuit_builder.rs | 2 +- src/gadgets/arithmetic.rs | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 012e032f..b8d2b9c4 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -300,7 +300,7 @@ impl, const D: usize> CircuitBuilder { let degree_bits = log2_strict(degree); let subgroup = F::two_adic_subgroup(degree_bits); - let constant_vecs = self.constant_polys(); + let constant_vecs = self.constant_polys(&prefixed_gates); let constants_commitment = ListPolynomialCommitment::new(constant_vecs, self.config.fri_config.rate_bits, false); diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index 7a9fd441..eb4db598 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -373,6 +373,29 @@ mod tests { use crate::fri::FriConfig; use crate::witness::PartialWitness; + #[test] + fn test_div() { + type F = CrandallField; + type FF = QuarticCrandallField; + const D: usize = 4; + + let config = CircuitConfig::large_config(); + + let mut builder = CircuitBuilder::::new(config); + + let x = F::rand(); + let y = F::rand(); + let z = x / y; + let xt = builder.constant(x); + let yt = builder.constant(y); + let zt = builder.constant(z); + let comp_zt = builder.div_unsafe(xt, yt); + builder.assert_equal(zt, comp_zt); + + let data = builder.build(); + let proof = data.prove(PartialWitness::new()); + } + #[test] fn test_div_extension() { type F = CrandallField; From 3ce9183970e254b452d40b01b14033ee4e089ece Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 22:57:50 +0200 Subject: [PATCH 13/20] Modify new test --- src/gadgets/arithmetic.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index eb4db598..2f8b1559 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -371,6 +371,8 @@ mod tests { use crate::field::extension_field::quartic::QuarticCrandallField; use crate::field::field::Field; use crate::fri::FriConfig; + use crate::gates::arithmetic::ArithmeticGate; + use crate::target::Target; use crate::witness::PartialWitness; #[test] @@ -385,15 +387,22 @@ mod tests { let x = F::rand(); let y = F::rand(); - let z = x / y; - let xt = builder.constant(x); - let yt = builder.constant(y); - let zt = builder.constant(z); - let comp_zt = builder.div_unsafe(xt, yt); + let mut pw = PartialWitness::new(); + /// Computes x*x + 0*y = x^2. + let square_gate = builder.add_gate(ArithmeticGate::new(), vec![F::ONE, F::ZERO]); + pw.set_target(Target::wire(square_gate, 0), x); + pw.set_target(Target::wire(square_gate, 1), x); + let x2t = Target::wire(square_gate, ArithmeticGate::WIRE_OUTPUT); + let yt = Target::wire(square_gate, ArithmeticGate::WIRE_ADDEND); + pw.set_target(yt, y); + // Constant for x*x/y. + let zt = builder.constant(x * x / y); + // Computed division for x*x/y using the division gadget. + let comp_zt = builder.div_unsafe(x2t, yt); builder.assert_equal(zt, comp_zt); let data = builder.build(); - let proof = data.prove(PartialWitness::new()); + let proof = data.prove(pw); } #[test] From 2e9d3f768e832cc5c0714db7e2d94e93027ff4a9 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 25 Jun 2021 09:56:15 +0200 Subject: [PATCH 14/20] Better error message when quotient hasn't correct degree --- src/polynomial/polynomial.rs | 9 ++++++--- src/prover.rs | 5 ++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index e176aaf8..3d6c12ba 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -2,6 +2,8 @@ use std::cmp::max; use std::iter::Sum; use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; +use anyhow::{ensure, Result}; + use crate::field::extension_field::Extendable; use crate::field::fft::{fft, ifft}; use crate::field::field::Field; @@ -139,19 +141,20 @@ impl PolynomialCoeffs { self.padded(self.len() << rate_bits) } - pub(crate) fn pad(&mut self, new_len: usize) { - assert!( + pub(crate) fn pad(&mut self, new_len: usize) -> Result<()> { + ensure!( new_len >= self.len(), "Trying to pad a polynomial of length {} to a length of {}.", self.len(), new_len ); self.coeffs.resize(new_len, F::ZERO); + Ok(()) } pub(crate) fn padded(&self, new_len: usize) -> Self { let mut poly = self.clone(); - poly.pad(new_len); + poly.pad(new_len).unwrap(); poly } diff --git a/src/prover.rs b/src/prover.rs index 67139128..42e5db98 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -107,7 +107,10 @@ pub(crate) fn prove, const D: usize>( .into_par_iter() .flat_map(|mut quotient_poly| { quotient_poly.trim(); - quotient_poly.pad(quotient_degree); + quotient_poly.pad(quotient_degree).expect( + "The quotient polynomial doesn't have the right degree.\ + This may be because the `Z`s polynomials are still too high degree.", + ); // Split t into degree-n chunks. quotient_poly.chunks(degree) }) From 727919b14f612853af24c689b0007b7fe3308468 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 25 Jun 2021 10:20:20 +0200 Subject: [PATCH 15/20] Comment and test for `coset_ifft` --- src/polynomial/commitment.rs | 5 ++++- src/polynomial/polynomial.rs | 27 ++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 5ace678c..b0666a2f 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -30,7 +30,10 @@ impl ListPolynomialCommitment { /// Creates a list polynomial commitment for the polynomials interpolating the values in `values`. pub fn new(values: Vec>, rate_bits: usize, blinding: bool) -> Self { let degree = values[0].len(); - let polynomials = values.iter().map(|v| v.clone().ifft()).collect::>(); + let polynomials = values + .par_iter() + .map(|v| v.clone().ifft()) + .collect::>(); let lde_values = timed!( Self::lde_values(&polynomials, rate_bits, blinding), "to compute LDE" diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index 3d6c12ba..888d7af0 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -36,6 +36,7 @@ impl PolynomialValues { ifft(self) } + /// Returns the polynomial whose evaluation on the coset `shift*H` is `self`. pub fn coset_ifft(self, shift: F) -> PolynomialCoeffs { let mut shifted_coeffs = self.ifft(); shifted_coeffs @@ -195,6 +196,7 @@ impl PolynomialCoeffs { fft(self) } + /// Returns the evaluation of the polynomial on the coset `shift*H`. pub fn coset_fft(self, shift: F) -> PolynomialValues { let modified_poly: Self = shift .powers() @@ -393,8 +395,31 @@ mod tests { .into_iter() .map(|x| poly.eval(x)) .collect::>(); - assert_eq!(coset_evals, naive_coset_evals); + + let ifft_coeffs = PolynomialValues::new(coset_evals).coset_ifft(shift); + assert_eq!(poly, ifft_coeffs.into()); + } + + #[test] + fn test_coset_ifft() { + type F = CrandallField; + + let k = 8; + let n = 1 << k; + let evals = PolynomialValues::new(F::rand_vec(n)); + let shift = F::rand(); + let coeffs = evals.clone().coset_ifft(shift); + + let generator = F::primitive_root_of_unity(k); + let naive_coset_evals = F::cyclic_subgroup_coset_known_order(generator, shift, n) + .into_iter() + .map(|x| coeffs.eval(x)) + .collect::>(); + assert_eq!(evals, naive_coset_evals.into()); + + let fft_evals = coeffs.coset_fft(shift); + assert_eq!(evals, fft_evals); } #[test] From 625377b4c071eb4b23a4923252146756550570b9 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 25 Jun 2021 11:24:26 +0200 Subject: [PATCH 16/20] Merge constant and sigma polynomials --- src/circuit_builder.rs | 39 +++++++++++++++++++---------------- src/circuit_data.rs | 31 +++++++++++++++++++--------- src/fri/recursive_verifier.rs | 3 +-- src/fri/verifier.rs | 3 +-- src/plonk_common.rs | 21 +++++++------------ src/polynomial/commitment.rs | 39 ++++++++++++++++++++++++----------- src/proof.rs | 10 +++++---- src/prover.rs | 14 +++++++------ src/verifier.rs | 3 +-- 9 files changed, 94 insertions(+), 69 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index b8d2b9c4..7d3bb137 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -230,7 +230,7 @@ impl, const D: usize> CircuitBuilder { } } - fn constant_polys(&self, gates: &[PrefixedGate]) -> Vec> { + fn constant_polys(&self, gates: &[PrefixedGate]) -> (Vec>, usize) { let num_constants = gates .iter() .map(|gate| gate.gate.0.num_constants() + gate.prefix.len()) @@ -253,10 +253,13 @@ impl, const D: usize> CircuitBuilder { }) .collect::>(); - transpose(&constants_per_gate) - .into_iter() - .map(PolynomialValues::new) - .collect() + ( + transpose(&constants_per_gate) + .into_iter() + .map(PolynomialValues::new) + .collect(), + num_constants, + ) } fn sigma_vecs(&self, k_is: &[F], subgroup: &[F]) -> Vec> { @@ -300,26 +303,26 @@ impl, const D: usize> CircuitBuilder { let degree_bits = log2_strict(degree); let subgroup = F::two_adic_subgroup(degree_bits); - let constant_vecs = self.constant_polys(&prefixed_gates); - let constants_commitment = - ListPolynomialCommitment::new(constant_vecs, self.config.fri_config.rate_bits, false); + let (constant_vecs, num_constants) = self.constant_polys(&prefixed_gates); let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires); let sigma_vecs = self.sigma_vecs(&k_is, &subgroup); - let sigmas_commitment = - ListPolynomialCommitment::new(sigma_vecs, self.config.fri_config.rate_bits, false); - let constants_root = constants_commitment.merkle_tree.root; - let sigmas_root = sigmas_commitment.merkle_tree.root; + let constants_sigmas_vecs = [constant_vecs, sigma_vecs].concat(); + let constants_sigmas_commitment = ListPolynomialCommitment::new( + constants_sigmas_vecs, + self.config.fri_config.rate_bits, + false, + ); + + let constants_sigmas_root = constants_sigmas_commitment.merkle_tree.root; let verifier_only = VerifierOnlyCircuitData { - constants_root, - sigmas_root, + constants_sigmas_root, }; let prover_only = ProverOnlyCircuitData { generators: self.generators, - constants_commitment, - sigmas_commitment, + constants_sigmas_commitment, subgroup, copy_constraints: self.copy_constraints, gate_instances: self.gate_instances, @@ -337,8 +340,7 @@ impl, const D: usize> CircuitBuilder { .expect("No gates?"); // TODO: This should also include an encoding of gate constraints. - let circuit_digest_parts = [constants_root.elements, sigmas_root.elements]; - let circuit_digest = hash_n_to_hash(circuit_digest_parts.concat(), false); + let circuit_digest = hash_n_to_hash(constants_sigmas_root.elements.to_vec(), false); let common = CommonCircuitData { config: self.config, @@ -346,6 +348,7 @@ impl, const D: usize> CircuitBuilder { gates: prefixed_gates, max_filtered_constraint_degree_bits: 3, // TODO: compute this correctly once filters land. num_gate_constraints, + num_constants, k_is, circuit_digest, }; diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 4ec9c0d9..e46b0c41 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -1,3 +1,5 @@ +use std::ops::Range; + use anyhow::Result; use crate::field::extension_field::Extendable; @@ -116,10 +118,8 @@ impl, const D: usize> VerifierCircuitData { /// Circuit data required by the prover, but not the verifier. pub(crate) struct ProverOnlyCircuitData, const D: usize> { pub generators: Vec>>, - /// Commitments to the constants polynomial. - pub constants_commitment: ListPolynomialCommitment, - /// Commitments to the sigma polynomial. - pub sigmas_commitment: ListPolynomialCommitment, + /// Commitments to the constants polynomials and sigma polynomials. + pub constants_sigmas_commitment: ListPolynomialCommitment, /// Subgroup of order `degree`. pub subgroup: Vec, /// The circuit's copy constraints. @@ -130,15 +130,12 @@ pub(crate) struct ProverOnlyCircuitData, const D: usize> { /// Circuit data required by the verifier, but not the prover. pub(crate) struct VerifierOnlyCircuitData { - /// A commitment to each constant polynomial. - pub(crate) constants_root: Hash, - - /// A commitment to each permutation polynomial. - pub(crate) sigmas_root: Hash, + /// A commitment to each constant polynomial and each permutation polynomial. + pub(crate) constants_sigmas_root: Hash, } /// Circuit data required by both the prover and the verifier. -pub(crate) struct CommonCircuitData, const D: usize> { +pub struct CommonCircuitData, const D: usize> { pub(crate) config: CircuitConfig, pub(crate) degree_bits: usize, @@ -152,6 +149,9 @@ pub(crate) struct CommonCircuitData, const D: usize> { /// The largest number of constraints imposed by any gate. pub(crate) num_gate_constraints: usize, + /// The number of constant wires. + pub(crate) num_constants: usize, + /// The `{k_i}` valued used in `S_ID_i` in Plonk's permutation argument. pub(crate) k_is: Vec, @@ -189,6 +189,17 @@ impl, const D: usize> CommonCircuitData { // 2 constraints for each Z check. self.config.num_challenges * 2 + self.num_gate_constraints } + + /// Range of the constant polynomials in the `constants_sigmas_commitment`. + pub fn constants_range(&self) -> Range { + 0..self.num_constants + } + + /// Range of the sigma polynomials in the `constants_sigmas_commitment`. + pub fn sigmas_range(&self) -> Range { + // `self.k_is.len() = num_routed_wires` is the number of sigma polynomials. + self.num_constants..self.num_constants + self.k_is.len() + } } /// The `Target` version of `VerifierCircuitData`, for use inside recursive circuits. Note that this diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 7c04d4a2..fc320f31 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -159,8 +159,7 @@ impl, const D: usize> CircuitBuilder { // Polynomials opened at `x`, i.e., the constants, sigmas and quotient polynomials. let single_evals = [ - PlonkPolynomials::CONSTANTS, - PlonkPolynomials::SIGMAS, + PlonkPolynomials::CONSTANTS_SIGMAS, PlonkPolynomials::QUOTIENT, ] .iter() diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index d88e8e0b..3db4f6f5 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -162,8 +162,7 @@ fn fri_combine_initial, const D: usize>( // Polynomials opened at `x`, i.e., the constants, sigmas and quotient polynomials. let single_evals = [ - PlonkPolynomials::CONSTANTS, - PlonkPolynomials::SIGMAS, + PlonkPolynomials::CONSTANTS_SIGMAS, PlonkPolynomials::QUOTIENT, ] .iter() diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 59961384..62dbb487 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -29,34 +29,29 @@ impl PolynomialsIndexBlinding { /// Holds the indices and blinding flags of the Plonk polynomials. pub struct PlonkPolynomials; impl PlonkPolynomials { - pub const CONSTANTS: PolynomialsIndexBlinding = PolynomialsIndexBlinding { + pub const CONSTANTS_SIGMAS: PolynomialsIndexBlinding = PolynomialsIndexBlinding { index: 0, blinding: false, }; - pub const SIGMAS: PolynomialsIndexBlinding = PolynomialsIndexBlinding { - index: 1, - blinding: false, - }; pub const WIRES: PolynomialsIndexBlinding = PolynomialsIndexBlinding { - index: 2, + index: 1, blinding: true, }; pub const ZS: PolynomialsIndexBlinding = PolynomialsIndexBlinding { - index: 3, + index: 2, blinding: true, }; pub const QUOTIENT: PolynomialsIndexBlinding = PolynomialsIndexBlinding { - index: 4, + index: 3, blinding: true, }; pub fn polynomials(i: usize) -> PolynomialsIndexBlinding { match i { - 0 => Self::CONSTANTS, - 1 => Self::SIGMAS, - 2 => Self::WIRES, - 3 => Self::ZS, - 4 => Self::QUOTIENT, + 0 => Self::CONSTANTS_SIGMAS, + 1 => Self::WIRES, + 2 => Self::ZS, + 3 => Self::QUOTIENT, _ => panic!("There are only 5 sets of polynomials in Plonk."), } } diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index b0666a2f..d90b295b 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -1,6 +1,7 @@ use anyhow::Result; use rayon::prelude::*; +use crate::circuit_data::CommonCircuitData; use crate::field::extension_field::Extendable; use crate::field::extension_field::{FieldExtension, Frobenius}; use crate::field::field::Field; @@ -119,14 +120,15 @@ impl ListPolynomialCommitment { /// Takes the commitments to the constants - sigmas - wires - zs - quotient — polynomials, /// and an opening point `zeta` and produces a batched opening proof + opening set. pub fn open_plonk( - commitments: &[&Self; 5], + commitments: &[&Self; 4], zeta: F::Extension, challenger: &mut Challenger, - config: &FriConfig, + common_data: &CommonCircuitData, ) -> (OpeningProof, OpeningSet) where F: Extendable, { + let config = &common_data.config.fri_config; assert!(D > 1, "Not implemented for D=1."); let degree_log = commitments[0].degree_log; let g = F::Extension::primitive_root_of_unity(degree_log); @@ -145,7 +147,7 @@ impl ListPolynomialCommitment { commitments[1], commitments[2], commitments[3], - commitments[4], + common_data, ); challenger.observe_opening_set(&os); @@ -157,8 +159,7 @@ impl ListPolynomialCommitment { // Polynomials opened at a single point. let single_polys = [ - PlonkPolynomials::CONSTANTS, - PlonkPolynomials::SIGMAS, + PlonkPolynomials::CONSTANTS_SIGMAS, PlonkPolynomials::QUOTIENT, ] .iter() @@ -291,6 +292,7 @@ mod tests { use anyhow::Result; use super::*; + use crate::circuit_data::CircuitConfig; use crate::plonk_common::PlonkPolynomials; fn gen_random_test_case, const D: usize>( @@ -318,7 +320,7 @@ mod tests { } fn check_batch_polynomial_commitment, const D: usize>() -> Result<()> { - let ks = [1, 2, 3, 5, 8]; + let ks = [10, 2, 3, 8]; let degree_log = 11; let fri_config = FriConfig { proof_of_work_bits: 2, @@ -326,12 +328,26 @@ mod tests { reduction_arity_bits: vec![2, 3, 1, 2], num_query_rounds: 3, }; + // We only care about `fri_config, num_constants`, and the length of `k_is` here. + let common_data = CommonCircuitData { + config: CircuitConfig { + fri_config, + ..CircuitConfig::large_config() + }, + degree_bits: 0, + gates: vec![], + max_filtered_constraint_degree_bits: 0, + num_gate_constraints: 0, + num_constants: 4, + k_is: vec![F::ONE; 6], + circuit_digest: Hash::from_partial(vec![]), + }; - let lpcs = (0..5) + let lpcs = (0..4) .map(|i| { ListPolynomialCommitment::::new( gen_random_test_case(ks[i], degree_log), - fri_config.rate_bits, + common_data.config.fri_config.rate_bits, PlonkPolynomials::polynomials(i).blinding, ) }) @@ -339,10 +355,10 @@ mod tests { let zeta = gen_random_point::(degree_log); let (proof, os) = ListPolynomialCommitment::open_plonk::( - &[&lpcs[0], &lpcs[1], &lpcs[2], &lpcs[3], &lpcs[4]], + &[&lpcs[0], &lpcs[1], &lpcs[2], &lpcs[3]], zeta, &mut Challenger::new(), - &fri_config, + &common_data, ); proof.verify( @@ -353,10 +369,9 @@ mod tests { lpcs[1].merkle_tree.root, lpcs[2].merkle_tree.root, lpcs[3].merkle_tree.root, - lpcs[4].merkle_tree.root, ], &mut Challenger::new(), - &fri_config, + &common_data.config.fri_config, ) } diff --git a/src/proof.rs b/src/proof.rs index 7536f3ee..47b67c9c 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -1,5 +1,6 @@ use std::convert::TryInto; +use crate::circuit_data::CommonCircuitData; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field::Field; @@ -160,11 +161,11 @@ impl, const D: usize> OpeningSet { pub fn new( z: F::Extension, g: F::Extension, - constant_commitment: &ListPolynomialCommitment, - plonk_sigmas_commitment: &ListPolynomialCommitment, + constants_sigmas_commitment: &ListPolynomialCommitment, wires_commitment: &ListPolynomialCommitment, plonk_zs_commitment: &ListPolynomialCommitment, quotient_polys_commitment: &ListPolynomialCommitment, + common_data: &CommonCircuitData, ) -> Self { let eval_commitment = |z: F::Extension, c: &ListPolynomialCommitment| { c.polynomials @@ -172,9 +173,10 @@ impl, const D: usize> OpeningSet { .map(|p| p.to_extension().eval(z)) .collect::>() }; + let constants_sigmas_eval = eval_commitment(z, constants_sigmas_commitment); Self { - constants: eval_commitment(z, constant_commitment), - plonk_s_sigmas: eval_commitment(z, plonk_sigmas_commitment), + constants: constants_sigmas_eval[common_data.constants_range()].to_vec(), + plonk_s_sigmas: constants_sigmas_eval[common_data.sigmas_range()].to_vec(), wires: eval_commitment(z, wires_commitment), plonk_zs: eval_commitment(z, plonk_zs_commitment), plonk_zs_right: eval_commitment(g * z, plonk_zs_commitment), diff --git a/src/prover.rs b/src/prover.rs index 42e5db98..728c17e5 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -134,15 +134,14 @@ pub(crate) fn prove, const D: usize>( let (opening_proof, openings) = timed!( ListPolynomialCommitment::open_plonk( &[ - &prover_data.constants_commitment, - &prover_data.sigmas_commitment, + &prover_data.constants_sigmas_commitment, &wires_commitment, &plonk_zs_commitment, "ient_polys_commitment, ], zeta, &mut challenger, - &common_data.config.fri_config + common_data, ), "to compute opening proofs" ); @@ -187,7 +186,9 @@ fn compute_z, const D: usize>( let x = subgroup[i - 1]; let mut numerator = F::ONE; let mut denominator = F::ONE; - let s_sigmas = prover_data.sigmas_commitment.original_values(i - 1); + let s_sigmas = &prover_data + .constants_sigmas_commitment + .original_values(i - 1)[common_data.sigmas_range()]; for j in 0..common_data.config.num_routed_wires { let wire_value = witness.get_wire(i - 1, j); let k_i = k_is[j]; @@ -247,8 +248,9 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( .map(|(i, x)| { let shifted_x = F::coset_shift() * x; let i_next = (i + next_step) % lde_size; - let local_constants = get_at_index(&prover_data.constants_commitment, i); - let s_sigmas = get_at_index(&prover_data.sigmas_commitment, i); + let local_constants_sigmas = get_at_index(&prover_data.constants_sigmas_commitment, i); + let local_constants = &local_constants_sigmas[common_data.constants_range()]; + let s_sigmas = &local_constants_sigmas[common_data.sigmas_range()]; let local_wires = get_at_index(wires_commitment, i); let local_plonk_zs = get_at_index(plonk_zs_commitment, i); let next_plonk_zs = get_at_index(plonk_zs_commitment, i_next); diff --git a/src/verifier.rs b/src/verifier.rs index a2750421..57bad7cf 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -64,8 +64,7 @@ pub(crate) fn verify, const D: usize>( let evaluations = proof.openings.clone(); let merkle_roots = &[ - verifier_data.constants_root, - verifier_data.sigmas_root, + verifier_data.constants_sigmas_root, proof.wires_root, proof.plonk_zs_root, proof.quotient_polys_root, From 810d1869a137a3cd86c9003d66b1f8142aa738a8 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 25 Jun 2021 11:49:29 +0200 Subject: [PATCH 17/20] Minor improvements --- src/circuit_builder.rs | 25 ++++++++++++++----------- src/circuit_data.rs | 2 +- src/prover.rs | 16 ++++++++++++---- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 7d3bb137..79d10be2 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -17,6 +17,7 @@ use crate::gates::noop::NoopGate; use crate::generator::{CopyGenerator, WitnessGenerator}; use crate::hash::hash_n_to_hash; use crate::permutation_argument::TargetPartitions; +use crate::plonk_common::PlonkPolynomials; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::polynomial::PolynomialValues; use crate::target::Target; @@ -230,7 +231,7 @@ impl, const D: usize> CircuitBuilder { } } - fn constant_polys(&self, gates: &[PrefixedGate]) -> (Vec>, usize) { + fn constant_polys(&self, gates: &[PrefixedGate]) -> Vec> { let num_constants = gates .iter() .map(|gate| gate.gate.0.num_constants() + gate.prefix.len()) @@ -253,13 +254,10 @@ impl, const D: usize> CircuitBuilder { }) .collect::>(); - ( - transpose(&constants_per_gate) - .into_iter() - .map(PolynomialValues::new) - .collect(), - num_constants, - ) + transpose(&constants_per_gate) + .into_iter() + .map(PolynomialValues::new) + .collect() } fn sigma_vecs(&self, k_is: &[F], subgroup: &[F]) -> Vec> { @@ -303,7 +301,8 @@ impl, const D: usize> CircuitBuilder { let degree_bits = log2_strict(degree); let subgroup = F::two_adic_subgroup(degree_bits); - let (constant_vecs, num_constants) = self.constant_polys(&prefixed_gates); + let constant_vecs = self.constant_polys(&prefixed_gates); + let num_constants = constant_vecs.len(); let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires); let sigma_vecs = self.sigma_vecs(&k_is, &subgroup); @@ -312,7 +311,7 @@ impl, const D: usize> CircuitBuilder { let constants_sigmas_commitment = ListPolynomialCommitment::new( constants_sigmas_vecs, self.config.fri_config.rate_bits, - false, + PlonkPolynomials::CONSTANTS_SIGMAS.blinding, ); let constants_sigmas_root = constants_sigmas_commitment.merkle_tree.root; @@ -340,7 +339,11 @@ impl, const D: usize> CircuitBuilder { .expect("No gates?"); // TODO: This should also include an encoding of gate constraints. - let circuit_digest = hash_n_to_hash(constants_sigmas_root.elements.to_vec(), false); + let circuit_digest_parts = [ + constants_sigmas_root.elements.to_vec(), + vec![/* Add other circuit data here */], + ]; + let circuit_digest = hash_n_to_hash(circuit_digest_parts.concat(), false); let common = CommonCircuitData { config: self.config, diff --git a/src/circuit_data.rs b/src/circuit_data.rs index e46b0c41..f2935e6c 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -190,7 +190,7 @@ impl, const D: usize> CommonCircuitData { self.config.num_challenges * 2 + self.num_gate_constraints } - /// Range of the constant polynomials in the `constants_sigmas_commitment`. + /// Range of the constants polynomials in the `constants_sigmas_commitment`. pub fn constants_range(&self) -> Range { 0..self.num_constants } diff --git a/src/prover.rs b/src/prover.rs index 728c17e5..a810c05f 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -7,7 +7,7 @@ use crate::circuit_data::{CommonCircuitData, ProverOnlyCircuitData}; use crate::field::extension_field::Extendable; use crate::generator::generate_partial_witness; use crate::plonk_challenger::Challenger; -use crate::plonk_common::{eval_vanishing_poly_base, ZeroPolyOnCoset}; +use crate::plonk_common::{eval_vanishing_poly_base, PlonkPolynomials, ZeroPolyOnCoset}; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::Proof; @@ -61,7 +61,11 @@ pub(crate) fn prove, const D: usize>( // TODO: Could try parallelizing the transpose, or not doing it explicitly, instead having // merkle_root_bit_rev_order do it implicitly. let wires_commitment = timed!( - ListPolynomialCommitment::new(wires_values, fri_config.rate_bits, true), + ListPolynomialCommitment::new( + wires_values, + fri_config.rate_bits, + PlonkPolynomials::WIRES.blinding + ), "to compute wires commitment" ); @@ -80,7 +84,11 @@ pub(crate) fn prove, const D: usize>( ); let plonk_zs_commitment = timed!( - ListPolynomialCommitment::new(plonk_z_vecs, fri_config.rate_bits, true), + ListPolynomialCommitment::new( + plonk_z_vecs, + fri_config.rate_bits, + PlonkPolynomials::ZS.blinding + ), "to commit to Z's" ); @@ -122,7 +130,7 @@ pub(crate) fn prove, const D: usize>( ListPolynomialCommitment::new_from_polys( all_quotient_poly_chunks, fri_config.rate_bits, - true + PlonkPolynomials::QUOTIENT.blinding ), "to commit to quotient polys" ); From c2b2ef921a668aaf21fdfeee14f1a558a8e2a59e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 28 Jun 2021 09:47:47 +0200 Subject: [PATCH 18/20] PR feedback --- src/circuit_data.rs | 3 +-- src/plonk_common.rs | 2 +- src/polynomial/commitment.rs | 3 ++- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/circuit_data.rs b/src/circuit_data.rs index f2935e6c..ff910ce0 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -197,8 +197,7 @@ impl, const D: usize> CommonCircuitData { /// Range of the sigma polynomials in the `constants_sigmas_commitment`. pub fn sigmas_range(&self) -> Range { - // `self.k_is.len() = num_routed_wires` is the number of sigma polynomials. - self.num_constants..self.num_constants + self.k_is.len() + self.num_constants..self.num_constants + self.config.num_routed_wires } } diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 62dbb487..f6e0e228 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -52,7 +52,7 @@ impl PlonkPolynomials { 1 => Self::WIRES, 2 => Self::ZS, 3 => Self::QUOTIENT, - _ => panic!("There are only 5 sets of polynomials in Plonk."), + _ => panic!("There are only 4 sets of polynomials in Plonk."), } } } diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index d90b295b..ecd3e875 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -328,10 +328,11 @@ mod tests { reduction_arity_bits: vec![2, 3, 1, 2], num_query_rounds: 3, }; - // We only care about `fri_config, num_constants`, and the length of `k_is` here. + // We only care about `fri_config, num_constants`, and `num_routed_wires` here. let common_data = CommonCircuitData { config: CircuitConfig { fri_config, + num_routed_wires: 6, ..CircuitConfig::large_config() }, degree_bits: 0, From 7734aed62cec6eaf30ea772c4c14ebe91688710a Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Mon, 28 Jun 2021 08:56:36 -0700 Subject: [PATCH 19/20] Performance tweaks (#77) --- src/circuit_builder.rs | 5 +++-- src/circuit_data.rs | 2 ++ src/polynomial/commitment.rs | 14 ++------------ src/prover.rs | 4 +--- 4 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 79d10be2..a006ec98 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -21,7 +21,7 @@ use crate::plonk_common::PlonkPolynomials; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::polynomial::PolynomialValues; use crate::target::Target; -use crate::util::{log2_strict, transpose}; +use crate::util::{log2_strict, transpose, transpose_poly_values}; use crate::wire::Wire; pub struct CircuitBuilder, const D: usize> { @@ -307,7 +307,7 @@ impl, const D: usize> CircuitBuilder { let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires); let sigma_vecs = self.sigma_vecs(&k_is, &subgroup); - let constants_sigmas_vecs = [constant_vecs, sigma_vecs].concat(); + let constants_sigmas_vecs = [constant_vecs, sigma_vecs.clone()].concat(); let constants_sigmas_commitment = ListPolynomialCommitment::new( constants_sigmas_vecs, self.config.fri_config.rate_bits, @@ -322,6 +322,7 @@ impl, const D: usize> CircuitBuilder { let prover_only = ProverOnlyCircuitData { generators: self.generators, constants_sigmas_commitment, + sigmas: transpose_poly_values(sigma_vecs), subgroup, copy_constraints: self.copy_constraints, gate_instances: self.gate_instances, diff --git a/src/circuit_data.rs b/src/circuit_data.rs index ff910ce0..afb37628 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -120,6 +120,8 @@ pub(crate) struct ProverOnlyCircuitData, const D: usize> { pub generators: Vec>>, /// Commitments to the constants polynomials and sigma polynomials. pub constants_sigmas_commitment: ListPolynomialCommitment, + /// The transpose of the list of sigma polynomials. + pub sigmas: Vec>, /// Subgroup of order `degree`. pub subgroup: Vec, /// The circuit's copy constraints. diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index ecd3e875..da647ffa 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -19,7 +19,6 @@ pub const SALT_SIZE: usize = 2; pub struct ListPolynomialCommitment { pub polynomials: Vec>, - pub values: Vec>, pub merkle_tree: MerkleTree, pub degree: usize, pub degree_log: usize, @@ -40,7 +39,7 @@ impl ListPolynomialCommitment { "to compute LDE" ); - Self::new_from_data(polynomials, values, lde_values, degree, rate_bits, blinding) + Self::new_from_data(polynomials, lde_values, degree, rate_bits, blinding) } /// Creates a list polynomial commitment for the polynomials `polynomials`. @@ -50,21 +49,16 @@ impl ListPolynomialCommitment { blinding: bool, ) -> Self { let degree = polynomials[0].len(); - let values = polynomials - .iter() - .map(|v| v.clone().fft()) - .collect::>(); let lde_values = timed!( Self::lde_values(&polynomials, rate_bits, blinding), "to compute LDE" ); - Self::new_from_data(polynomials, values, lde_values, degree, rate_bits, blinding) + Self::new_from_data(polynomials, lde_values, degree, rate_bits, blinding) } fn new_from_data( polynomials: Vec>, - values: Vec>, lde_values: Vec>, degree: usize, rate_bits: usize, @@ -76,7 +70,6 @@ impl ListPolynomialCommitment { Self { polynomials, - values, merkle_tree, degree, degree_log: log2_strict(degree), @@ -108,9 +101,6 @@ impl ListPolynomialCommitment { .collect() } - pub fn original_values(&self, index: usize) -> Vec { - self.values.iter().map(|v| v.values[index]).collect() - } pub fn get_lde_values(&self, index: usize) -> &[F] { let index = reverse_bits(index, self.degree_log + self.rate_bits); let slice = &self.merkle_tree.leaves[index]; diff --git a/src/prover.rs b/src/prover.rs index a810c05f..e8cd7f46 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -194,9 +194,7 @@ fn compute_z, const D: usize>( let x = subgroup[i - 1]; let mut numerator = F::ONE; let mut denominator = F::ONE; - let s_sigmas = &prover_data - .constants_sigmas_commitment - .original_values(i - 1)[common_data.sigmas_range()]; + let s_sigmas = &prover_data.sigmas[i - 1]; for j in 0..common_data.config.num_routed_wires { let wire_value = witness.get_wire(i - 1, j); let k_i = k_is[j]; From 57c86143bc4aa4d73fa865c4ba1d14cbd4ab0679 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 29 Jun 2021 08:06:06 +0200 Subject: [PATCH 20/20] Fix mistake in comment --- src/plonk_common.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plonk_common.rs b/src/plonk_common.rs index f6e0e228..db304c0a 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -256,7 +256,7 @@ impl ZeroPolyOnCoset { self.evals[i % self.rate] } - /// Returns `Z_H(g * w^i)`. + /// Returns `1 / Z_H(g * w^i)`. pub fn eval_inverse(&self, i: usize) -> F { self.inverses[i % self.rate] }