mirror of https://github.com/zcash/halo2.git
Merge pull request #176 from zcash/general-lookup
Generalise lookup argument to work over expressions
This commit is contained in:
commit
fb6111df5b
|
@ -283,8 +283,12 @@ fn main() {
|
|||
* ... ... ... 0 0
|
||||
* ]
|
||||
*/
|
||||
meta.lookup(&[a.into()], &[sl.into()]);
|
||||
meta.lookup(&[a.into(), b.into()], &[sl.into(), sl2.into()]);
|
||||
let a_ = meta.query_any(a.into(), Rotation::cur());
|
||||
let b_ = meta.query_any(b.into(), Rotation::cur());
|
||||
let sl_ = meta.query_any(sl.into(), Rotation::cur());
|
||||
let sl2_ = meta.query_any(sl2.into(), Rotation::cur());
|
||||
meta.lookup(&[a_.clone()], &[sl_.clone()]);
|
||||
meta.lookup(&[a_, b_], &[sl_, sl2_]);
|
||||
|
||||
meta.create_gate("Combined add-mult", |meta| {
|
||||
let d = meta.query_advice(d, Rotation::next());
|
||||
|
|
45
src/dev.rs
45
src/dev.rs
|
@ -5,8 +5,8 @@ use ff::Field;
|
|||
use crate::{
|
||||
arithmetic::{FieldExt, Group},
|
||||
plonk::{
|
||||
permutation, Advice, Any, Assignment, Circuit, Column, ColumnType, ConstraintSystem, Error,
|
||||
Fixed,
|
||||
permutation, Advice, Assignment, Circuit, Column, ColumnType, ConstraintSystem, Error,
|
||||
Expression, Fixed,
|
||||
},
|
||||
poly::Rotation,
|
||||
};
|
||||
|
@ -143,7 +143,7 @@ pub enum VerifyFailure {
|
|||
/// );
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct MockProver<F: Group> {
|
||||
pub struct MockProver<F: Group + Field> {
|
||||
n: u32,
|
||||
cs: ConstraintSystem<F>,
|
||||
|
||||
|
@ -296,6 +296,7 @@ impl<F: FieldExt> MockProver<F> {
|
|||
}
|
||||
|
||||
if gate.evaluate(
|
||||
&|scalar| scalar,
|
||||
&load(n, row, &self.cs.fixed_queries, &self.fixed),
|
||||
&load(n, row, &self.cs.advice_queries, &self.advice),
|
||||
&load(n, row, &self.cs.instance_queries, &self.instance),
|
||||
|
@ -316,21 +317,41 @@ impl<F: FieldExt> MockProver<F> {
|
|||
// Check that all lookups exist in their respective tables.
|
||||
for (lookup_index, lookup) in self.cs.lookups.iter().enumerate() {
|
||||
for input_row in 0..n {
|
||||
let load = |column: &Column<Any>, row| match column.column_type() {
|
||||
Any::Fixed => self.fixed[column.index()][row as usize],
|
||||
Any::Advice => self.advice[column.index()][row as usize],
|
||||
Any::Instance => self.instance[column.index()][row as usize],
|
||||
let load = |expression: &Expression<F>, row| {
|
||||
expression.evaluate(
|
||||
&|scalar| scalar,
|
||||
&|index| {
|
||||
let column_index = self.cs.fixed_queries[index].0.index();
|
||||
self.fixed[column_index][row as usize]
|
||||
},
|
||||
&|index| {
|
||||
let column_index = self.cs.advice_queries[index].0.index();
|
||||
self.advice[column_index][row as usize]
|
||||
},
|
||||
&|index| {
|
||||
let column_index = self.cs.instance_queries[index].0.index();
|
||||
self.instance[column_index][row as usize]
|
||||
},
|
||||
&|a, b| a + b,
|
||||
&|a, b| a * b,
|
||||
&|a, scalar| a * scalar,
|
||||
)
|
||||
};
|
||||
|
||||
let inputs: Vec<_> = lookup
|
||||
.input_columns
|
||||
.input_expressions
|
||||
.iter()
|
||||
.map(|c| load(c, input_row))
|
||||
.collect();
|
||||
if !(0..n)
|
||||
.map(|table_row| lookup.table_columns.iter().map(move |c| load(c, table_row)))
|
||||
.any(|table_row| table_row.eq(inputs.iter().cloned()))
|
||||
{
|
||||
let lookup_passes = (0..n)
|
||||
.map(|table_row| {
|
||||
lookup
|
||||
.table_expressions
|
||||
.iter()
|
||||
.map(move |c| load(c, table_row))
|
||||
})
|
||||
.any(|table_row| table_row.eq(inputs.iter().cloned()));
|
||||
if !lookup_passes {
|
||||
return Err(VerifyFailure::Lookup {
|
||||
lookup_index,
|
||||
row: input_row as usize,
|
||||
|
|
62
src/plonk.rs
62
src/plonk.rs
|
@ -467,8 +467,12 @@ fn test_proving() {
|
|||
* ... ... ... 0 0
|
||||
* ]
|
||||
*/
|
||||
meta.lookup(&[a.into()], &[sl.into()]);
|
||||
meta.lookup(&[a.into(), b.into()], &[sl.into(), sl2.into()]);
|
||||
let a_ = meta.query_any(a.into(), Rotation::cur());
|
||||
let b_ = meta.query_any(b.into(), Rotation::cur());
|
||||
let sl_ = meta.query_any(sl.into(), Rotation::cur());
|
||||
let sl2_ = meta.query_any(sl2.into(), Rotation::cur());
|
||||
meta.lookup(&[a_.clone()], &[sl_.clone()]);
|
||||
meta.lookup(&[a_ * b_], &[sl_ * sl2_]);
|
||||
|
||||
meta.create_gate("Combined add-mult", |meta| {
|
||||
let d = meta.query_advice(d, Rotation::next());
|
||||
|
@ -905,39 +909,37 @@ fn test_proving() {
|
|||
],
|
||||
lookups: [
|
||||
Argument {
|
||||
input_columns: [
|
||||
Column {
|
||||
index: 1,
|
||||
column_type: Advice,
|
||||
},
|
||||
input_expressions: [
|
||||
Advice(
|
||||
0,
|
||||
),
|
||||
],
|
||||
table_columns: [
|
||||
Column {
|
||||
index: 6,
|
||||
column_type: Fixed,
|
||||
},
|
||||
table_expressions: [
|
||||
Fixed(
|
||||
0,
|
||||
),
|
||||
],
|
||||
},
|
||||
Argument {
|
||||
input_columns: [
|
||||
Column {
|
||||
index: 1,
|
||||
column_type: Advice,
|
||||
},
|
||||
Column {
|
||||
index: 2,
|
||||
column_type: Advice,
|
||||
},
|
||||
input_expressions: [
|
||||
Product(
|
||||
Advice(
|
||||
0,
|
||||
),
|
||||
Advice(
|
||||
1,
|
||||
),
|
||||
),
|
||||
],
|
||||
table_columns: [
|
||||
Column {
|
||||
index: 6,
|
||||
column_type: Fixed,
|
||||
},
|
||||
Column {
|
||||
index: 7,
|
||||
column_type: Fixed,
|
||||
},
|
||||
table_expressions: [
|
||||
Product(
|
||||
Fixed(
|
||||
0,
|
||||
),
|
||||
Fixed(
|
||||
1,
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -224,6 +224,8 @@ pub trait Circuit<F: Field> {
|
|||
/// Low-degree expression representing an identity that must hold over the committed columns.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Expression<F> {
|
||||
/// This is a constant polynomial
|
||||
Constant(F),
|
||||
/// This is a fixed column queried at a certain relative location
|
||||
Fixed(usize),
|
||||
/// This is an advice (witness) column queried at a certain relative location
|
||||
|
@ -243,6 +245,7 @@ impl<F: Field> Expression<F> {
|
|||
/// operations.
|
||||
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,
|
||||
|
@ -251,11 +254,13 @@ impl<F: Field> Expression<F> {
|
|||
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::Sum(a, b) => {
|
||||
let a = a.evaluate(
|
||||
constant,
|
||||
fixed_column,
|
||||
advice_column,
|
||||
instance_column,
|
||||
|
@ -264,6 +269,7 @@ impl<F: Field> Expression<F> {
|
|||
scaled,
|
||||
);
|
||||
let b = b.evaluate(
|
||||
constant,
|
||||
fixed_column,
|
||||
advice_column,
|
||||
instance_column,
|
||||
|
@ -275,6 +281,7 @@ impl<F: Field> Expression<F> {
|
|||
}
|
||||
Expression::Product(a, b) => {
|
||||
let a = a.evaluate(
|
||||
constant,
|
||||
fixed_column,
|
||||
advice_column,
|
||||
instance_column,
|
||||
|
@ -283,6 +290,7 @@ impl<F: Field> Expression<F> {
|
|||
scaled,
|
||||
);
|
||||
let b = b.evaluate(
|
||||
constant,
|
||||
fixed_column,
|
||||
advice_column,
|
||||
instance_column,
|
||||
|
@ -294,6 +302,7 @@ impl<F: Field> Expression<F> {
|
|||
}
|
||||
Expression::Scaled(a, f) => {
|
||||
let a = a.evaluate(
|
||||
constant,
|
||||
fixed_column,
|
||||
advice_column,
|
||||
instance_column,
|
||||
|
@ -309,6 +318,7 @@ impl<F: Field> Expression<F> {
|
|||
/// Compute the degree of this polynomial
|
||||
pub fn degree(&self) -> usize {
|
||||
match self {
|
||||
Expression::Constant(_) => 0,
|
||||
Expression::Fixed(_) => 1,
|
||||
Expression::Advice(_) => 1,
|
||||
Expression::Instance(_) => 1,
|
||||
|
@ -362,7 +372,7 @@ pub(crate) struct PointIndex(pub usize);
|
|||
/// This is a description of the circuit environment, such as the gate, column and
|
||||
/// permutation arrangements.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConstraintSystem<F> {
|
||||
pub struct ConstraintSystem<F: Field> {
|
||||
pub(crate) num_fixed_columns: usize,
|
||||
pub(crate) num_advice_columns: usize,
|
||||
pub(crate) num_instance_columns: usize,
|
||||
|
@ -376,8 +386,8 @@ pub struct ConstraintSystem<F> {
|
|||
pub(crate) permutations: Vec<permutation::Argument>,
|
||||
|
||||
// Vector of lookup arguments, where each corresponds to a sequence of
|
||||
// input columns and a sequence of table columns involved in the lookup.
|
||||
pub(crate) lookups: Vec<lookup::Argument>,
|
||||
// input expressions and a sequence of table expressions involved in the lookup.
|
||||
pub(crate) lookups: Vec<lookup::Argument<F>>,
|
||||
}
|
||||
|
||||
/// Represents the minimal parameters that determine a `ConstraintSystem`.
|
||||
|
@ -391,7 +401,7 @@ pub struct PinnedConstraintSystem<'a, F: Field> {
|
|||
instance_queries: &'a Vec<(Column<Instance>, Rotation)>,
|
||||
fixed_queries: &'a Vec<(Column<Fixed>, Rotation)>,
|
||||
permutations: &'a Vec<permutation::Argument>,
|
||||
lookups: &'a Vec<lookup::Argument>,
|
||||
lookups: &'a Vec<lookup::Argument<F>>,
|
||||
}
|
||||
|
||||
struct PinnedGates<'a, F: Field>(&'a Vec<(&'static str, Expression<F>)>);
|
||||
|
@ -451,26 +461,20 @@ impl<F: Field> ConstraintSystem<F> {
|
|||
index
|
||||
}
|
||||
|
||||
/// Add a lookup argument for some input columns and table columns.
|
||||
/// The function will panic if the number of input columns and table
|
||||
/// columns are not the same.
|
||||
/// Add a lookup argument for some input expressions and table expressions.
|
||||
/// The function will panic if the number of input expressions and table
|
||||
/// expressions are not the same.
|
||||
pub fn lookup(
|
||||
&mut self,
|
||||
input_columns: &[Column<Any>],
|
||||
table_columns: &[Column<Any>],
|
||||
input_expressions: &[Expression<F>],
|
||||
table_expressions: &[Expression<F>],
|
||||
) -> usize {
|
||||
assert_eq!(input_columns.len(), table_columns.len());
|
||||
assert_eq!(input_expressions.len(), table_expressions.len());
|
||||
|
||||
let index = self.lookups.len();
|
||||
|
||||
for input in input_columns {
|
||||
self.query_any_index(*input, Rotation::cur());
|
||||
}
|
||||
for table in table_columns {
|
||||
self.query_any_index(*table, Rotation::cur());
|
||||
}
|
||||
self.lookups
|
||||
.push(lookup::Argument::new(input_columns, table_columns));
|
||||
.push(lookup::Argument::new(input_expressions, table_expressions));
|
||||
|
||||
index
|
||||
}
|
||||
|
|
|
@ -1,30 +1,31 @@
|
|||
use super::circuit::{Any, Column};
|
||||
use super::circuit::Expression;
|
||||
use ff::Field;
|
||||
|
||||
pub(crate) mod prover;
|
||||
pub(crate) mod verifier;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Argument {
|
||||
pub input_columns: Vec<Column<Any>>,
|
||||
pub table_columns: Vec<Column<Any>>,
|
||||
pub(crate) struct Argument<F: Field> {
|
||||
pub input_expressions: Vec<Expression<F>>,
|
||||
pub table_expressions: Vec<Expression<F>>,
|
||||
}
|
||||
|
||||
impl Argument {
|
||||
pub fn new(input_columns: &[Column<Any>], table_columns: &[Column<Any>]) -> Self {
|
||||
assert_eq!(input_columns.len(), table_columns.len());
|
||||
impl<F: Field> Argument<F> {
|
||||
pub fn new(input_expressions: &[Expression<F>], table_expressions: &[Expression<F>]) -> Self {
|
||||
assert_eq!(input_expressions.len(), table_expressions.len());
|
||||
Argument {
|
||||
input_columns: input_columns.to_vec(),
|
||||
table_columns: table_columns.to_vec(),
|
||||
input_expressions: input_expressions.to_vec(),
|
||||
table_expressions: table_expressions.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn required_degree(&self) -> usize {
|
||||
assert_eq!(self.input_columns.len(), self.table_columns.len());
|
||||
assert_eq!(self.input_expressions.len(), self.table_expressions.len());
|
||||
|
||||
// degree 2:
|
||||
// l_0(X) * (1 - z'(X)) = 0
|
||||
//
|
||||
// degree 3:
|
||||
// degree (1 + input_degree + table_degree):
|
||||
// z'(X) (a'(X) + \beta) (s'(X) + \gamma)
|
||||
// - z'(\omega^{-1} X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta) (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma)
|
||||
//
|
||||
|
@ -33,6 +34,15 @@ impl Argument {
|
|||
//
|
||||
// degree 2:
|
||||
// (a′(X)−s′(X))⋅(a′(X)−a′(\omega{-1} X)) = 0
|
||||
3
|
||||
let mut input_degree = 1;
|
||||
for expr in self.input_expressions.iter() {
|
||||
input_degree = std::cmp::max(input_degree, expr.degree());
|
||||
}
|
||||
let mut table_degree = 1;
|
||||
for expr in self.table_expressions.iter() {
|
||||
table_degree = std::cmp::max(table_degree, expr.degree());
|
||||
}
|
||||
|
||||
1 + input_degree + table_degree
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::super::{
|
||||
circuit::{Any, Column},
|
||||
ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, Error, ProvingKey,
|
||||
circuit::Expression, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, Error,
|
||||
ProvingKey,
|
||||
};
|
||||
use super::Argument;
|
||||
use crate::{
|
||||
|
@ -13,21 +13,25 @@ use crate::{
|
|||
transcript::TranscriptWrite,
|
||||
};
|
||||
use ff::Field;
|
||||
use std::{collections::BTreeMap, iter};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
iter,
|
||||
ops::{Mul, MulAssign},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(in crate::plonk) struct Permuted<'a, C: CurveAffine> {
|
||||
unpermuted_input_columns: Vec<&'a Polynomial<C::Scalar, LagrangeCoeff>>,
|
||||
unpermuted_input_cosets: Vec<&'a Polynomial<C::Scalar, ExtendedLagrangeCoeff>>,
|
||||
permuted_input_column: Polynomial<C::Scalar, LagrangeCoeff>,
|
||||
pub(in crate::plonk) struct Permuted<C: CurveAffine> {
|
||||
unpermuted_input_expressions: Vec<Polynomial<C::Scalar, LagrangeCoeff>>,
|
||||
unpermuted_input_cosets: Vec<Polynomial<C::Scalar, ExtendedLagrangeCoeff>>,
|
||||
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_columns: Vec<&'a Polynomial<C::Scalar, LagrangeCoeff>>,
|
||||
unpermuted_table_cosets: Vec<&'a Polynomial<C::Scalar, ExtendedLagrangeCoeff>>,
|
||||
permuted_table_column: Polynomial<C::Scalar, LagrangeCoeff>,
|
||||
unpermuted_table_expressions: Vec<Polynomial<C::Scalar, LagrangeCoeff>>,
|
||||
unpermuted_table_cosets: Vec<Polynomial<C::Scalar, ExtendedLagrangeCoeff>>,
|
||||
permuted_table_expression: Polynomial<C::Scalar, LagrangeCoeff>,
|
||||
permuted_table_poly: Polynomial<C::Scalar, Coeff>,
|
||||
permuted_table_coset: Polynomial<C::Scalar, ExtendedLagrangeCoeff>,
|
||||
permuted_table_blind: Blind<C::Scalar>,
|
||||
|
@ -35,8 +39,8 @@ pub(in crate::plonk) struct Permuted<'a, C: CurveAffine> {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(in crate::plonk) struct Committed<'a, C: CurveAffine> {
|
||||
permuted: Permuted<'a, C>,
|
||||
pub(in crate::plonk) struct Committed<C: CurveAffine> {
|
||||
permuted: Permuted<C>,
|
||||
product_poly: Polynomial<C::Scalar, Coeff>,
|
||||
product_coset: Polynomial<C::Scalar, ExtendedLagrangeCoeff>,
|
||||
product_inv_coset: Polynomial<C::Scalar, ExtendedLagrangeCoeff>,
|
||||
|
@ -57,17 +61,17 @@ pub(in crate::plonk) struct Evaluated<C: CurveAffine> {
|
|||
constructed: Constructed<C>,
|
||||
}
|
||||
|
||||
impl Argument {
|
||||
/// Given a Lookup with input columns [A_0, A_1, ..., A_{m-1}] and table columns
|
||||
impl<F: FieldExt> Argument<F> {
|
||||
/// Given a Lookup with input expressions [A_0, A_1, ..., A_{m-1}] and table expressions
|
||||
/// [S_0, S_1, ..., S_{m-1}], this method
|
||||
/// - constructs A_compressed = \theta^{m-1} A_0 + theta^{m-2} A_1 + ... + \theta A_{m-2} + A_{m-1}
|
||||
/// and S_compressed = \theta^{m-1} S_0 + theta^{m-2} S_1 + ... + \theta S_{m-2} + S_{m-1},
|
||||
/// - permutes A_compressed and S_compressed using permute_column_pair() helper,
|
||||
/// - permutes A_compressed and S_compressed using permute_expression_pair() helper,
|
||||
/// obtaining A' and S', and
|
||||
/// - constructs Permuted<C> struct using permuted_input_value = A', and
|
||||
/// permuted_table_column = S'.
|
||||
/// permuted_table_expression = S'.
|
||||
/// The Permuted<C> struct is used to update the Lookup, and is then returned.
|
||||
pub(in crate::plonk) fn commit_permuted<'a, C: CurveAffine, T: TranscriptWrite<C>>(
|
||||
pub(in crate::plonk) fn commit_permuted<'a, C, T: TranscriptWrite<C>>(
|
||||
&self,
|
||||
pk: &ProvingKey<C>,
|
||||
params: &Params<C>,
|
||||
|
@ -80,60 +84,115 @@ impl Argument {
|
|||
fixed_cosets: &'a [Polynomial<C::Scalar, ExtendedLagrangeCoeff>],
|
||||
instance_cosets: &'a [Polynomial<C::Scalar, ExtendedLagrangeCoeff>],
|
||||
transcript: &mut T,
|
||||
) -> Result<Permuted<'a, C>, Error> {
|
||||
// Closure to get values of columns and compress them
|
||||
let compress_columns = |columns: &[Column<Any>]| {
|
||||
// Values of input columns involved in the lookup
|
||||
let (unpermuted_columns, unpermuted_cosets): (Vec<_>, Vec<_>) = columns
|
||||
) -> Result<Permuted<C>, Error>
|
||||
where
|
||||
C: CurveAffine<Scalar = F>,
|
||||
C::Projective: Mul<F, Output = C::Projective> + MulAssign<F>,
|
||||
{
|
||||
// Closure to get values of expressions and compress them
|
||||
let compress_expressions = |expressions: &[Expression<C::Scalar>]| {
|
||||
// Values of input expressions involved in the lookup
|
||||
let unpermuted_expressions: Vec<_> = expressions
|
||||
.iter()
|
||||
.map(|&column| {
|
||||
let (values, cosets) = match column.column_type() {
|
||||
Any::Advice => (advice_values, advice_cosets),
|
||||
Any::Fixed => (fixed_values, fixed_cosets),
|
||||
Any::Instance => (instance_values, instance_cosets),
|
||||
};
|
||||
(
|
||||
&values[column.index()],
|
||||
&cosets[pk.vk.cs.get_any_query_index(column, Rotation::cur())],
|
||||
.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;
|
||||
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;
|
||||
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;
|
||||
instance_values[column_index].clone().rotate(rotation)
|
||||
},
|
||||
&|a, b| a + &b,
|
||||
&|a, b| {
|
||||
let mut modified_a = vec![C::Scalar::one(); params.n as usize];
|
||||
parallelize(&mut modified_a, |modified_a, start| {
|
||||
for ((modified_a, a), b) in modified_a
|
||||
.iter_mut()
|
||||
.zip(a[start..].iter())
|
||||
.zip(b[start..].iter())
|
||||
{
|
||||
*modified_a *= *a * b;
|
||||
}
|
||||
});
|
||||
pk.vk.domain.lagrange_from_vec(modified_a)
|
||||
},
|
||||
&|a, scalar| a * scalar,
|
||||
)
|
||||
})
|
||||
.unzip();
|
||||
.collect();
|
||||
|
||||
// Compressed version of columns
|
||||
let compressed_column = unpermuted_columns
|
||||
let unpermuted_cosets: Vec<_> = expressions
|
||||
.iter()
|
||||
.fold(domain.empty_lagrange(), |acc, column| acc * *theta + column);
|
||||
.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(),
|
||||
&|a, b| a + &b,
|
||||
&|a, b| a * &b,
|
||||
&|a, scalar| a * scalar,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
(unpermuted_columns, unpermuted_cosets, compressed_column)
|
||||
// Compressed version of expressions
|
||||
let compressed_expression = unpermuted_expressions
|
||||
.iter()
|
||||
.fold(domain.empty_lagrange(), |acc, expression| {
|
||||
acc * *theta + expression
|
||||
});
|
||||
|
||||
(
|
||||
unpermuted_expressions,
|
||||
unpermuted_cosets,
|
||||
compressed_expression,
|
||||
)
|
||||
};
|
||||
|
||||
// Closure to construct commitment to column of values
|
||||
let commit_column = |column: &Polynomial<C::Scalar, LagrangeCoeff>| {
|
||||
let poly = pk.vk.domain.lagrange_to_coeff(column.clone());
|
||||
// Closure to construct commitment to vector of values
|
||||
let commit_values = |values: &Polynomial<C::Scalar, LagrangeCoeff>| {
|
||||
let poly = pk.vk.domain.lagrange_to_coeff(values.clone());
|
||||
let blind = Blind(C::Scalar::rand());
|
||||
let commitment = params.commit_lagrange(&column, blind).to_affine();
|
||||
let commitment = params.commit_lagrange(&values, blind).to_affine();
|
||||
(poly, blind, commitment)
|
||||
};
|
||||
|
||||
// Get values of input columns involved in the lookup and compress them
|
||||
let (unpermuted_input_columns, unpermuted_input_cosets, compressed_input_column) =
|
||||
compress_columns(&self.input_columns);
|
||||
// Get values of input expressions involved in the lookup and compress them
|
||||
let (unpermuted_input_expressions, unpermuted_input_cosets, compressed_input_expression) =
|
||||
compress_expressions(&self.input_expressions);
|
||||
|
||||
// Get values of table columns involved in the lookup and compress them
|
||||
let (unpermuted_table_columns, unpermuted_table_cosets, compressed_table_column) =
|
||||
compress_columns(&self.table_columns);
|
||||
// Get values of table expressions involved in the lookup and compress them
|
||||
let (unpermuted_table_expressions, unpermuted_table_cosets, compressed_table_expression) =
|
||||
compress_expressions(&self.table_expressions);
|
||||
|
||||
// Permute compressed (InputColumn, TableColumn) pair
|
||||
let (permuted_input_column, permuted_table_column) =
|
||||
permute_column_pair::<C>(domain, &compressed_input_column, &compressed_table_column)?;
|
||||
// Permute compressed (InputExpression, TableExpression) pair
|
||||
let (permuted_input_expression, permuted_table_expression) = permute_expression_pair::<C>(
|
||||
domain,
|
||||
&compressed_input_expression,
|
||||
&compressed_table_expression,
|
||||
)?;
|
||||
|
||||
// Commit to permuted input column
|
||||
// Commit to permuted input expression
|
||||
let (permuted_input_poly, permuted_input_blind, permuted_input_commitment) =
|
||||
commit_column(&permuted_input_column);
|
||||
commit_values(&permuted_input_expression);
|
||||
|
||||
// Commit to permuted table column
|
||||
// Commit to permuted table expression
|
||||
let (permuted_table_poly, permuted_table_blind, permuted_table_commitment) =
|
||||
commit_column(&permuted_table_column);
|
||||
commit_values(&permuted_table_expression);
|
||||
|
||||
// Hash permuted input commitment
|
||||
transcript
|
||||
|
@ -159,17 +218,17 @@ impl Argument {
|
|||
.coeff_to_extended(permuted_table_poly.clone(), Rotation::cur());
|
||||
|
||||
Ok(Permuted {
|
||||
unpermuted_input_columns,
|
||||
unpermuted_input_expressions,
|
||||
unpermuted_input_cosets,
|
||||
permuted_input_column,
|
||||
permuted_input_expression,
|
||||
permuted_input_poly,
|
||||
permuted_input_coset,
|
||||
permuted_input_inv_coset,
|
||||
permuted_input_blind,
|
||||
permuted_input_commitment,
|
||||
unpermuted_table_columns,
|
||||
unpermuted_table_expressions,
|
||||
unpermuted_table_cosets,
|
||||
permuted_table_column,
|
||||
permuted_table_expression,
|
||||
permuted_table_poly,
|
||||
permuted_table_coset,
|
||||
permuted_table_blind,
|
||||
|
@ -178,9 +237,9 @@ impl Argument {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CurveAffine> Permuted<'a, C> {
|
||||
/// Given a Lookup with input columns, table columns, and the permuted
|
||||
/// input column and permuted table column, this method constructs the
|
||||
impl<C: CurveAffine> Permuted<C> {
|
||||
/// Given a Lookup with input expressions, table expressions, and the permuted
|
||||
/// input expression and permuted table expression, this method constructs the
|
||||
/// grand product polynomial over the lookup. The grand product polynomial
|
||||
/// is used to populate the Product<C> struct. The Product<C> struct is
|
||||
/// added to the Lookup and finally returned by the method.
|
||||
|
@ -192,25 +251,25 @@ impl<'a, C: CurveAffine> Permuted<'a, C> {
|
|||
beta: ChallengeBeta<C>,
|
||||
gamma: ChallengeGamma<C>,
|
||||
transcript: &mut T,
|
||||
) -> Result<Committed<'a, C>, Error> {
|
||||
) -> Result<Committed<C>, Error> {
|
||||
// Goal is to compute the products of fractions
|
||||
//
|
||||
// Numerator: (\theta^{m-1} a_0(\omega^i) + \theta^{m-2} a_1(\omega^i) + ... + \theta a_{m-2}(\omega^i) + a_{m-1}(\omega^i) + \beta)
|
||||
// * (\theta^{m-1} s_0(\omega^i) + \theta^{m-2} s_1(\omega^i) + ... + \theta s_{m-2}(\omega^i) + s_{m-1}(\omega^i) + \gamma)
|
||||
// Denominator: (a'(\omega^i) + \beta) (s'(\omega^i) + \gamma)
|
||||
//
|
||||
// where a_j(X) is the jth input column in this lookup,
|
||||
// where a'(X) is the compression of the permuted input columns,
|
||||
// s_j(X) is the jth table column in this lookup,
|
||||
// s'(X) is the compression of the permuted table columns,
|
||||
// and i is the ith row of the column.
|
||||
// where a_j(X) is the jth input expression in this lookup,
|
||||
// where a'(X) is the compression of the permuted input expressions,
|
||||
// s_j(X) is the jth table expression in this lookup,
|
||||
// s'(X) is the compression of the permuted table expressions,
|
||||
// and i is the ith row of the expression.
|
||||
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 expression and permuted table expression
|
||||
parallelize(&mut lookup_product, |lookup_product, start| {
|
||||
for ((lookup_product, permuted_input_value), permuted_table_value) in lookup_product
|
||||
.iter_mut()
|
||||
.zip(self.permuted_input_column[start..].iter())
|
||||
.zip(self.permuted_table_column[start..].iter())
|
||||
.zip(self.permuted_input_expression[start..].iter())
|
||||
.zip(self.permuted_table_expression[start..].iter())
|
||||
{
|
||||
*lookup_product = (*beta + permuted_input_value) * &(*gamma + permuted_table_value);
|
||||
}
|
||||
|
@ -227,18 +286,18 @@ impl<'a, C: CurveAffine> Permuted<'a, C> {
|
|||
for (i, product) in product.iter_mut().enumerate() {
|
||||
let i = i + start;
|
||||
|
||||
// Compress unpermuted input columns
|
||||
// Compress unpermuted input expressions
|
||||
let mut input_term = C::Scalar::zero();
|
||||
for unpermuted_input_column in self.unpermuted_input_columns.iter() {
|
||||
for unpermuted_input_expression in self.unpermuted_input_expressions.iter() {
|
||||
input_term *= &*theta;
|
||||
input_term += &unpermuted_input_column[i];
|
||||
input_term += &unpermuted_input_expression[i];
|
||||
}
|
||||
|
||||
// Compress unpermuted table columns
|
||||
// Compress unpermuted table expressions
|
||||
let mut table_term = C::Scalar::zero();
|
||||
for unpermuted_table_column in self.unpermuted_table_columns.iter() {
|
||||
for unpermuted_table_expression in self.unpermuted_table_expressions.iter() {
|
||||
table_term *= &*theta;
|
||||
table_term += &unpermuted_table_column[i];
|
||||
table_term += &unpermuted_table_expression[i];
|
||||
}
|
||||
|
||||
*product *= &(input_term + &*beta);
|
||||
|
@ -252,12 +311,12 @@ impl<'a, C: CurveAffine> Permuted<'a, C> {
|
|||
// * (\theta^{m-1} s_0(\omega^i) + \theta^{m-2} s_1(\omega^i) + ... + \theta s_{m-2}(\omega^i) + s_{m-1}(\omega^i) + \gamma)
|
||||
// Denominator: (a'(\omega^i) + \beta) (s'(\omega^i) + \gamma)
|
||||
//
|
||||
// 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,
|
||||
// s_j(\omega^i) is the jth table column in this lookup,
|
||||
// s'(\omega^i) is the permuted table column,
|
||||
// and i is the ith row of the column.
|
||||
// where there are m input expressions and m table expressions,
|
||||
// a_j(\omega^i) is the jth input expression in this lookup,
|
||||
// a'j(\omega^i) is the permuted input expression,
|
||||
// s_j(\omega^i) is the jth table expression in this lookup,
|
||||
// s'(\omega^i) is the permuted table expression,
|
||||
// and i is the ith row of the expression.
|
||||
|
||||
// Compute the evaluations of the lookup product polynomial
|
||||
// over our domain, starting with z[0] = 1
|
||||
|
@ -283,21 +342,21 @@ impl<'a, C: CurveAffine> Permuted<'a, C> {
|
|||
let prev_idx = (n + i - 1) % n;
|
||||
|
||||
let mut left = z[i];
|
||||
let permuted_input_value = &self.permuted_input_column[i];
|
||||
let permuted_input_value = &self.permuted_input_expression[i];
|
||||
|
||||
let permuted_table_value = &self.permuted_table_column[i];
|
||||
let permuted_table_value = &self.permuted_table_expression[i];
|
||||
|
||||
left *= &(*beta + permuted_input_value);
|
||||
left *= &(*gamma + permuted_table_value);
|
||||
|
||||
let mut right = z[prev_idx];
|
||||
let mut input_term = self
|
||||
.unpermuted_input_columns
|
||||
.unpermuted_input_expressions
|
||||
.iter()
|
||||
.fold(C::Scalar::zero(), |acc, input| acc * &*theta + &input[i]);
|
||||
|
||||
let mut table_term = self
|
||||
.unpermuted_table_columns
|
||||
.unpermuted_table_expressions
|
||||
.iter()
|
||||
.fold(C::Scalar::zero(), |acc, table| acc * &*theta + &table[i]);
|
||||
|
||||
|
@ -320,7 +379,7 @@ impl<'a, C: CurveAffine> Permuted<'a, C> {
|
|||
.write_point(product_commitment)
|
||||
.map_err(|_| Error::TranscriptError)?;
|
||||
|
||||
Ok(Committed::<'a, C> {
|
||||
Ok(Committed::<C> {
|
||||
permuted: self,
|
||||
product_poly: z,
|
||||
product_coset,
|
||||
|
@ -331,9 +390,9 @@ impl<'a, C: CurveAffine> Permuted<'a, C> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CurveAffine> Committed<'a, C> {
|
||||
/// Given a Lookup with input columns, table columns, permuted input
|
||||
/// column, permuted table column, and grand product polynomial, this
|
||||
impl<'a, C: CurveAffine> Committed<C> {
|
||||
/// Given a Lookup with input expressions, table expressions, permuted input
|
||||
/// expression, permuted table expression, and grand product polynomial, this
|
||||
/// method constructs constraints that must hold between these values.
|
||||
/// This method returns the constraints as a vector of polynomials in
|
||||
/// the extended evaluation domain.
|
||||
|
@ -376,14 +435,14 @@ impl<'a, C: CurveAffine> Committed<'a, C> {
|
|||
for (i, right) in right.iter_mut().enumerate() {
|
||||
let i = i + start;
|
||||
|
||||
// Compress the unpermuted input columns
|
||||
// Compress the unpermuted input expressions
|
||||
let mut input_term = C::Scalar::zero();
|
||||
for input in permuted.unpermuted_input_cosets.iter() {
|
||||
input_term *= &*theta;
|
||||
input_term += &input[i];
|
||||
}
|
||||
|
||||
// Compress the unpermuted table columns
|
||||
// Compress the unpermuted table expressions
|
||||
let mut table_term = C::Scalar::zero();
|
||||
for table in permuted.unpermuted_table_cosets.iter() {
|
||||
table_term *= &*theta;
|
||||
|
@ -398,15 +457,15 @@ impl<'a, C: CurveAffine> Committed<'a, C> {
|
|||
|
||||
Some(left - &right)
|
||||
})
|
||||
// Check that the first values in the permuted input column and permuted
|
||||
// fixed column are the same.
|
||||
// Check that the first values in the permuted input expression and permuted
|
||||
// fixed expression are the same.
|
||||
// l_0(X) * (a'(X) - s'(X)) = 0
|
||||
.chain(Some(
|
||||
(permuted.permuted_input_coset.clone() - &permuted.permuted_table_coset) * &pk.l0,
|
||||
))
|
||||
// Check that each value in the permuted lookup input column is either
|
||||
// Check that each value in the permuted lookup input expression is either
|
||||
// equal to the value above it, or the value at the same index in the
|
||||
// permuted table column.
|
||||
// permuted table expression.
|
||||
// (a′(X)−s′(X))⋅(a′(X)−a′(\omega{-1} X)) = 0
|
||||
.chain(Some(
|
||||
(permuted.permuted_input_coset.clone() - &permuted.permuted_table_coset)
|
||||
|
@ -502,39 +561,41 @@ impl<C: CurveAffine> Evaluated<C> {
|
|||
}
|
||||
}
|
||||
|
||||
type ColumnPair<F> = (Polynomial<F, LagrangeCoeff>, Polynomial<F, LagrangeCoeff>);
|
||||
type ExpressionPair<F> = (Polynomial<F, LagrangeCoeff>, Polynomial<F, LagrangeCoeff>);
|
||||
|
||||
/// Given a column of input values A and a column of table values S,
|
||||
/// Given a vector of input values A and a vector of table values S,
|
||||
/// this method permutes A and S to produce A' and S', such that:
|
||||
/// - like values in A' are vertically adjacent to each other; and
|
||||
/// - the first row in a sequence of like values in A' is the row
|
||||
/// that has the corresponding value in S'.
|
||||
/// This method returns (A', S') if no errors are encountered.
|
||||
fn permute_column_pair<C: CurveAffine>(
|
||||
fn permute_expression_pair<C: CurveAffine>(
|
||||
domain: &EvaluationDomain<C::Scalar>,
|
||||
input_column: &Polynomial<C::Scalar, LagrangeCoeff>,
|
||||
table_column: &Polynomial<C::Scalar, LagrangeCoeff>,
|
||||
) -> Result<ColumnPair<C::Scalar>, Error> {
|
||||
let mut permuted_input_column = input_column.clone();
|
||||
input_expression: &Polynomial<C::Scalar, LagrangeCoeff>,
|
||||
table_expression: &Polynomial<C::Scalar, LagrangeCoeff>,
|
||||
) -> Result<ExpressionPair<C::Scalar>, Error> {
|
||||
let mut permuted_input_expression = input_expression.clone();
|
||||
|
||||
// Sort input lookup column values
|
||||
permuted_input_column.sort();
|
||||
// Sort input lookup expression values
|
||||
permuted_input_expression.sort();
|
||||
|
||||
// A BTreeMap of each unique element in the table column and its count
|
||||
// A BTreeMap of each unique element in the table expression and its count
|
||||
let mut leftover_table_map: BTreeMap<C::Scalar, u32> =
|
||||
table_column.iter().fold(BTreeMap::new(), |mut acc, coeff| {
|
||||
*acc.entry(*coeff).or_insert(0) += 1;
|
||||
acc
|
||||
});
|
||||
let mut permuted_table_coeffs = vec![C::Scalar::zero(); table_column.len()];
|
||||
table_expression
|
||||
.iter()
|
||||
.fold(BTreeMap::new(), |mut acc, coeff| {
|
||||
*acc.entry(*coeff).or_insert(0) += 1;
|
||||
acc
|
||||
});
|
||||
let mut permuted_table_coeffs = vec![C::Scalar::zero(); table_expression.len()];
|
||||
|
||||
let mut repeated_input_rows = permuted_input_column
|
||||
let mut repeated_input_rows = permuted_input_expression
|
||||
.iter()
|
||||
.zip(permuted_table_coeffs.iter_mut())
|
||||
.enumerate()
|
||||
.filter_map(|(row, (input_value, table_value))| {
|
||||
// If this is the first occurence of `input_value` in the input column
|
||||
if row == 0 || *input_value != permuted_input_column[row - 1] {
|
||||
// If this is the first occurence of `input_value` in the input expression
|
||||
if row == 0 || *input_value != permuted_input_expression[row - 1] {
|
||||
*table_value = *input_value;
|
||||
// Remove one instance of input_value from leftover_table_map
|
||||
if let Some(count) = leftover_table_map.get_mut(&input_value) {
|
||||
|
@ -560,11 +621,11 @@ fn permute_column_pair<C: CurveAffine>(
|
|||
}
|
||||
assert!(repeated_input_rows.is_empty());
|
||||
|
||||
let mut permuted_table_column = domain.empty_lagrange();
|
||||
let mut permuted_table_expression = domain.empty_lagrange();
|
||||
parallelize(
|
||||
&mut permuted_table_column,
|
||||
|permuted_table_column, start| {
|
||||
for (permuted_table_value, permuted_table_coeff) in permuted_table_column
|
||||
&mut permuted_table_expression,
|
||||
|permuted_table_expression, start| {
|
||||
for (permuted_table_value, permuted_table_coeff) in permuted_table_expression
|
||||
.iter_mut()
|
||||
.zip(permuted_table_coeffs[start..].iter())
|
||||
{
|
||||
|
@ -573,5 +634,5 @@ fn permute_column_pair<C: CurveAffine>(
|
|||
},
|
||||
);
|
||||
|
||||
Ok((permuted_input_column, permuted_table_column))
|
||||
Ok((permuted_input_expression, permuted_table_expression))
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use std::iter;
|
||||
|
||||
use super::super::circuit::{Any, Column};
|
||||
use super::super::circuit::Expression;
|
||||
use super::Argument;
|
||||
use crate::{
|
||||
arithmetic::CurveAffine,
|
||||
arithmetic::{CurveAffine, FieldExt},
|
||||
plonk::{ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, Error, VerifyingKey},
|
||||
poly::{multiopen::VerifierQuery, Rotation},
|
||||
transcript::TranscriptRead,
|
||||
|
@ -29,7 +29,7 @@ pub struct Evaluated<C: CurveAffine> {
|
|||
permuted_table_eval: C::Scalar,
|
||||
}
|
||||
|
||||
impl Argument {
|
||||
impl<F: FieldExt> Argument<F> {
|
||||
pub(in crate::plonk) fn read_permuted_commitments<C: CurveAffine, T: TranscriptRead<C>>(
|
||||
&self,
|
||||
transcript: &mut T,
|
||||
|
@ -99,9 +99,8 @@ impl<C: CurveAffine> Committed<C> {
|
|||
impl<C: CurveAffine> Evaluated<C> {
|
||||
pub(in crate::plonk) fn expressions<'a>(
|
||||
&'a self,
|
||||
vk: &'a VerifyingKey<C>,
|
||||
l_0: C::Scalar,
|
||||
argument: &'a Argument,
|
||||
argument: &'a Argument<C::Scalar>,
|
||||
theta: ChallengeTheta<C>,
|
||||
beta: ChallengeBeta<C>,
|
||||
gamma: ChallengeGamma<C>,
|
||||
|
@ -116,22 +115,25 @@ impl<C: CurveAffine> Evaluated<C> {
|
|||
* &(self.permuted_input_eval + &*beta)
|
||||
* &(self.permuted_table_eval + &*gamma);
|
||||
|
||||
let compress_columns = |columns: &[Column<Any>]| {
|
||||
columns
|
||||
let compress_expressions = |expressions: &[Expression<C::Scalar>]| {
|
||||
expressions
|
||||
.iter()
|
||||
.map(|column| {
|
||||
let index = vk.cs.get_any_query_index(*column, Rotation::cur());
|
||||
match column.column_type() {
|
||||
Any::Advice => advice_evals[index],
|
||||
Any::Fixed => fixed_evals[index],
|
||||
Any::Instance => instance_evals[index],
|
||||
}
|
||||
.map(|expression| {
|
||||
expression.evaluate(
|
||||
&|scalar| scalar,
|
||||
&|index| fixed_evals[index],
|
||||
&|index| advice_evals[index],
|
||||
&|index| instance_evals[index],
|
||||
&|a, b| a + &b,
|
||||
&|a, b| a * &b,
|
||||
&|a, scalar| a * scalar,
|
||||
)
|
||||
})
|
||||
.fold(C::Scalar::zero(), |acc, eval| acc * &*theta + &eval)
|
||||
};
|
||||
let right = self.product_inv_eval
|
||||
* &(compress_columns(&argument.input_columns) + &*beta)
|
||||
* &(compress_columns(&argument.table_columns) + &*gamma);
|
||||
* &(compress_expressions(&argument.input_expressions) + &*beta)
|
||||
* &(compress_expressions(&argument.table_expressions) + &*gamma);
|
||||
|
||||
left - &right
|
||||
};
|
||||
|
|
|
@ -246,7 +246,7 @@ pub fn create_proof<C: CurveAffine, T: TranscriptWrite<C>, ConcreteCircuit: Circ
|
|||
// Sample theta challenge for keeping lookup columns linearly independent
|
||||
let theta = ChallengeTheta::get(transcript);
|
||||
|
||||
let lookups: Vec<Vec<lookup::prover::Permuted<'_, C>>> = instance
|
||||
let lookups: Vec<Vec<lookup::prover::Permuted<C>>> = instance
|
||||
.iter()
|
||||
.zip(advice.iter())
|
||||
.map(|(instance, advice)| -> Result<Vec<_>, Error> {
|
||||
|
@ -307,7 +307,7 @@ pub fn create_proof<C: CurveAffine, T: TranscriptWrite<C>, ConcreteCircuit: Circ
|
|||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let lookups: Vec<Vec<lookup::prover::Committed<'_, C>>> = lookups
|
||||
let lookups: Vec<Vec<lookup::prover::Committed<C>>> = lookups
|
||||
.into_iter()
|
||||
.map(|lookups| -> Result<Vec<_>, _> {
|
||||
// Construct and commit to products for each lookup
|
||||
|
@ -373,6 +373,7 @@ pub fn create_proof<C: CurveAffine, T: TranscriptWrite<C>, ConcreteCircuit: Circ
|
|||
// Custom constraints
|
||||
.chain(meta.gates.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(),
|
||||
|
|
|
@ -168,6 +168,7 @@ pub fn verify_proof<'a, C: CurveAffine, T: TranscriptRead<C>>(
|
|||
// Evaluate the circuit using the custom gates provided
|
||||
.chain(vk.cs.gates.iter().map(move |(_, poly)| {
|
||||
poly.evaluate(
|
||||
&|scalar| scalar,
|
||||
&|index| fixed_evals[index],
|
||||
&|index| advice_evals[index],
|
||||
&|index| instance_evals[index],
|
||||
|
@ -201,7 +202,6 @@ pub fn verify_proof<'a, C: CurveAffine, T: TranscriptRead<C>>(
|
|||
.zip(vk.cs.lookups.iter())
|
||||
.flat_map(move |(p, argument)| {
|
||||
p.expressions(
|
||||
vk,
|
||||
l_0,
|
||||
argument,
|
||||
theta,
|
||||
|
|
16
src/poly.rs
16
src/poly.rs
|
@ -187,6 +187,22 @@ impl<'a, F: Field> Mul<&'a Polynomial<F, ExtendedLagrangeCoeff>>
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, F: Field> Polynomial<F, LagrangeCoeff> {
|
||||
/// Rotates the values in a Lagrange basis polynomial by `Rotation`
|
||||
pub fn rotate(&self, rotation: Rotation) -> Polynomial<F, LagrangeCoeff> {
|
||||
let mut values = self.values.clone();
|
||||
if rotation.0 < 0 {
|
||||
values.rotate_right((-rotation.0) as usize);
|
||||
} else {
|
||||
values.rotate_left(rotation.0 as usize);
|
||||
}
|
||||
Polynomial {
|
||||
values,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, F: Field, B: Basis> Mul<F> for Polynomial<F, B> {
|
||||
type Output = Polynomial<F, B>;
|
||||
|
||||
|
|
|
@ -177,6 +177,14 @@ impl<G: Group> EvaluationDomain<G> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns a constant polynomial in the Lagrange coefficient basis
|
||||
pub fn constant_lagrange(&self, scalar: G) -> Polynomial<G, LagrangeCoeff> {
|
||||
Polynomial {
|
||||
values: vec![scalar; self.n as usize],
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an empty (zero) polynomial in the extended Lagrange coefficient
|
||||
/// basis
|
||||
pub fn empty_extended(&self) -> Polynomial<G, ExtendedLagrangeCoeff> {
|
||||
|
@ -186,6 +194,15 @@ impl<G: Group> EvaluationDomain<G> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns a constant polynomial in the extended Lagrange coefficient
|
||||
/// basis
|
||||
pub fn constant_extended(&self, scalar: G) -> Polynomial<G, ExtendedLagrangeCoeff> {
|
||||
Polynomial {
|
||||
values: vec![scalar; self.extended_len()],
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// This takes us from an n-length vector into the coefficient form.
|
||||
///
|
||||
/// This function will panic if the provided vector is not the correct
|
||||
|
@ -396,3 +413,40 @@ pub struct PinnedEvaluationDomain<'a, G: Group> {
|
|||
extended_k: &'a u32,
|
||||
omega: &'a G::Scalar,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rotate() {
|
||||
use crate::arithmetic::eval_polynomial;
|
||||
use crate::pasta::pallas::Scalar;
|
||||
let domain = EvaluationDomain::<Scalar>::new(1, 3);
|
||||
|
||||
let mut poly = domain.empty_lagrange();
|
||||
assert_eq!(poly.len(), 8);
|
||||
for value in poly.iter_mut() {
|
||||
*value = Scalar::rand();
|
||||
}
|
||||
|
||||
let poly_rotated_cur = poly.rotate(Rotation::cur());
|
||||
let poly_rotated_next = poly.rotate(Rotation::next());
|
||||
let poly_rotated_prev = poly.rotate(Rotation::prev());
|
||||
|
||||
let poly = domain.lagrange_to_coeff(poly);
|
||||
let poly_rotated_cur = domain.lagrange_to_coeff(poly_rotated_cur);
|
||||
let poly_rotated_next = domain.lagrange_to_coeff(poly_rotated_next);
|
||||
let poly_rotated_prev = domain.lagrange_to_coeff(poly_rotated_prev);
|
||||
|
||||
let x = Scalar::rand();
|
||||
|
||||
assert_eq!(
|
||||
eval_polynomial(&poly[..], x),
|
||||
eval_polynomial(&poly_rotated_cur[..], x)
|
||||
);
|
||||
assert_eq!(
|
||||
eval_polynomial(&poly[..], x * domain.omega),
|
||||
eval_polynomial(&poly_rotated_next[..], x)
|
||||
);
|
||||
assert_eq!(
|
||||
eval_polynomial(&poly[..], x * domain.omega_inv),
|
||||
eval_polynomial(&poly_rotated_prev[..], x)
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue