Merge pull request #322 from zcash/coset-optimization

Support rotations while in extended domain
This commit is contained in:
ebfull 2021-07-19 09:16:05 -06:00 committed by GitHub
commit ca44b40009
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 415 additions and 249 deletions

View File

@ -610,8 +610,9 @@ impl<F: FieldExt> MockProver<F> {
row: i32, row: i32,
queries: &'a [(Column<T>, Rotation)], queries: &'a [(Column<T>, Rotation)],
cells: &'a [Vec<F>], cells: &'a [Vec<F>],
) -> impl Fn(usize) -> Value<F> + 'a { ) -> impl Fn(usize, usize, Rotation) -> Value<F> + 'a
move |index| { {
move |index, _, _| {
let (column, at) = &queries[index]; let (column, at) = &queries[index];
let resolved_row = (row + at.0) % n; let resolved_row = (row + at.0) % n;
Value::Real(cells[column.index()][resolved_row as usize]) Value::Real(cells[column.index()][resolved_row as usize])
@ -623,8 +624,9 @@ impl<F: FieldExt> MockProver<F> {
row: i32, row: i32,
queries: &'a [(Column<T>, Rotation)], queries: &'a [(Column<T>, Rotation)],
cells: &'a [Vec<CellValue<F>>], cells: &'a [Vec<CellValue<F>>],
) -> impl Fn(usize) -> Value<F> + 'a { ) -> impl Fn(usize, usize, Rotation) -> Value<F> + 'a
move |index| { {
move |index, _, _| {
let (column, at) = &queries[index]; let (column, at) = &queries[index];
let resolved_row = (row + at.0) % n; let resolved_row = (row + at.0) % n;
cells[column.index()][resolved_row as usize].into() cells[column.index()][resolved_row as usize].into()
@ -674,7 +676,7 @@ impl<F: FieldExt> MockProver<F> {
let load = |expression: &Expression<F>, row| { let load = |expression: &Expression<F>, row| {
expression.evaluate( expression.evaluate(
&|scalar| Value::Real(scalar), &|scalar| Value::Real(scalar),
&|index| { &|index, _, _| {
let query = self.cs.fixed_queries[index]; let query = self.cs.fixed_queries[index];
let column_index = query.0.index(); let column_index = query.0.index();
let rotation = query.1 .0; let rotation = query.1 .0;
@ -682,7 +684,7 @@ impl<F: FieldExt> MockProver<F> {
[(row as i32 + n + rotation) as usize % n as usize] [(row as i32 + n + rotation) as usize % n as usize]
.into() .into()
}, },
&|index| { &|index, _, _| {
let query = self.cs.advice_queries[index]; let query = self.cs.advice_queries[index];
let column_index = query.0.index(); let column_index = query.0.index();
let rotation = query.1 .0; let rotation = query.1 .0;
@ -690,7 +692,7 @@ impl<F: FieldExt> MockProver<F> {
[(row as i32 + n + rotation) as usize % n as usize] [(row as i32 + n + rotation) as usize % n as usize]
.into() .into()
}, },
&|index| { &|index, _, _| {
let query = self.cs.instance_queries[index]; let query = self.cs.instance_queries[index];
let column_index = query.0.index(); let column_index = query.0.index();
let rotation = query.1 .0; let rotation = query.1 .0;

View File

@ -537,11 +537,32 @@ pub enum Expression<F> {
/// This is a constant polynomial /// This is a constant polynomial
Constant(F), Constant(F),
/// This is a fixed column queried at a certain relative location /// This is a fixed column queried at a certain relative location
Fixed(usize), Fixed {
/// Query index
query_index: usize,
/// Column index
column_index: usize,
/// Rotation of this query
rotation: Rotation,
},
/// This is an advice (witness) column queried at a certain relative location /// This is an advice (witness) column queried at a certain relative location
Advice(usize), Advice {
/// Query index
query_index: usize,
/// Column index
column_index: usize,
/// Rotation of this query
rotation: Rotation,
},
/// This is an instance (external) column queried at a certain relative location /// This is an instance (external) column queried at a certain relative location
Instance(usize), Instance {
/// Query index
query_index: usize,
/// Column index
column_index: usize,
/// Rotation of this query
rotation: Rotation,
},
/// This is the sum of two polynomials /// This is the sum of two polynomials
Sum(Box<Expression<F>>, Box<Expression<F>>), Sum(Box<Expression<F>>, Box<Expression<F>>),
/// This is the product of two polynomials /// This is the product of two polynomials
@ -556,18 +577,30 @@ impl<F: Field> Expression<F> {
pub fn evaluate<T>( pub fn evaluate<T>(
&self, &self,
constant: &impl Fn(F) -> T, constant: &impl Fn(F) -> T,
fixed_column: &impl Fn(usize) -> T, fixed_column: &impl Fn(usize, usize, Rotation) -> T,
advice_column: &impl Fn(usize) -> T, advice_column: &impl Fn(usize, usize, Rotation) -> T,
instance_column: &impl Fn(usize) -> T, instance_column: &impl Fn(usize, usize, Rotation) -> T,
sum: &impl Fn(T, T) -> T, sum: &impl Fn(T, T) -> T,
product: &impl Fn(T, T) -> T, product: &impl Fn(T, T) -> T,
scaled: &impl Fn(T, F) -> T, scaled: &impl Fn(T, F) -> T,
) -> T { ) -> T {
match self { match self {
Expression::Constant(scalar) => constant(*scalar), Expression::Constant(scalar) => constant(*scalar),
Expression::Fixed(index) => fixed_column(*index), Expression::Fixed {
Expression::Advice(index) => advice_column(*index), query_index,
Expression::Instance(index) => instance_column(*index), column_index,
rotation,
} => fixed_column(*query_index, *column_index, *rotation),
Expression::Advice {
query_index,
column_index,
rotation,
} => advice_column(*query_index, *column_index, *rotation),
Expression::Instance {
query_index,
column_index,
rotation,
} => instance_column(*query_index, *column_index, *rotation),
Expression::Sum(a, b) => { Expression::Sum(a, b) => {
let a = a.evaluate( let a = a.evaluate(
constant, constant,
@ -629,9 +662,9 @@ impl<F: Field> Expression<F> {
pub fn degree(&self) -> usize { pub fn degree(&self) -> usize {
match self { match self {
Expression::Constant(_) => 0, Expression::Constant(_) => 0,
Expression::Fixed(_) => 1, Expression::Fixed { .. } => 1,
Expression::Advice(_) => 1, Expression::Advice { .. } => 1,
Expression::Instance(_) => 1, Expression::Instance { .. } => 1,
Expression::Sum(a, b) => max(a.degree(), b.degree()), Expression::Sum(a, b) => max(a.degree(), b.degree()),
Expression::Product(a, b) => a.degree() + b.degree(), Expression::Product(a, b) => a.degree() + b.degree(),
Expression::Scaled(poly, _) => poly.degree(), Expression::Scaled(poly, _) => poly.degree(),
@ -1150,43 +1183,49 @@ impl<'a, F: Field> VirtualCells<'a, F> {
pub fn query_selector(&mut self, selector: Selector) -> Expression<F> { pub fn query_selector(&mut self, selector: Selector) -> Expression<F> {
// Selectors are always queried at the current row. // Selectors are always queried at the current row.
self.queried_selectors.push(selector); self.queried_selectors.push(selector);
Expression::Fixed(self.meta.query_fixed_index(selector.0, Rotation::cur())) Expression::Fixed {
query_index: self.meta.query_fixed_index(selector.0, Rotation::cur()),
column_index: (selector.0).index,
rotation: Rotation::cur(),
}
} }
/// Query a fixed column at a relative position /// Query a fixed column at a relative position
pub fn query_fixed(&mut self, column: Column<Fixed>, at: Rotation) -> Expression<F> { pub fn query_fixed(&mut self, column: Column<Fixed>, at: Rotation) -> Expression<F> {
self.queried_cells.push((column, at).into()); self.queried_cells.push((column, at).into());
Expression::Fixed(self.meta.query_fixed_index(column, at)) Expression::Fixed {
query_index: self.meta.query_fixed_index(column, at),
column_index: column.index,
rotation: at,
}
} }
/// Query an advice column at a relative position /// Query an advice column at a relative position
pub fn query_advice(&mut self, column: Column<Advice>, at: Rotation) -> Expression<F> { pub fn query_advice(&mut self, column: Column<Advice>, at: Rotation) -> Expression<F> {
self.queried_cells.push((column, at).into()); self.queried_cells.push((column, at).into());
Expression::Advice(self.meta.query_advice_index(column, at)) Expression::Advice {
query_index: self.meta.query_advice_index(column, at),
column_index: column.index,
rotation: at,
}
} }
/// Query an instance column at a relative position /// Query an instance column at a relative position
pub fn query_instance(&mut self, column: Column<Instance>, at: Rotation) -> Expression<F> { pub fn query_instance(&mut self, column: Column<Instance>, at: Rotation) -> Expression<F> {
self.queried_cells.push((column, at).into()); self.queried_cells.push((column, at).into());
Expression::Instance(self.meta.query_instance_index(column, at)) Expression::Instance {
query_index: self.meta.query_instance_index(column, at),
column_index: column.index,
rotation: at,
}
} }
/// Query an Any column at a relative position /// Query an Any column at a relative position
pub fn query_any(&mut self, column: Column<Any>, at: Rotation) -> Expression<F> { pub fn query_any(&mut self, column: Column<Any>, at: Rotation) -> Expression<F> {
self.queried_cells.push((column, at).into());
match column.column_type() { match column.column_type() {
Any::Advice => Expression::Advice( Any::Advice => self.query_advice(Column::<Advice>::try_from(column).unwrap(), at),
self.meta Any::Fixed => self.query_fixed(Column::<Fixed>::try_from(column).unwrap(), at),
.query_advice_index(Column::<Advice>::try_from(column).unwrap(), at), Any::Instance => self.query_instance(Column::<Instance>::try_from(column).unwrap(), at),
),
Any::Fixed => Expression::Fixed(
self.meta
.query_fixed_index(Column::<Fixed>::try_from(column).unwrap(), at),
),
Any::Instance => Expression::Instance(
self.meta
.query_instance_index(Column::<Instance>::try_from(column).unwrap(), at),
),
} }
} }
} }

View File

@ -14,7 +14,7 @@ use super::{
}; };
use crate::poly::{ use crate::poly::{
commitment::{Blind, Params}, commitment::{Blind, Params},
EvaluationDomain, Rotation, EvaluationDomain,
}; };
use crate::{arithmetic::CurveAffine, poly::batch_invert_assigned}; use crate::{arithmetic::CurveAffine, poly::batch_invert_assigned};
@ -243,14 +243,9 @@ where
.map(|poly| vk.domain.lagrange_to_coeff(poly.clone())) .map(|poly| vk.domain.lagrange_to_coeff(poly.clone()))
.collect(); .collect();
let fixed_cosets = vk let fixed_cosets = fixed_polys
.cs
.fixed_queries
.iter() .iter()
.map(|&(column, at)| { .map(|poly| vk.domain.coeff_to_extended(poly.clone()))
let poly = fixed_polys[column.index()].clone();
vk.domain.coeff_to_extended(poly, at)
})
.collect(); .collect();
let permutation_pk = assembly let permutation_pk = assembly
@ -262,7 +257,7 @@ where
let mut l0 = vk.domain.empty_lagrange(); let mut l0 = vk.domain.empty_lagrange();
l0[0] = C::Scalar::one(); l0[0] = C::Scalar::one();
let l0 = vk.domain.lagrange_to_coeff(l0); let l0 = vk.domain.lagrange_to_coeff(l0);
let l0 = vk.domain.coeff_to_extended(l0, Rotation::cur()); let l0 = vk.domain.coeff_to_extended(l0);
// Compute l_blind(X) which evaluates to 1 for each blinding factor row // Compute l_blind(X) which evaluates to 1 for each blinding factor row
// and 0 otherwise over the domain. // and 0 otherwise over the domain.
@ -271,14 +266,14 @@ where
*evaluation = C::Scalar::one(); *evaluation = C::Scalar::one();
} }
let l_blind = vk.domain.lagrange_to_coeff(l_blind); let l_blind = vk.domain.lagrange_to_coeff(l_blind);
let l_blind = vk.domain.coeff_to_extended(l_blind, Rotation::cur()); let l_blind = vk.domain.coeff_to_extended(l_blind);
// Compute l_last(X) which evaluates to 1 on the first inactive row (just // Compute l_last(X) which evaluates to 1 on the first inactive row (just
// before the blinding factors) and 0 otherwise over the domain // before the blinding factors) and 0 otherwise over the domain
let mut l_last = vk.domain.empty_lagrange(); let mut l_last = vk.domain.empty_lagrange();
l_last[params.n as usize - cs.blinding_factors() - 1] = C::Scalar::one(); l_last[params.n as usize - cs.blinding_factors() - 1] = C::Scalar::one();
let l_last = vk.domain.lagrange_to_coeff(l_last); let l_last = vk.domain.lagrange_to_coeff(l_last);
let l_last = vk.domain.coeff_to_extended(l_last, Rotation::cur()); let l_last = vk.domain.coeff_to_extended(l_last);
Ok(ProvingKey { Ok(ProvingKey {
vk, vk,

View File

@ -27,7 +27,6 @@ pub(in crate::plonk) struct Permuted<C: CurveAffine> {
permuted_input_expression: Polynomial<C::Scalar, LagrangeCoeff>, permuted_input_expression: Polynomial<C::Scalar, LagrangeCoeff>,
permuted_input_poly: Polynomial<C::Scalar, Coeff>, permuted_input_poly: Polynomial<C::Scalar, Coeff>,
permuted_input_coset: Polynomial<C::Scalar, ExtendedLagrangeCoeff>, permuted_input_coset: Polynomial<C::Scalar, ExtendedLagrangeCoeff>,
permuted_input_inv_coset: Polynomial<C::Scalar, ExtendedLagrangeCoeff>,
permuted_input_blind: Blind<C::Scalar>, permuted_input_blind: Blind<C::Scalar>,
permuted_input_commitment: C, permuted_input_commitment: C,
unpermuted_table_expressions: Vec<Polynomial<C::Scalar, LagrangeCoeff>>, unpermuted_table_expressions: Vec<Polynomial<C::Scalar, LagrangeCoeff>>,
@ -44,7 +43,6 @@ pub(in crate::plonk) struct Committed<C: CurveAffine> {
permuted: Permuted<C>, permuted: Permuted<C>,
product_poly: Polynomial<C::Scalar, Coeff>, product_poly: Polynomial<C::Scalar, Coeff>,
product_coset: Polynomial<C::Scalar, ExtendedLagrangeCoeff>, product_coset: Polynomial<C::Scalar, ExtendedLagrangeCoeff>,
product_next_coset: Polynomial<C::Scalar, ExtendedLagrangeCoeff>,
product_blind: Blind<C::Scalar>, product_blind: Blind<C::Scalar>,
product_commitment: C, product_commitment: C,
} }
@ -103,22 +101,13 @@ impl<F: FieldExt> Argument<F> {
.map(|expression| { .map(|expression| {
expression.evaluate( expression.evaluate(
&|scalar| pk.vk.domain.constant_lagrange(scalar), &|scalar| pk.vk.domain.constant_lagrange(scalar),
&|index| { &|_, column_index, rotation| {
let query = pk.vk.cs.fixed_queries[index];
let column_index = query.0.index();
let rotation = query.1;
fixed_values[column_index].clone().rotate(rotation) fixed_values[column_index].clone().rotate(rotation)
}, },
&|index| { &|_, column_index, rotation| {
let query = pk.vk.cs.advice_queries[index];
let column_index = query.0.index();
let rotation = query.1;
advice_values[column_index].clone().rotate(rotation) advice_values[column_index].clone().rotate(rotation)
}, },
&|index| { &|_, column_index, rotation| {
let query = pk.vk.cs.instance_queries[index];
let column_index = query.0.index();
let rotation = query.1;
instance_values[column_index].clone().rotate(rotation) instance_values[column_index].clone().rotate(rotation)
}, },
&|a, b| a + &b, &|a, b| a + &b,
@ -145,9 +134,21 @@ impl<F: FieldExt> Argument<F> {
.map(|expression| { .map(|expression| {
expression.evaluate( expression.evaluate(
&|scalar| pk.vk.domain.constant_extended(scalar), &|scalar| pk.vk.domain.constant_extended(scalar),
&|index| fixed_cosets[index].clone(), &|_, column_index, rotation| {
&|index| advice_cosets[index].clone(), pk.vk
&|index| instance_cosets[index].clone(), .domain
.rotate_extended(&fixed_cosets[column_index], rotation)
},
&|_, column_index, rotation| {
pk.vk
.domain
.rotate_extended(&advice_cosets[column_index], rotation)
},
&|_, column_index, rotation| {
pk.vk
.domain
.rotate_extended(&instance_cosets[column_index], rotation)
},
&|a, b| a + &b, &|a, b| a + &b,
&|a, b| a * &b, &|a, b| a * &b,
&|a, scalar| a * scalar, &|a, scalar| a * scalar,
@ -212,18 +213,8 @@ impl<F: FieldExt> Argument<F> {
.write_point(permuted_table_commitment) .write_point(permuted_table_commitment)
.map_err(|_| Error::TranscriptError)?; .map_err(|_| Error::TranscriptError)?;
let permuted_input_coset = pk let permuted_input_coset = pk.vk.domain.coeff_to_extended(permuted_input_poly.clone());
.vk let permuted_table_coset = pk.vk.domain.coeff_to_extended(permuted_table_poly.clone());
.domain
.coeff_to_extended(permuted_input_poly.clone(), Rotation::cur());
let permuted_input_inv_coset = pk
.vk
.domain
.coeff_to_extended(permuted_input_poly.clone(), Rotation(-1));
let permuted_table_coset = pk
.vk
.domain
.coeff_to_extended(permuted_table_poly.clone(), Rotation::cur());
Ok(Permuted { Ok(Permuted {
unpermuted_input_expressions, unpermuted_input_expressions,
@ -231,7 +222,6 @@ impl<F: FieldExt> Argument<F> {
permuted_input_expression, permuted_input_expression,
permuted_input_poly, permuted_input_poly,
permuted_input_coset, permuted_input_coset,
permuted_input_inv_coset,
permuted_input_blind, permuted_input_blind,
permuted_input_commitment, permuted_input_commitment,
unpermuted_table_expressions, unpermuted_table_expressions,
@ -392,8 +382,7 @@ impl<C: CurveAffine> Permuted<C> {
let product_blind = Blind(C::Scalar::rand()); let product_blind = Blind(C::Scalar::rand());
let product_commitment = params.commit_lagrange(&z, product_blind).to_affine(); let product_commitment = params.commit_lagrange(&z, product_blind).to_affine();
let z = pk.vk.domain.lagrange_to_coeff(z); let z = pk.vk.domain.lagrange_to_coeff(z);
let product_coset = pk.vk.domain.coeff_to_extended(z.clone(), Rotation::cur()); let product_coset = pk.vk.domain.coeff_to_extended(z.clone());
let product_next_coset = pk.vk.domain.coeff_to_extended(z.clone(), Rotation::next());
// Hash product commitment // Hash product commitment
transcript transcript
@ -404,7 +393,6 @@ impl<C: CurveAffine> Permuted<C> {
permuted: self, permuted: self,
product_poly: z, product_poly: z,
product_coset, product_coset,
product_next_coset,
product_commitment, product_commitment,
product_blind, product_blind,
}) })
@ -427,6 +415,7 @@ impl<'a, C: CurveAffine> Committed<C> {
Constructed<C>, Constructed<C>,
impl Iterator<Item = Polynomial<C::Scalar, ExtendedLagrangeCoeff>> + 'a, impl Iterator<Item = Polynomial<C::Scalar, ExtendedLagrangeCoeff>> + 'a,
) { ) {
let domain = &pk.vk.domain;
let permuted = self.permuted; let permuted = self.permuted;
let active_rows = Polynomial::one_minus(pk.l_last.clone() + &pk.l_blind); let active_rows = Polynomial::one_minus(pk.l_last.clone() + &pk.l_blind);
@ -447,7 +436,7 @@ impl<'a, C: CurveAffine> Committed<C> {
// ) = 0 // ) = 0
.chain({ .chain({
// z(\omega X) (a'(X) + \beta) (s'(X) + \gamma) // z(\omega X) (a'(X) + \beta) (s'(X) + \gamma)
let mut left = self.product_next_coset.clone(); let mut left = domain.rotate_extended(&self.product_coset, Rotation::next());
parallelize(&mut left, |left, start| { parallelize(&mut left, |left, start| {
for ((left, permuted_input), permuted_table) in left for ((left, permuted_input), permuted_table) in left
.iter_mut() .iter_mut()
@ -499,7 +488,11 @@ impl<'a, C: CurveAffine> Committed<C> {
// (1 - (l_last + l_blind)) * (a(X) s(X))⋅(a(X) a(\omega^{-1} X)) = 0 // (1 - (l_last + l_blind)) * (a(X) s(X))⋅(a(X) a(\omega^{-1} X)) = 0
.chain(Some( .chain(Some(
(permuted.permuted_input_coset.clone() - &permuted.permuted_table_coset) (permuted.permuted_input_coset.clone() - &permuted.permuted_table_coset)
* &(permuted.permuted_input_coset.clone() - &permuted.permuted_input_inv_coset) * &(domain.sub_extended(
permuted.permuted_input_coset.clone(),
&permuted.permuted_input_coset,
Rotation::prev(),
))
* &active_rows, * &active_rows,
)); ));

View File

@ -134,9 +134,9 @@ impl<C: CurveAffine> Evaluated<C> {
.map(|expression| { .map(|expression| {
expression.evaluate( expression.evaluate(
&|scalar| scalar, &|scalar| scalar,
&|index| fixed_evals[index], &|index, _, _| fixed_evals[index],
&|index| advice_evals[index], &|index, _, _| advice_evals[index],
&|index| instance_evals[index], &|index, _, _| instance_evals[index],
&|a, b| a + &b, &|a, b| a + &b,
&|a, b| a * &b, &|a, b| a * &b,
&|a, scalar| a * &scalar, &|a, scalar| a * &scalar,

View File

@ -7,7 +7,7 @@ use crate::{
plonk::{Any, Column, Error}, plonk::{Any, Column, Error},
poly::{ poly::{
commitment::{Blind, Params}, commitment::{Blind, Params},
EvaluationDomain, Rotation, EvaluationDomain,
}, },
}; };
@ -199,7 +199,7 @@ impl Assembly {
permutations.push(permutation_poly.clone()); permutations.push(permutation_poly.clone());
let poly = domain.lagrange_to_coeff(permutation_poly); let poly = domain.lagrange_to_coeff(permutation_poly);
polys.push(poly.clone()); polys.push(poly.clone());
cosets.push(domain.coeff_to_extended(poly, Rotation::cur())); cosets.push(domain.coeff_to_extended(poly));
} }
ProvingKey { ProvingKey {
permutations, permutations,

View File

@ -18,8 +18,6 @@ use crate::{
pub struct CommittedSet<C: CurveAffine> { pub struct CommittedSet<C: CurveAffine> {
permutation_product_poly: Polynomial<C::Scalar, Coeff>, permutation_product_poly: Polynomial<C::Scalar, Coeff>,
permutation_product_coset: Polynomial<C::Scalar, ExtendedLagrangeCoeff>, permutation_product_coset: Polynomial<C::Scalar, ExtendedLagrangeCoeff>,
permutation_product_coset_next: Polynomial<C::Scalar, ExtendedLagrangeCoeff>,
permutation_product_coset_last: Option<Polynomial<C::Scalar, ExtendedLagrangeCoeff>>,
permutation_product_blind: Blind<C::Scalar>, permutation_product_blind: Blind<C::Scalar>,
} }
@ -75,12 +73,11 @@ impl Argument {
let mut sets = vec![]; let mut sets = vec![];
let mut iter = self for (columns, permutations) in self
.columns .columns
.chunks(chunk_len) .chunks(chunk_len)
.zip(pkey.permutations.chunks(chunk_len)); .zip(pkey.permutations.chunks(chunk_len))
{
while let Some((columns, permutations)) = iter.next() {
// Goal is to compute the products of fractions // Goal is to compute the products of fractions
// //
// (p_j(\omega^i) + \delta^j \omega^i \beta + \gamma) / // (p_j(\omega^i) + \delta^j \omega^i \beta + \gamma) /
@ -168,18 +165,7 @@ impl Argument {
let z = domain.lagrange_to_coeff(z); let z = domain.lagrange_to_coeff(z);
let permutation_product_poly = z.clone(); let permutation_product_poly = z.clone();
// We only keep these around if there's another set afterward. let permutation_product_coset = domain.coeff_to_extended(z.clone());
let permutation_product_coset_last = if iter.len() > 0 {
// Keep the polynomial around, rotated to l_last.
Some(
domain.coeff_to_extended(z.clone(), Rotation(-((blinding_factors + 1) as i32))),
)
} else {
None
};
let permutation_product_coset = domain.coeff_to_extended(z.clone(), Rotation::cur());
let permutation_product_coset_next = domain.coeff_to_extended(z, Rotation::next());
let permutation_product_commitment = let permutation_product_commitment =
permutation_product_commitment_projective.to_affine(); permutation_product_commitment_projective.to_affine();
@ -192,8 +178,6 @@ impl Argument {
sets.push(CommittedSet { sets.push(CommittedSet {
permutation_product_poly, permutation_product_poly,
permutation_product_coset, permutation_product_coset,
permutation_product_coset_next,
permutation_product_coset_last,
permutation_product_blind, permutation_product_blind,
}); });
} }
@ -219,6 +203,8 @@ impl<C: CurveAffine> Committed<C> {
) { ) {
let domain = &pk.vk.domain; let domain = &pk.vk.domain;
let chunk_len = pk.vk.cs.degree() - 2; let chunk_len = pk.vk.cs.degree() - 2;
let blinding_factors = pk.vk.cs.blinding_factors();
let last_rotation = Rotation(-((blinding_factors + 1) as i32));
let constructed = Constructed { let constructed = Constructed {
sets: self sets: self
@ -252,9 +238,11 @@ impl<C: CurveAffine> Committed<C> {
.skip(1) .skip(1)
.zip(self.sets.iter()) .zip(self.sets.iter())
.map(|(set, last_set)| { .map(|(set, last_set)| {
(set.permutation_product_coset.clone() domain.sub_extended(
- &last_set.permutation_product_coset_last.as_ref().unwrap()) set.permutation_product_coset.clone(),
* &pk.l0 &last_set.permutation_product_coset,
last_rotation,
) * &pk.l0
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
) )
@ -270,22 +258,14 @@ impl<C: CurveAffine> Committed<C> {
.zip(pkey.cosets.chunks(chunk_len)) .zip(pkey.cosets.chunks(chunk_len))
.enumerate() .enumerate()
.map(move |(chunk_index, ((set, columns), cosets))| { .map(move |(chunk_index, ((set, columns), cosets))| {
let mut left = set.permutation_product_coset_next; let mut left = domain
.rotate_extended(&set.permutation_product_coset, Rotation::next());
for (values, permutation) in columns for (values, permutation) in columns
.iter() .iter()
.map(|&column| match column.column_type() { .map(|&column| match column.column_type() {
Any::Advice => { Any::Advice => &advice_cosets[column.index()],
&advice_cosets Any::Fixed => &fixed_cosets[column.index()],
[pk.vk.cs.get_any_query_index(column, Rotation::cur())] Any::Instance => &instance_cosets[column.index()],
}
Any::Fixed => {
&fixed_cosets
[pk.vk.cs.get_any_query_index(column, Rotation::cur())]
}
Any::Instance => {
&instance_cosets
[pk.vk.cs.get_any_query_index(column, Rotation::cur())]
}
}) })
.zip(cosets.iter()) .zip(cosets.iter())
{ {
@ -306,17 +286,9 @@ impl<C: CurveAffine> Committed<C> {
* &(C::Scalar::DELTA.pow_vartime(&[(chunk_index * chunk_len) as u64])); * &(C::Scalar::DELTA.pow_vartime(&[(chunk_index * chunk_len) as u64]));
let step = domain.get_extended_omega(); let step = domain.get_extended_omega();
for values in columns.iter().map(|&column| match column.column_type() { for values in columns.iter().map(|&column| match column.column_type() {
Any::Advice => { Any::Advice => &advice_cosets[column.index()],
&advice_cosets Any::Fixed => &fixed_cosets[column.index()],
[pk.vk.cs.get_any_query_index(column, Rotation::cur())] Any::Instance => &instance_cosets[column.index()],
}
Any::Fixed => {
&fixed_cosets[pk.vk.cs.get_any_query_index(column, Rotation::cur())]
}
Any::Instance => {
&instance_cosets
[pk.vk.cs.get_any_query_index(column, Rotation::cur())]
}
}) { }) {
parallelize(&mut right, move |right, start| { parallelize(&mut right, move |right, start| {
let mut beta_term = let mut beta_term =

View File

@ -103,13 +103,9 @@ pub fn create_proof<
}) })
.collect(); .collect();
let instance_cosets: Vec<_> = meta let instance_cosets: Vec<_> = instance_polys
.instance_queries
.iter() .iter()
.map(|&(column, at)| { .map(|poly| domain.coeff_to_extended(poly.clone()))
let poly = instance_polys[column.index()].clone();
domain.coeff_to_extended(poly, at)
})
.collect(); .collect();
Ok(InstanceSingle { Ok(InstanceSingle {
@ -300,13 +296,9 @@ pub fn create_proof<
.map(|poly| domain.lagrange_to_coeff(poly)) .map(|poly| domain.lagrange_to_coeff(poly))
.collect(); .collect();
let advice_cosets: Vec<_> = meta let advice_cosets: Vec<_> = advice_polys
.advice_queries
.iter() .iter()
.map(|&(column, at)| { .map(|poly| domain.coeff_to_extended(poly.clone()))
let poly = advice_polys[column.index()].clone();
domain.coeff_to_extended(poly, at)
})
.collect(); .collect();
Ok(AdviceSingle { Ok(AdviceSingle {
@ -434,9 +426,23 @@ pub fn create_proof<
gate.polynomials().iter().map(move |poly| { gate.polynomials().iter().map(move |poly| {
poly.evaluate( poly.evaluate(
&|scalar| pk.vk.domain.constant_extended(scalar), &|scalar| pk.vk.domain.constant_extended(scalar),
&|index| pk.fixed_cosets[index].clone(), &|_, column_index, rotation| {
&|index| advice.advice_cosets[index].clone(), pk.vk
&|index| instance.instance_cosets[index].clone(), .domain
.rotate_extended(&pk.fixed_cosets[column_index], rotation)
},
&|_, column_index, rotation| {
pk.vk.domain.rotate_extended(
&advice.advice_cosets[column_index],
rotation,
)
},
&|_, column_index, rotation| {
pk.vk.domain.rotate_extended(
&instance.instance_cosets[column_index],
rotation,
)
},
&|a, b| a + &b, &|a, b| a + &b,
&|a, b| a * &b, &|a, b| a * &b,
&|a, scalar| a * scalar, &|a, scalar| a * scalar,

View File

@ -184,9 +184,9 @@ pub fn verify_proof<'params, C: CurveAffine, E: EncodedChallenge<C>, T: Transcri
gate.polynomials().iter().map(move |poly| { gate.polynomials().iter().map(move |poly| {
poly.evaluate( poly.evaluate(
&|scalar| scalar, &|scalar| scalar,
&|index| fixed_evals[index], &|index, _, _| fixed_evals[index],
&|index| advice_evals[index], &|index, _, _| advice_evals[index],
&|index| instance_evals[index], &|index, _, _| instance_evals[index],
&|a, b| a + &b, &|a, b| a + &b,
&|a, b| a * &b, &|a, b| a * &b,
&|a, scalar| a * &scalar, &|a, scalar| a * &scalar,

View File

@ -239,26 +239,10 @@ impl<G: Group> EvaluationDomain<G> {
pub fn coeff_to_extended( pub fn coeff_to_extended(
&self, &self,
mut a: Polynomial<G, Coeff>, mut a: Polynomial<G, Coeff>,
rotation: Rotation,
) -> Polynomial<G, ExtendedLagrangeCoeff> { ) -> Polynomial<G, ExtendedLagrangeCoeff> {
assert_eq!(a.values.len(), 1 << self.k); assert_eq!(a.values.len(), 1 << self.k);
assert!(rotation.0 != i32::MIN); self.distribute_powers_zeta(&mut a.values, true);
if rotation.0 == 0 {
// In this special case, the powers of zeta repeat so we do not need
// to compute them.
Self::distribute_powers_zeta(&mut a.values);
} else {
let mut g = self.g_coset;
if rotation.0 > 0 {
g *= &self.omega.pow_vartime(&[rotation.0 as u64, 0, 0, 0]);
} else {
g *= &self
.omega_inv
.pow_vartime(&[rotation.0.abs() as u64, 0, 0, 0]);
}
Self::distribute_powers(&mut a.values, g);
}
a.values.resize(self.extended_len(), G::group_zero()); a.values.resize(self.extended_len(), G::group_zero());
best_fft(&mut a.values, self.extended_omega, self.extended_k); best_fft(&mut a.values, self.extended_omega, self.extended_k);
@ -268,6 +252,102 @@ impl<G: Group> EvaluationDomain<G> {
} }
} }
fn op_extended(
&self,
mut left: Polynomial<G, ExtendedLagrangeCoeff>,
right: &Polynomial<G, ExtendedLagrangeCoeff>,
rotation: Rotation,
op: impl Fn(&mut G, &G) + Send + Sync + 'static,
) -> Polynomial<G, ExtendedLagrangeCoeff>
where
G: Field,
{
// Rotation while in the extended domain is simply exaggerated by how
// much larger the extended domain is compared to the original domain.
let rotation = (1 << (self.extended_k - self.k)) * rotation.0;
parallelize(&mut left.values, |lhs, start| {
let start = ((((start + self.extended_len()) as i32) + rotation) as usize)
% self.extended_len();
for (lhs, rhs) in lhs
.iter_mut()
.zip(right.values[start..].iter().chain(right.values.iter()))
{
op(lhs, rhs);
}
});
left
}
/// Multiply two polynomials in the extended domain, rotating the latter
/// polynomial over the original domain first.
pub fn mul_extended(
&self,
left: Polynomial<G, ExtendedLagrangeCoeff>,
right: &Polynomial<G, ExtendedLagrangeCoeff>,
rotation: Rotation,
) -> Polynomial<G, ExtendedLagrangeCoeff>
where
G: Field,
{
self.op_extended(left, right, rotation, |lhs, rhs| {
*lhs *= *rhs;
})
}
/// Add two polynomials in the extended domain, rotating the latter
/// polynomial over the original domain first.
pub fn add_extended(
&self,
left: Polynomial<G, ExtendedLagrangeCoeff>,
right: &Polynomial<G, ExtendedLagrangeCoeff>,
rotation: Rotation,
) -> Polynomial<G, ExtendedLagrangeCoeff>
where
G: Field,
{
self.op_extended(left, right, rotation, |lhs, rhs| {
*lhs += *rhs;
})
}
/// Subtract a polynomial from another in the extended domain, rotating the
/// former polynomial over the original domain first.
pub fn sub_extended(
&self,
left: Polynomial<G, ExtendedLagrangeCoeff>,
right: &Polynomial<G, ExtendedLagrangeCoeff>,
rotation: Rotation,
) -> Polynomial<G, ExtendedLagrangeCoeff>
where
G: Field,
{
self.op_extended(left, right, rotation, |lhs, rhs| {
*lhs -= *rhs;
})
}
/// Rotate the extended domain polynomial over the original domain.
pub fn rotate_extended(
&self,
poly: &Polynomial<G, ExtendedLagrangeCoeff>,
rotation: Rotation,
) -> Polynomial<G, ExtendedLagrangeCoeff> {
let new_rotation = ((1 << (self.extended_k - self.k)) * rotation.0.abs()) as usize;
let mut poly = poly.clone();
if rotation.0 >= 0 {
poly.values.rotate_left(new_rotation);
} else {
poly.values.rotate_right(new_rotation);
}
poly
}
/// This takes us from the extended evaluation domain and gets us the /// This takes us from the extended evaluation domain and gets us the
/// quotient polynomial coefficients. /// quotient polynomial coefficients.
/// ///
@ -287,7 +367,7 @@ impl<G: Group> EvaluationDomain<G> {
// Distribute powers to move from coset; opposite from the // Distribute powers to move from coset; opposite from the
// transformation we performed earlier. // transformation we performed earlier.
Self::distribute_powers(&mut a.values, self.g_coset_inv); self.distribute_powers_zeta(&mut a.values, false);
// Truncate it to match the size of the quotient polynomial; the // Truncate it to match the size of the quotient polynomial; the
// evaluation domain might be slightly larger than necessary because // evaluation domain might be slightly larger than necessary because
@ -321,15 +401,22 @@ impl<G: Group> EvaluationDomain<G> {
} }
} }
// Given a slice of group elements `[a_0, a_1, a_2, ...]`, this returns /// Given a slice of group elements `[a_0, a_1, a_2, ...]`, this returns
// `[a_0, [zeta]a_1, [zeta^2]a_2, a_3, [zeta]a_4, [zeta^2]a_5, a_6, ...]`, /// `[a_0, [zeta]a_1, [zeta^2]a_2, a_3, [zeta]a_4, [zeta^2]a_5, a_6, ...]`,
// where zeta is a cube root of unity in the multiplicative subgroup with /// where zeta is a cube root of unity in the multiplicative subgroup with
// order (p - 1), i.e. zeta^3 = 1. /// order (p - 1), i.e. zeta^3 = 1.
fn distribute_powers_zeta(mut a: &mut [G]) { ///
let coset_powers = [G::Scalar::ZETA, G::Scalar::ZETA.square()]; /// `into_coset` should be set to `true` when moving into the coset,
/// and `false` when moving out. This toggles the choice of `zeta`.
fn distribute_powers_zeta(&self, mut a: &mut [G], into_coset: bool) {
let coset_powers = if into_coset {
[self.g_coset, self.g_coset_inv]
} else {
[self.g_coset_inv, self.g_coset]
};
parallelize(&mut a, |a, mut index| { parallelize(&mut a, |a, mut index| {
for a in a { for a in a {
// Distribute powers to move into coset // Distribute powers to move into/from coset
let i = index % (coset_powers.len() + 1); let i = index % (coset_powers.len() + 1);
if i != 0 { if i != 0 {
a.group_scale(&coset_powers[i - 1]); a.group_scale(&coset_powers[i - 1]);
@ -339,18 +426,6 @@ impl<G: Group> EvaluationDomain<G> {
}); });
} }
// Given a length-`n` slice of group elements `a` and a scalar `g`, this
// returns `[a_0, [g]a_1, [g^2]a_2, [g^3]a_3, ..., [g^n-1] a_{n-1}]`.
fn distribute_powers(mut a: &mut [G], g: G::Scalar) {
parallelize(&mut a, |a, index| {
let mut cur = g.pow_vartime(&[index as u64, 0, 0, 0]);
for a in a {
a.group_scale(&cur);
cur *= &g;
}
});
}
fn ifft(a: &mut [G], omega_inv: G::Scalar, log_n: u32, divisor: G::Scalar) { fn ifft(a: &mut [G], omega_inv: G::Scalar, log_n: u32, divisor: G::Scalar) {
best_fft(a, omega_inv, log_n); best_fft(a, omega_inv, log_n);
parallelize(a, |a, _| { parallelize(a, |a, _| {

View File

@ -521,74 +521,134 @@ fn plonk_api() {
Sum( Sum(
Sum( Sum(
Product( Product(
Advice( Advice {
query_index: 0,
column_index: 1,
rotation: Rotation(
0, 0,
), ),
Fixed( },
3, Fixed {
), query_index: 3,
), column_index: 2,
Product( rotation: Rotation(
Advice(
1,
),
Fixed(
4,
),
),
),
Product(
Product(
Advice(
0, 0,
), ),
Advice( },
1, ),
Product(
Advice {
query_index: 1,
column_index: 2,
rotation: Rotation(
0,
),
},
Fixed {
query_index: 4,
column_index: 3,
rotation: Rotation(
0,
),
},
), ),
), ),
Fixed( Product(
6, Product(
Advice {
query_index: 0,
column_index: 1,
rotation: Rotation(
0,
), ),
},
Advice {
query_index: 1,
column_index: 2,
rotation: Rotation(
0,
),
},
),
Fixed {
query_index: 6,
column_index: 1,
rotation: Rotation(
0,
),
},
), ),
), ),
Scaled( Scaled(
Product( Product(
Advice( Advice {
2, query_index: 2,
column_index: 3,
rotation: Rotation(
0,
), ),
Fixed( },
5, Fixed {
query_index: 5,
column_index: 4,
rotation: Rotation(
0,
), ),
},
), ),
0x40000000000000000000000000000000224698fc094cf91b992d30ed00000000, 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000000,
), ),
), ),
Product( Product(
Fixed( Fixed {
2, query_index: 2,
column_index: 0,
rotation: Rotation(
0,
), ),
},
Product( Product(
Advice( Advice {
3, query_index: 3,
column_index: 4,
rotation: Rotation(
1,
), ),
Advice( },
4, Advice {
query_index: 4,
column_index: 0,
rotation: Rotation(
-1,
), ),
},
), ),
), ),
), ),
Product( Product(
Fixed( Fixed {
7, query_index: 7,
column_index: 5,
rotation: Rotation(
0,
), ),
},
Sum( Sum(
Advice( Advice {
query_index: 0,
column_index: 1,
rotation: Rotation(
0, 0,
), ),
},
Scaled( Scaled(
Instance( Instance {
query_index: 0,
column_index: 0,
rotation: Rotation(
0, 0,
), ),
},
0x40000000000000000000000000000000224698fc094cf91b992d30ed00000000, 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000000,
), ),
), ),
@ -807,35 +867,59 @@ fn plonk_api() {
lookups: [ lookups: [
Argument { Argument {
input_expressions: [ input_expressions: [
Advice( Advice {
query_index: 0,
column_index: 1,
rotation: Rotation(
0, 0,
), ),
},
], ],
table_expressions: [ table_expressions: [
Fixed( Fixed {
query_index: 0,
column_index: 6,
rotation: Rotation(
0, 0,
), ),
},
], ],
}, },
Argument { Argument {
input_expressions: [ input_expressions: [
Product( Product(
Advice( Advice {
query_index: 0,
column_index: 1,
rotation: Rotation(
0, 0,
), ),
Advice( },
1, Advice {
query_index: 1,
column_index: 2,
rotation: Rotation(
0,
), ),
},
), ),
], ],
table_expressions: [ table_expressions: [
Product( Product(
Fixed( Fixed {
query_index: 0,
column_index: 6,
rotation: Rotation(
0, 0,
), ),
Fixed( },
1, Fixed {
query_index: 1,
column_index: 7,
rotation: Rotation(
0,
), ),
},
), ),
], ],
}, },