Optimisations and documentation updates

This commit is contained in:
therealyingtong 2020-12-03 12:39:44 +08:00
parent e51ab7eaa7
commit 9a3d1b1d05
4 changed files with 77 additions and 74 deletions

View file

@ -364,6 +364,9 @@ impl<F: Field> ConstraintSystem<F> {
input_columns: &[Column<Any>], input_columns: &[Column<Any>],
table_columns: &[Column<Any>], table_columns: &[Column<Any>],
) -> usize { ) -> usize {
// The function will panic if the number of input columns and table columns are not the same.
assert_eq!(input_columns.len(), table_columns.len());
let index = self.lookups.len(); let index = self.lookups.len();
if self.lookups.is_empty() { if self.lookups.is_empty() {
let at = Rotation(-1); let at = Rotation(-1);

View file

@ -192,12 +192,12 @@ impl Argument {
.commit_lagrange(&permuted_table_value, permuted_table_blind) .commit_lagrange(&permuted_table_value, permuted_table_blind)
.to_affine(); .to_affine();
// Hash each permuted input commitment // Hash permuted input commitment
transcript transcript
.absorb_point(&permuted_input_commitment) .absorb_point(&permuted_input_commitment)
.map_err(|_| Error::TranscriptError)?; .map_err(|_| Error::TranscriptError)?;
// Hash each permuted table commitment // Hash permuted table commitment
transcript transcript
.absorb_point(&permuted_table_commitment) .absorb_point(&permuted_table_commitment)
.map_err(|_| Error::TranscriptError)?; .map_err(|_| Error::TranscriptError)?;
@ -239,16 +239,15 @@ impl<'a, C: CurveAffine> Permuted<'a, C> {
) -> Result<Committed<'a, C>, Error> { ) -> Result<Committed<'a, C>, Error> {
// Goal is to compute the products of fractions // Goal is to compute the products of fractions
// //
// (a_1(\omega^i) + \theta a_2(\omega^i) + ... + beta)(s_1(\omega^i) + \theta(\omega^i) + ... + \gamma) / // (\theta^{m-1} a_1(\omega^i) + \theta^{m-2} a_2(\omega^i) + ... + a_m(\omega^i) + \beta)(\theta^{m-1} s_1(\omega^i) + \theta^{m-2} s_2(\omega^i) + ... + s_m(\omega^i) + \gamma)/
// (a'(\omega^i) + \beta)(s'(\omega^i) + \gamma) // (a'(\omega^i) + \beta) (s'(\omega^i) + \gamma)
// //
// where a_j(X) is the jth input column in this lookup, // where a_j(X) is the jth input column in this lookup,
// where a'(X) is the compression of the permuted input columns, // where a'(X) is the compression of the permuted input columns,
// s_j(X) is the jth table column in this lookup, // s_j(X) is the jth table column in this lookup,
// s'(X) is the compression of the permuted table columns, // s'(X) is the compression of the permuted table columns,
// and i is the ith row of the column. // and i is the ith row of the column.
let mut lookup_product = vec![C::Scalar::one(); params.n as usize]; let mut lookup_product = vec![C::Scalar::zero(); params.n as usize];
// Denominator uses the permuted input column and permuted table column // Denominator uses the permuted input column and permuted table column
parallelize(&mut lookup_product, |lookup_product, start| { parallelize(&mut lookup_product, |lookup_product, start| {
for ((lookup_product, permuted_input_value), permuted_table_value) in lookup_product for ((lookup_product, permuted_input_value), permuted_table_value) in lookup_product
@ -256,8 +255,7 @@ impl<'a, C: CurveAffine> Permuted<'a, C> {
.zip(self.permuted_input_value[start..].iter()) .zip(self.permuted_input_value[start..].iter())
.zip(self.permuted_table_value[start..].iter()) .zip(self.permuted_table_value[start..].iter())
{ {
*lookup_product *= &(*beta + permuted_input_value); *lookup_product = (*beta + permuted_input_value) * &(*gamma + permuted_table_value);
*lookup_product *= &(*gamma + permuted_table_value);
} }
}); });
@ -266,7 +264,7 @@ impl<'a, C: CurveAffine> Permuted<'a, C> {
lookup_product.iter_mut().batch_invert(); lookup_product.iter_mut().batch_invert();
// Finish the computation of the entire fraction by computing the numerators // Finish the computation of the entire fraction by computing the numerators
// (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) // (\theta^{m-1} a_1(\omega^i) + \theta^{m-2} a_2(\omega^i) + ... + \theta a_{m-1}(\omega^i) + a_m(\omega^i) + \beta)(\theta^{m-1} s_1(\omega^i) + \theta^{m-2} s_2(\omega^i) + ... + \theta s_{m-1}(\omega^i) + s_m(\omega^i) + \gamma)
// Compress unpermuted input columns // Compress unpermuted input columns
let mut input_term = vec![C::Scalar::zero(); params.n as usize]; let mut input_term = vec![C::Scalar::zero(); params.n as usize];
for unpermuted_input_value in self.unpermuted_input_values.iter() { for unpermuted_input_value in self.unpermuted_input_values.iter() {
@ -309,10 +307,10 @@ impl<'a, C: CurveAffine> Permuted<'a, C> {
// The product vector is a vector of products of fractions of the form // The product vector is a vector of products of fractions of the form
// //
// (a_1(\omega^i) + \theta a_2(\omega^i) + ... + \beta)(s_1(\omega^i) + \theta s_2(\omega^i) + ... + \gamma)/ // (\theta^{m-1} a_1(\omega^i) + \theta^{m-2} a_2(\omega^i) + ... + \theta a_{m-1}(\omega^i) + a_m(\omega^i) + \beta)(\theta^{m-1} s_1(\omega^i) + \theta^{m-2} s_2(\omega^i) + ... + \theta s_{m-1}(\omega^i) + s_m(\omega^i) + \gamma)
// (a'(\omega^i) + \beta) (s'(\omega^i) + \gamma)
// //
// where a_j(\omega^i) is the jth input column in this lookup, // where there are m input columns and m table columns,
// a_j(\omega^i) is the jth input column in this lookup,
// a'j(\omega^i) is the permuted input column, // a'j(\omega^i) is the permuted input column,
// s_j(\omega^i) is the jth table column in this lookup, // s_j(\omega^i) is the jth table column in this lookup,
// s'(\omega^i) is the permuted table column, // s'(\omega^i) is the permuted table column,
@ -320,12 +318,13 @@ impl<'a, C: CurveAffine> Permuted<'a, C> {
// Compute the evaluations of the lookup product polynomial // Compute the evaluations of the lookup product polynomial
// over our domain, starting with z[0] = 1 // over our domain, starting with z[0] = 1
let mut z = vec![C::Scalar::one()]; let z = iter::once(C::Scalar::one())
for row in 1..(params.n as usize) { .chain(lookup_product.into_iter().skip(1))
let mut tmp = z[row - 1]; .scan(C::Scalar::one(), |state, cur| {
tmp *= &lookup_product[row]; *state *= &cur;
z.push(tmp); Some(*state)
} })
.collect::<Vec<_>>();
let z = pk.vk.domain.lagrange_from_vec(z); let z = pk.vk.domain.lagrange_from_vec(z);
#[cfg(feature = "sanity-checks")] #[cfg(feature = "sanity-checks")]
@ -336,7 +335,7 @@ impl<'a, C: CurveAffine> Permuted<'a, C> {
let n = params.n as usize; let n = params.n as usize;
// z'(X) (a'(X) + \beta) (s'(X) + \gamma) // z'(X) (a'(X) + \beta) (s'(X) + \gamma)
// - z'(\omega^{-1} X) (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) // - z'(\omega^{-1} X) (\theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) + \beta) (\theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) + \gamma)
for i in 0..n { for i in 0..n {
let prev_idx = (n + i - 1) % n; let prev_idx = (n + i - 1) % n;
@ -374,7 +373,7 @@ impl<'a, C: CurveAffine> Permuted<'a, C> {
.coeff_to_extended(z.clone(), Rotation::default()); .coeff_to_extended(z.clone(), Rotation::default());
let product_inv_coset = pk.vk.domain.coeff_to_extended(z.clone(), Rotation(-1)); let product_inv_coset = pk.vk.domain.coeff_to_extended(z.clone(), Rotation(-1));
// Hash each product commitment // Hash product commitment
transcript transcript
.absorb_point(&product_commitment) .absorb_point(&product_commitment)
.map_err(|_| Error::TranscriptError)?; .map_err(|_| Error::TranscriptError)?;
@ -417,7 +416,7 @@ impl<'a, C: CurveAffine> Committed<'a, C> {
Polynomial::one_minus(self.product_coset.clone()) * &pk.l0, Polynomial::one_minus(self.product_coset.clone()) * &pk.l0,
)) ))
// z'(X) (a'(X) + \beta) (s'(X) + \gamma) // z'(X) (a'(X) + \beta) (s'(X) + \gamma)
// - z'(\omega^{-1} X) (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) // - z'(\omega^{-1} X) (\theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) + \beta) (\theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) + \gamma)
.chain({ .chain({
// z'(X) (a'(X) + \beta) (s'(X) + \gamma) // z'(X) (a'(X) + \beta) (s'(X) + \gamma)
let mut left = self.product_coset.clone(); let mut left = self.product_coset.clone();
@ -432,13 +431,13 @@ impl<'a, C: CurveAffine> Committed<'a, C> {
} }
}); });
// z'(\omega^{-1} X) (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) // z'(\omega^{-1} X) (\theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) + \beta) (\theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) + \gamma)
let mut right = self.product_inv_coset; let mut right = self.product_inv_coset;
let mut input_terms = pk.vk.domain.empty_extended(); let mut input_terms = pk.vk.domain.empty_extended();
// Compress the unpermuted input columns // Compress the unpermuted input columns
for input in permuted.unpermuted_input_cosets.iter() { for input in permuted.unpermuted_input_cosets.iter() {
// (a_1(X) + \theta a_2(X) + ...) // \theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X)
parallelize(&mut input_terms, |input_term, start| { parallelize(&mut input_terms, |input_term, start| {
for (input_term, input) in input_term.iter_mut().zip(input[start..].iter()) for (input_term, input) in input_term.iter_mut().zip(input[start..].iter())
{ {
@ -451,7 +450,7 @@ impl<'a, C: CurveAffine> Committed<'a, C> {
let mut table_terms = pk.vk.domain.empty_extended(); let mut table_terms = pk.vk.domain.empty_extended();
// Compress the unpermuted table columns // Compress the unpermuted table columns
for table in permuted.unpermuted_table_cosets.iter() { for table in permuted.unpermuted_table_cosets.iter() {
// (s_1(X) + \theta s_2(X) + ...) // \theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X)
parallelize(&mut table_terms, |table_term, start| { parallelize(&mut table_terms, |table_term, start| {
for (table_term, table) in table_term.iter_mut().zip(table[start..].iter()) for (table_term, table) in table_term.iter_mut().zip(table[start..].iter())
{ {
@ -633,28 +632,31 @@ fn permute_column_pair<C: CurveAffine>(
*acc.entry(*coeff).or_insert(0) += 1; *acc.entry(*coeff).or_insert(0) += 1;
acc acc
}); });
let mut repeated_input_rows = vec![];
let mut permuted_table_coeffs = vec![C::Scalar::zero(); table_column.len()]; let mut permuted_table_coeffs = vec![C::Scalar::zero(); table_column.len()];
for row in 0..permuted_input_column.len() { let mut repeated_input_rows = permuted_input_column
let input_value = permuted_input_column[row]; .iter()
.zip(permuted_table_coeffs.iter_mut())
// If this is the first occurence of `input_value` in the input column .enumerate()
if row == 0 || input_value != permuted_input_column[row - 1] { .filter_map(|(row, (input_value, table_value))| {
permuted_table_coeffs[row] = input_value; // If this is the first occurence of `input_value` in the input column
// Remove one instance of input_value from leftover_table_map if row == 0 || *input_value != permuted_input_column[row - 1] {
if let Some(count) = leftover_table_map.get_mut(&input_value) { *table_value = *input_value;
assert!(*count > 0); // Remove one instance of input_value from leftover_table_map
*count -= 1; if let Some(count) = leftover_table_map.get_mut(&input_value) {
assert!(*count > 0);
*count -= 1;
None
} else {
// Return error if input_value not found
Some(Err(Error::ConstraintSystemFailure))
}
// If input value is repeated
} else { } else {
// Return error if input_value not found Some(Ok(row))
return Err(Error::ConstraintSystemFailure);
} }
// If input value is repeated })
} else { .collect::<Result<Vec<_>, _>>()?;
repeated_input_rows.push(row);
}
}
// Populate permuted table at unfilled rows with leftover table elements // Populate permuted table at unfilled rows with leftover table elements
for (coeff, count) in leftover_table_map.iter() { for (coeff, count) in leftover_table_map.iter() {

View file

@ -23,8 +23,7 @@ impl<C: CurveAffine> Proof<C> {
.map_err(|_| Error::TranscriptError)?; .map_err(|_| Error::TranscriptError)?;
transcript transcript
.absorb_point(&self.permuted_table_commitment) .absorb_point(&self.permuted_table_commitment)
.map_err(|_| Error::TranscriptError)?; .map_err(|_| Error::TranscriptError)
Ok(())
} }
pub(crate) fn absorb_product_commitment<HBase: Hasher<C::Base>, HScalar: Hasher<C::Scalar>>( pub(crate) fn absorb_product_commitment<HBase: Hasher<C::Base>, HScalar: Hasher<C::Scalar>>(
@ -33,8 +32,7 @@ impl<C: CurveAffine> Proof<C> {
) -> Result<(), Error> { ) -> Result<(), Error> {
transcript transcript
.absorb_point(&self.product_commitment) .absorb_point(&self.product_commitment)
.map_err(|_| Error::TranscriptError)?; .map_err(|_| Error::TranscriptError)
Ok(())
} }
pub(in crate::plonk) fn expressions<'a>( pub(in crate::plonk) fn expressions<'a>(
@ -51,7 +49,7 @@ impl<C: CurveAffine> Proof<C> {
) -> impl Iterator<Item = C::Scalar> + 'a { ) -> impl Iterator<Item = C::Scalar> + 'a {
let product_expression = || { let product_expression = || {
// z'(X) (a'(X) + \beta) (s'(X) + \gamma) // z'(X) (a'(X) + \beta) (s'(X) + \gamma)
// - z'(\omega^{-1} X) (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) // - z'(\omega^{-1} X) (\theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) + \beta) (\theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) + \gamma)
let left = self.product_eval let left = self.product_eval
* &(self.permuted_input_eval + &beta) * &(self.permuted_input_eval + &beta)
* &(self.permuted_table_eval + &gamma); * &(self.permuted_table_eval + &gamma);
@ -94,13 +92,15 @@ impl<C: CurveAffine> Proof<C> {
) )
.chain( .chain(
// z'(X) (a'(X) + \beta) (s'(X) + \gamma) // z'(X) (a'(X) + \beta) (s'(X) + \gamma)
// - z'(\omega^{-1} X) (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) // - z'(\omega^{-1} X) (\theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) + \beta) (\theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) + \gamma)
Some(product_expression()), Some(product_expression()),
) )
.chain(Some( .chain(Some(
// l_0(X) * (a'(X) - s'(X)) = 0
l_0 * &(self.permuted_input_eval - &self.permuted_table_eval), l_0 * &(self.permuted_input_eval - &self.permuted_table_eval),
)) ))
.chain(Some( .chain(Some(
// (a(X)s(X))⋅(a(X)a(\omega{-1} X)) = 0
(self.permuted_input_eval - &self.permuted_table_eval) (self.permuted_input_eval - &self.permuted_table_eval)
* &(self.permuted_input_eval - &self.permuted_input_inv_eval), * &(self.permuted_input_eval - &self.permuted_input_inv_eval),
)) ))

View file

@ -179,23 +179,21 @@ impl<C: CurveAffine> Proof<C> {
.lookups .lookups
.iter() .iter()
.map(|lookup| { .map(|lookup| {
lookup lookup.commit_permuted(
.commit_permuted( &pk,
&pk, &params,
&params, &domain,
&domain, theta,
theta, &witness.advice,
&witness.advice, &pk.fixed_values,
&pk.fixed_values, &aux,
&aux, &advice_cosets,
&advice_cosets, &pk.fixed_cosets,
&pk.fixed_cosets, &aux_cosets,
&aux_cosets, &mut transcript,
&mut transcript, )
)
.unwrap()
}) })
.collect::<Vec<_>>(); .collect::<Result<Vec<_>, _>>()?;
// Sample beta challenge // Sample beta challenge
let beta = ChallengeBeta::get(&mut transcript); let beta = ChallengeBeta::get(&mut transcript);
@ -220,12 +218,8 @@ impl<C: CurveAffine> Proof<C> {
// Construct and commit to products for each lookup // Construct and commit to products for each lookup
let lookups = lookups let lookups = lookups
.into_iter() .into_iter()
.map(|lookup| { .map(|lookup| lookup.commit_product(&pk, &params, theta, beta, gamma, &mut transcript))
lookup .collect::<Result<Vec<_>, _>>()?;
.commit_product(&pk, &params, theta, beta, gamma, &mut transcript)
.unwrap()
})
.collect::<Vec<_>>();
// Obtain challenge for keeping all separate gates linearly independent // Obtain challenge for keeping all separate gates linearly independent
let y = ChallengeY::<C::Scalar>::get(&mut transcript); let y = ChallengeY::<C::Scalar>::get(&mut transcript);
@ -238,10 +232,14 @@ impl<C: CurveAffine> Proof<C> {
.unwrap_or_default(); .unwrap_or_default();
// Evaluate the h(X) polynomial's constraint system expressions for the lookup constraints, if any. // Evaluate the h(X) polynomial's constraint system expressions for the lookup constraints, if any.
let (lookups, lookup_expressions): (Vec<_>, Vec<_>) = lookups let (lookups, lookup_expressions): (Vec<_>, Vec<_>) = {
.into_iter() let tmp = lookups
.map(|p| p.construct(pk, theta, beta, gamma).unwrap()) .into_iter()
.unzip(); .map(|p| p.construct(pk, theta, beta, gamma))
.collect::<Result<Vec<_>, _>>()?;
tmp.into_iter().unzip()
};
// Evaluate the h(X) polynomial's constraint system expressions for the constraints provided // Evaluate the h(X) polynomial's constraint system expressions for the constraints provided
let h_poly = iter::empty() let h_poly = iter::empty()