-
Notifications
You must be signed in to change notification settings - Fork 116
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
Combine coefficient polynomials #168
Changes from 13 commits
cae9120
9c55ece
80038aa
65777c9
3a7a822
b65b7a3
e447c32
2d30e4c
7b55976
b1554fd
2327d27
844f31e
8326fbd
0d92752
3cebe5c
c552c06
c40c3c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,9 +9,11 @@ use crate::wires::{GateWires, COLUMNS, GENERICS}; | |
use ark_ff::FftField; | ||
use array_init::array_init; | ||
|
||
pub const MUL_COEFF: usize = GENERICS; | ||
pub const CONSTANT_COEFF: usize = GENERICS + 1; | ||
|
||
impl<F: FftField> CircuitGate<F> { | ||
// TODO(mimoo): why qw is length 15 if the polynomial side only uses 3? | ||
pub fn create_generic(row: usize, wires: GateWires, qw: [F; COLUMNS], qm: F, qc: F) -> Self { | ||
pub fn create_generic(row: usize, wires: GateWires, qw: [F; GENERICS], qm: F, qc: F) -> Self { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had meant to look into why it was set to the full columns, thanks for fixing that! |
||
let mut c = qw.to_vec(); | ||
c.push(qm); | ||
c.push(qc); | ||
|
@@ -28,7 +30,7 @@ impl<F: FftField> CircuitGate<F> { | |
pub fn create_generic_add(row: usize, wires: GateWires) -> Self { | ||
let on = F::one(); | ||
let off = F::zero(); | ||
let qw: [F; COLUMNS] = array_init(|col| { | ||
let qw: [F; GENERICS] = array_init(|col| { | ||
if col < 2 { | ||
on | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. btw this function is missing the fact that you can scale the operand freely, which I didn't think about back when I wrote that. |
||
} else if col == 2 { | ||
|
@@ -44,23 +46,23 @@ impl<F: FftField> CircuitGate<F> { | |
pub fn create_generic_mul(row: usize, wires: GateWires) -> Self { | ||
let on = F::one(); | ||
let off = F::zero(); | ||
let qw: [F; COLUMNS] = array_init(|col| if col == 2 { on } else { off }); | ||
let qw: [F; GENERICS] = array_init(|col| if col == 2 { on } else { off }); | ||
Self::create_generic(row, wires, qw, -on, off) | ||
} | ||
|
||
/// creates a constant gate | ||
pub fn create_generic_const(row: usize, wires: GateWires, constant: F) -> Self { | ||
let on = F::one(); | ||
let off = F::zero(); | ||
let qw: [F; COLUMNS] = array_init(|col| if col == 0 { on } else { off }); | ||
let qw: [F; GENERICS] = array_init(|col| if col == 0 { on } else { off }); | ||
Self::create_generic(row, wires, qw, off, -constant) | ||
} | ||
|
||
/// creates a public input gate | ||
pub fn create_generic_public(row: usize, wires: GateWires) -> Self { | ||
let on = F::one(); | ||
let off = F::zero(); | ||
let qw: [F; COLUMNS] = array_init(|col| if col == 0 { on } else { off }); | ||
let qw: [F; GENERICS] = array_init(|col| if col == 0 { on } else { off }); | ||
Self::create_generic(row, wires, qw, off, off) | ||
} | ||
|
||
|
@@ -73,8 +75,8 @@ impl<F: FftField> CircuitGate<F> { | |
let right = this[1]; | ||
|
||
// selector vectors | ||
let mul_selector = self.c[COLUMNS]; | ||
let constant_selector = self.c[COLUMNS + 1]; | ||
let mul_selector = self.c[MUL_COEFF]; | ||
let constant_selector = self.c[CONSTANT_COEFF]; | ||
|
||
// constants | ||
let zero = F::zero(); | ||
|
@@ -102,4 +104,12 @@ impl<F: FftField> CircuitGate<F> { | |
// all good | ||
return Ok(()); | ||
} | ||
|
||
pub fn generic(&self) -> F { | ||
if self.typ == GateType::Generic { | ||
F::one() | ||
} else { | ||
F::zero() | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,8 +5,7 @@ This source file implements Plonk circuit constraint primitive. | |
*****************************************************************************************************************/ | ||
|
||
use crate::domains::EvaluationDomains; | ||
use crate::gate::{CircuitGate, GateType}; | ||
use crate::gates::poseidon::*; | ||
use crate::gate::CircuitGate; | ||
pub use crate::polynomial::{WitnessEvals, WitnessOverDomains, WitnessShifts}; | ||
use crate::wires::*; | ||
use ark_ff::{FftField, SquareRootField, Zero}; | ||
|
@@ -38,19 +37,20 @@ pub struct ConstraintSystem<F: FftField> { | |
/// zero-knowledge polynomial | ||
pub zkpm: DP<F>, | ||
|
||
// Coefficient polynomials. These define constant that gates can use as they like. | ||
// --------------------------------------- | ||
|
||
/// coefficients polynomials in coefficient form | ||
pub coefficientsm: [DP<F>; COLUMNS], | ||
/// coefficients polynomials in evaluation form | ||
pub coefficients4: [E<F, D<F>>; COLUMNS], | ||
mimoo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Generic constraint selector polynomials | ||
// --------------------------------------- | ||
/// linear wire constraint polynomial | ||
pub qwm: [DP<F>; GENERICS], | ||
/// multiplication polynomial | ||
pub qmm: DP<F>, | ||
/// constant wire polynomial | ||
pub qc: DP<F>, | ||
pub genericm: DP<F>, | ||
|
||
// Poseidon selector polynomials | ||
// ----------------------------- | ||
/// round constant polynomials | ||
pub rcm: [[DP<F>; SPONGE_WIDTH]; ROUNDS_PER_ROW], | ||
/// poseidon constraint selector polynomial | ||
pub psm: DP<F>, | ||
|
||
|
@@ -71,10 +71,8 @@ pub struct ConstraintSystem<F: FftField> { | |
|
||
// Generic constraint selector polynomials | ||
// --------------------------------------- | ||
/// left input wire polynomial over domain.d4 | ||
pub qwl: [E<F, D<F>>; GENERICS], | ||
/// multiplication evaluations over domain.d4 | ||
pub qml: E<F, D<F>>, | ||
pub generic4: E<F, D<F>>, | ||
|
||
// permutation polynomials | ||
// ----------------------- | ||
|
@@ -227,36 +225,6 @@ impl<F: FftField + SquareRootField> ConstraintSystem<F> { | |
let zkpm = zk_polynomial(domain.d1); | ||
|
||
// compute generic constraint polynomials | ||
let qwm: [DP<F>; GENERICS] = array_init(|i| { | ||
E::<F, D<F>>::from_vec_and_domain( | ||
gates | ||
.iter() | ||
.map(|gate| { | ||
if gate.typ == GateType::Generic { | ||
gate.c[WIRES[i]] | ||
} else { | ||
F::zero() | ||
} | ||
}) | ||
.collect(), | ||
domain.d1, | ||
) | ||
.interpolate() | ||
}); | ||
let qmm = E::<F, D<F>>::from_vec_and_domain( | ||
gates | ||
.iter() | ||
.map(|gate| { | ||
if gate.typ == GateType::Generic { | ||
gate.c[COLUMNS] | ||
} else { | ||
F::zero() | ||
} | ||
}) | ||
.collect(), | ||
domain.d1, | ||
) | ||
.interpolate(); | ||
|
||
// compute poseidon constraint polynomials | ||
let psm = E::<F, D<F>>::from_vec_and_domain( | ||
|
@@ -290,42 +258,29 @@ impl<F: FftField + SquareRootField> ConstraintSystem<F> { | |
let sigmal8 = array_init(|i| sigmam[i].evaluate_over_domain_by_ref(domain.d8)); | ||
|
||
// generic constraint polynomials | ||
let qwl = array_init(|i| qwm[i].evaluate_over_domain_by_ref(domain.d4)); | ||
let qml = qmm.evaluate_over_domain_by_ref(domain.d4); | ||
let qc = E::<F, D<F>>::from_vec_and_domain( | ||
gates | ||
.iter() | ||
.map(|gate| { | ||
if gate.typ == GateType::Generic { | ||
gate.c[COLUMNS + 1] | ||
} else { | ||
F::zero() | ||
} | ||
}) | ||
.collect(), | ||
|
||
let genericm = E::<F, D<F>>::from_vec_and_domain( | ||
gates.iter().map(|gate| gate.generic()).collect(), | ||
domain.d1, | ||
) | ||
.interpolate(); | ||
let generic4 = genericm.evaluate_over_domain_by_ref(domain.d4); | ||
|
||
// poseidon constraint polynomials | ||
let rcm = array_init(|round| { | ||
array_init(|col| { | ||
let coefficientsm: [_; COLUMNS] = | ||
array_init(|i| { | ||
E::<F, D<F>>::from_vec_and_domain( | ||
gates | ||
.iter() | ||
.map(|gate| { | ||
if gate.typ == GateType::Poseidon { | ||
gate.rc()[round][col] | ||
} else { | ||
F::zero() | ||
} | ||
}) | ||
.collect(), | ||
domain.d1, | ||
) | ||
gates.iter().map(|gate| { | ||
if i < gate.c.len() { | ||
gate.c[i] | ||
} else { | ||
F::zero() | ||
} | ||
}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. one-liner: gate.c.get(i).unwrap_or(F::zero()) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. interesting, maybe it'd be good to document somewhere that if you need static coefficients for your gate, you should use the coefficient polynomials/commitments There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually the one liner doesn't work because you get a borrow from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
.collect(), | ||
domain.d1) | ||
.interpolate() | ||
}) | ||
}); | ||
}); | ||
let coefficients4 = array_init(|i| coefficientsm[i].evaluate_over_domain_by_ref(domain.d4)); | ||
|
||
let ps4 = psm.evaluate_over_domain_by_ref(domain.d4); | ||
let ps8 = psm.evaluate_over_domain_by_ref(domain.d8); | ||
|
@@ -362,12 +317,10 @@ impl<F: FftField + SquareRootField> ConstraintSystem<F> { | |
sigmal1, | ||
sigmal8, | ||
sigmam, | ||
qwl, | ||
qml, | ||
qwm, | ||
qmm, | ||
qc, | ||
rcm, | ||
genericm, | ||
generic4, | ||
coefficientsm, | ||
coefficients4, | ||
ps4, | ||
ps8, | ||
psm, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ use ark_poly::{ | |
univariate::DensePolynomial, EvaluationDomain, Evaluations, Radix2EvaluationDomain as D, | ||
}; | ||
use o1_utils::ExtendedDensePolynomial; | ||
use crate::gates::generic::{MUL_COEFF, CONSTANT_COEFF}; | ||
|
||
impl<F: FftField + SquareRootField> ConstraintSystem<F> { | ||
/// generic constraint quotient poly contribution computation | ||
|
@@ -21,47 +22,51 @@ impl<F: FftField + SquareRootField> ConstraintSystem<F> { | |
public: &DensePolynomial<F>, | ||
) -> (Evaluations<F, D<F>>, DensePolynomial<F>) { | ||
// w[0](x) * w[1](x) * qml(x) | ||
let multiplication = &(&witness_d4[0] * &witness_d4[1]) * &self.qml; | ||
let mut multiplication = &witness_d4[0] * &witness_d4[1]; | ||
multiplication *= &self.coefficients4[MUL_COEFF]; | ||
|
||
// presence of left, right, and output wire | ||
// w[0](x) * qwl[0](x) + w[1](x) * qwl[1](x) + w[2](x) * qwl[2](x) | ||
let mut wires = self.zero4.clone(); | ||
for (w, q) in witness_d4.iter().zip(self.qwl.iter()) { | ||
wires += &(w * q); | ||
let mut eval_part = multiplication; | ||
for (w, q) in witness_d4.iter().zip(self.coefficients4.iter()).take(GENERICS) { | ||
eval_part += &(w * q); | ||
} | ||
eval_part += &self.coefficients4[CONSTANT_COEFF]; | ||
eval_part *= &self.generic4; | ||
|
||
// return in lagrange and monomial form for optimization purpose | ||
let eval_part = &multiplication + &wires; | ||
let poly_part = &self.qc + public; | ||
let poly_part = public.clone(); | ||
(eval_part, poly_part) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if the poly part is now just the public polynomial, then let's not pass the public argument and return the poly_part in this function :o wdyt? |
||
} | ||
|
||
/// produces w[0](zeta) * w[1](zeta), w[0](zeta), w[1](zeta), w[2](zeta), 1 | ||
pub fn gnrc_scalars(w_zeta: &[F; COLUMNS]) -> Vec<F> { | ||
let mut res = vec![w_zeta[0] * &w_zeta[1]]; | ||
for i in 0..GENERICS { | ||
res.push(w_zeta[i]); | ||
} | ||
/// produces | ||
/// generic(zeta) * w[0](zeta) * w[1](zeta), | ||
/// generic(zeta) * w[0](zeta), | ||
/// generic(zeta) * w[1](zeta), | ||
/// generic(zeta) * w[2](zeta) | ||
pub fn gnrc_scalars(w_zeta: &[F; COLUMNS], generic_zeta: F) -> Vec<F> { | ||
let mut res = vec![generic_zeta * w_zeta[0] * &w_zeta[1]]; | ||
res.extend((0..GENERICS).map(|i| generic_zeta * w_zeta[i])); | ||
return res; | ||
} | ||
|
||
/// generic constraint linearization poly contribution computation | ||
pub fn gnrc_lnrz(&self, w_zeta: &[F; COLUMNS]) -> DensePolynomial<F> { | ||
let scalars = Self::gnrc_scalars(w_zeta); | ||
pub fn gnrc_lnrz(&self, w_zeta: &[F; COLUMNS], generic_zeta: F) -> DensePolynomial<F> { | ||
let scalars = Self::gnrc_scalars(w_zeta, generic_zeta); | ||
|
||
// w[0](zeta) * qwm[0] + w[1](zeta) * qwm[1] + w[2](zeta) * qwm[2] | ||
let mut res = self | ||
.qwm | ||
.coefficientsm | ||
.iter() | ||
.zip(scalars[1..].iter()) | ||
.map(|(q, s)| q.scale(*s)) | ||
.fold(DensePolynomial::<F>::zero(), |x, y| &x + &y); | ||
|
||
// multiplication | ||
res += &self.qmm.scale(scalars[0]); | ||
res += &self.coefficientsm[MUL_COEFF].scale(scalars[0]); | ||
|
||
// constant selector | ||
res += &self.qc; | ||
res += &self.coefficientsm[CONSTANT_COEFF].scale(generic_zeta); | ||
|
||
// l * qwm[0] + r * qwm[1] + o * qwm[2] + l * r * qmm + qc | ||
res | ||
|
@@ -74,32 +79,31 @@ impl<F: FftField + SquareRootField> ConstraintSystem<F> { | |
public: &DensePolynomial<F>, | ||
) -> bool { | ||
// multiplication | ||
let multiplication = &(&witness[0] * &witness[1]) * &self.qmm; | ||
let multiplication = &(&witness[0] * &witness[1]) * &self.coefficientsm[MUL_COEFF]; | ||
|
||
// addition (of left, right, output wires) | ||
if self.qwm.len() != GENERICS { | ||
return false; | ||
} | ||
let mut wires = DensePolynomial::zero(); | ||
for (w, q) in witness.iter().zip(self.qwm.iter()) { | ||
for (w, q) in witness.iter().zip(self.coefficientsm.iter()).take(GENERICS) { | ||
wires += &(w * q); | ||
} | ||
|
||
// compute f | ||
let mut f = &multiplication + &wires; | ||
f += &self.qc; | ||
f += &self.coefficientsm[CONSTANT_COEFF]; | ||
f = &f * &self.genericm; | ||
f += public; | ||
|
||
// verify that each row evaluates to zero | ||
let values: Vec<_> = witness | ||
.iter() | ||
.zip(self.qwl.iter()) | ||
.zip(self.coefficients4.iter()) | ||
.take(GENERICS) | ||
.map(|(w, q)| (w, q.interpolate_by_ref())) | ||
.collect(); | ||
|
||
// | ||
for (row, elem) in self.domain.d1.elements().enumerate() { | ||
let qc = self.qc.evaluate(&elem); | ||
let qc = self.coefficientsm[CONSTANT_COEFF].evaluate(&elem); | ||
|
||
// qc check | ||
if qc != F::zero() { | ||
|
@@ -121,7 +125,7 @@ impl<F: FftField + SquareRootField> ConstraintSystem<F> { | |
} | ||
println!( | ||
" q_M = {} | mul = {}", | ||
self.qmm.evaluate(&elem), | ||
self.coefficientsm[MUL_COEFF].evaluate(&elem), | ||
multiplication.evaluate(&elem) | ||
); | ||
println!(" q_C = {}", qc); | ||
|
@@ -227,7 +231,9 @@ mod tests { | |
|
||
// compute linearization f(z) | ||
let w_zeta: [Fp; COLUMNS] = array_init(|col| witness[col].evaluate(&zeta)); | ||
let f = cs.gnrc_lnrz(&w_zeta); | ||
let generic_zeta = cs.genericm.evaluate(&zeta); | ||
|
||
let f = cs.gnrc_lnrz(&w_zeta, generic_zeta); | ||
let f_zeta = f.evaluate(&zeta); | ||
|
||
// check that f(z) = t(z) * Z_H(z) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is great. Also "index" more than "coeff" no?