diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 4a86ef48..3a26497e 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -10,7 +10,6 @@ use crate::circuit_data::{ use crate::field::cosets::get_unique_coset_shifts; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; -use crate::field::field::Field; use crate::gates::constant::ConstantGate; use crate::gates::gate::{GateInstance, GateRef}; use crate::gates::noop::NoopGate; diff --git a/src/field/extension_field/algebra.rs b/src/field/extension_field/algebra.rs new file mode 100644 index 00000000..eac0bbd8 --- /dev/null +++ b/src/field/extension_field/algebra.rs @@ -0,0 +1,251 @@ +use crate::field::extension_field::OEF; +use std::fmt::{Debug, Display, Formatter}; +use std::iter::{Product, Sum}; +use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +/// Let `F_D` be the optimal extension field `F[X]/(X^D-W)`. Then `ExtensionAlgebra` is the quotient `F_D[X]/(X^D-W)`. +/// It's a `D`-dimensional algebra over `F_D` useful to lift the multiplication over `F_D` to a multiplication over `(F_D)^D`. +#[derive(Copy, Clone)] +pub struct ExtensionAlgebra, const D: usize>([F; D]); + +impl, const D: usize> ExtensionAlgebra { + pub const ZERO: Self = Self([F::ZERO; D]); + + pub fn one() -> Self { + F::ONE.into() + } + + pub fn from_basefield_array(arr: [F; D]) -> Self { + Self(arr) + } + + pub fn to_basefield_array(self) -> [F; D] { + self.0 + } +} + +impl, const D: usize> From for ExtensionAlgebra { + fn from(x: F) -> Self { + let mut arr = [F::ZERO; D]; + arr[0] = x; + Self(arr) + } +} + +impl, const D: usize> Display for ExtensionAlgebra { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "({})", self.0[0])?; + for i in 1..D { + write!(f, " + ({})*b^{}", self.0[i], i)?; + } + Ok(()) + } +} + +impl, const D: usize> Debug for ExtensionAlgebra { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(self, f) + } +} + +impl, const D: usize> Neg for ExtensionAlgebra { + type Output = Self; + + #[inline] + fn neg(self) -> Self { + let mut arr = self.0; + arr.iter_mut().for_each(|x| *x = -*x); + Self(arr) + } +} + +impl, const D: usize> Add for ExtensionAlgebra { + type Output = Self; + + #[inline] + fn add(self, rhs: Self) -> Self { + let mut arr = self.0; + arr.iter_mut().zip(&rhs.0).for_each(|(x, &y)| *x += y); + Self(arr) + } +} + +impl, const D: usize> AddAssign for ExtensionAlgebra { + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs; + } +} + +impl, const D: usize> Sum for ExtensionAlgebra { + fn sum>(iter: I) -> Self { + iter.fold(Self::ZERO, |acc, x| acc + x) + } +} + +impl, const D: usize> Sub for ExtensionAlgebra { + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self { + let mut arr = self.0; + arr.iter_mut().zip(&rhs.0).for_each(|(x, &y)| *x -= y); + Self(arr) + } +} + +impl, const D: usize> SubAssign for ExtensionAlgebra { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs; + } +} + +impl, const D: usize> Mul for ExtensionAlgebra { + type Output = Self; + + #[inline] + fn mul(self, rhs: Self) -> Self { + let mut res = [F::ZERO; D]; + let w = F::from_basefield(F::W); + for i in 0..D { + for j in 0..D { + res[(i + j) % D] += if i + j < D { + self.0[i] * rhs.0[j] + } else { + w * self.0[i] * rhs.0[j] + } + } + } + Self(res) + } +} + +impl, const D: usize> MulAssign for ExtensionAlgebra { + #[inline] + fn mul_assign(&mut self, rhs: Self) { + *self = *self * rhs; + } +} + +impl, const D: usize> Product for ExtensionAlgebra { + fn product>(iter: I) -> Self { + iter.fold(Self::one(), |acc, x| acc * x) + } +} + +/// A polynomial in coefficient form. +#[derive(Clone, Debug)] +pub struct PolynomialCoeffsAlgebra, const D: usize> { + pub(crate) coeffs: Vec>, +} + +impl, const D: usize> PolynomialCoeffsAlgebra { + pub fn new(coeffs: Vec>) -> Self { + PolynomialCoeffsAlgebra { coeffs } + } + + pub fn eval(&self, x: ExtensionAlgebra) -> ExtensionAlgebra { + self.coeffs + .iter() + .rev() + .fold(ExtensionAlgebra::ZERO, |acc, &c| acc * x + c) + } +} + +#[cfg(test)] +mod tests { + use crate::field::crandall_field::CrandallField; + use crate::field::extension_field::algebra::ExtensionAlgebra; + use crate::field::extension_field::{Extendable, FieldExtension}; + use crate::field::field::Field; + use itertools::Itertools; + + /// Tests that the multiplication on the extension algebra lifts that of the field extension. + fn test_extension_algebra, const D: usize>() { + #[derive(Copy, Clone, Debug)] + enum ZeroOne { + Zero, + One, + } + + let to_field = |zo: &ZeroOne| match zo { + ZeroOne::Zero => F::ZERO, + ZeroOne::One => F::ONE, + }; + let to_fields = |x: &[ZeroOne], y: &[ZeroOne]| -> (F::Extension, F::Extension) { + let mut arr0 = [F::ZERO; D]; + let mut arr1 = [F::ZERO; D]; + arr0.copy_from_slice(&x.iter().map(to_field).collect::>()); + arr1.copy_from_slice(&y.iter().map(to_field).collect::>()); + ( + >::Extension::from_basefield_array(arr0), + >::Extension::from_basefield_array(arr1), + ) + }; + + // Standard MLE formula. + let selector = |xs: Vec, ts: &[F::Extension]| -> F::Extension { + (0..2 * D) + .map(|i| match xs[i] { + ZeroOne::Zero => F::Extension::ONE - ts[i], + ZeroOne::One => ts[i], + }) + .product() + }; + + let mul_mle = |ts: Vec| -> [F::Extension; D] { + let mut ans = [F::Extension::ZERO; D]; + for xs in (0..2 * D) + .map(|_| vec![ZeroOne::Zero, ZeroOne::One]) + .multi_cartesian_product() + { + let (a, b) = to_fields(&xs[..D], &xs[D..]); + let c = a * b; + let res = selector(xs, &ts); + for i in 0..D { + ans[i] += res * c.to_basefield_array()[i].into(); + } + } + ans + }; + + let ts = F::Extension::rand_vec(2 * D); + let mut arr0 = [F::Extension::ZERO; D]; + let mut arr1 = [F::Extension::ZERO; D]; + arr0.copy_from_slice(&ts[..D]); + arr1.copy_from_slice(&ts[D..]); + let x = ExtensionAlgebra::from_basefield_array(arr0); + let y = ExtensionAlgebra::from_basefield_array(arr1); + let z = x * y; + + dbg!(z.0, mul_mle(ts.clone())); + assert_eq!(z.0, mul_mle(ts)); + } + + mod base { + use super::*; + + #[test] + fn test_algebra() { + test_extension_algebra::(); + } + } + + mod quadratic { + use super::*; + + #[test] + fn test_algebra() { + test_extension_algebra::(); + } + } + + mod quartic { + use super::*; + + #[test] + fn test_algebra() { + test_extension_algebra::(); + } + } +} diff --git a/src/field/extension_field/mod.rs b/src/field/extension_field/mod.rs index 7e74be78..60d2b2e1 100644 --- a/src/field/extension_field/mod.rs +++ b/src/field/extension_field/mod.rs @@ -1,8 +1,8 @@ use crate::field::field::Field; +pub mod algebra; pub mod quadratic; pub mod quartic; -mod quartic_quartic; pub mod target; /// Optimal extension field trait. diff --git a/src/field/extension_field/quartic.rs b/src/field/extension_field/quartic.rs index 6bd8ac55..2acac183 100644 --- a/src/field/extension_field/quartic.rs +++ b/src/field/extension_field/quartic.rs @@ -6,8 +6,7 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssi use rand::Rng; use crate::field::crandall_field::CrandallField; -use crate::field::extension_field::quartic_quartic::QuarticQuarticCrandallField; -use crate::field::extension_field::{Extendable, FieldExtension, OEF}; +use crate::field::extension_field::{FieldExtension, OEF}; use crate::field::field::Field; /// A quartic extension of `CrandallField`. @@ -239,10 +238,6 @@ impl DivAssign for QuarticCrandallField { } } -impl Extendable<4> for QuarticCrandallField { - type Extension = QuarticQuarticCrandallField; -} - #[cfg(test)] mod tests { use crate::field::extension_field::quartic::QuarticCrandallField; diff --git a/src/field/extension_field/quartic_quartic.rs b/src/field/extension_field/quartic_quartic.rs deleted file mode 100644 index 62fad962..00000000 --- a/src/field/extension_field/quartic_quartic.rs +++ /dev/null @@ -1,259 +0,0 @@ -use crate::field::crandall_field::CrandallField; -use crate::field::extension_field::quartic::QuarticCrandallField; -use crate::field::extension_field::{FieldExtension, OEF}; -use crate::field::field::Field; -use rand::Rng; -use std::fmt::{Debug, Display, Formatter}; -use std::hash::Hash; -use std::iter::{Product, Sum}; -use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - -/// A quartic extension of `QuarticCrandallField`. -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct QuarticQuarticCrandallField(pub(crate) [QuarticCrandallField; 4]); - -impl OEF<4> for QuarticQuarticCrandallField { - // Verifiable in Sage with - // p = 2^64 - 9 * 2^28 + 1 - // F = GF(p) - // PR_F. = PolynomialRing(F) - // assert (x^4 - 3).is_irreducible() - // F4. = F.extension(x^4 - 3) - // PR_F4. = PolynomialRing(F4) - // assert (x^4 - y).is_irreducible() - // F44. = F4.extension(x^4 - y) - const W: QuarticCrandallField = QuarticCrandallField([ - CrandallField(0), - CrandallField(1), - CrandallField(0), - CrandallField(0), - ]); -} - -impl FieldExtension<4> for QuarticQuarticCrandallField { - type BaseField = QuarticCrandallField; - - fn to_basefield_array(&self) -> [Self::BaseField; 4] { - self.0 - } - - fn from_basefield_array(arr: [Self::BaseField; 4]) -> Self { - Self(arr) - } - - fn from_basefield(x: Self::BaseField) -> Self { - x.into() - } -} - -impl From<>::BaseField> for QuarticQuarticCrandallField { - fn from(x: >::BaseField) -> Self { - Self([ - x, - >::BaseField::ZERO, - >::BaseField::ZERO, - >::BaseField::ZERO, - ]) - } -} - -impl Field for QuarticQuarticCrandallField { - const ZERO: Self = Self([QuarticCrandallField::ZERO; 4]); - const ONE: Self = Self([ - QuarticCrandallField::ONE, - QuarticCrandallField::ZERO, - QuarticCrandallField::ZERO, - QuarticCrandallField::ZERO, - ]); - const TWO: Self = Self([ - QuarticCrandallField::TWO, - QuarticCrandallField::ZERO, - QuarticCrandallField::ZERO, - QuarticCrandallField::ZERO, - ]); - const NEG_ONE: Self = Self([ - QuarticCrandallField::NEG_ONE, - QuarticCrandallField::ZERO, - QuarticCrandallField::ZERO, - QuarticCrandallField::ZERO, - ]); - - // Does not fit in 64-bits. - const ORDER: u64 = 0; - const TWO_ADICITY: usize = 32; - const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([ - QuarticCrandallField([ - CrandallField(7562951059982399618), - CrandallField(16734862117167184487), - CrandallField(8532193866847630013), - CrandallField(15462716295551021898), - ]), - QuarticCrandallField([ - CrandallField(16143979237658148445), - CrandallField(12004617499933809221), - CrandallField(11826153143854535879), - CrandallField(14780824604953232397), - ]), - QuarticCrandallField([ - CrandallField(12779077039546101185), - CrandallField(15745975127331074164), - CrandallField(4297791107105154033), - CrandallField(5966855376644799108), - ]), - QuarticCrandallField([ - CrandallField(1942992936904935291), - CrandallField(6041097781717465159), - CrandallField(16875726992388585780), - CrandallField(17742746479895474446), - ]), - ]); - const POWER_OF_TWO_GENERATOR: Self = Self([ - QuarticCrandallField::ZERO, - QuarticCrandallField([ - CrandallField::ZERO, - CrandallField::ZERO, - CrandallField::ZERO, - CrandallField(6809469153480715254), - ]), - QuarticCrandallField::ZERO, - QuarticCrandallField::ZERO, - ]); - - fn try_inverse(&self) -> Option { - todo!() - } - - fn to_canonical_u64(&self) -> u64 { - panic!("Doesn't fit!") - } - - fn from_canonical_u64(n: u64) -> Self { - >::BaseField::from_canonical_u64(n).into() - } - - fn rand_from_rng(rng: &mut R) -> Self { - Self([ - >::BaseField::rand_from_rng(rng), - >::BaseField::rand_from_rng(rng), - >::BaseField::rand_from_rng(rng), - >::BaseField::rand_from_rng(rng), - ]) - } -} - -impl Display for QuarticQuarticCrandallField { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "({}) + ({})*b + ({})*b^2 + ({})*b^3", - self.0[0], self.0[1], self.0[2], self.0[3] - ) - } -} - -impl Debug for QuarticQuarticCrandallField { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - Display::fmt(self, f) - } -} - -impl Neg for QuarticQuarticCrandallField { - type Output = Self; - - #[inline] - fn neg(self) -> Self { - Self([-self.0[0], -self.0[1], -self.0[2], -self.0[3]]) - } -} - -impl Add for QuarticQuarticCrandallField { - type Output = Self; - - #[inline] - fn add(self, rhs: Self) -> Self { - Self([ - self.0[0] + rhs.0[0], - self.0[1] + rhs.0[1], - self.0[2] + rhs.0[2], - self.0[3] + rhs.0[3], - ]) - } -} - -impl AddAssign for QuarticQuarticCrandallField { - fn add_assign(&mut self, rhs: Self) { - *self = *self + rhs; - } -} - -impl Sum for QuarticQuarticCrandallField { - fn sum>(iter: I) -> Self { - iter.fold(Self::ZERO, |acc, x| acc + x) - } -} - -impl Sub for QuarticQuarticCrandallField { - type Output = Self; - - #[inline] - fn sub(self, rhs: Self) -> Self { - Self([ - self.0[0] - rhs.0[0], - self.0[1] - rhs.0[1], - self.0[2] - rhs.0[2], - self.0[3] - rhs.0[3], - ]) - } -} - -impl SubAssign for QuarticQuarticCrandallField { - #[inline] - fn sub_assign(&mut self, rhs: Self) { - *self = *self - rhs; - } -} - -impl Mul for QuarticQuarticCrandallField { - type Output = Self; - - #[inline] - fn mul(self, rhs: Self) -> Self { - let Self([a0, a1, a2, a3]) = self; - let Self([b0, b1, b2, b3]) = rhs; - - let c0 = a0 * b0 + >::W * (a1 * b3 + a2 * b2 + a3 * b1); - let c1 = a0 * b1 + a1 * b0 + >::W * (a2 * b3 + a3 * b2); - let c2 = a0 * b2 + a1 * b1 + a2 * b0 + >::W * a3 * b3; - let c3 = a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; - - Self([c0, c1, c2, c3]) - } -} - -impl MulAssign for QuarticQuarticCrandallField { - #[inline] - fn mul_assign(&mut self, rhs: Self) { - *self = *self * rhs; - } -} - -impl Product for QuarticQuarticCrandallField { - fn product>(iter: I) -> Self { - iter.fold(Self::ONE, |acc, x| acc * x) - } -} - -impl Div for QuarticQuarticCrandallField { - type Output = Self; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn div(self, rhs: Self) -> Self::Output { - self * rhs.inverse() - } -} - -impl DivAssign for QuarticQuarticCrandallField { - fn div_assign(&mut self, rhs: Self) { - *self = *self / rhs; - } -} diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs index c5b1fe53..901cf854 100644 --- a/src/field/extension_field/target.rs +++ b/src/field/extension_field/target.rs @@ -1,4 +1,5 @@ use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::algebra::ExtensionAlgebra; use crate::field::extension_field::{Extendable, FieldExtension, OEF}; use crate::field::field::Field; use crate::target::Target; @@ -29,9 +30,9 @@ impl ExtensionTarget { /// `Target`s representing an element of an extension of an extension field. #[derive(Copy, Clone, Debug)] -pub struct ExtensionExtensionTarget(pub [ExtensionTarget; D]); +pub struct ExtensionAlgebraTarget(pub [ExtensionTarget; D]); -impl ExtensionExtensionTarget { +impl ExtensionAlgebraTarget { pub fn to_ext_target_array(&self) -> [ExtensionTarget; D] { self.0 } @@ -47,19 +48,16 @@ impl, const D: usize> CircuitBuilder { ExtensionTarget(parts) } - pub fn constant_ext_ext( + pub fn constant_ext_algebra( &mut self, - c: <>::Extension as Extendable>::Extension, - ) -> ExtensionExtensionTarget - where - F::Extension: Extendable, - { + c: ExtensionAlgebra, + ) -> ExtensionAlgebraTarget { let c_parts = c.to_basefield_array(); let mut parts = [self.zero_extension(); D]; for i in 0..D { parts[i] = self.constant_extension(c_parts[i]); } - ExtensionExtensionTarget(parts) + ExtensionAlgebraTarget(parts) } pub fn zero_extension(&mut self) -> ExtensionTarget { @@ -74,11 +72,8 @@ impl, const D: usize> CircuitBuilder { self.constant_extension(F::Extension::TWO) } - pub fn zero_ext_ext(&mut self) -> ExtensionExtensionTarget - where - F::Extension: Extendable, - { - self.constant_ext_ext(<>::Extension as Extendable>::Extension::ZERO) + pub fn zero_ext_algebra(&mut self) -> ExtensionAlgebraTarget { + self.constant_ext_algebra(ExtensionAlgebra::ZERO) } pub fn add_extension( @@ -92,11 +87,11 @@ impl, const D: usize> CircuitBuilder { a } - pub fn add_ext_ext( + pub fn add_ext_algebra( &mut self, - mut a: ExtensionExtensionTarget, - b: ExtensionExtensionTarget, - ) -> ExtensionExtensionTarget { + mut a: ExtensionAlgebraTarget, + b: ExtensionAlgebraTarget, + ) -> ExtensionAlgebraTarget { for i in 0..D { a.0[i] = self.add_extension(a.0[i], b.0[i]); } @@ -122,11 +117,11 @@ impl, const D: usize> CircuitBuilder { a } - pub fn sub_ext_ext( + pub fn sub_ext_algebra( &mut self, - mut a: ExtensionExtensionTarget, - b: ExtensionExtensionTarget, - ) -> ExtensionExtensionTarget { + mut a: ExtensionAlgebraTarget, + b: ExtensionAlgebraTarget, + ) -> ExtensionAlgebraTarget { for i in 0..D { a.0[i] = self.sub_extension(a.0[i], b.0[i]); } @@ -152,29 +147,25 @@ impl, const D: usize> CircuitBuilder { ExtensionTarget(res) } - pub fn mul_ext_ext( + pub fn mul_ext_algebra( &mut self, - mut a: ExtensionExtensionTarget, - b: ExtensionExtensionTarget, - ) -> ExtensionExtensionTarget - where - F::Extension: Extendable, - { + a: ExtensionAlgebraTarget, + b: ExtensionAlgebraTarget, + ) -> ExtensionAlgebraTarget { let mut res = [self.zero_extension(); D]; - let w = self - .constant_extension(<>::Extension as Extendable>::Extension::W); + let w = self.constant(F::Extension::W); for i in 0..D { for j in 0..D { let ai_bi = self.mul_extension(a.0[i], b.0[j]); res[(i + j) % D] = if i + j < D { self.add_extension(ai_bi, res[(i + j) % D]) } else { - let w_ai_bi = self.mul_extension(w, ai_bi); + let w_ai_bi = self.scalar_mul_ext(w, ai_bi); self.add_extension(w_ai_bi, res[(i + j) % D]) } } } - ExtensionExtensionTarget(res) + ExtensionAlgebraTarget(res) } pub fn mul_many_extension(&mut self, terms: &[ExtensionTarget]) -> ExtensionTarget { @@ -207,14 +198,11 @@ impl, const D: usize> CircuitBuilder { /// Returns `a * b`, where `b` is in the extension of the extension field, and `a` is in the /// extension field. - pub fn scalar_mul_ext_ext( + pub fn scalar_mul_ext_algebra( &mut self, a: ExtensionTarget, - mut b: ExtensionExtensionTarget, - ) -> ExtensionExtensionTarget - where - F::Extension: Extendable, - { + mut b: ExtensionAlgebraTarget, + ) -> ExtensionAlgebraTarget { for i in 0..D { b.0[i] = self.mul_extension(a, b.0[i]); } diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 156d170f..41baa84e 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -166,7 +166,7 @@ fn fri_combine_initial, const D: usize>( let openings = os .constants .iter() - .chain(&os.plonk_sigmas) + .chain(&os.plonk_s_sigmas) .chain(&os.quotient_polys); let numerator = izip!(evals, openings, &mut alpha_powers) .map(|(e, &o, a)| a * (e - o)) diff --git a/src/gadgets/polynomial.rs b/src/gadgets/polynomial.rs index cac51452..543be834 100644 --- a/src/gadgets/polynomial.rs +++ b/src/gadgets/polynomial.rs @@ -1,5 +1,5 @@ use crate::circuit_builder::CircuitBuilder; -use crate::field::extension_field::target::{ExtensionExtensionTarget, ExtensionTarget}; +use crate::field::extension_field::target::{ExtensionAlgebraTarget, ExtensionTarget}; use crate::field::extension_field::Extendable; use crate::target::Target; @@ -33,22 +33,21 @@ impl PolynomialCoeffsExtTarget { } } -pub struct PolynomialCoeffsExtExtTarget(pub Vec>); +pub struct PolynomialCoeffsExtAlgebraTarget(pub Vec>); -impl PolynomialCoeffsExtExtTarget { +impl PolynomialCoeffsExtAlgebraTarget { pub fn eval_scalar( &self, builder: &mut CircuitBuilder, point: ExtensionTarget, - ) -> ExtensionExtensionTarget + ) -> ExtensionAlgebraTarget where F: Extendable, - F::Extension: Extendable, { - let mut acc = builder.zero_ext_ext(); + let mut acc = builder.zero_ext_algebra(); for &c in self.0.iter().rev() { - let tmp = builder.scalar_mul_ext_ext(point, acc); - acc = builder.add_ext_ext(tmp, c); + let tmp = builder.scalar_mul_ext_algebra(point, acc); + acc = builder.add_ext_algebra(tmp, c); } acc } @@ -56,16 +55,15 @@ impl PolynomialCoeffsExtExtTarget { pub fn eval( &self, builder: &mut CircuitBuilder, - point: ExtensionExtensionTarget, - ) -> ExtensionExtensionTarget + point: ExtensionAlgebraTarget, + ) -> ExtensionAlgebraTarget where F: Extendable, - F::Extension: Extendable, { - let mut acc = builder.zero_ext_ext(); + let mut acc = builder.zero_ext_algebra(); for &c in self.0.iter().rev() { - let tmp = builder.mul_ext_ext(point, acc); - acc = builder.add_ext_ext(tmp, c); + let tmp = builder.mul_ext_algebra(point, acc); + acc = builder.add_ext_algebra(tmp, c); } acc } diff --git a/src/gates/interpolation.rs b/src/gates/interpolation.rs index 53e0b7e7..9bf76c2c 100644 --- a/src/gates/interpolation.rs +++ b/src/gates/interpolation.rs @@ -3,13 +3,13 @@ use std::marker::PhantomData; use std::ops::Range; use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::algebra::PolynomialCoeffsAlgebra; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::lagrange::interpolant; -use crate::gadgets::polynomial::PolynomialCoeffsExtExtTarget; +use crate::gadgets::polynomial::PolynomialCoeffsExtAlgebraTarget; use crate::gates::gate::{Gate, GateRef}; use crate::generator::{SimpleGenerator, WitnessGenerator}; -use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::wire::Wire; @@ -21,18 +21,12 @@ use crate::witness::PartialWitness; /// to evaluate the interpolant at. It computes the interpolant and outputs its evaluation at the /// given point. #[derive(Clone, Debug)] -pub(crate) struct InterpolationGate, const D: usize> -where - F::Extension: Extendable, -{ +pub(crate) struct InterpolationGate, const D: usize> { num_points: usize, _phantom: PhantomData, } -impl, const D: usize> InterpolationGate -where - F::Extension: Extendable, -{ +impl, const D: usize> InterpolationGate { pub fn new(num_points: usize) -> GateRef { let gate = Self { num_points, @@ -99,10 +93,7 @@ where } } -impl, const D: usize> Gate for InterpolationGate -where - F::Extension: Extendable, -{ +impl, const D: usize> Gate for InterpolationGate { fn id(&self) -> String { format!("{:?}", self, D) } @@ -111,19 +102,19 @@ where let mut constraints = Vec::with_capacity(self.num_constraints()); let coeffs = (0..self.num_points) - .map(|i| vars.get_local_ext_ext(self.wires_coeff(i))) + .map(|i| vars.get_local_ext_algebra(self.wires_coeff(i))) .collect(); - let interpolant = PolynomialCoeffs::new(coeffs); + let interpolant = PolynomialCoeffsAlgebra::new(coeffs); for i in 0..self.num_points { let point = vars.local_wires[self.wire_point(i)]; - let value = vars.get_local_ext_ext(self.wires_value(i)); + let value = vars.get_local_ext_algebra(self.wires_value(i)); let computed_value = interpolant.eval(point.into()); constraints.extend(&(value - computed_value).to_basefield_array()); } - let evaluation_point = vars.get_local_ext_ext(self.wires_evaluation_point()); - let evaluation_value = vars.get_local_ext_ext(self.wires_evaluation_value()); + let evaluation_point = vars.get_local_ext_algebra(self.wires_evaluation_point()); + let evaluation_value = vars.get_local_ext_algebra(self.wires_evaluation_value()); let computed_evaluation_value = interpolant.eval(evaluation_point); constraints.extend(&(evaluation_value - computed_evaluation_value).to_basefield_array()); @@ -138,27 +129,27 @@ where let mut constraints = Vec::with_capacity(self.num_constraints()); let coeffs = (0..self.num_points) - .map(|i| vars.get_local_ext_ext(self.wires_coeff(i))) + .map(|i| vars.get_local_ext_algebra(self.wires_coeff(i))) .collect(); - let interpolant = PolynomialCoeffsExtExtTarget(coeffs); + let interpolant = PolynomialCoeffsExtAlgebraTarget(coeffs); for i in 0..self.num_points { let point = vars.local_wires[self.wire_point(i)]; - let value = vars.get_local_ext_ext(self.wires_value(i)); + let value = vars.get_local_ext_algebra(self.wires_value(i)); let computed_value = interpolant.eval_scalar(builder, point); constraints.extend( &builder - .sub_ext_ext(value, computed_value) + .sub_ext_algebra(value, computed_value) .to_ext_target_array(), ); } - let evaluation_point = vars.get_local_ext_ext(self.wires_evaluation_point()); - let evaluation_value = vars.get_local_ext_ext(self.wires_evaluation_value()); + let evaluation_point = vars.get_local_ext_algebra(self.wires_evaluation_point()); + let evaluation_value = vars.get_local_ext_algebra(self.wires_evaluation_value()); let computed_evaluation_value = interpolant.eval(builder, evaluation_point); constraints.extend( &builder - .sub_ext_ext(evaluation_value, computed_evaluation_value) + .sub_ext_algebra(evaluation_value, computed_evaluation_value) .to_ext_target_array(), ); @@ -199,19 +190,13 @@ where } } -struct InterpolationGenerator, const D: usize> -where - F::Extension: Extendable, -{ +struct InterpolationGenerator, const D: usize> { gate_index: usize, gate: InterpolationGate, _phantom: PhantomData, } -impl, const D: usize> SimpleGenerator for InterpolationGenerator -where - F::Extension: Extendable, -{ +impl, const D: usize> SimpleGenerator for InterpolationGenerator { fn dependencies(&self) -> Vec { let local_target = |input| { Target::Wire(Wire { @@ -281,9 +266,14 @@ mod tests { use std::marker::PhantomData; use crate::field::crandall_field::CrandallField; + use crate::field::extension_field::quartic::QuarticCrandallField; + use crate::field::extension_field::FieldExtension; + use crate::field::field::Field; use crate::gates::gate::Gate; use crate::gates::gate_testing::test_low_degree; use crate::gates::interpolation::InterpolationGate; + use crate::polynomial::polynomial::PolynomialCoeffs; + use crate::vars::EvaluationVars; #[test] fn wire_indices() { @@ -310,4 +300,67 @@ mod tests { type F = CrandallField; test_low_degree(InterpolationGate::::new(4)); } + + #[test] + fn test_gate_constraint() { + type F = CrandallField; + type FF = QuarticCrandallField; + const D: usize = 4; + + /// Returns the local wires for an interpolation gate for given coeffs, points and eval point. + fn get_wires( + num_points: usize, + coeffs: PolynomialCoeffs, + points: Vec, + eval_point: FF, + ) -> Vec { + let mut v = vec![F::ZERO; num_points * 5 + (coeffs.len() + 3) * D]; + for j in 0..num_points { + v[j] = points[j]; + } + for j in 0..num_points { + for i in 0..D { + v[num_points + D * j + i] = >::to_basefield_array( + &coeffs.eval(points[j].into()), + )[i]; + } + } + for i in 0..D { + v[num_points * 5 + i] = + >::to_basefield_array(&eval_point)[i]; + } + for i in 0..D { + v[num_points * 5 + D + i] = + >::to_basefield_array(&coeffs.eval(eval_point))[i]; + } + for i in 0..coeffs.len() { + for (j, input) in + (0..D).zip(num_points * 5 + (2 + i) * D..num_points * 5 + (3 + i) * D) + { + v[input] = >::to_basefield_array(&coeffs.coeffs[i])[j]; + } + } + v.iter().map(|&x| x.into()).collect::>() + } + + // Get a working row for InterpolationGate. + let coeffs = PolynomialCoeffs::new(vec![FF::rand(), FF::rand()]); + let points = vec![F::rand(), F::rand()]; + let eval_point = FF::rand(); + let gate = InterpolationGate:: { + num_points: 2, + _phantom: PhantomData, + }; + let vars = EvaluationVars { + local_constants: &[], + local_wires: &get_wires(2, coeffs, points, eval_point), + }; + + assert!( + gate.eval_unfiltered(vars.clone()) + .iter() + .all(|x| x.is_zero()), + "Gate constraints are not satisfied." + ); + } } diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index 2b1739d5..bcb2c8cd 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -67,7 +67,7 @@ impl Challenger { { let OpeningSet { constants, - plonk_sigmas, + plonk_s_sigmas, wires, plonk_zs, plonk_zs_right, @@ -75,7 +75,7 @@ impl Challenger { } = os; for v in &[ constants, - plonk_sigmas, + plonk_s_sigmas, wires, plonk_zs, plonk_zs_right, diff --git a/src/plonk_common.rs b/src/plonk_common.rs index daa131ef..8ac34d29 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -1,4 +1,5 @@ use crate::circuit_builder::CircuitBuilder; +use crate::circuit_data::CommonCircuitData; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field::Field; @@ -6,6 +7,105 @@ use crate::gates::gate::GateRef; use crate::target::Target; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; +/// Evaluate the vanishing polynomial at `x`. In this context, the vanishing polynomial is a random +/// linear combination of gate constraints, plus some other terms relating to the permutation +/// argument. All such terms should vanish on `H`. +pub(crate) fn eval_vanishing_poly, const D: usize>( + common_data: &CommonCircuitData, + x: F::Extension, + vars: EvaluationVars, + local_plonk_zs: &[F::Extension], + next_plonk_zs: &[F::Extension], + s_sigmas: &[F::Extension], + betas: &[F], + gammas: &[F], + alphas: &[F], +) -> Vec { + let constraint_terms = + evaluate_gate_constraints(&common_data.gates, common_data.num_gate_constraints, vars); + + // The L_1(x) (Z(x) - 1) vanishing terms. + let mut vanishing_z_1_terms = Vec::new(); + // The Z(x) f'(x) - g'(x) Z(g x) terms. + let mut vanishing_v_shift_terms = Vec::new(); + + for i in 0..common_data.config.num_challenges { + let z_x = local_plonk_zs[i]; + let z_gz = next_plonk_zs[i]; + vanishing_z_1_terms.push(eval_l_1(common_data.degree(), x) * (z_x - F::Extension::ONE)); + + let mut f_prime = F::Extension::ONE; + let mut g_prime = F::Extension::ONE; + for j in 0..common_data.config.num_routed_wires { + let wire_value = vars.local_wires[j]; + let k_i = common_data.k_is[j]; + let s_id = x * k_i.into(); + let s_sigma = s_sigmas[j]; + f_prime *= wire_value + s_id * betas[i].into() + gammas[i].into(); + g_prime *= wire_value + s_sigma * betas[i].into() + gammas[i].into(); + } + vanishing_v_shift_terms.push(f_prime * z_x - g_prime * z_gz); + } + + let vanishing_terms = [ + vanishing_z_1_terms, + vanishing_v_shift_terms, + constraint_terms, + ] + .concat(); + + let alphas = &alphas.iter().map(|&a| a.into()).collect::>(); + reduce_with_powers_multi(&vanishing_terms, alphas) +} + +/// Like `eval_vanishing_poly`, but specialized for base field points. +pub(crate) fn eval_vanishing_poly_base, const D: usize>( + common_data: &CommonCircuitData, + x: F, + vars: EvaluationVarsBase, + local_plonk_zs: &[F], + next_plonk_zs: &[F], + s_sigmas: &[F], + betas: &[F], + gammas: &[F], + alphas: &[F], +) -> Vec { + let constraint_terms = + evaluate_gate_constraints_base(&common_data.gates, common_data.num_gate_constraints, vars); + + // The L_1(x) (Z(x) - 1) vanishing terms. + let mut vanishing_z_1_terms = Vec::new(); + // The Z(x) f'(x) - g'(x) Z(g x) terms. + let mut vanishing_v_shift_terms = Vec::new(); + + for i in 0..common_data.config.num_challenges { + let z_x = local_plonk_zs[i]; + let z_gz = next_plonk_zs[i]; + vanishing_z_1_terms.push(eval_l_1(common_data.degree(), x) * (z_x - F::ONE)); + + let mut f_prime = F::ONE; + let mut g_prime = F::ONE; + for j in 0..common_data.config.num_routed_wires { + let wire_value = vars.local_wires[j]; + let k_i = common_data.k_is[j]; + let s_id = k_i * x; + let s_sigma = s_sigmas[j]; + f_prime *= wire_value + betas[i] * s_id + gammas[i]; + g_prime *= wire_value + betas[i] * s_sigma + gammas[i]; + } + vanishing_v_shift_terms.push(f_prime * z_x - g_prime * z_gz); + } + + let vanishing_terms = [ + vanishing_z_1_terms, + vanishing_v_shift_terms, + constraint_terms, + ] + .concat(); + + reduce_with_powers_multi(&vanishing_terms, alphas) +} + /// Evaluates all gate constraints. /// /// `num_gate_constraints` is the largest number of constraints imposed by any gate. It is not diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 9eee99cc..22fc4114 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -126,7 +126,7 @@ impl ListPolynomialCommitment { poly_count += 1; &(&acc * alpha) + &p.to_extension() }); - let composition_eval = [&os.constants, &os.plonk_sigmas, &os.quotient_polys] + let composition_eval = [&os.constants, &os.plonk_s_sigmas, &os.quotient_polys] .iter() .flat_map(|v| v.iter()) .rev() diff --git a/src/proof.rs b/src/proof.rs index 51e32938..59b7bb9c 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -147,7 +147,7 @@ pub struct FriProofTarget { /// The purported values of each polynomial at a single point. pub struct OpeningSet, const D: usize> { pub constants: Vec, - pub plonk_sigmas: Vec, + pub plonk_s_sigmas: Vec, pub wires: Vec, pub plonk_zs: Vec, pub plonk_zs_right: Vec, @@ -172,7 +172,7 @@ impl, const D: usize> OpeningSet { }; Self { constants: eval_commitment(z, constant_commitment), - plonk_sigmas: eval_commitment(z, plonk_sigmas_commitment), + plonk_s_sigmas: eval_commitment(z, plonk_sigmas_commitment), wires: eval_commitment(z, wires_commitment), plonk_zs: eval_commitment(z, plonk_zs_commitment), plonk_zs_right: eval_commitment(g * z, plonk_zs_commitment), diff --git a/src/prover.rs b/src/prover.rs index d7dc0df8..7a492eaa 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -9,7 +9,7 @@ use crate::field::fft::ifft; use crate::field::field::Field; use crate::generator::generate_partial_witness; use crate::plonk_challenger::Challenger; -use crate::plonk_common::{eval_l_1, evaluate_gate_constraints_base, reduce_with_powers_multi}; +use crate::plonk_common::eval_vanishing_poly_base; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::Proof; @@ -115,7 +115,7 @@ pub(crate) fn prove, const D: usize>( let zeta = challenger.get_extension_challenge(); - let (opening_proof, openings) = timed!( + let (opening_proof, mut openings) = timed!( ListPolynomialCommitment::open_plonk( &[ &prover_data.constants_commitment, @@ -192,7 +192,7 @@ fn compute_vanishing_polys, const D: usize>( local_constants, local_wires, }; - compute_vanishing_poly_entry( + eval_vanishing_poly_base( common_data, x, vars, @@ -212,56 +212,6 @@ fn compute_vanishing_polys, const D: usize>( .collect() } -/// Evaluate the vanishing polynomial at `x`. In this context, the vanishing polynomial is a random -/// linear combination of gate constraints, plus some other terms relating to the permutation -/// argument. All such terms should vanish on `H`. -fn compute_vanishing_poly_entry, const D: usize>( - common_data: &CommonCircuitData, - x: F, - vars: EvaluationVarsBase, - local_plonk_zs: &[F], - next_plonk_zs: &[F], - s_sigmas: &[F], - betas: &[F], - gammas: &[F], - alphas: &[F], -) -> Vec { - let constraint_terms = - evaluate_gate_constraints_base(&common_data.gates, common_data.num_gate_constraints, vars); - - // The L_1(x) (Z(x) - 1) vanishing terms. - let mut vanishing_z_1_terms = Vec::new(); - // The Z(x) f'(x) - g'(x) Z(g x) terms. - let mut vanishing_v_shift_terms = Vec::new(); - - for i in 0..common_data.config.num_challenges { - let z_x = local_plonk_zs[i]; - let z_gz = next_plonk_zs[i]; - vanishing_z_1_terms.push(eval_l_1(common_data.degree(), x) * (z_x - F::ONE)); - - let mut f_prime = F::ONE; - let mut g_prime = F::ONE; - for j in 0..common_data.config.num_routed_wires { - let wire_value = vars.local_wires[j]; - let k_i = common_data.k_is[j]; - let s_id = k_i * x; - let s_sigma = s_sigmas[j]; - f_prime *= wire_value + betas[i] * s_id + gammas[i]; - g_prime *= wire_value + betas[i] * s_sigma + gammas[i]; - } - vanishing_v_shift_terms.push(f_prime * z_x - g_prime * z_gz); - } - - let vanishing_terms = [ - vanishing_z_1_terms, - vanishing_v_shift_terms, - constraint_terms, - ] - .concat(); - - reduce_with_powers_multi(&vanishing_terms, alphas) -} - fn compute_wire_polynomial( input: usize, witness: &PartialWitness, diff --git a/src/vars.rs b/src/vars.rs index 88d0759a..74f15f23 100644 --- a/src/vars.rs +++ b/src/vars.rs @@ -1,8 +1,9 @@ use std::convert::TryInto; use std::ops::Range; -use crate::field::extension_field::target::{ExtensionExtensionTarget, ExtensionTarget}; -use crate::field::extension_field::{Extendable, FieldExtension}; +use crate::field::extension_field::algebra::ExtensionAlgebra; +use crate::field::extension_field::target::{ExtensionAlgebraTarget, ExtensionTarget}; +use crate::field::extension_field::Extendable; use crate::field::field::Field; #[derive(Copy, Clone)] @@ -18,16 +19,13 @@ pub struct EvaluationVarsBase<'a, F: Field> { } impl<'a, F: Extendable, const D: usize> EvaluationVars<'a, F, D> { - pub fn get_local_ext_ext( + pub fn get_local_ext_algebra( &self, wire_range: Range, - ) -> <>::Extension as Extendable>::Extension - where - F::Extension: Extendable, - { + ) -> ExtensionAlgebra { debug_assert_eq!(wire_range.len(), D); let arr = self.local_wires[wire_range].try_into().unwrap(); - <>::Extension as Extendable>::Extension::from_basefield_array(arr) + ExtensionAlgebra::from_basefield_array(arr) } } @@ -38,9 +36,9 @@ pub struct EvaluationTargets<'a, const D: usize> { } impl<'a, const D: usize> EvaluationTargets<'a, D> { - pub fn get_local_ext_ext(&self, wire_range: Range) -> ExtensionExtensionTarget { + pub fn get_local_ext_algebra(&self, wire_range: Range) -> ExtensionAlgebraTarget { debug_assert_eq!(wire_range.len(), D); let arr = self.local_wires[wire_range].try_into().unwrap(); - ExtensionExtensionTarget(arr) + ExtensionAlgebraTarget(arr) } } diff --git a/src/verifier.rs b/src/verifier.rs index 98568c13..d2a7bb63 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -1,9 +1,11 @@ -use anyhow::Result; +use anyhow::{ensure, Result}; use crate::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData}; use crate::field::extension_field::Extendable; use crate::plonk_challenger::Challenger; +use crate::plonk_common::{eval_vanishing_poly, eval_zero_poly}; use crate::proof::Proof; +use crate::vars::EvaluationVars; pub(crate) fn verify, const D: usize>( proof: Proof, @@ -29,7 +31,35 @@ pub(crate) fn verify, const D: usize>( challenger.observe_hash(&proof.quotient_polys_root); let zeta = challenger.get_extension_challenge(); - // TODO: Compute PI(zeta), Z_H(zeta), etc. and check the identity at zeta. + let local_constants = &proof.openings.constants; + let local_wires = &proof.openings.wires; + let vars = EvaluationVars { + local_constants, + local_wires, + }; + let local_plonk_zs = &proof.openings.plonk_zs; + let next_plonk_zs = &proof.openings.plonk_zs_right; + let s_sigmas = &proof.openings.plonk_s_sigmas; + + // Evaluate the vanishing polynomial at our challenge point, zeta. + let vanishing_polys_zeta = eval_vanishing_poly( + common_data, + zeta, + vars, + local_plonk_zs, + next_plonk_zs, + s_sigmas, + &betas, + &gammas, + &alphas, + ); + + // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta. + let quotient_polys_zeta = proof.openings.quotient_polys; + let z_h_zeta = eval_zero_poly(common_data.degree(), zeta); + for i in 0..num_challenges { + ensure!(vanishing_polys_zeta[i] == z_h_zeta * quotient_polys_zeta[i]); + } let evaluations = proof.openings;