From 4e361726d0a03e8a7d0f333bf405c140f0856ddb Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 8 Nov 2021 15:50:33 +0100 Subject: [PATCH 01/12] Use partial product chain --- src/plonk/prover.rs | 3 +- src/util/partial_products.rs | 88 ++++++++++++++++++------------------ 2 files changed, 44 insertions(+), 47 deletions(-) diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 031354fa..427880c3 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -268,8 +268,7 @@ fn wires_permutation_partial_products, const D: usi let quotient_partials = partial_products("ient_values, degree); // This is the final product for the quotient. - let quotient = quotient_partials - [common_data.num_partial_products.0 - common_data.num_partial_products.1..] + let quotient = quotient_partials[common_data.num_partial_products.1..] .iter() .copied() .product(); diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index 633047d0..83b0e396 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -1,5 +1,5 @@ use std::iter::Product; -use std::ops::Sub; +use std::ops::{MulAssign, Sub}; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; @@ -9,17 +9,18 @@ use crate::util::ceil_div_usize; /// Compute partial products of the original vector `v` such that all products consist of `max_degree` /// or less elements. This is done until we've computed the product `P` of all elements in the vector. -pub fn partial_products(v: &[T], max_degree: usize) -> Vec { +pub fn partial_products(v: &[T], max_degree: usize) -> Vec { + debug_assert!(max_degree > 1); let mut res = Vec::new(); - let mut remainder = v.to_vec(); - while remainder.len() > max_degree { - let new_partials = remainder - .chunks(max_degree) - // TODO: can filter out chunks of length 1. - .map(|chunk| chunk.iter().copied().product()) - .collect::>(); - res.extend_from_slice(&new_partials); - remainder = new_partials; + let mut acc = v[0]; + let chunk_size = max_degree - 1; + let num_chunks = ceil_div_usize(v.len() - 1, chunk_size) - 1; + for i in 0..num_chunks { + acc *= v[1 + i * chunk_size..1 + (i + 1) * chunk_size] + .iter() + .copied() + .product(); + res.push(acc); } res @@ -29,34 +30,33 @@ pub fn partial_products(v: &[T], max_degree: usize) -> Vec /// vector of length `n`, and `b` is the number of elements needed to compute the final product. pub fn num_partial_products(n: usize, max_degree: usize) -> (usize, usize) { debug_assert!(max_degree > 1); - let mut res = 0; - let mut remainder = n; - while remainder > max_degree { - let new_partials_len = ceil_div_usize(remainder, max_degree); - res += new_partials_len; - remainder = new_partials_len; - } + let chunk_size = max_degree - 1; + let num_chunks = ceil_div_usize(n - 1, chunk_size) - 1; - (res, remainder) + (num_chunks, 1 + num_chunks * chunk_size) } /// Checks that the partial products of `v` are coherent with those in `partials` by only computing /// products of size `max_degree` or less. -pub fn check_partial_products>( +pub fn check_partial_products>( v: &[T], mut partials: &[T], max_degree: usize, ) -> Vec { + debug_assert!(max_degree > 1); + let mut partials = partials.iter(); let mut res = Vec::new(); - let mut remainder = v; - while remainder.len() > max_degree { - let products = remainder - .chunks(max_degree) - .map(|chunk| chunk.iter().copied().product::()); - let products_len = products.len(); - res.extend(products.zip(partials).map(|(a, &b)| a - b)); - (remainder, partials) = partials.split_at(products_len); + let mut acc = v[0]; + let chunk_size = max_degree - 1; + let num_chunks = ceil_div_usize(v.len() - 1, chunk_size) - 1; + for i in 0..num_chunks { + acc *= v[1 + i * chunk_size..1 + (i + 1) * chunk_size] + .iter() + .copied() + .product(); + res.push(acc - *partials.next().unwrap()); } + debug_assert!(partials.next().is_none()); res } @@ -67,22 +67,20 @@ pub fn check_partial_products_recursively, const D: partials: &[ExtensionTarget], max_degree: usize, ) -> Vec> { + debug_assert!(max_degree > 1); + let mut partials = partials.iter(); let mut res = Vec::new(); - let mut remainder = v.to_vec(); - let mut partials = partials.to_vec(); - while remainder.len() > max_degree { - let products = remainder - .chunks(max_degree) - .map(|chunk| builder.mul_many_extension(chunk)) - .collect::>(); - res.extend( - products - .iter() - .zip(&partials) - .map(|(&a, &b)| builder.sub_extension(a, b)), - ); - remainder = partials.drain(..products.len()).collect(); + let mut acc = v[0]; + let chunk_size = max_degree - 1; + let num_chunks = ceil_div_usize(v.len() - 1, chunk_size) - 1; + for i in 0..num_chunks { + let mut chunk = v[1 + i * chunk_size..1 + (i + 1) * chunk_size].to_vec(); + chunk.push(acc); + acc = builder.mul_many_extension(&chunk); + + res.push(builder.sub_extension(acc, *partials.next().unwrap())); } + debug_assert!(partials.next().is_none()); res } @@ -97,15 +95,15 @@ mod tests { fn test_partial_products() { let v = vec![1, 2, 3, 4, 5, 6]; let p = partial_products(&v, 2); - assert_eq!(p, vec![2, 12, 30, 24, 30]); + assert_eq!(p, vec![2, 6, 24, 120]); let nums = num_partial_products(v.len(), 2); assert_eq!(p.len(), nums.0); assert!(check_partial_products(&v, &p, 2) .iter() .all(|x| x.is_zero())); assert_eq!( + *p.last().unwrap() * v[nums.1..].iter().copied().product::(), v.into_iter().product::(), - p[p.len() - nums.1..].iter().copied().product(), ); let v = vec![1, 2, 3, 4, 5, 6]; @@ -117,8 +115,8 @@ mod tests { .iter() .all(|x| x.is_zero())); assert_eq!( + *p.last().unwrap() * v[nums.1..].iter().copied().product::(), v.into_iter().product::(), - p[p.len() - nums.1..].iter().copied().product(), ); } } From bd1672cbf2ac822b24c6402a592ffba4eb0e14db Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 9 Nov 2021 13:56:19 +0100 Subject: [PATCH 02/12] Working --- src/plonk/circuit_builder.rs | 2 +- src/plonk/prover.rs | 46 +++++++++++++--- src/plonk/vanishing_poly.rs | 54 ++++++++++++------- src/util/partial_products.rs | 100 +++++++++++++++++------------------ 4 files changed, 126 insertions(+), 76 deletions(-) diff --git a/src/plonk/circuit_builder.rs b/src/plonk/circuit_builder.rs index 5dcde1e0..87ea4f3a 100644 --- a/src/plonk/circuit_builder.rs +++ b/src/plonk/circuit_builder.rs @@ -777,7 +777,7 @@ impl, const D: usize> CircuitBuilder { .expect("No gates?"); let num_partial_products = - num_partial_products(self.config.num_routed_wires, quotient_degree_factor); + num_partial_products(self.config.num_routed_wires, quotient_degree_factor - 1); // TODO: This should also include an encoding of gate constraints. let circuit_digest_parts = [ diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 427880c3..b925e6de 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -17,7 +17,7 @@ use crate::plonk::vanishing_poly::eval_vanishing_poly_base_batch; use crate::plonk::vars::EvaluationVarsBase; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::timed; -use crate::util::partial_products::partial_products; +use crate::util::partial_products::{check_partial_products, partial_products}; use crate::util::timing::TimingTree; use crate::util::{log2_ceil, transpose}; @@ -63,6 +63,7 @@ pub(crate) fn prove, const D: usize>( .map(|column| PolynomialValues::new(column.clone())) .collect() ); + let wires = wires_values.iter().map(|v| v.values[0]).collect::>(); let wires_commitment = timed!( timing, @@ -108,6 +109,33 @@ pub(crate) fn prove, const D: usize>( partial_products.iter_mut().for_each(|part| { part.remove(0); }); + // let part = partial_products[0].clone(); + // let v = part.iter().map(|v| v.values[0]).collect::>(); + // dbg!(); + // let numerator_values = (0..common_data.config.num_routed_wires) + // .map(|j| { + // let wire_value = wires[j]; + // let k_i = common_data.k_is[j]; + // let s_id = k_i; + // wire_value + s_id * betas[0] + gammas[0] + // }) + // .collect::>(); + // let denominator_values = (0..common_data.config.num_routed_wires) + // .map(|j| { + // let wire_value = wires[j]; + // let s_sigma = s_sigmas[j]; + // wire_value + s_sigma * betas[0] + gammas[0] + // }) + // .collect::>(); + // let quotient_values = (0..common_data.config.num_routed_wires) + // .map(|j| numerator_values[j] / denominator_values[j]) + // .collect::>(); + // + // // // The partial products considered for this iteration of `i`. + // // let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods]; + // // Check the quotient partial products. + // let mut partial_product_check = check_partial_products("ient_values, &v, quotient_degree); + // dbg!(partial_product_check); let zs_partial_products = [plonk_z_vecs, partial_products.concat()].concat(); let zs_partial_products_commitment = timed!( @@ -238,7 +266,7 @@ fn wires_permutation_partial_products, const D: usi prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, ) -> Vec> { - let degree = common_data.quotient_degree_factor; + let degree = common_data.quotient_degree_factor - 1; let subgroup = &prover_data.subgroup; let k_is = &common_data.k_is; let values = subgroup @@ -266,12 +294,18 @@ fn wires_permutation_partial_products, const D: usi .collect::>(); let quotient_partials = partial_products("ient_values, degree); + dbg!(check_partial_products( + "ient_values, + "ient_partials, + degree + )); // This is the final product for the quotient. - let quotient = quotient_partials[common_data.num_partial_products.1..] - .iter() - .copied() - .product(); + let quotient = *quotient_partials.last().unwrap() + * quotient_values[common_data.num_partial_products.1..] + .iter() + .copied() + .product(); // We add the quotient at the beginning of the vector to reuse them later in the computation of `Z`. [vec![quotient], quotient_partials].concat() diff --git a/src/plonk/vanishing_poly.rs b/src/plonk/vanishing_poly.rs index 28c6a287..9bc4feb5 100644 --- a/src/plonk/vanishing_poly.rs +++ b/src/plonk/vanishing_poly.rs @@ -27,7 +27,7 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( gammas: &[F], alphas: &[F], ) -> Vec { - let max_degree = common_data.quotient_degree_factor; + let max_degree = common_data.quotient_degree_factor - 1; let (num_prods, final_num_prod) = common_data.num_partial_products; let constraint_terms = @@ -73,20 +73,27 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( check_partial_products("ient_values, current_partial_products, max_degree); // The first checks are of the form `q - n/d` which is a rational function not a polynomial. // We multiply them by `d` to get checks of the form `q*d - n` which low-degree polynomials. - denominator_values - .chunks(max_degree) - .zip(partial_product_check.iter_mut()) - .for_each(|(d, q)| { - *q *= d.iter().copied().product(); - }); + for (j, q) in partial_product_check.iter_mut().enumerate() { + let range = j * (max_degree - 1)..(j + 1) * (max_degree - 1); + *q *= denominator_values[range].iter().copied().product(); + } + // denominator_values + // .chunks(max_degree) + // .zip(partial_product_check.iter_mut()) + // .for_each(|(d, q)| { + // *q *= d.iter().copied().product(); + // }); vanishing_partial_products_terms.extend(partial_product_check); // The quotient final product is the product of the last `final_num_prod` elements. - let quotient: F::Extension = current_partial_products[num_prods - final_num_prod..] + let quotient: F::Extension = *current_partial_products.last().unwrap() + * quotient_values[final_num_prod..].iter().copied().product(); + let mut wanted = quotient * z_x - z_gz; + wanted *= denominator_values[final_num_prod..] .iter() .copied() .product(); - vanishing_v_shift_terms.push(quotient * z_x - z_gz); + vanishing_v_shift_terms.push(wanted); } let vanishing_terms = [ @@ -124,7 +131,7 @@ pub(crate) fn eval_vanishing_poly_base_batch, const assert_eq!(partial_products_batch.len(), n); assert_eq!(s_sigmas_batch.len(), n); - let max_degree = common_data.quotient_degree_factor; + let max_degree = common_data.quotient_degree_factor - 1; let (num_prods, final_num_prod) = common_data.num_partial_products; let num_gate_constraints = common_data.num_gate_constraints; @@ -189,20 +196,31 @@ pub(crate) fn eval_vanishing_poly_base_batch, const check_partial_products("ient_values, current_partial_products, max_degree); // The first checks are of the form `q - n/d` which is a rational function not a polynomial. // We multiply them by `d` to get checks of the form `q*d - n` which low-degree polynomials. - denominator_values - .chunks(max_degree) - .zip(partial_product_check.iter_mut()) - .for_each(|(d, q)| { - *q *= d.iter().copied().product(); - }); + for (j, q) in partial_product_check.iter_mut().enumerate() { + let range = j * (max_degree - 1)..(j + 1) * (max_degree - 1); + *q *= denominator_values[range].iter().copied().product(); + } + // denominator_values + // .chunks(max_degree) + // .zip(partial_product_check.iter_mut()) + // .for_each(|(d, q)| { + // *q *= d.iter().copied().product(); + // }); vanishing_partial_products_terms.extend(partial_product_check); // The quotient final product is the product of the last `final_num_prod` elements. - let quotient: F = current_partial_products[num_prods - final_num_prod..] + let quotient: F = *current_partial_products.last().unwrap() + * quotient_values[final_num_prod..].iter().copied().product(); + // let quotient: F = current_partial_products[num_prods - final_num_prod..] + // .iter() + // .copied() + // .product(); + let mut wanted = quotient * z_x - z_gz; + wanted *= denominator_values[final_num_prod..] .iter() .copied() .product(); - vanishing_v_shift_terms.push(quotient * z_x - z_gz); + vanishing_v_shift_terms.push(wanted); numerator_values.clear(); denominator_values.clear(); diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index 83b0e396..1e361101 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -3,20 +3,20 @@ use std::ops::{MulAssign, Sub}; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; -use crate::field::field_types::RichField; +use crate::field::field_types::{Field, RichField}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::util::ceil_div_usize; /// Compute partial products of the original vector `v` such that all products consist of `max_degree` /// or less elements. This is done until we've computed the product `P` of all elements in the vector. -pub fn partial_products(v: &[T], max_degree: usize) -> Vec { +pub fn partial_products(v: &[F], max_degree: usize) -> Vec { debug_assert!(max_degree > 1); let mut res = Vec::new(); - let mut acc = v[0]; + let mut acc = F::ONE; let chunk_size = max_degree - 1; - let num_chunks = ceil_div_usize(v.len() - 1, chunk_size) - 1; + let num_chunks = ceil_div_usize(v.len(), chunk_size) - 1; for i in 0..num_chunks { - acc *= v[1 + i * chunk_size..1 + (i + 1) * chunk_size] + acc *= v[i * chunk_size..(i + 1) * chunk_size] .iter() .copied() .product(); @@ -31,30 +31,28 @@ pub fn partial_products(v: &[T], max_degree: usiz pub fn num_partial_products(n: usize, max_degree: usize) -> (usize, usize) { debug_assert!(max_degree > 1); let chunk_size = max_degree - 1; - let num_chunks = ceil_div_usize(n - 1, chunk_size) - 1; + let num_chunks = ceil_div_usize(n, chunk_size) - 1; - (num_chunks, 1 + num_chunks * chunk_size) + (num_chunks, num_chunks * chunk_size) } /// Checks that the partial products of `v` are coherent with those in `partials` by only computing /// products of size `max_degree` or less. -pub fn check_partial_products>( - v: &[T], - mut partials: &[T], - max_degree: usize, -) -> Vec { +pub fn check_partial_products(v: &[F], mut partials: &[F], max_degree: usize) -> Vec { debug_assert!(max_degree > 1); let mut partials = partials.iter(); let mut res = Vec::new(); - let mut acc = v[0]; + let mut acc = F::ONE; let chunk_size = max_degree - 1; - let num_chunks = ceil_div_usize(v.len() - 1, chunk_size) - 1; + let num_chunks = ceil_div_usize(v.len(), chunk_size) - 1; for i in 0..num_chunks { - acc *= v[1 + i * chunk_size..1 + (i + 1) * chunk_size] + acc *= v[i * chunk_size..(i + 1) * chunk_size] .iter() .copied() .product(); - res.push(acc - *partials.next().unwrap()); + let bacc = *partials.next().unwrap(); + res.push(acc - bacc); + acc = bacc; } debug_assert!(partials.next().is_none()); @@ -85,38 +83,38 @@ pub fn check_partial_products_recursively, const D: res } -#[cfg(test)] -mod tests { - use num::Zero; - - use super::*; - - #[test] - fn test_partial_products() { - let v = vec![1, 2, 3, 4, 5, 6]; - let p = partial_products(&v, 2); - assert_eq!(p, vec![2, 6, 24, 120]); - let nums = num_partial_products(v.len(), 2); - assert_eq!(p.len(), nums.0); - assert!(check_partial_products(&v, &p, 2) - .iter() - .all(|x| x.is_zero())); - assert_eq!( - *p.last().unwrap() * v[nums.1..].iter().copied().product::(), - v.into_iter().product::(), - ); - - let v = vec![1, 2, 3, 4, 5, 6]; - let p = partial_products(&v, 3); - assert_eq!(p, vec![6, 120]); - let nums = num_partial_products(v.len(), 3); - assert_eq!(p.len(), nums.0); - assert!(check_partial_products(&v, &p, 3) - .iter() - .all(|x| x.is_zero())); - assert_eq!( - *p.last().unwrap() * v[nums.1..].iter().copied().product::(), - v.into_iter().product::(), - ); - } -} +// #[cfg(test)] +// mod tests { +// use num::Zero; +// +// use super::*; +// +// #[test] +// fn test_partial_products() { +// let v = vec![1, 2, 3, 4, 5, 6]; +// let p = partial_products(&v, 2); +// assert_eq!(p, vec![2, 6, 24, 120]); +// let nums = num_partial_products(v.len(), 2); +// assert_eq!(p.len(), nums.0); +// assert!(check_partial_products(&v, &p, 2) +// .iter() +// .all(|x| x.is_zero())); +// assert_eq!( +// *p.last().unwrap() * v[nums.1..].iter().copied().product::(), +// v.into_iter().product::(), +// ); +// +// let v = vec![1, 2, 3, 4, 5, 6]; +// let p = partial_products(&v, 3); +// assert_eq!(p, vec![6, 120]); +// let nums = num_partial_products(v.len(), 3); +// assert_eq!(p.len(), nums.0); +// assert!(check_partial_products(&v, &p, 3) +// .iter() +// .all(|x| x.is_zero())); +// assert_eq!( +// *p.last().unwrap() * v[nums.1..].iter().copied().product::(), +// v.into_iter().product::(), +// ); +// } +// } From 9617c221730bf960f30e640b115a43d5bd4d4c02 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 9 Nov 2021 14:24:04 +0100 Subject: [PATCH 03/12] Increase degree --- src/plonk/circuit_builder.rs | 2 +- src/plonk/prover.rs | 35 +---------------------------------- src/plonk/vanishing_poly.rs | 24 ++++-------------------- src/util/partial_products.rs | 6 +++--- 4 files changed, 9 insertions(+), 58 deletions(-) diff --git a/src/plonk/circuit_builder.rs b/src/plonk/circuit_builder.rs index 87ea4f3a..5dcde1e0 100644 --- a/src/plonk/circuit_builder.rs +++ b/src/plonk/circuit_builder.rs @@ -777,7 +777,7 @@ impl, const D: usize> CircuitBuilder { .expect("No gates?"); let num_partial_products = - num_partial_products(self.config.num_routed_wires, quotient_degree_factor - 1); + num_partial_products(self.config.num_routed_wires, quotient_degree_factor); // TODO: This should also include an encoding of gate constraints. let circuit_digest_parts = [ diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index b925e6de..22f9411c 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -63,7 +63,6 @@ pub(crate) fn prove, const D: usize>( .map(|column| PolynomialValues::new(column.clone())) .collect() ); - let wires = wires_values.iter().map(|v| v.values[0]).collect::>(); let wires_commitment = timed!( timing, @@ -109,33 +108,6 @@ pub(crate) fn prove, const D: usize>( partial_products.iter_mut().for_each(|part| { part.remove(0); }); - // let part = partial_products[0].clone(); - // let v = part.iter().map(|v| v.values[0]).collect::>(); - // dbg!(); - // let numerator_values = (0..common_data.config.num_routed_wires) - // .map(|j| { - // let wire_value = wires[j]; - // let k_i = common_data.k_is[j]; - // let s_id = k_i; - // wire_value + s_id * betas[0] + gammas[0] - // }) - // .collect::>(); - // let denominator_values = (0..common_data.config.num_routed_wires) - // .map(|j| { - // let wire_value = wires[j]; - // let s_sigma = s_sigmas[j]; - // wire_value + s_sigma * betas[0] + gammas[0] - // }) - // .collect::>(); - // let quotient_values = (0..common_data.config.num_routed_wires) - // .map(|j| numerator_values[j] / denominator_values[j]) - // .collect::>(); - // - // // // The partial products considered for this iteration of `i`. - // // let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods]; - // // Check the quotient partial products. - // let mut partial_product_check = check_partial_products("ient_values, &v, quotient_degree); - // dbg!(partial_product_check); let zs_partial_products = [plonk_z_vecs, partial_products.concat()].concat(); let zs_partial_products_commitment = timed!( @@ -266,7 +238,7 @@ fn wires_permutation_partial_products, const D: usi prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, ) -> Vec> { - let degree = common_data.quotient_degree_factor - 1; + let degree = common_data.quotient_degree_factor; let subgroup = &prover_data.subgroup; let k_is = &common_data.k_is; let values = subgroup @@ -294,11 +266,6 @@ fn wires_permutation_partial_products, const D: usi .collect::>(); let quotient_partials = partial_products("ient_values, degree); - dbg!(check_partial_products( - "ient_values, - "ient_partials, - degree - )); // This is the final product for the quotient. let quotient = *quotient_partials.last().unwrap() diff --git a/src/plonk/vanishing_poly.rs b/src/plonk/vanishing_poly.rs index 9bc4feb5..b9b4d241 100644 --- a/src/plonk/vanishing_poly.rs +++ b/src/plonk/vanishing_poly.rs @@ -27,7 +27,7 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( gammas: &[F], alphas: &[F], ) -> Vec { - let max_degree = common_data.quotient_degree_factor - 1; + let max_degree = common_data.quotient_degree_factor; let (num_prods, final_num_prod) = common_data.num_partial_products; let constraint_terms = @@ -74,15 +74,9 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( // The first checks are of the form `q - n/d` which is a rational function not a polynomial. // We multiply them by `d` to get checks of the form `q*d - n` which low-degree polynomials. for (j, q) in partial_product_check.iter_mut().enumerate() { - let range = j * (max_degree - 1)..(j + 1) * (max_degree - 1); + let range = j * max_degree..(j + 1) * max_degree; *q *= denominator_values[range].iter().copied().product(); } - // denominator_values - // .chunks(max_degree) - // .zip(partial_product_check.iter_mut()) - // .for_each(|(d, q)| { - // *q *= d.iter().copied().product(); - // }); vanishing_partial_products_terms.extend(partial_product_check); // The quotient final product is the product of the last `final_num_prod` elements. @@ -131,7 +125,7 @@ pub(crate) fn eval_vanishing_poly_base_batch, const assert_eq!(partial_products_batch.len(), n); assert_eq!(s_sigmas_batch.len(), n); - let max_degree = common_data.quotient_degree_factor - 1; + let max_degree = common_data.quotient_degree_factor; let (num_prods, final_num_prod) = common_data.num_partial_products; let num_gate_constraints = common_data.num_gate_constraints; @@ -197,24 +191,14 @@ pub(crate) fn eval_vanishing_poly_base_batch, const // The first checks are of the form `q - n/d` which is a rational function not a polynomial. // We multiply them by `d` to get checks of the form `q*d - n` which low-degree polynomials. for (j, q) in partial_product_check.iter_mut().enumerate() { - let range = j * (max_degree - 1)..(j + 1) * (max_degree - 1); + let range = j * max_degree..(j + 1) * max_degree; *q *= denominator_values[range].iter().copied().product(); } - // denominator_values - // .chunks(max_degree) - // .zip(partial_product_check.iter_mut()) - // .for_each(|(d, q)| { - // *q *= d.iter().copied().product(); - // }); vanishing_partial_products_terms.extend(partial_product_check); // The quotient final product is the product of the last `final_num_prod` elements. let quotient: F = *current_partial_products.last().unwrap() * quotient_values[final_num_prod..].iter().copied().product(); - // let quotient: F = current_partial_products[num_prods - final_num_prod..] - // .iter() - // .copied() - // .product(); let mut wanted = quotient * z_x - z_gz; wanted *= denominator_values[final_num_prod..] .iter() diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index 1e361101..bc5fce45 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -13,7 +13,7 @@ pub fn partial_products(v: &[F], max_degree: usize) -> Vec { debug_assert!(max_degree > 1); let mut res = Vec::new(); let mut acc = F::ONE; - let chunk_size = max_degree - 1; + let chunk_size = max_degree; let num_chunks = ceil_div_usize(v.len(), chunk_size) - 1; for i in 0..num_chunks { acc *= v[i * chunk_size..(i + 1) * chunk_size] @@ -30,7 +30,7 @@ pub fn partial_products(v: &[F], max_degree: usize) -> Vec { /// vector of length `n`, and `b` is the number of elements needed to compute the final product. pub fn num_partial_products(n: usize, max_degree: usize) -> (usize, usize) { debug_assert!(max_degree > 1); - let chunk_size = max_degree - 1; + let chunk_size = max_degree; let num_chunks = ceil_div_usize(n, chunk_size) - 1; (num_chunks, num_chunks * chunk_size) @@ -43,7 +43,7 @@ pub fn check_partial_products(v: &[F], mut partials: &[F], max_degree: let mut partials = partials.iter(); let mut res = Vec::new(); let mut acc = F::ONE; - let chunk_size = max_degree - 1; + let chunk_size = max_degree; let num_chunks = ceil_div_usize(v.len(), chunk_size) - 1; for i in 0..num_chunks { acc *= v[i * chunk_size..(i + 1) * chunk_size] From 7cf965ded511011c39f0126a69e57d308f39cdec Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 9 Nov 2021 15:18:43 +0100 Subject: [PATCH 04/12] All tests pass --- src/plonk/vanishing_poly.rs | 37 +++++++++++++++++++++++++++--------- src/util/partial_products.rs | 25 ++++++++++++------------ 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/plonk/vanishing_poly.rs b/src/plonk/vanishing_poly.rs index b9b4d241..7b9d0c6c 100644 --- a/src/plonk/vanishing_poly.rs +++ b/src/plonk/vanishing_poly.rs @@ -396,20 +396,39 @@ pub(crate) fn eval_vanishing_poly_recursively, cons ); // The first checks are of the form `q - n/d` which is a rational function not a polynomial. // We multiply them by `d` to get checks of the form `q*d - n` which low-degree polynomials. - denominator_values - .chunks(max_degree) - .zip(partial_product_check.iter_mut()) - .for_each(|(d, q)| { - let mut v = d.to_vec(); + // denominator_values + // .chunks(max_degree) + // .zip(partial_product_check.iter_mut()) + // .for_each(|(d, q)| { + // let mut v = d.to_vec(); + // v.push(*q); + // *q = builder.mul_many_extension(&v); + // }); + for (j, q) in partial_product_check.iter_mut().enumerate() { + let range = j * max_degree..(j + 1) * max_degree; + *q = builder.mul_many_extension(&{ + let mut v = denominator_values[range].to_vec(); v.push(*q); - *q = builder.mul_many_extension(&v); + v }); + } vanishing_partial_products_terms.extend(partial_product_check); // The quotient final product is the product of the last `final_num_prod` elements. - let quotient = - builder.mul_many_extension(¤t_partial_products[num_prods - final_num_prod..]); - vanishing_v_shift_terms.push(builder.mul_sub_extension(quotient, z_x, z_gz)); + // let quotient = + // builder.mul_many_extension(¤t_partial_products[num_prods - final_num_prod..]); + let quotient = builder.mul_many_extension(&{ + let mut v = quotient_values[final_num_prod..].to_vec(); + v.push(*current_partial_products.last().unwrap()); + v + }); + let mut wanted = builder.mul_sub_extension(quotient, z_x, z_gz); + wanted = builder.mul_many_extension(&{ + let mut v = denominator_values[final_num_prod..].to_vec(); + v.push(wanted); + v + }); + vanishing_v_shift_terms.push(wanted); } let vanishing_terms = [ diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index bc5fce45..398ec35f 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -14,7 +14,7 @@ pub fn partial_products(v: &[F], max_degree: usize) -> Vec { let mut res = Vec::new(); let mut acc = F::ONE; let chunk_size = max_degree; - let num_chunks = ceil_div_usize(v.len(), chunk_size) - 1; + let num_chunks = v.len() / chunk_size; for i in 0..num_chunks { acc *= v[i * chunk_size..(i + 1) * chunk_size] .iter() @@ -31,7 +31,7 @@ pub fn partial_products(v: &[F], max_degree: usize) -> Vec { pub fn num_partial_products(n: usize, max_degree: usize) -> (usize, usize) { debug_assert!(max_degree > 1); let chunk_size = max_degree; - let num_chunks = ceil_div_usize(n, chunk_size) - 1; + let num_chunks = n / chunk_size; (num_chunks, num_chunks * chunk_size) } @@ -44,15 +44,15 @@ pub fn check_partial_products(v: &[F], mut partials: &[F], max_degree: let mut res = Vec::new(); let mut acc = F::ONE; let chunk_size = max_degree; - let num_chunks = ceil_div_usize(v.len(), chunk_size) - 1; + let num_chunks = v.len() / chunk_size; for i in 0..num_chunks { acc *= v[i * chunk_size..(i + 1) * chunk_size] .iter() .copied() .product(); - let bacc = *partials.next().unwrap(); - res.push(acc - bacc); - acc = bacc; + let new_acc = *partials.next().unwrap(); + res.push(acc - new_acc); + acc = new_acc; } debug_assert!(partials.next().is_none()); @@ -68,15 +68,16 @@ pub fn check_partial_products_recursively, const D: debug_assert!(max_degree > 1); let mut partials = partials.iter(); let mut res = Vec::new(); - let mut acc = v[0]; - let chunk_size = max_degree - 1; - let num_chunks = ceil_div_usize(v.len() - 1, chunk_size) - 1; + let mut acc = builder.one_extension(); + let chunk_size = max_degree; + let num_chunks = v.len() / chunk_size; for i in 0..num_chunks { - let mut chunk = v[1 + i * chunk_size..1 + (i + 1) * chunk_size].to_vec(); + let mut chunk = v[i * chunk_size..(i + 1) * chunk_size].to_vec(); chunk.push(acc); acc = builder.mul_many_extension(&chunk); - - res.push(builder.sub_extension(acc, *partials.next().unwrap())); + let new_acc = *partials.next().unwrap(); + res.push(builder.sub_extension(acc, new_acc)); + acc = new_acc; } debug_assert!(partials.next().is_none()); From abc706ee26a94fe95767703950612d89eb8e8a13 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 9 Nov 2021 17:18:15 +0100 Subject: [PATCH 05/12] Fix partial product test --- src/util/partial_products.rs | 93 ++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index 398ec35f..38177de8 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -27,7 +27,7 @@ pub fn partial_products(v: &[F], max_degree: usize) -> Vec { } /// Returns a tuple `(a,b)`, where `a` is the length of the output of `partial_products()` on a -/// vector of length `n`, and `b` is the number of elements needed to compute the final product. +/// vector of length `n`, and `b` is the number of original elements consumed in `partial_products()`. pub fn num_partial_products(n: usize, max_degree: usize) -> (usize, usize) { debug_assert!(max_degree > 1); let chunk_size = max_degree; @@ -84,38 +84,59 @@ pub fn check_partial_products_recursively, const D: res } -// #[cfg(test)] -// mod tests { -// use num::Zero; -// -// use super::*; -// -// #[test] -// fn test_partial_products() { -// let v = vec![1, 2, 3, 4, 5, 6]; -// let p = partial_products(&v, 2); -// assert_eq!(p, vec![2, 6, 24, 120]); -// let nums = num_partial_products(v.len(), 2); -// assert_eq!(p.len(), nums.0); -// assert!(check_partial_products(&v, &p, 2) -// .iter() -// .all(|x| x.is_zero())); -// assert_eq!( -// *p.last().unwrap() * v[nums.1..].iter().copied().product::(), -// v.into_iter().product::(), -// ); -// -// let v = vec![1, 2, 3, 4, 5, 6]; -// let p = partial_products(&v, 3); -// assert_eq!(p, vec![6, 120]); -// let nums = num_partial_products(v.len(), 3); -// assert_eq!(p.len(), nums.0); -// assert!(check_partial_products(&v, &p, 3) -// .iter() -// .all(|x| x.is_zero())); -// assert_eq!( -// *p.last().unwrap() * v[nums.1..].iter().copied().product::(), -// v.into_iter().product::(), -// ); -// } -// } +#[cfg(test)] +mod tests { + use num::Zero; + + use super::*; + use crate::field::goldilocks_field::GoldilocksField; + + #[test] + fn test_partial_products() { + type F = GoldilocksField; + let v = [1, 2, 3, 4, 5, 6] + .into_iter() + .map(|&i| F::from_canonical_u64(i)) + .collect::>(); + let p = partial_products(&v, 2); + assert_eq!( + p, + [2, 24, 720] + .into_iter() + .map(|&i| F::from_canonical_u64(i)) + .collect::>() + ); + + let nums = num_partial_products(v.len(), 2); + assert_eq!(p.len(), nums.0); + assert!(check_partial_products(&v, &p, 2) + .iter() + .all(|x| x.is_zero())); + assert_eq!( + *p.last().unwrap() * v[nums.1..].iter().copied().product::(), + v.into_iter().product::(), + ); + + let v = [1, 2, 3, 4, 5, 6] + .into_iter() + .map(|&i| F::from_canonical_u64(i)) + .collect::>(); + let p = partial_products(&v, 3); + assert_eq!( + p, + [6, 720] + .into_iter() + .map(|&i| F::from_canonical_u64(i)) + .collect::>() + ); + let nums = num_partial_products(v.len(), 3); + assert_eq!(p.len(), nums.0); + assert!(check_partial_products(&v, &p, 3) + .iter() + .all(|x| x.is_zero())); + assert_eq!( + *p.last().unwrap() * v[nums.1..].iter().copied().product::(), + v.into_iter().product::(), + ); + } +} From 067f81e24f8b1bc15aab8ab2b75494400fcd338e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 9 Nov 2021 17:25:22 +0100 Subject: [PATCH 06/12] Comments and cleaning --- src/plonk/vanishing_poly.rs | 74 ++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 42 deletions(-) diff --git a/src/plonk/vanishing_poly.rs b/src/plonk/vanishing_poly.rs index 7b9d0c6c..4976eaba 100644 --- a/src/plonk/vanishing_poly.rs +++ b/src/plonk/vanishing_poly.rs @@ -69,25 +69,25 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( // The partial products considered for this iteration of `i`. let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods]; // Check the quotient partial products. - let mut partial_product_check = + let mut partial_product_checks = check_partial_products("ient_values, current_partial_products, max_degree); - // The first checks are of the form `q - n/d` which is a rational function not a polynomial. - // We multiply them by `d` to get checks of the form `q*d - n` which low-degree polynomials. - for (j, q) in partial_product_check.iter_mut().enumerate() { + // The partial products are products of quotients, so we multiply them by the product of the + // corresponding denominators to make sure they are polynomials. + for (j, partial_product_check) in partial_product_checks.iter_mut().enumerate() { let range = j * max_degree..(j + 1) * max_degree; - *q *= denominator_values[range].iter().copied().product(); + *partial_product_check *= denominator_values[range].iter().copied().product(); } - vanishing_partial_products_terms.extend(partial_product_check); + vanishing_partial_products_terms.extend(partial_product_checks); - // The quotient final product is the product of the last `final_num_prod` elements. let quotient: F::Extension = *current_partial_products.last().unwrap() * quotient_values[final_num_prod..].iter().copied().product(); - let mut wanted = quotient * z_x - z_gz; - wanted *= denominator_values[final_num_prod..] + let mut v_shift_term = quotient * z_x - z_gz; + // Need to multiply by the denominators to make sure we get a polynomial. + v_shift_term *= denominator_values[final_num_prod..] .iter() .copied() .product(); - vanishing_v_shift_terms.push(wanted); + vanishing_v_shift_terms.push(v_shift_term); } let vanishing_terms = [ @@ -186,25 +186,25 @@ pub(crate) fn eval_vanishing_poly_base_batch, const // The partial products considered for this iteration of `i`. let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods]; // Check the numerator partial products. - let mut partial_product_check = + let mut partial_product_checks = check_partial_products("ient_values, current_partial_products, max_degree); - // The first checks are of the form `q - n/d` which is a rational function not a polynomial. - // We multiply them by `d` to get checks of the form `q*d - n` which low-degree polynomials. - for (j, q) in partial_product_check.iter_mut().enumerate() { + // The partial products are products of quotients, so we multiply them by the product of the + // corresponding denominators to make sure they are polynomials. + for (j, partial_product_check) in partial_product_checks.iter_mut().enumerate() { let range = j * max_degree..(j + 1) * max_degree; - *q *= denominator_values[range].iter().copied().product(); + *partial_product_check *= denominator_values[range].iter().copied().product(); } - vanishing_partial_products_terms.extend(partial_product_check); + vanishing_partial_products_terms.extend(partial_product_checks); - // The quotient final product is the product of the last `final_num_prod` elements. let quotient: F = *current_partial_products.last().unwrap() * quotient_values[final_num_prod..].iter().copied().product(); - let mut wanted = quotient * z_x - z_gz; - wanted *= denominator_values[final_num_prod..] + let mut v_shift_term = quotient * z_x - z_gz; + // Need to multiply by the denominators to make sure we get a polynomial. + v_shift_term *= denominator_values[final_num_prod..] .iter() .copied() .product(); - vanishing_v_shift_terms.push(wanted); + vanishing_v_shift_terms.push(v_shift_term); numerator_values.clear(); denominator_values.clear(); @@ -388,47 +388,37 @@ pub(crate) fn eval_vanishing_poly_recursively, cons // The partial products considered for this iteration of `i`. let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods]; // Check the quotient partial products. - let mut partial_product_check = check_partial_products_recursively( + let mut partial_product_checks = check_partial_products_recursively( builder, "ient_values, current_partial_products, max_degree, ); - // The first checks are of the form `q - n/d` which is a rational function not a polynomial. - // We multiply them by `d` to get checks of the form `q*d - n` which low-degree polynomials. - // denominator_values - // .chunks(max_degree) - // .zip(partial_product_check.iter_mut()) - // .for_each(|(d, q)| { - // let mut v = d.to_vec(); - // v.push(*q); - // *q = builder.mul_many_extension(&v); - // }); - for (j, q) in partial_product_check.iter_mut().enumerate() { + // The partial products are products of quotients, so we multiply them by the product of the + // corresponding denominators to make sure they are polynomials. + for (j, partial_product_check) in partial_product_checks.iter_mut().enumerate() { let range = j * max_degree..(j + 1) * max_degree; - *q = builder.mul_many_extension(&{ + *partial_product_check = builder.mul_many_extension(&{ let mut v = denominator_values[range].to_vec(); - v.push(*q); + v.push(*partial_product_check); v }); } - vanishing_partial_products_terms.extend(partial_product_check); + vanishing_partial_products_terms.extend(partial_product_checks); - // The quotient final product is the product of the last `final_num_prod` elements. - // let quotient = - // builder.mul_many_extension(¤t_partial_products[num_prods - final_num_prod..]); let quotient = builder.mul_many_extension(&{ let mut v = quotient_values[final_num_prod..].to_vec(); v.push(*current_partial_products.last().unwrap()); v }); - let mut wanted = builder.mul_sub_extension(quotient, z_x, z_gz); - wanted = builder.mul_many_extension(&{ + let mut v_shift_term = builder.mul_sub_extension(quotient, z_x, z_gz); + // Need to multiply by the denominators to make sure we get a polynomial. + v_shift_term = builder.mul_many_extension(&{ let mut v = denominator_values[final_num_prod..].to_vec(); - v.push(wanted); + v.push(v_shift_term); v }); - vanishing_v_shift_terms.push(wanted); + vanishing_v_shift_terms.push(v_shift_term); } let vanishing_terms = [ From 3717ff701e9fd0540c10cba9c806e4549de9bce7 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 9 Nov 2021 17:33:14 +0100 Subject: [PATCH 07/12] Minor --- src/plonk/circuit_data.rs | 4 ++-- src/plonk/prover.rs | 2 +- src/util/partial_products.rs | 8 +------- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/plonk/circuit_data.rs b/src/plonk/circuit_data.rs index 9ba05a87..869543af 100644 --- a/src/plonk/circuit_data.rs +++ b/src/plonk/circuit_data.rs @@ -190,8 +190,8 @@ pub struct CommonCircuitData, const D: usize> { /// The `{k_i}` valued used in `S_ID_i` in Plonk's permutation argument. pub(crate) k_is: Vec, - /// The number of partial products needed to compute the `Z` polynomials and the number - /// of partial products needed to compute the final product. + /// The number of partial products needed to compute the `Z` polynomials and + /// the number of original elements consumed in `partial_products()`. pub(crate) num_partial_products: (usize, usize), /// A digest of the "circuit" (i.e. the instance, minus public inputs), which can be used to diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 22f9411c..6c57217c 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -17,7 +17,7 @@ use crate::plonk::vanishing_poly::eval_vanishing_poly_base_batch; use crate::plonk::vars::EvaluationVarsBase; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::timed; -use crate::util::partial_products::{check_partial_products, partial_products}; +use crate::util::partial_products::partial_products; use crate::util::timing::TimingTree; use crate::util::{log2_ceil, transpose}; diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index 38177de8..1b3821b6 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -1,11 +1,7 @@ -use std::iter::Product; -use std::ops::{MulAssign, Sub}; - use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field_types::{Field, RichField}; use crate::plonk::circuit_builder::CircuitBuilder; -use crate::util::ceil_div_usize; /// Compute partial products of the original vector `v` such that all products consist of `max_degree` /// or less elements. This is done until we've computed the product `P` of all elements in the vector. @@ -38,7 +34,7 @@ pub fn num_partial_products(n: usize, max_degree: usize) -> (usize, usize) { /// Checks that the partial products of `v` are coherent with those in `partials` by only computing /// products of size `max_degree` or less. -pub fn check_partial_products(v: &[F], mut partials: &[F], max_degree: usize) -> Vec { +pub fn check_partial_products(v: &[F], partials: &[F], max_degree: usize) -> Vec { debug_assert!(max_degree > 1); let mut partials = partials.iter(); let mut res = Vec::new(); @@ -86,8 +82,6 @@ pub fn check_partial_products_recursively, const D: #[cfg(test)] mod tests { - use num::Zero; - use super::*; use crate::field::goldilocks_field::GoldilocksField; From ff943138f325621b2b97aaddf429d690555a56e0 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 10 Nov 2021 09:38:47 +0100 Subject: [PATCH 08/12] Apply suggestions from code review Co-authored-by: Daniel Lubarov --- src/util/partial_products.rs | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index 1b3821b6..37b51825 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -10,12 +10,8 @@ pub fn partial_products(v: &[F], max_degree: usize) -> Vec { let mut res = Vec::new(); let mut acc = F::ONE; let chunk_size = max_degree; - let num_chunks = v.len() / chunk_size; - for i in 0..num_chunks { - acc *= v[i * chunk_size..(i + 1) * chunk_size] - .iter() - .copied() - .product(); + for chunk in v.chunks_exact(chunk_size) { + acc *= chunk.iter().copied().product(); res.push(acc); } @@ -40,12 +36,8 @@ pub fn check_partial_products(v: &[F], partials: &[F], max_degree: usi let mut res = Vec::new(); let mut acc = F::ONE; let chunk_size = max_degree; - let num_chunks = v.len() / chunk_size; - for i in 0..num_chunks { - acc *= v[i * chunk_size..(i + 1) * chunk_size] - .iter() - .copied() - .product(); + for chunk in v.chunks_exact(chunk_size) { + acc *= chunk.iter().copied().product(); let new_acc = *partials.next().unwrap(); res.push(acc - new_acc); acc = new_acc; @@ -66,13 +58,11 @@ pub fn check_partial_products_recursively, const D: let mut res = Vec::new(); let mut acc = builder.one_extension(); let chunk_size = max_degree; - let num_chunks = v.len() / chunk_size; - for i in 0..num_chunks { - let mut chunk = v[i * chunk_size..(i + 1) * chunk_size].to_vec(); - chunk.push(acc); - acc = builder.mul_many_extension(&chunk); + for chunk in v.chunks_exact(chunk_size) { + let chunk_product = builder.mul_many_extension(chunk); let new_acc = *partials.next().unwrap(); - res.push(builder.sub_extension(acc, new_acc)); + // Assert that new_acc = acc * chunk_product. + res.push(builder.mul_sub_extension(acc, chunk_product, new_acc)); acc = new_acc; } debug_assert!(partials.next().is_none()); From 32f09ac2dfc026f4c3e9771cdaa293f59e21de0c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 10 Nov 2021 18:13:27 +0100 Subject: [PATCH 09/12] Remove quotients and work directly with numerators and denominators in partial products check --- src/plonk/vanishing_poly.rs | 99 +++++++++++++----------------------- src/util/partial_products.rs | 41 ++++++++++----- 2 files changed, 64 insertions(+), 76 deletions(-) diff --git a/src/plonk/vanishing_poly.rs b/src/plonk/vanishing_poly.rs index 4976eaba..43e52994 100644 --- a/src/plonk/vanishing_poly.rs +++ b/src/plonk/vanishing_poly.rs @@ -62,31 +62,26 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( wire_value + s_sigma.scalar_mul(betas[i]) + gammas[i].into() }) .collect::>(); - let quotient_values = (0..common_data.config.num_routed_wires) - .map(|j| numerator_values[j] / denominator_values[j]) - .collect::>(); // The partial products considered for this iteration of `i`. let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods]; // Check the quotient partial products. - let mut partial_product_checks = - check_partial_products("ient_values, current_partial_products, max_degree); - // The partial products are products of quotients, so we multiply them by the product of the - // corresponding denominators to make sure they are polynomials. - for (j, partial_product_check) in partial_product_checks.iter_mut().enumerate() { - let range = j * max_degree..(j + 1) * max_degree; - *partial_product_check *= denominator_values[range].iter().copied().product(); - } + let partial_product_checks = check_partial_products( + &numerator_values, + &denominator_values, + current_partial_products, + max_degree, + ); vanishing_partial_products_terms.extend(partial_product_checks); - let quotient: F::Extension = *current_partial_products.last().unwrap() - * quotient_values[final_num_prod..].iter().copied().product(); - let mut v_shift_term = quotient * z_x - z_gz; - // Need to multiply by the denominators to make sure we get a polynomial. - v_shift_term *= denominator_values[final_num_prod..] - .iter() - .copied() - .product(); + let v_shift_term = *current_partial_products.last().unwrap() + * numerator_values[final_num_prod..].iter().copied().product() + * z_x + - z_gz + * denominator_values[final_num_prod..] + .iter() + .copied() + .product(); vanishing_v_shift_terms.push(v_shift_term); } @@ -139,7 +134,6 @@ pub(crate) fn eval_vanishing_poly_base_batch, const let mut numerator_values = Vec::with_capacity(num_routed_wires); let mut denominator_values = Vec::with_capacity(num_routed_wires); - let mut quotient_values = Vec::with_capacity(num_routed_wires); // The L_1(x) (Z(x) - 1) vanishing terms. let mut vanishing_z_1_terms = Vec::with_capacity(num_challenges); @@ -178,37 +172,30 @@ pub(crate) fn eval_vanishing_poly_base_batch, const let s_sigma = s_sigmas[j]; wire_value + betas[i] * s_sigma + gammas[i] })); - let denominator_inverses = F::batch_multiplicative_inverse(&denominator_values); - quotient_values.extend( - (0..num_routed_wires).map(|j| numerator_values[j] * denominator_inverses[j]), - ); // The partial products considered for this iteration of `i`. let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods]; // Check the numerator partial products. - let mut partial_product_checks = - check_partial_products("ient_values, current_partial_products, max_degree); - // The partial products are products of quotients, so we multiply them by the product of the - // corresponding denominators to make sure they are polynomials. - for (j, partial_product_check) in partial_product_checks.iter_mut().enumerate() { - let range = j * max_degree..(j + 1) * max_degree; - *partial_product_check *= denominator_values[range].iter().copied().product(); - } + let partial_product_checks = check_partial_products( + &numerator_values, + &denominator_values, + current_partial_products, + max_degree, + ); vanishing_partial_products_terms.extend(partial_product_checks); - let quotient: F = *current_partial_products.last().unwrap() - * quotient_values[final_num_prod..].iter().copied().product(); - let mut v_shift_term = quotient * z_x - z_gz; - // Need to multiply by the denominators to make sure we get a polynomial. - v_shift_term *= denominator_values[final_num_prod..] - .iter() - .copied() - .product(); + let v_shift_term = *current_partial_products.last().unwrap() + * numerator_values[final_num_prod..].iter().copied().product() + * z_x + - z_gz + * denominator_values[final_num_prod..] + .iter() + .copied() + .product(); vanishing_v_shift_terms.push(v_shift_term); numerator_values.clear(); denominator_values.clear(); - quotient_values.clear(); } let vanishing_terms = vanishing_z_1_terms @@ -365,7 +352,6 @@ pub(crate) fn eval_vanishing_poly_recursively, cons let mut numerator_values = Vec::new(); let mut denominator_values = Vec::new(); - let mut quotient_values = Vec::new(); for j in 0..common_data.config.num_routed_wires { let wire_value = vars.local_wires[j]; @@ -378,46 +364,33 @@ pub(crate) fn eval_vanishing_poly_recursively, cons let numerator = builder.mul_add_extension(beta_ext, s_ids[j], wire_value_plus_gamma); let denominator = builder.mul_add_extension(beta_ext, s_sigmas[j], wire_value_plus_gamma); - let quotient = builder.div_extension(numerator, denominator); - numerator_values.push(numerator); denominator_values.push(denominator); - quotient_values.push(quotient); } // The partial products considered for this iteration of `i`. let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods]; // Check the quotient partial products. - let mut partial_product_checks = check_partial_products_recursively( + let partial_product_checks = check_partial_products_recursively( builder, - "ient_values, + &numerator_values, + &denominator_values, current_partial_products, max_degree, ); - // The partial products are products of quotients, so we multiply them by the product of the - // corresponding denominators to make sure they are polynomials. - for (j, partial_product_check) in partial_product_checks.iter_mut().enumerate() { - let range = j * max_degree..(j + 1) * max_degree; - *partial_product_check = builder.mul_many_extension(&{ - let mut v = denominator_values[range].to_vec(); - v.push(*partial_product_check); - v - }); - } vanishing_partial_products_terms.extend(partial_product_checks); - let quotient = builder.mul_many_extension(&{ - let mut v = quotient_values[final_num_prod..].to_vec(); + let nume_acc = builder.mul_many_extension(&{ + let mut v = numerator_values[final_num_prod..].to_vec(); v.push(*current_partial_products.last().unwrap()); v }); - let mut v_shift_term = builder.mul_sub_extension(quotient, z_x, z_gz); - // Need to multiply by the denominators to make sure we get a polynomial. - v_shift_term = builder.mul_many_extension(&{ + let z_gz_denominators = builder.mul_many_extension(&{ let mut v = denominator_values[final_num_prod..].to_vec(); - v.push(v_shift_term); + v.push(z_gz); v }); + let v_shift_term = builder.mul_sub_extension(nume_acc, z_x, z_gz_denominators); vanishing_v_shift_terms.push(v_shift_term); } diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index 37b51825..c3c4659a 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -28,18 +28,26 @@ pub fn num_partial_products(n: usize, max_degree: usize) -> (usize, usize) { (num_chunks, num_chunks * chunk_size) } -/// Checks that the partial products of `v` are coherent with those in `partials` by only computing +/// Checks that the partial products of `numerators/denominators` are coherent with those in `partials` by only computing /// products of size `max_degree` or less. -pub fn check_partial_products(v: &[F], partials: &[F], max_degree: usize) -> Vec { +pub fn check_partial_products( + numerators: &[F], + denominators: &[F], + partials: &[F], + max_degree: usize, +) -> Vec { debug_assert!(max_degree > 1); let mut partials = partials.iter(); let mut res = Vec::new(); let mut acc = F::ONE; let chunk_size = max_degree; - for chunk in v.chunks_exact(chunk_size) { - acc *= chunk.iter().copied().product(); - let new_acc = *partials.next().unwrap(); - res.push(acc - new_acc); + for (nume_chunk, deno_chunk) in numerators + .chunks_exact(chunk_size) + .zip(denominators.chunks_exact(chunk_size)) + { + acc *= nume_chunk.iter().copied().product(); + let mut new_acc = *partials.next().unwrap(); + res.push(acc - new_acc * deno_chunk.iter().copied().product()); acc = new_acc; } debug_assert!(partials.next().is_none()); @@ -49,7 +57,8 @@ pub fn check_partial_products(v: &[F], partials: &[F], max_degree: usi pub fn check_partial_products_recursively, const D: usize>( builder: &mut CircuitBuilder, - v: &[ExtensionTarget], + numerators: &[ExtensionTarget], + denominators: &[ExtensionTarget], partials: &[ExtensionTarget], max_degree: usize, ) -> Vec> { @@ -58,11 +67,16 @@ pub fn check_partial_products_recursively, const D: let mut res = Vec::new(); let mut acc = builder.one_extension(); let chunk_size = max_degree; - for chunk in v.chunks_exact(chunk_size) { - let chunk_product = builder.mul_many_extension(chunk); + for (nume_chunk, deno_chunk) in numerators + .chunks_exact(chunk_size) + .zip(denominators.chunks_exact(chunk_size)) + { + let nume_product = builder.mul_many_extension(nume_chunk); + let deno_product = builder.mul_many_extension(deno_chunk); let new_acc = *partials.next().unwrap(); - // Assert that new_acc = acc * chunk_product. - res.push(builder.mul_sub_extension(acc, chunk_product, new_acc)); + let new_acc_deno = builder.mul_extension(new_acc, deno_product); + // Assert that new_acc*deno_product = acc * nume_product. + res.push(builder.mul_sub_extension(acc, nume_product, new_acc_deno)); acc = new_acc; } debug_assert!(partials.next().is_none()); @@ -78,6 +92,7 @@ mod tests { #[test] fn test_partial_products() { type F = GoldilocksField; + let denominators = vec![F::ONE; 6]; let v = [1, 2, 3, 4, 5, 6] .into_iter() .map(|&i| F::from_canonical_u64(i)) @@ -93,7 +108,7 @@ mod tests { let nums = num_partial_products(v.len(), 2); assert_eq!(p.len(), nums.0); - assert!(check_partial_products(&v, &p, 2) + assert!(check_partial_products(&v, &denominators, &p, 2) .iter() .all(|x| x.is_zero())); assert_eq!( @@ -115,7 +130,7 @@ mod tests { ); let nums = num_partial_products(v.len(), 3); assert_eq!(p.len(), nums.0); - assert!(check_partial_products(&v, &p, 3) + assert!(check_partial_products(&v, &denominators, &p, 3) .iter() .all(|x| x.is_zero())); assert_eq!( From 3084367133ad03c7bc74973975dc1d313a338fda Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 10 Nov 2021 18:36:35 +0100 Subject: [PATCH 10/12] Start accumulator at Z(x) --- src/plonk/prover.rs | 14 ++++++++------ src/plonk/vanishing_poly.rs | 7 ++++--- src/util/partial_products.rs | 8 ++++---- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 6c57217c..be356d9f 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -100,7 +100,7 @@ pub(crate) fn prove, const D: usize>( let plonk_z_vecs = timed!( timing, "compute Z's", - compute_zs(&partial_products, common_data) + compute_zs(&mut partial_products, common_data) ); // The first polynomial in `partial_products` represent the final product used in the @@ -286,24 +286,26 @@ fn wires_permutation_partial_products, const D: usi } fn compute_zs, const D: usize>( - partial_products: &[Vec>], + partial_products: &mut [Vec>], common_data: &CommonCircuitData, ) -> Vec> { (0..common_data.config.num_challenges) - .map(|i| compute_z(&partial_products[i], common_data)) + .map(|i| compute_z(&mut partial_products[i], common_data)) .collect() } /// Compute the `Z` polynomial by reusing the computations done in `wires_permutation_partial_products`. fn compute_z, const D: usize>( - partial_products: &[PolynomialValues], + partial_products: &mut [PolynomialValues], common_data: &CommonCircuitData, ) -> PolynomialValues { let mut plonk_z_points = vec![F::ONE]; for i in 1..common_data.degree() { - let quotient = partial_products[0].values[i - 1]; let last = *plonk_z_points.last().unwrap(); - plonk_z_points.push(last * quotient); + for q in partial_products.iter_mut() { + q.values[i - 1] *= last; + } + plonk_z_points.push(partial_products[0].values[i - 1]); } plonk_z_points.into() } diff --git a/src/plonk/vanishing_poly.rs b/src/plonk/vanishing_poly.rs index 43e52994..899d69a6 100644 --- a/src/plonk/vanishing_poly.rs +++ b/src/plonk/vanishing_poly.rs @@ -70,13 +70,13 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( &numerator_values, &denominator_values, current_partial_products, + z_x, max_degree, ); vanishing_partial_products_terms.extend(partial_product_checks); let v_shift_term = *current_partial_products.last().unwrap() * numerator_values[final_num_prod..].iter().copied().product() - * z_x - z_gz * denominator_values[final_num_prod..] .iter() @@ -180,13 +180,13 @@ pub(crate) fn eval_vanishing_poly_base_batch, const &numerator_values, &denominator_values, current_partial_products, + z_x, max_degree, ); vanishing_partial_products_terms.extend(partial_product_checks); let v_shift_term = *current_partial_products.last().unwrap() * numerator_values[final_num_prod..].iter().copied().product() - * z_x - z_gz * denominator_values[final_num_prod..] .iter() @@ -376,6 +376,7 @@ pub(crate) fn eval_vanishing_poly_recursively, cons &numerator_values, &denominator_values, current_partial_products, + z_x, max_degree, ); vanishing_partial_products_terms.extend(partial_product_checks); @@ -390,7 +391,7 @@ pub(crate) fn eval_vanishing_poly_recursively, cons v.push(z_gz); v }); - let v_shift_term = builder.mul_sub_extension(nume_acc, z_x, z_gz_denominators); + let v_shift_term = builder.sub_extension(nume_acc, z_gz_denominators); vanishing_v_shift_terms.push(v_shift_term); } diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index c3c4659a..92419a56 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -34,12 +34,12 @@ pub fn check_partial_products( numerators: &[F], denominators: &[F], partials: &[F], + mut acc: F, max_degree: usize, ) -> Vec { debug_assert!(max_degree > 1); let mut partials = partials.iter(); let mut res = Vec::new(); - let mut acc = F::ONE; let chunk_size = max_degree; for (nume_chunk, deno_chunk) in numerators .chunks_exact(chunk_size) @@ -60,12 +60,12 @@ pub fn check_partial_products_recursively, const D: numerators: &[ExtensionTarget], denominators: &[ExtensionTarget], partials: &[ExtensionTarget], + mut acc: ExtensionTarget, max_degree: usize, ) -> Vec> { debug_assert!(max_degree > 1); let mut partials = partials.iter(); let mut res = Vec::new(); - let mut acc = builder.one_extension(); let chunk_size = max_degree; for (nume_chunk, deno_chunk) in numerators .chunks_exact(chunk_size) @@ -108,7 +108,7 @@ mod tests { let nums = num_partial_products(v.len(), 2); assert_eq!(p.len(), nums.0); - assert!(check_partial_products(&v, &denominators, &p, 2) + assert!(check_partial_products(&v, &denominators, &p, F::ONE, 2) .iter() .all(|x| x.is_zero())); assert_eq!( @@ -130,7 +130,7 @@ mod tests { ); let nums = num_partial_products(v.len(), 3); assert_eq!(p.len(), nums.0); - assert!(check_partial_products(&v, &denominators, &p, 3) + assert!(check_partial_products(&v, &denominators, &p, F::ONE, 3) .iter() .all(|x| x.is_zero())); assert_eq!( From 9139d1350a0fb464af3ad729660ddfbafc4409ba Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Thu, 11 Nov 2021 07:16:16 -0800 Subject: [PATCH 11/12] Minor refactor of partial product code (#351) --- src/plonk/prover.rs | 89 ++++++++++++------------------------ src/plonk/vanishing_poly.rs | 38 ++++++--------- src/util/partial_products.rs | 79 +++++++++++++++++--------------- 3 files changed, 83 insertions(+), 123 deletions(-) diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index be356d9f..1c247998 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -17,9 +17,10 @@ use crate::plonk::vanishing_poly::eval_vanishing_poly_base_batch; use crate::plonk::vars::EvaluationVarsBase; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::timed; -use crate::util::partial_products::partial_products; +use crate::util::partial_products::{partial_products_and_z_gx, quotient_chunk_products}; use crate::util::timing::TimingTree; use crate::util::{log2_ceil, transpose}; +use std::mem::swap; pub(crate) fn prove, const D: usize>( prover_data: &ProverOnlyCircuitData, @@ -91,28 +92,21 @@ pub(crate) fn prove, const D: usize>( common_data.quotient_degree_factor < common_data.config.num_routed_wires, "When the number of routed wires is smaller that the degree, we should change the logic to avoid computing partial products." ); - let mut partial_products = timed!( + let mut partial_products_and_zs = timed!( timing, "compute partial products", all_wires_permutation_partial_products(&witness, &betas, &gammas, prover_data, common_data) ); - let plonk_z_vecs = timed!( - timing, - "compute Z's", - compute_zs(&mut partial_products, common_data) - ); + // Z is expected at the front of our batch; see `zs_range` and `partial_products_range`. + let plonk_z_vecs = partial_products_and_zs.iter_mut() + .map(|partial_products_and_z| partial_products_and_z.pop().unwrap()) + .collect(); + let zs_partial_products = [plonk_z_vecs, partial_products_and_zs.concat()].concat(); - // The first polynomial in `partial_products` represent the final product used in the - // computation of `Z`. It isn't needed anymore so we discard it. - partial_products.iter_mut().for_each(|part| { - part.remove(0); - }); - - let zs_partial_products = [plonk_z_vecs, partial_products.concat()].concat(); - let zs_partial_products_commitment = timed!( + let partial_products_and_zs_commitment = timed!( timing, - "commit to Z's", + "commit to partial products and Z's", PolynomialBatchCommitment::from_values( zs_partial_products, config.rate_bits, @@ -123,7 +117,7 @@ pub(crate) fn prove, const D: usize>( ) ); - challenger.observe_cap(&zs_partial_products_commitment.merkle_tree.cap); + challenger.observe_cap(&partial_products_and_zs_commitment.merkle_tree.cap); let alphas = challenger.get_n_challenges(num_challenges); @@ -135,7 +129,7 @@ pub(crate) fn prove, const D: usize>( prover_data, &public_inputs_hash, &wires_commitment, - &zs_partial_products_commitment, + &partial_products_and_zs_commitment, &betas, &gammas, &alphas, @@ -184,7 +178,7 @@ pub(crate) fn prove, const D: usize>( &[ &prover_data.constants_sigmas_commitment, &wires_commitment, - &zs_partial_products_commitment, + &partial_products_and_zs_commitment, "ient_polys_commitment, ], zeta, @@ -196,7 +190,7 @@ pub(crate) fn prove, const D: usize>( let proof = Proof { wires_cap: wires_commitment.merkle_tree.cap, - plonk_zs_partial_products_cap: zs_partial_products_commitment.merkle_tree.cap, + plonk_zs_partial_products_cap: partial_products_and_zs_commitment.merkle_tree.cap, quotient_polys_cap: quotient_polys_commitment.merkle_tree.cap, openings, opening_proof, @@ -217,7 +211,7 @@ fn all_wires_permutation_partial_products, const D: ) -> Vec>> { (0..common_data.config.num_challenges) .map(|i| { - wires_permutation_partial_products( + wires_permutation_partial_products_and_zs( witness, betas[i], gammas[i], @@ -231,7 +225,7 @@ fn all_wires_permutation_partial_products, const D: /// Compute the partial products used in the `Z` polynomial. /// Returns the polynomials interpolating `partial_products(f / g)` /// where `f, g` are the products in the definition of `Z`: `Z(g^i) = f / g`. -fn wires_permutation_partial_products, const D: usize>( +fn wires_permutation_partial_products_and_zs, const D: usize>( witness: &MatrixWitness, beta: F, gamma: F, @@ -241,7 +235,8 @@ fn wires_permutation_partial_products, const D: usi let degree = common_data.quotient_degree_factor; let subgroup = &prover_data.subgroup; let k_is = &common_data.k_is; - let values = subgroup + let (num_prods, final_num_prod) = common_data.num_partial_products; + let all_quotient_chunk_products = subgroup .par_iter() .enumerate() .map(|(i, &x)| { @@ -265,51 +260,25 @@ fn wires_permutation_partial_products, const D: usi .map(|(num, den_inv)| num * den_inv) .collect::>(); - let quotient_partials = partial_products("ient_values, degree); - - // This is the final product for the quotient. - let quotient = *quotient_partials.last().unwrap() - * quotient_values[common_data.num_partial_products.1..] - .iter() - .copied() - .product(); - - // We add the quotient at the beginning of the vector to reuse them later in the computation of `Z`. - [vec![quotient], quotient_partials].concat() + quotient_chunk_products("ient_values, degree) }) .collect::>(); - transpose(&values) + let mut z_x = F::ONE; + let mut all_partial_products_and_zs = Vec::new(); + for quotient_chunk_products in all_quotient_chunk_products { + let mut partial_products_and_z_gx = partial_products_and_z_gx(z_x, "ient_chunk_products); + // The last term is Z(gx), but we replace it with Z(x), otherwise Z would end up shifted. + swap(&mut z_x, &mut partial_products_and_z_gx[num_prods]); + all_partial_products_and_zs.push(partial_products_and_z_gx); + } + + transpose(&all_partial_products_and_zs) .into_par_iter() .map(PolynomialValues::new) .collect() } -fn compute_zs, const D: usize>( - partial_products: &mut [Vec>], - common_data: &CommonCircuitData, -) -> Vec> { - (0..common_data.config.num_challenges) - .map(|i| compute_z(&mut partial_products[i], common_data)) - .collect() -} - -/// Compute the `Z` polynomial by reusing the computations done in `wires_permutation_partial_products`. -fn compute_z, const D: usize>( - partial_products: &mut [PolynomialValues], - common_data: &CommonCircuitData, -) -> PolynomialValues { - let mut plonk_z_points = vec![F::ONE]; - for i in 1..common_data.degree() { - let last = *plonk_z_points.last().unwrap(); - for q in partial_products.iter_mut() { - q.values[i - 1] *= last; - } - plonk_z_points.push(partial_products[0].values[i - 1]); - } - plonk_z_points.into() -} - const BATCH_SIZE: usize = 32; fn compute_quotient_polys<'a, F: RichField + Extendable, const D: usize>( diff --git a/src/plonk/vanishing_poly.rs b/src/plonk/vanishing_poly.rs index 899d69a6..f91e027b 100644 --- a/src/plonk/vanishing_poly.rs +++ b/src/plonk/vanishing_poly.rs @@ -75,13 +75,10 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( ); vanishing_partial_products_terms.extend(partial_product_checks); - let v_shift_term = *current_partial_products.last().unwrap() - * numerator_values[final_num_prod..].iter().copied().product() - - z_gz - * denominator_values[final_num_prod..] - .iter() - .copied() - .product(); + let final_nume_product = numerator_values[final_num_prod..].iter().copied().product(); + let final_deno_product = denominator_values[final_num_prod..].iter().copied().product(); + let last_partial = *current_partial_products.last().unwrap(); + let v_shift_term = last_partial * final_nume_product - z_gz * final_deno_product; vanishing_v_shift_terms.push(v_shift_term); } @@ -185,13 +182,10 @@ pub(crate) fn eval_vanishing_poly_base_batch, const ); vanishing_partial_products_terms.extend(partial_product_checks); - let v_shift_term = *current_partial_products.last().unwrap() - * numerator_values[final_num_prod..].iter().copied().product() - - z_gz - * denominator_values[final_num_prod..] - .iter() - .copied() - .product(); + let final_nume_product = numerator_values[final_num_prod..].iter().copied().product(); + let final_deno_product = denominator_values[final_num_prod..].iter().copied().product(); + let last_partial = *current_partial_products.last().unwrap(); + let v_shift_term = last_partial * final_nume_product - z_gz * final_deno_product; vanishing_v_shift_terms.push(v_shift_term); numerator_values.clear(); @@ -381,17 +375,11 @@ pub(crate) fn eval_vanishing_poly_recursively, cons ); vanishing_partial_products_terms.extend(partial_product_checks); - let nume_acc = builder.mul_many_extension(&{ - let mut v = numerator_values[final_num_prod..].to_vec(); - v.push(*current_partial_products.last().unwrap()); - v - }); - let z_gz_denominators = builder.mul_many_extension(&{ - let mut v = denominator_values[final_num_prod..].to_vec(); - v.push(z_gz); - v - }); - let v_shift_term = builder.sub_extension(nume_acc, z_gz_denominators); + let final_nume_product = builder.mul_many_extension(&numerator_values[final_num_prod..]); + let final_deno_product = builder.mul_many_extension(&denominator_values[final_num_prod..]); + let z_gz_denominators = builder.mul_extension(z_gz, final_deno_product); + let last_partial = *current_partial_products.last().unwrap(); + let v_shift_term = builder.mul_sub_extension(last_partial, final_nume_product, z_gz_denominators); vanishing_v_shift_terms.push(v_shift_term); } diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index 92419a56..1e7a4f4b 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -2,19 +2,30 @@ use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field_types::{Field, RichField}; use crate::plonk::circuit_builder::CircuitBuilder; +use itertools::Itertools; + +pub(crate) fn quotient_chunk_products( + quotient_values: &[F], + max_degree: usize, +) -> Vec { + debug_assert!(max_degree > 1); + assert!(quotient_values.len() > 0); + let chunk_size = max_degree; + quotient_values.chunks(chunk_size) + .map(|chunk| chunk.iter().copied().product()) + .collect() +} /// Compute partial products of the original vector `v` such that all products consist of `max_degree` /// or less elements. This is done until we've computed the product `P` of all elements in the vector. -pub fn partial_products(v: &[F], max_degree: usize) -> Vec { - debug_assert!(max_degree > 1); +pub(crate) fn partial_products_and_z_gx(z_x: F, quotient_chunk_products: &[F]) -> Vec { + assert!(quotient_chunk_products.len() > 0); let mut res = Vec::new(); - let mut acc = F::ONE; - let chunk_size = max_degree; - for chunk in v.chunks_exact(chunk_size) { - acc *= chunk.iter().copied().product(); + let mut acc = z_x; + for "ient_chunk_product in quotient_chunk_products { + acc *= quotient_chunk_product; res.push(acc); } - res } @@ -30,24 +41,26 @@ pub fn num_partial_products(n: usize, max_degree: usize) -> (usize, usize) { /// Checks that the partial products of `numerators/denominators` are coherent with those in `partials` by only computing /// products of size `max_degree` or less. -pub fn check_partial_products( +pub(crate) fn check_partial_products( numerators: &[F], denominators: &[F], partials: &[F], - mut acc: F, + z_x: F, max_degree: usize, ) -> Vec { debug_assert!(max_degree > 1); + let mut acc = z_x; let mut partials = partials.iter(); let mut res = Vec::new(); let chunk_size = max_degree; for (nume_chunk, deno_chunk) in numerators .chunks_exact(chunk_size) - .zip(denominators.chunks_exact(chunk_size)) + .zip_eq(denominators.chunks_exact(chunk_size)) { - acc *= nume_chunk.iter().copied().product(); - let mut new_acc = *partials.next().unwrap(); - res.push(acc - new_acc * deno_chunk.iter().copied().product()); + let num_chunk_product = nume_chunk.iter().copied().product(); + let den_chunk_product = deno_chunk.iter().copied().product(); + let new_acc = *partials.next().unwrap(); + res.push(acc * num_chunk_product - new_acc * den_chunk_product); acc = new_acc; } debug_assert!(partials.next().is_none()); @@ -55,7 +68,7 @@ pub fn check_partial_products( res } -pub fn check_partial_products_recursively, const D: usize>( +pub(crate) fn check_partial_products_recursively, const D: usize>( builder: &mut CircuitBuilder, numerators: &[ExtensionTarget], denominators: &[ExtensionTarget], @@ -93,18 +106,11 @@ mod tests { fn test_partial_products() { type F = GoldilocksField; let denominators = vec![F::ONE; 6]; - let v = [1, 2, 3, 4, 5, 6] - .into_iter() - .map(|&i| F::from_canonical_u64(i)) - .collect::>(); - let p = partial_products(&v, 2); - assert_eq!( - p, - [2, 24, 720] - .into_iter() - .map(|&i| F::from_canonical_u64(i)) - .collect::>() - ); + let v = field_vec(&[1, 2, 3, 4, 5, 6]); + let quotient_chunks_prods = quotient_chunk_products(&v, 2); + assert_eq!(quotient_chunks_prods, field_vec(&[2, 12, 30])); + let p = partial_products_and_z_gx(F::ONE, "ient_chunks_prods); + assert_eq!(p, field_vec(&[2, 24, 720])); let nums = num_partial_products(v.len(), 2); assert_eq!(p.len(), nums.0); @@ -116,18 +122,11 @@ mod tests { v.into_iter().product::(), ); - let v = [1, 2, 3, 4, 5, 6] - .into_iter() - .map(|&i| F::from_canonical_u64(i)) - .collect::>(); - let p = partial_products(&v, 3); - assert_eq!( - p, - [6, 720] - .into_iter() - .map(|&i| F::from_canonical_u64(i)) - .collect::>() - ); + let v = field_vec(&[1, 2, 3, 4, 5, 6]); + let quotient_chunks_prods = quotient_chunk_products(&v, 3); + assert_eq!(quotient_chunks_prods, field_vec(&[6, 120])); + let p = partial_products_and_z_gx(F::ONE, "ient_chunks_prods); + assert_eq!(p, field_vec(&[6, 720])); let nums = num_partial_products(v.len(), 3); assert_eq!(p.len(), nums.0); assert!(check_partial_products(&v, &denominators, &p, F::ONE, 3) @@ -138,4 +137,8 @@ mod tests { v.into_iter().product::(), ); } + + fn field_vec(xs: &[usize]) -> Vec { + xs.iter().map(|&x| F::from_canonical_usize(x)).collect() + } } From 21d3b127e3eb288019bc2047af4e30ce15631e62 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 12 Nov 2021 09:15:37 +0100 Subject: [PATCH 12/12] Cargo fmt --- src/plonk/prover.rs | 9 ++++++--- src/plonk/vanishing_poly.rs | 13 ++++++++++--- src/util/partial_products.rs | 6 ++++-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 1c247998..1dd17cb8 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -1,3 +1,5 @@ +use std::mem::swap; + use anyhow::Result; use rayon::prelude::*; @@ -20,7 +22,6 @@ use crate::timed; use crate::util::partial_products::{partial_products_and_z_gx, quotient_chunk_products}; use crate::util::timing::TimingTree; use crate::util::{log2_ceil, transpose}; -use std::mem::swap; pub(crate) fn prove, const D: usize>( prover_data: &ProverOnlyCircuitData, @@ -99,7 +100,8 @@ pub(crate) fn prove, const D: usize>( ); // Z is expected at the front of our batch; see `zs_range` and `partial_products_range`. - let plonk_z_vecs = partial_products_and_zs.iter_mut() + let plonk_z_vecs = partial_products_and_zs + .iter_mut() .map(|partial_products_and_z| partial_products_and_z.pop().unwrap()) .collect(); let zs_partial_products = [plonk_z_vecs, partial_products_and_zs.concat()].concat(); @@ -267,7 +269,8 @@ fn wires_permutation_partial_products_and_zs, const let mut z_x = F::ONE; let mut all_partial_products_and_zs = Vec::new(); for quotient_chunk_products in all_quotient_chunk_products { - let mut partial_products_and_z_gx = partial_products_and_z_gx(z_x, "ient_chunk_products); + let mut partial_products_and_z_gx = + partial_products_and_z_gx(z_x, "ient_chunk_products); // The last term is Z(gx), but we replace it with Z(x), otherwise Z would end up shifted. swap(&mut z_x, &mut partial_products_and_z_gx[num_prods]); all_partial_products_and_zs.push(partial_products_and_z_gx); diff --git a/src/plonk/vanishing_poly.rs b/src/plonk/vanishing_poly.rs index f91e027b..2be91b40 100644 --- a/src/plonk/vanishing_poly.rs +++ b/src/plonk/vanishing_poly.rs @@ -76,7 +76,10 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( vanishing_partial_products_terms.extend(partial_product_checks); let final_nume_product = numerator_values[final_num_prod..].iter().copied().product(); - let final_deno_product = denominator_values[final_num_prod..].iter().copied().product(); + let final_deno_product = denominator_values[final_num_prod..] + .iter() + .copied() + .product(); let last_partial = *current_partial_products.last().unwrap(); let v_shift_term = last_partial * final_nume_product - z_gz * final_deno_product; vanishing_v_shift_terms.push(v_shift_term); @@ -183,7 +186,10 @@ pub(crate) fn eval_vanishing_poly_base_batch, const vanishing_partial_products_terms.extend(partial_product_checks); let final_nume_product = numerator_values[final_num_prod..].iter().copied().product(); - let final_deno_product = denominator_values[final_num_prod..].iter().copied().product(); + let final_deno_product = denominator_values[final_num_prod..] + .iter() + .copied() + .product(); let last_partial = *current_partial_products.last().unwrap(); let v_shift_term = last_partial * final_nume_product - z_gz * final_deno_product; vanishing_v_shift_terms.push(v_shift_term); @@ -379,7 +385,8 @@ pub(crate) fn eval_vanishing_poly_recursively, cons let final_deno_product = builder.mul_many_extension(&denominator_values[final_num_prod..]); let z_gz_denominators = builder.mul_extension(z_gz, final_deno_product); let last_partial = *current_partial_products.last().unwrap(); - let v_shift_term = builder.mul_sub_extension(last_partial, final_nume_product, z_gz_denominators); + let v_shift_term = + builder.mul_sub_extension(last_partial, final_nume_product, z_gz_denominators); vanishing_v_shift_terms.push(v_shift_term); } diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index 1e7a4f4b..c4133b4d 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -1,8 +1,9 @@ +use itertools::Itertools; + use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field_types::{Field, RichField}; use crate::plonk::circuit_builder::CircuitBuilder; -use itertools::Itertools; pub(crate) fn quotient_chunk_products( quotient_values: &[F], @@ -11,7 +12,8 @@ pub(crate) fn quotient_chunk_products( debug_assert!(max_degree > 1); assert!(quotient_values.len() > 0); let chunk_size = max_degree; - quotient_values.chunks(chunk_size) + quotient_values + .chunks(chunk_size) .map(|chunk| chunk.iter().copied().product()) .collect() }