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

Combine coefficient polynomials #168

Merged
merged 17 commits into from
Oct 12, 2021
26 changes: 18 additions & 8 deletions circuits/plonk-15-wires/src/gates/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Contributor

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?


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 {
Copy link
Contributor

Choose a reason for hiding this comment

The 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);
Expand All @@ -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
Copy link
Contributor

@mimoo mimoo Oct 7, 2021

Choose a reason for hiding this comment

The 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 {
Expand All @@ -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)
}

Expand All @@ -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();
Expand Down Expand Up @@ -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()
}
}
}
111 changes: 32 additions & 79 deletions circuits/plonk-15-wires/src/nolookup/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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],

// 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>,

Expand All @@ -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
// -----------------------
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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()
}
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one-liner:

gate.c.get(i).unwrap_or(F::zero())

Copy link
Contributor

Choose a reason for hiding this comment

The 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

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 get. I guess you could make it work by mapping but seemed kind more awkward than this to me

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gate.c.get(i).copied().unwrap_or(F::zero()) ?

.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);
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions circuits/plonk-15-wires/src/nolookup/scalars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ pub struct ProofEvaluations<Field> {
/// permutation polynomials
/// (PERMUTS-1 evaluations because the last permutation is only used in commitment form)
pub s: [Field; PERMUTS - 1],
/// evaluation of the generic selector polynomial
pub generic_selector: Field,
/// evaluation of the poseidon selector polynomial
pub poseidon_selector: Field,
}

impl<F: FftField> ProofEvaluations<Vec<F>> {
Expand All @@ -28,6 +32,8 @@ impl<F: FftField> ProofEvaluations<Vec<F>> {
s: array_init(|i| DensePolynomial::eval_polynomial(&self.s[i], pt)),
w: array_init(|i| DensePolynomial::eval_polynomial(&self.w[i], pt)),
z: DensePolynomial::eval_polynomial(&self.z, pt),
generic_selector: DensePolynomial::eval_polynomial(&self.generic_selector, pt),
poseidon_selector: DensePolynomial::eval_polynomial(&self.poseidon_selector, pt),
}
}
}
Expand Down
60 changes: 33 additions & 27 deletions circuits/plonk-15-wires/src/polynomials/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Expand All @@ -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() {
Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
Loading