mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-05-28 20:59:26 +00:00
Merge pull request #346 from mir-protocol/partial_product_chain
Use chain structure to compute partial products
This commit is contained in:
commit
a48eb2f81d
@ -190,8 +190,8 @@ pub struct CommonCircuitData<F: RichField + Extendable<D>, const D: usize> {
|
|||||||
/// The `{k_i}` valued used in `S_ID_i` in Plonk's permutation argument.
|
/// The `{k_i}` valued used in `S_ID_i` in Plonk's permutation argument.
|
||||||
pub(crate) k_is: Vec<F>,
|
pub(crate) k_is: Vec<F>,
|
||||||
|
|
||||||
/// The number of partial products needed to compute the `Z` polynomials and the number
|
/// The number of partial products needed to compute the `Z` polynomials and
|
||||||
/// of partial products needed to compute the final product.
|
/// the number of original elements consumed in `partial_products()`.
|
||||||
pub(crate) num_partial_products: (usize, usize),
|
pub(crate) num_partial_products: (usize, usize),
|
||||||
|
|
||||||
/// A digest of the "circuit" (i.e. the instance, minus public inputs), which can be used to
|
/// A digest of the "circuit" (i.e. the instance, minus public inputs), which can be used to
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
use std::mem::swap;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
|
||||||
@ -17,7 +19,7 @@ use crate::plonk::vanishing_poly::eval_vanishing_poly_base_batch;
|
|||||||
use crate::plonk::vars::EvaluationVarsBase;
|
use crate::plonk::vars::EvaluationVarsBase;
|
||||||
use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues};
|
use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues};
|
||||||
use crate::timed;
|
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::timing::TimingTree;
|
||||||
use crate::util::{log2_ceil, transpose};
|
use crate::util::{log2_ceil, transpose};
|
||||||
|
|
||||||
@ -91,28 +93,22 @@ pub(crate) fn prove<F: RichField + Extendable<D>, const D: usize>(
|
|||||||
common_data.quotient_degree_factor < common_data.config.num_routed_wires,
|
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."
|
"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,
|
timing,
|
||||||
"compute partial products",
|
"compute partial products",
|
||||||
all_wires_permutation_partial_products(&witness, &betas, &gammas, prover_data, common_data)
|
all_wires_permutation_partial_products(&witness, &betas, &gammas, prover_data, common_data)
|
||||||
);
|
);
|
||||||
|
|
||||||
let plonk_z_vecs = timed!(
|
// Z is expected at the front of our batch; see `zs_range` and `partial_products_range`.
|
||||||
timing,
|
let plonk_z_vecs = partial_products_and_zs
|
||||||
"compute Z's",
|
.iter_mut()
|
||||||
compute_zs(&partial_products, common_data)
|
.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
|
let partial_products_and_zs_commitment = timed!(
|
||||||
// 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!(
|
|
||||||
timing,
|
timing,
|
||||||
"commit to Z's",
|
"commit to partial products and Z's",
|
||||||
PolynomialBatchCommitment::from_values(
|
PolynomialBatchCommitment::from_values(
|
||||||
zs_partial_products,
|
zs_partial_products,
|
||||||
config.rate_bits,
|
config.rate_bits,
|
||||||
@ -123,7 +119,7 @@ pub(crate) fn prove<F: RichField + Extendable<D>, 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);
|
let alphas = challenger.get_n_challenges(num_challenges);
|
||||||
|
|
||||||
@ -135,7 +131,7 @@ pub(crate) fn prove<F: RichField + Extendable<D>, const D: usize>(
|
|||||||
prover_data,
|
prover_data,
|
||||||
&public_inputs_hash,
|
&public_inputs_hash,
|
||||||
&wires_commitment,
|
&wires_commitment,
|
||||||
&zs_partial_products_commitment,
|
&partial_products_and_zs_commitment,
|
||||||
&betas,
|
&betas,
|
||||||
&gammas,
|
&gammas,
|
||||||
&alphas,
|
&alphas,
|
||||||
@ -184,7 +180,7 @@ pub(crate) fn prove<F: RichField + Extendable<D>, const D: usize>(
|
|||||||
&[
|
&[
|
||||||
&prover_data.constants_sigmas_commitment,
|
&prover_data.constants_sigmas_commitment,
|
||||||
&wires_commitment,
|
&wires_commitment,
|
||||||
&zs_partial_products_commitment,
|
&partial_products_and_zs_commitment,
|
||||||
"ient_polys_commitment,
|
"ient_polys_commitment,
|
||||||
],
|
],
|
||||||
zeta,
|
zeta,
|
||||||
@ -196,7 +192,7 @@ pub(crate) fn prove<F: RichField + Extendable<D>, const D: usize>(
|
|||||||
|
|
||||||
let proof = Proof {
|
let proof = Proof {
|
||||||
wires_cap: wires_commitment.merkle_tree.cap,
|
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,
|
quotient_polys_cap: quotient_polys_commitment.merkle_tree.cap,
|
||||||
openings,
|
openings,
|
||||||
opening_proof,
|
opening_proof,
|
||||||
@ -217,7 +213,7 @@ fn all_wires_permutation_partial_products<F: RichField + Extendable<D>, const D:
|
|||||||
) -> Vec<Vec<PolynomialValues<F>>> {
|
) -> Vec<Vec<PolynomialValues<F>>> {
|
||||||
(0..common_data.config.num_challenges)
|
(0..common_data.config.num_challenges)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
wires_permutation_partial_products(
|
wires_permutation_partial_products_and_zs(
|
||||||
witness,
|
witness,
|
||||||
betas[i],
|
betas[i],
|
||||||
gammas[i],
|
gammas[i],
|
||||||
@ -231,7 +227,7 @@ fn all_wires_permutation_partial_products<F: RichField + Extendable<D>, const D:
|
|||||||
/// Compute the partial products used in the `Z` polynomial.
|
/// Compute the partial products used in the `Z` polynomial.
|
||||||
/// Returns the polynomials interpolating `partial_products(f / g)`
|
/// Returns the polynomials interpolating `partial_products(f / g)`
|
||||||
/// where `f, g` are the products in the definition of `Z`: `Z(g^i) = f / g`.
|
/// where `f, g` are the products in the definition of `Z`: `Z(g^i) = f / g`.
|
||||||
fn wires_permutation_partial_products<F: RichField + Extendable<D>, const D: usize>(
|
fn wires_permutation_partial_products_and_zs<F: RichField + Extendable<D>, const D: usize>(
|
||||||
witness: &MatrixWitness<F>,
|
witness: &MatrixWitness<F>,
|
||||||
beta: F,
|
beta: F,
|
||||||
gamma: F,
|
gamma: F,
|
||||||
@ -241,7 +237,8 @@ fn wires_permutation_partial_products<F: RichField + Extendable<D>, const D: usi
|
|||||||
let degree = common_data.quotient_degree_factor;
|
let degree = common_data.quotient_degree_factor;
|
||||||
let subgroup = &prover_data.subgroup;
|
let subgroup = &prover_data.subgroup;
|
||||||
let k_is = &common_data.k_is;
|
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()
|
.par_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, &x)| {
|
.map(|(i, &x)| {
|
||||||
@ -265,49 +262,26 @@ fn wires_permutation_partial_products<F: RichField + Extendable<D>, const D: usi
|
|||||||
.map(|(num, den_inv)| num * den_inv)
|
.map(|(num, den_inv)| num * den_inv)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let quotient_partials = partial_products("ient_values, degree);
|
quotient_chunk_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..]
|
|
||||||
.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()
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
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()
|
.into_par_iter()
|
||||||
.map(PolynomialValues::new)
|
.map(PolynomialValues::new)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_zs<F: RichField + Extendable<D>, const D: usize>(
|
|
||||||
partial_products: &[Vec<PolynomialValues<F>>],
|
|
||||||
common_data: &CommonCircuitData<F, D>,
|
|
||||||
) -> Vec<PolynomialValues<F>> {
|
|
||||||
(0..common_data.config.num_challenges)
|
|
||||||
.map(|i| compute_z(&partial_products[i], common_data))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compute the `Z` polynomial by reusing the computations done in `wires_permutation_partial_products`.
|
|
||||||
fn compute_z<F: RichField + Extendable<D>, const D: usize>(
|
|
||||||
partial_products: &[PolynomialValues<F>],
|
|
||||||
common_data: &CommonCircuitData<F, D>,
|
|
||||||
) -> PolynomialValues<F> {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
plonk_z_points.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
const BATCH_SIZE: usize = 32;
|
const BATCH_SIZE: usize = 32;
|
||||||
|
|
||||||
fn compute_quotient_polys<'a, F: RichField + Extendable<D>, const D: usize>(
|
fn compute_quotient_polys<'a, F: RichField + Extendable<D>, const D: usize>(
|
||||||
|
|||||||
@ -62,31 +62,27 @@ pub(crate) fn eval_vanishing_poly<F: RichField + Extendable<D>, const D: usize>(
|
|||||||
wire_value + s_sigma.scalar_mul(betas[i]) + gammas[i].into()
|
wire_value + s_sigma.scalar_mul(betas[i]) + gammas[i].into()
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let quotient_values = (0..common_data.config.num_routed_wires)
|
|
||||||
.map(|j| numerator_values[j] / denominator_values[j])
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// The partial products considered for this iteration of `i`.
|
// The partial products considered for this iteration of `i`.
|
||||||
let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods];
|
let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods];
|
||||||
// Check the quotient partial products.
|
// Check the quotient partial products.
|
||||||
let mut partial_product_check =
|
let partial_product_checks = check_partial_products(
|
||||||
check_partial_products("ient_values, current_partial_products, max_degree);
|
&numerator_values,
|
||||||
// The first checks are of the form `q - n/d` which is a rational function not a polynomial.
|
&denominator_values,
|
||||||
// We multiply them by `d` to get checks of the form `q*d - n` which low-degree polynomials.
|
current_partial_products,
|
||||||
denominator_values
|
z_x,
|
||||||
.chunks(max_degree)
|
max_degree,
|
||||||
.zip(partial_product_check.iter_mut())
|
);
|
||||||
.for_each(|(d, q)| {
|
vanishing_partial_products_terms.extend(partial_product_checks);
|
||||||
*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 final_nume_product = numerator_values[final_num_prod..].iter().copied().product();
|
||||||
let quotient: F::Extension = current_partial_products[num_prods - final_num_prod..]
|
let final_deno_product = denominator_values[final_num_prod..]
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
.product();
|
.product();
|
||||||
vanishing_v_shift_terms.push(quotient * z_x - z_gz);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
let vanishing_terms = [
|
let vanishing_terms = [
|
||||||
@ -138,7 +134,6 @@ pub(crate) fn eval_vanishing_poly_base_batch<F: RichField + Extendable<D>, const
|
|||||||
|
|
||||||
let mut numerator_values = Vec::with_capacity(num_routed_wires);
|
let mut numerator_values = Vec::with_capacity(num_routed_wires);
|
||||||
let mut denominator_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.
|
// The L_1(x) (Z(x) - 1) vanishing terms.
|
||||||
let mut vanishing_z_1_terms = Vec::with_capacity(num_challenges);
|
let mut vanishing_z_1_terms = Vec::with_capacity(num_challenges);
|
||||||
@ -177,36 +172,30 @@ pub(crate) fn eval_vanishing_poly_base_batch<F: RichField + Extendable<D>, const
|
|||||||
let s_sigma = s_sigmas[j];
|
let s_sigma = s_sigmas[j];
|
||||||
wire_value + betas[i] * s_sigma + gammas[i]
|
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`.
|
// The partial products considered for this iteration of `i`.
|
||||||
let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods];
|
let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods];
|
||||||
// Check the numerator partial products.
|
// Check the numerator partial products.
|
||||||
let mut partial_product_check =
|
let partial_product_checks = check_partial_products(
|
||||||
check_partial_products("ient_values, current_partial_products, max_degree);
|
&numerator_values,
|
||||||
// The first checks are of the form `q - n/d` which is a rational function not a polynomial.
|
&denominator_values,
|
||||||
// We multiply them by `d` to get checks of the form `q*d - n` which low-degree polynomials.
|
current_partial_products,
|
||||||
denominator_values
|
z_x,
|
||||||
.chunks(max_degree)
|
max_degree,
|
||||||
.zip(partial_product_check.iter_mut())
|
);
|
||||||
.for_each(|(d, q)| {
|
vanishing_partial_products_terms.extend(partial_product_checks);
|
||||||
*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 final_nume_product = numerator_values[final_num_prod..].iter().copied().product();
|
||||||
let quotient: F = current_partial_products[num_prods - final_num_prod..]
|
let final_deno_product = denominator_values[final_num_prod..]
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
.product();
|
.product();
|
||||||
vanishing_v_shift_terms.push(quotient * z_x - z_gz);
|
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();
|
numerator_values.clear();
|
||||||
denominator_values.clear();
|
denominator_values.clear();
|
||||||
quotient_values.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let vanishing_terms = vanishing_z_1_terms
|
let vanishing_terms = vanishing_z_1_terms
|
||||||
@ -363,7 +352,6 @@ pub(crate) fn eval_vanishing_poly_recursively<F: RichField + Extendable<D>, cons
|
|||||||
|
|
||||||
let mut numerator_values = Vec::new();
|
let mut numerator_values = Vec::new();
|
||||||
let mut denominator_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 {
|
for j in 0..common_data.config.num_routed_wires {
|
||||||
let wire_value = vars.local_wires[j];
|
let wire_value = vars.local_wires[j];
|
||||||
@ -376,38 +364,30 @@ pub(crate) fn eval_vanishing_poly_recursively<F: RichField + Extendable<D>, cons
|
|||||||
let numerator = builder.mul_add_extension(beta_ext, s_ids[j], wire_value_plus_gamma);
|
let numerator = builder.mul_add_extension(beta_ext, s_ids[j], wire_value_plus_gamma);
|
||||||
let denominator =
|
let denominator =
|
||||||
builder.mul_add_extension(beta_ext, s_sigmas[j], wire_value_plus_gamma);
|
builder.mul_add_extension(beta_ext, s_sigmas[j], wire_value_plus_gamma);
|
||||||
let quotient = builder.div_extension(numerator, denominator);
|
|
||||||
|
|
||||||
numerator_values.push(numerator);
|
numerator_values.push(numerator);
|
||||||
denominator_values.push(denominator);
|
denominator_values.push(denominator);
|
||||||
quotient_values.push(quotient);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The partial products considered for this iteration of `i`.
|
// The partial products considered for this iteration of `i`.
|
||||||
let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods];
|
let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods];
|
||||||
// Check the quotient partial products.
|
// Check the quotient partial products.
|
||||||
let mut partial_product_check = check_partial_products_recursively(
|
let partial_product_checks = check_partial_products_recursively(
|
||||||
builder,
|
builder,
|
||||||
"ient_values,
|
&numerator_values,
|
||||||
|
&denominator_values,
|
||||||
current_partial_products,
|
current_partial_products,
|
||||||
|
z_x,
|
||||||
max_degree,
|
max_degree,
|
||||||
);
|
);
|
||||||
// The first checks are of the form `q - n/d` which is a rational function not a polynomial.
|
vanishing_partial_products_terms.extend(partial_product_checks);
|
||||||
// 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);
|
|
||||||
});
|
|
||||||
vanishing_partial_products_terms.extend(partial_product_check);
|
|
||||||
|
|
||||||
// The quotient final product is the product of the last `final_num_prod` elements.
|
let final_nume_product = builder.mul_many_extension(&numerator_values[final_num_prod..]);
|
||||||
let quotient =
|
let final_deno_product = builder.mul_many_extension(&denominator_values[final_num_prod..]);
|
||||||
builder.mul_many_extension(¤t_partial_products[num_prods - final_num_prod..]);
|
let z_gz_denominators = builder.mul_extension(z_gz, final_deno_product);
|
||||||
vanishing_v_shift_terms.push(builder.mul_sub_extension(quotient, z_x, z_gz));
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
let vanishing_terms = [
|
let vanishing_terms = [
|
||||||
|
|||||||
@ -1,124 +1,146 @@
|
|||||||
use std::iter::Product;
|
use itertools::Itertools;
|
||||||
use std::ops::Sub;
|
|
||||||
|
|
||||||
use crate::field::extension_field::target::ExtensionTarget;
|
use crate::field::extension_field::target::ExtensionTarget;
|
||||||
use crate::field::extension_field::Extendable;
|
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::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::util::ceil_div_usize;
|
|
||||||
|
pub(crate) fn quotient_chunk_products<F: Field>(
|
||||||
|
quotient_values: &[F],
|
||||||
|
max_degree: usize,
|
||||||
|
) -> Vec<F> {
|
||||||
|
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`
|
/// 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.
|
/// or less elements. This is done until we've computed the product `P` of all elements in the vector.
|
||||||
pub fn partial_products<T: Product + Copy>(v: &[T], max_degree: usize) -> Vec<T> {
|
pub(crate) fn partial_products_and_z_gx<F: Field>(z_x: F, quotient_chunk_products: &[F]) -> Vec<F> {
|
||||||
|
assert!(quotient_chunk_products.len() > 0);
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
let mut remainder = v.to_vec();
|
let mut acc = z_x;
|
||||||
while remainder.len() > max_degree {
|
for "ient_chunk_product in quotient_chunk_products {
|
||||||
let new_partials = remainder
|
acc *= quotient_chunk_product;
|
||||||
.chunks(max_degree)
|
res.push(acc);
|
||||||
// TODO: can filter out chunks of length 1.
|
|
||||||
.map(|chunk| chunk.iter().copied().product())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
res.extend_from_slice(&new_partials);
|
|
||||||
remainder = new_partials;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a tuple `(a,b)`, where `a` is the length of the output of `partial_products()` on a
|
/// 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) {
|
pub fn num_partial_products(n: usize, max_degree: usize) -> (usize, usize) {
|
||||||
debug_assert!(max_degree > 1);
|
debug_assert!(max_degree > 1);
|
||||||
let mut res = 0;
|
let chunk_size = max_degree;
|
||||||
let mut remainder = n;
|
let num_chunks = n / chunk_size;
|
||||||
while remainder > max_degree {
|
|
||||||
let new_partials_len = ceil_div_usize(remainder, max_degree);
|
|
||||||
res += new_partials_len;
|
|
||||||
remainder = new_partials_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
(res, remainder)
|
(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.
|
/// products of size `max_degree` or less.
|
||||||
pub fn check_partial_products<T: Product + Copy + Sub<Output = T>>(
|
pub(crate) fn check_partial_products<F: Field>(
|
||||||
v: &[T],
|
numerators: &[F],
|
||||||
mut partials: &[T],
|
denominators: &[F],
|
||||||
|
partials: &[F],
|
||||||
|
z_x: F,
|
||||||
max_degree: usize,
|
max_degree: usize,
|
||||||
) -> Vec<T> {
|
) -> Vec<F> {
|
||||||
|
debug_assert!(max_degree > 1);
|
||||||
|
let mut acc = z_x;
|
||||||
|
let mut partials = partials.iter();
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
let mut remainder = v;
|
let chunk_size = max_degree;
|
||||||
while remainder.len() > max_degree {
|
for (nume_chunk, deno_chunk) in numerators
|
||||||
let products = remainder
|
.chunks_exact(chunk_size)
|
||||||
.chunks(max_degree)
|
.zip_eq(denominators.chunks_exact(chunk_size))
|
||||||
.map(|chunk| chunk.iter().copied().product::<T>());
|
{
|
||||||
let products_len = products.len();
|
let num_chunk_product = nume_chunk.iter().copied().product();
|
||||||
res.extend(products.zip(partials).map(|(a, &b)| a - b));
|
let den_chunk_product = deno_chunk.iter().copied().product();
|
||||||
(remainder, partials) = partials.split_at(products_len);
|
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());
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_partial_products_recursively<F: RichField + Extendable<D>, const D: usize>(
|
pub(crate) fn check_partial_products_recursively<F: RichField + Extendable<D>, const D: usize>(
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
v: &[ExtensionTarget<D>],
|
numerators: &[ExtensionTarget<D>],
|
||||||
|
denominators: &[ExtensionTarget<D>],
|
||||||
partials: &[ExtensionTarget<D>],
|
partials: &[ExtensionTarget<D>],
|
||||||
|
mut acc: ExtensionTarget<D>,
|
||||||
max_degree: usize,
|
max_degree: usize,
|
||||||
) -> Vec<ExtensionTarget<D>> {
|
) -> Vec<ExtensionTarget<D>> {
|
||||||
|
debug_assert!(max_degree > 1);
|
||||||
|
let mut partials = partials.iter();
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
let mut remainder = v.to_vec();
|
let chunk_size = max_degree;
|
||||||
let mut partials = partials.to_vec();
|
for (nume_chunk, deno_chunk) in numerators
|
||||||
while remainder.len() > max_degree {
|
.chunks_exact(chunk_size)
|
||||||
let products = remainder
|
.zip(denominators.chunks_exact(chunk_size))
|
||||||
.chunks(max_degree)
|
{
|
||||||
.map(|chunk| builder.mul_many_extension(chunk))
|
let nume_product = builder.mul_many_extension(nume_chunk);
|
||||||
.collect::<Vec<_>>();
|
let deno_product = builder.mul_many_extension(deno_chunk);
|
||||||
res.extend(
|
let new_acc = *partials.next().unwrap();
|
||||||
products
|
let new_acc_deno = builder.mul_extension(new_acc, deno_product);
|
||||||
.iter()
|
// Assert that new_acc*deno_product = acc * nume_product.
|
||||||
.zip(&partials)
|
res.push(builder.mul_sub_extension(acc, nume_product, new_acc_deno));
|
||||||
.map(|(&a, &b)| builder.sub_extension(a, b)),
|
acc = new_acc;
|
||||||
);
|
|
||||||
remainder = partials.drain(..products.len()).collect();
|
|
||||||
}
|
}
|
||||||
|
debug_assert!(partials.next().is_none());
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use num::Zero;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::field::goldilocks_field::GoldilocksField;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_partial_products() {
|
fn test_partial_products() {
|
||||||
let v = vec![1, 2, 3, 4, 5, 6];
|
type F = GoldilocksField;
|
||||||
let p = partial_products(&v, 2);
|
let denominators = vec![F::ONE; 6];
|
||||||
assert_eq!(p, vec![2, 12, 30, 24, 30]);
|
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);
|
let nums = num_partial_products(v.len(), 2);
|
||||||
assert_eq!(p.len(), nums.0);
|
assert_eq!(p.len(), nums.0);
|
||||||
assert!(check_partial_products(&v, &p, 2)
|
assert!(check_partial_products(&v, &denominators, &p, F::ONE, 2)
|
||||||
.iter()
|
.iter()
|
||||||
.all(|x| x.is_zero()));
|
.all(|x| x.is_zero()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
v.into_iter().product::<i32>(),
|
*p.last().unwrap() * v[nums.1..].iter().copied().product::<F>(),
|
||||||
p[p.len() - nums.1..].iter().copied().product(),
|
v.into_iter().product::<F>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let v = vec![1, 2, 3, 4, 5, 6];
|
let v = field_vec(&[1, 2, 3, 4, 5, 6]);
|
||||||
let p = partial_products(&v, 3);
|
let quotient_chunks_prods = quotient_chunk_products(&v, 3);
|
||||||
assert_eq!(p, vec![6, 120]);
|
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);
|
let nums = num_partial_products(v.len(), 3);
|
||||||
assert_eq!(p.len(), nums.0);
|
assert_eq!(p.len(), nums.0);
|
||||||
assert!(check_partial_products(&v, &p, 3)
|
assert!(check_partial_products(&v, &denominators, &p, F::ONE, 3)
|
||||||
.iter()
|
.iter()
|
||||||
.all(|x| x.is_zero()));
|
.all(|x| x.is_zero()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
v.into_iter().product::<i32>(),
|
*p.last().unwrap() * v[nums.1..].iter().copied().product::<F>(),
|
||||||
p[p.len() - nums.1..].iter().copied().product(),
|
v.into_iter().product::<F>(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn field_vec<F: Field>(xs: &[usize]) -> Vec<F> {
|
||||||
|
xs.iter().map(|&x| F::from_canonical_usize(x)).collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user