Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSM: Bring in quotient polynomial and constraints #1839

Merged
merged 14 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion kimchi/src/circuits/berkeley_columns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub enum Column {
}

impl GenericColumn for Column {
fn domain(&self) -> Domain {
fn column_domain(&self) -> Domain {
match self {
Column::Index(GateType::Generic) => Domain::D4,
Column::Index(GateType::CompleteAdd) => Domain::D4,
Expand Down
9 changes: 6 additions & 3 deletions kimchi/src/circuits/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,10 @@ fn unnormalized_lagrange_basis<F: FftField>(domain: &D<F>, i: i32, pt: &F) -> F
}

pub trait GenericColumn {
fn domain(&self) -> Domain;
// TODO These two traits must work together but it is NOT obvious. Change interface.
/// Defines the domain over which the column is evaluated, as
/// contained in the `ColumnEnvironment`.
fn column_domain(&self) -> Domain;
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
Expand Down Expand Up @@ -969,7 +972,7 @@ impl<C, Column> Expr<C, Column> {
Expr::Atom(ExprInner::Constant(c))
}

fn degree(&self, d1_size: u64, zk_rows: u64) -> u64 {
pub fn degree(&self, d1_size: u64, zk_rows: u64) -> u64 {
use ExprInner::*;
use Operations::*;
match self {
Expand Down Expand Up @@ -1981,7 +1984,7 @@ impl<F: FftField, Column: Copy + GenericColumn> Expr<F, Column> {
}
};
EvalResult::SubEvals {
domain: col.domain(),
domain: col.column_domain(),
shift: row.shift(),
evals,
}
Expand Down
77 changes: 77 additions & 0 deletions msm/src/column_env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use ark_ff::FftField;
use ark_poly::{Evaluations, Radix2EvaluationDomain};

use kimchi::circuits::domains::EvaluationDomains;
use kimchi::circuits::expr::{Challenges, ColumnEnvironment, Constants, Domain};

/// The collection of polynomials (all in evaluation form) and constants
/// required to evaluate an expression as a polynomial.
///
/// All are evaluations.
pub struct MSMColumnEnvironment<'a, F: FftField> {
/// The witness column polynomials
pub witness: &'a Vec<Evaluations<F, Radix2EvaluationDomain<F>>>,
/// The coefficient column polynomials
pub coefficients: &'a Vec<Evaluations<F, Radix2EvaluationDomain<F>>>,
/// The value `prod_{j != 1} (1 - omega^j)`, used for efficiently
/// computing the evaluations of the unnormalized Lagrange basis polynomials.
pub l0_1: F,
/// Constant values required
pub constants: Constants<F>,
/// Challenges from the IOP.
pub challenges: Challenges<F>,
/// The domains used in the PLONK argument.
pub domain: EvaluationDomains<F>,
/// Lookup specific polynomials
// TODO define MVlookup one
pub lookup: Option<()>,
}

impl<'a, F: FftField> ColumnEnvironment<'a, F> for MSMColumnEnvironment<'a, F> {
type Column = crate::columns::Column;

fn get_column(
&self,
col: &Self::Column,
) -> Option<&'a Evaluations<F, Radix2EvaluationDomain<F>>> {
let witness_columns_n: usize = self.witness.len();
let coefficients_columns_n: usize = self.coefficients.len();
let crate::columns::Column::X(i) = col;
let i = *i;
if i < witness_columns_n {
let res = &self.witness[i];
Some(res)
} else if i < witness_columns_n + coefficients_columns_n {
Some(&self.coefficients[i])
} else {
None
}
}

fn get_domain(&self, d: Domain) -> Radix2EvaluationDomain<F> {
match d {
Domain::D1 => self.domain.d1,
Domain::D2 => self.domain.d2,
Domain::D4 => self.domain.d4,
Domain::D8 => self.domain.d8,
}
}

fn get_constants(&self) -> &Constants<F> {
&self.constants
}

fn get_challenges(&self) -> &Challenges<F> {
&self.challenges
}

fn vanishes_on_zero_knowledge_and_previous_rows(
&self,
) -> &'a Evaluations<F, Radix2EvaluationDomain<F>> {
panic!("Not supposed to be used in MSM")
}

fn l0_1(&self) -> F {
self.l0_1
}
}
14 changes: 14 additions & 0 deletions msm/src/columns.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use kimchi::circuits::expr::{Domain, GenericColumn};

use crate::LIMBS_NUM;

// @volhovm: maybe this needs to be a trait
Expand All @@ -7,6 +9,16 @@ pub enum Column {
X(usize),
}

impl GenericColumn for Column {
fn column_domain(&self) -> Domain {
// TODO FIXME check this is a tricky variable it should match the evalution in column
// this must be bigger or equal than degree chosen in runtime inside evaluations() for
// evaluating an expression = degree of expression that is evaluated
// And also ... in some cases... bigger than the witness column size? Equal?
Domain::D4
}
}

/// A datatype expressing a generalized column, but with potentially
/// more convenient interface than a bare column.
pub trait ColumnIndexer {
Expand All @@ -19,6 +31,7 @@ pub enum MSMColumnIndexer {
A(usize),
B(usize),
C(usize),
D(usize),
}

impl ColumnIndexer for MSMColumnIndexer {
Expand All @@ -31,6 +44,7 @@ impl ColumnIndexer for MSMColumnIndexer {
MSMColumnIndexer::A(i) => to_column_inner(0, i),
MSMColumnIndexer::B(i) => to_column_inner(1, i),
MSMColumnIndexer::C(i) => to_column_inner(2, i),
MSMColumnIndexer::D(i) => to_column_inner(3, i),
}
}
}
102 changes: 86 additions & 16 deletions msm/src/constraint.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use ark_ff::Zero;
volhovm marked this conversation as resolved.
Show resolved Hide resolved
use ark_poly::Radix2EvaluationDomain;
use num_bigint::BigUint;

use crate::{
columns::{Column, ColumnIndexer, MSMColumnIndexer},
lookups::LookupTableIDs,
Expand All @@ -7,12 +11,14 @@ use crate::{
};
use kimchi::{
circuits::{
expr::{ConstantExpr, ConstantExprInner, Expr, ExprInner, Operations, Variable},
expr::{
Challenges, ColumnEvaluations, ConstantExpr, ConstantExprInner, Constants, Expr,
ExprError, ExprInner, Operations, Variable,
},
gate::CurrOrNext,
},
curve::KimchiCurve,
};
use num_bigint::BigUint;
use o1_utils::{field_helpers::FieldHelpers, foreign_field::ForeignElement};

/// Used to represent constraints as multi variate polynomials. The variables
Expand Down Expand Up @@ -62,23 +68,20 @@ pub struct WitnessColumnsIndexer<T> {
pub(crate) a: [T; LIMBS_NUM],
pub(crate) b: [T; LIMBS_NUM],
pub(crate) c: [T; LIMBS_NUM],
pub(crate) d: [T; LIMBS_NUM],
}

#[allow(dead_code)]
/// Builder environment for a native group `G`.
pub struct BuilderEnv<G: KimchiCurve> {
// TODO something like a running list of constraints
/// Aggregated constraints.
pub(crate) constraints: Vec<MSMExpr<G::ScalarField>>,
pub struct MSMCircuitEnv<G: KimchiCurve> {
/// Aggregated witness, in raw form. For accessing [`Witness`], see the
/// `get_witness` method.
pub(crate) witness_raw: Vec<WitnessColumnsIndexer<G::ScalarField>>,
witness_raw: Vec<WitnessColumnsIndexer<G::ScalarField>>,
}

impl BuilderEnv<BN254G1Affine> {
impl MSMCircuitEnv<BN254G1Affine> {
pub fn empty() -> Self {
BuilderEnv {
constraints: vec![],
MSMCircuitEnv {
witness_raw: vec![],
}
}
Expand All @@ -94,11 +97,13 @@ impl BuilderEnv<BN254G1Affine> {
a: wc_a,
b: wc_b,
c: wc_c,
d: wc_d,
} = wc;
for i in 0..LIMBS_NUM {
cols[i].push(wc_a[i]);
cols[LIMBS_NUM + i].push(wc_b[i]);
cols[2 * LIMBS_NUM + i].push(wc_c[i]);
cols[3 * LIMBS_NUM + i].push(wc_d[i]);
}
}

Expand All @@ -108,8 +113,9 @@ impl BuilderEnv<BN254G1Affine> {
}
}

pub fn add_test_addition(&mut self, a: Ff1, b: Ff1) {
let mut limb_constraints: Vec<_> = vec![];
/// Access exprs generated in the environment so far.
pub fn get_exprs_add(&self) -> Vec<MSMExpr<Fp>> {
let mut limb_exprs: Vec<_> = vec![];
for i in 0..LIMBS_NUM {
let limb_constraint = {
let a_i = MSMExpr::Atom(
Expand All @@ -128,12 +134,52 @@ impl BuilderEnv<BN254G1Affine> {
}));
a_i + b_i - c_i
};
limb_constraints.push(limb_constraint);
limb_exprs.push(limb_constraint);
}
let combined_constraint =
Expr::combine_constraints(0..(limb_constraints.len() as u32), limb_constraints);
self.constraints.push(combined_constraint);
limb_exprs
}

// TEST
pub fn get_exprs_mul(&self) -> Vec<MSMExpr<Fp>> {
let mut limb_exprs: Vec<_> = vec![];
for i in 0..LIMBS_NUM {
let limb_constraint = {
let a_i = MSMExpr::Atom(
ExprInner::<Operations<ConstantExprInner<Fp>>, Column>::Cell(Variable {
col: MSMColumnIndexer::A(i).ix_to_column(),
row: CurrOrNext::Curr,
}),
);
let b_i = MSMExpr::Atom(ExprInner::Cell(Variable {
col: MSMColumnIndexer::B(i).ix_to_column(),
row: CurrOrNext::Curr,
}));
let d_i = MSMExpr::Atom(ExprInner::Cell(Variable {
col: MSMColumnIndexer::D(i).ix_to_column(),
row: CurrOrNext::Curr,
}));
a_i * b_i - d_i
};
limb_exprs.push(limb_constraint);
}
limb_exprs
}

pub fn eval_expressions<Evaluations: ColumnEvaluations<Fp, Column = crate::columns::Column>>(
&self,
d: Radix2EvaluationDomain<Fp>,
pt: Fp,
evals: &Evaluations,
c: &Constants<Fp>,
chals: &Challenges<Fp>,
) -> Result<Vec<Fp>, ExprError<Column>> {
self.get_exprs_add()
.iter()
.map(|expr| expr.evaluate_(d, pt, evals, c, chals))
.collect()
}

pub fn add_test_addition(&mut self, a: Ff1, b: Ff1) {
let a_limbs: [Fp; LIMBS_NUM] = limb_decompose(&a);
let b_limbs: [Fp; LIMBS_NUM] = limb_decompose(&b);
let c_limbs_vec: Vec<Fp> = a_limbs
Expand All @@ -144,11 +190,35 @@ impl BuilderEnv<BN254G1Affine> {
let c_limbs: [Fp; LIMBS_NUM] = c_limbs_vec
.try_into()
.unwrap_or_else(|_| panic!("Length mismatch"));
let d_limbs: [Fp; LIMBS_NUM] = [Zero::zero(); LIMBS_NUM];

self.witness_raw.push(WitnessColumnsIndexer {
a: a_limbs,
b: b_limbs,
c: c_limbs,
d: d_limbs,
});
}

pub fn add_test_multiplication(&mut self, a: Ff1, b: Ff1) {
let a_limbs: [Fp; LIMBS_NUM] = limb_decompose(&a);
let b_limbs: [Fp; LIMBS_NUM] = limb_decompose(&b);
let d_limbs_vec: Vec<Fp> = a_limbs
.iter()
.zip(b_limbs.iter())
.map(|(ai, bi)| *ai * *bi)
.collect();
let d_limbs: [Fp; LIMBS_NUM] = d_limbs_vec
.try_into()
.unwrap_or_else(|_| panic!("Length mismatch"));

let c_limbs: [Fp; LIMBS_NUM] = [Zero::zero(); LIMBS_NUM];

self.witness_raw.push(WitnessColumnsIndexer {
a: a_limbs,
b: b_limbs,
c: c_limbs,
d: d_limbs,
});
}
}
Loading