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

View File

@ -537,11 +537,32 @@ pub enum Expression<F> {
/// This is a constant polynomial
Constant(F),
/// 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
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
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
Sum(Box<Expression<F>>, Box<Expression<F>>),
/// This is the product of two polynomials
@ -556,18 +577,30 @@ impl<F: Field> Expression<F> {
pub fn evaluate<T>(
&self,
constant: &impl Fn(F) -> T,
fixed_column: &impl Fn(usize) -> T,
advice_column: &impl Fn(usize) -> T,
instance_column: &impl Fn(usize) -> T,
fixed_column: &impl Fn(usize, usize, Rotation) -> T,
advice_column: &impl Fn(usize, usize, Rotation) -> T,
instance_column: &impl Fn(usize, usize, Rotation) -> T,
sum: &impl Fn(T, T) -> T,
product: &impl Fn(T, T) -> T,
scaled: &impl Fn(T, F) -> T,
) -> T {
match self {
Expression::Constant(scalar) => constant(*scalar),
Expression::Fixed(index) => fixed_column(*index),
Expression::Advice(index) => advice_column(*index),
Expression::Instance(index) => instance_column(*index),
Expression::Fixed {
query_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) => {
let a = a.evaluate(
constant,
@ -629,9 +662,9 @@ impl<F: Field> Expression<F> {
pub fn degree(&self) -> usize {
match self {
Expression::Constant(_) => 0,
Expression::Fixed(_) => 1,
Expression::Advice(_) => 1,
Expression::Instance(_) => 1,
Expression::Fixed { .. } => 1,
Expression::Advice { .. } => 1,
Expression::Instance { .. } => 1,
Expression::Sum(a, b) => max(a.degree(), b.degree()),
Expression::Product(a, b) => a.degree() + b.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> {
// Selectors are always queried at the current row.
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
pub fn query_fixed(&mut self, column: Column<Fixed>, at: Rotation) -> Expression<F> {
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
pub fn query_advice(&mut self, column: Column<Advice>, at: Rotation) -> Expression<F> {
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
pub fn query_instance(&mut self, column: Column<Instance>, at: Rotation) -> Expression<F> {
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
pub fn query_any(&mut self, column: Column<Any>, at: Rotation) -> Expression<F> {
self.queried_cells.push((column, at).into());
match column.column_type() {
Any::Advice => Expression::Advice(
self.meta
.query_advice_index(Column::<Advice>::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),
),
Any::Advice => self.query_advice(Column::<Advice>::try_from(column).unwrap(), at),
Any::Fixed => self.query_fixed(Column::<Fixed>::try_from(column).unwrap(), at),
Any::Instance => self.query_instance(Column::<Instance>::try_from(column).unwrap(), at),
}
}
}

View File

@ -14,7 +14,7 @@ use super::{
};
use crate::poly::{
commitment::{Blind, Params},
EvaluationDomain, Rotation,
EvaluationDomain,
};
use crate::{arithmetic::CurveAffine, poly::batch_invert_assigned};
@ -243,14 +243,9 @@ where
.map(|poly| vk.domain.lagrange_to_coeff(poly.clone()))
.collect();
let fixed_cosets = vk
.cs
.fixed_queries
let fixed_cosets = fixed_polys
.iter()
.map(|&(column, at)| {
let poly = fixed_polys[column.index()].clone();
vk.domain.coeff_to_extended(poly, at)
})
.map(|poly| vk.domain.coeff_to_extended(poly.clone()))
.collect();
let permutation_pk = assembly
@ -262,7 +257,7 @@ where
let mut l0 = vk.domain.empty_lagrange();
l0[0] = C::Scalar::one();
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
// and 0 otherwise over the domain.
@ -271,14 +266,14 @@ where
*evaluation = C::Scalar::one();
}
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
// before the blinding factors) and 0 otherwise over the domain
let mut l_last = vk.domain.empty_lagrange();
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.coeff_to_extended(l_last, Rotation::cur());
let l_last = vk.domain.coeff_to_extended(l_last);
Ok(ProvingKey {
vk,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -239,26 +239,10 @@ impl<G: Group> EvaluationDomain<G> {
pub fn coeff_to_extended(
&self,
mut a: Polynomial<G, Coeff>,
rotation: Rotation,
) -> Polynomial<G, ExtendedLagrangeCoeff> {
assert_eq!(a.values.len(), 1 << self.k);
assert!(rotation.0 != i32::MIN);
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);
}
self.distribute_powers_zeta(&mut a.values, true);
a.values.resize(self.extended_len(), G::group_zero());
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
/// quotient polynomial coefficients.
///
@ -287,7 +367,7 @@ impl<G: Group> EvaluationDomain<G> {
// Distribute powers to move from coset; opposite from the
// 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
// 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
// `[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
// 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()];
/// 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, ...]`,
/// where zeta is a cube root of unity in the multiplicative subgroup with
/// order (p - 1), i.e. zeta^3 = 1.
///
/// `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| {
for a in a {
// Distribute powers to move into coset
// Distribute powers to move into/from coset
let i = index % (coset_powers.len() + 1);
if i != 0 {
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) {
best_fft(a, omega_inv, log_n);
parallelize(a, |a, _| {

View File

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