Merge branch 'main' into recursive_verifier

This commit is contained in:
wborgeaud 2021-06-09 10:04:45 +02:00
commit 7f63276623
16 changed files with 528 additions and 425 deletions

View File

@ -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;

View File

@ -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<F_D>` 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<F: OEF<D>, const D: usize>([F; D]);
impl<F: OEF<D>, const D: usize> ExtensionAlgebra<F, D> {
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<F: OEF<D>, const D: usize> From<F> for ExtensionAlgebra<F, D> {
fn from(x: F) -> Self {
let mut arr = [F::ZERO; D];
arr[0] = x;
Self(arr)
}
}
impl<F: OEF<D>, const D: usize> Display for ExtensionAlgebra<F, D> {
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<F: OEF<D>, const D: usize> Debug for ExtensionAlgebra<F, D> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, f)
}
}
impl<F: OEF<D>, const D: usize> Neg for ExtensionAlgebra<F, D> {
type Output = Self;
#[inline]
fn neg(self) -> Self {
let mut arr = self.0;
arr.iter_mut().for_each(|x| *x = -*x);
Self(arr)
}
}
impl<F: OEF<D>, const D: usize> Add for ExtensionAlgebra<F, D> {
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<F: OEF<D>, const D: usize> AddAssign for ExtensionAlgebra<F, D> {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl<F: OEF<D>, const D: usize> Sum for ExtensionAlgebra<F, D> {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::ZERO, |acc, x| acc + x)
}
}
impl<F: OEF<D>, const D: usize> Sub for ExtensionAlgebra<F, D> {
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<F: OEF<D>, const D: usize> SubAssign for ExtensionAlgebra<F, D> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl<F: OEF<D>, const D: usize> Mul for ExtensionAlgebra<F, D> {
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<F: OEF<D>, const D: usize> MulAssign for ExtensionAlgebra<F, D> {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<F: OEF<D>, const D: usize> Product for ExtensionAlgebra<F, D> {
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::one(), |acc, x| acc * x)
}
}
/// A polynomial in coefficient form.
#[derive(Clone, Debug)]
pub struct PolynomialCoeffsAlgebra<F: OEF<D>, const D: usize> {
pub(crate) coeffs: Vec<ExtensionAlgebra<F, D>>,
}
impl<F: OEF<D>, const D: usize> PolynomialCoeffsAlgebra<F, D> {
pub fn new(coeffs: Vec<ExtensionAlgebra<F, D>>) -> Self {
PolynomialCoeffsAlgebra { coeffs }
}
pub fn eval(&self, x: ExtensionAlgebra<F, D>) -> ExtensionAlgebra<F, D> {
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<F: Extendable<D>, 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::<Vec<_>>());
arr1.copy_from_slice(&y.iter().map(to_field).collect::<Vec<_>>());
(
<F as Extendable<D>>::Extension::from_basefield_array(arr0),
<F as Extendable<D>>::Extension::from_basefield_array(arr1),
)
};
// Standard MLE formula.
let selector = |xs: Vec<ZeroOne>, 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>| -> [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::<CrandallField, 1>();
}
}
mod quadratic {
use super::*;
#[test]
fn test_algebra() {
test_extension_algebra::<CrandallField, 2>();
}
}
mod quartic {
use super::*;
#[test]
fn test_algebra() {
test_extension_algebra::<CrandallField, 4>();
}
}
}

View File

@ -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.

View File

@ -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;

View File

@ -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.<x> = PolynomialRing(F)
// assert (x^4 - 3).is_irreducible()
// F4.<y> = F.extension(x^4 - 3)
// PR_F4.<z> = PolynomialRing(F4)
// assert (x^4 - y).is_irreducible()
// F44.<w> = 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<<Self as FieldExtension<4>>::BaseField> for QuarticQuarticCrandallField {
fn from(x: <Self as FieldExtension<4>>::BaseField) -> Self {
Self([
x,
<Self as FieldExtension<4>>::BaseField::ZERO,
<Self as FieldExtension<4>>::BaseField::ZERO,
<Self as FieldExtension<4>>::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<Self> {
todo!()
}
fn to_canonical_u64(&self) -> u64 {
panic!("Doesn't fit!")
}
fn from_canonical_u64(n: u64) -> Self {
<Self as FieldExtension<4>>::BaseField::from_canonical_u64(n).into()
}
fn rand_from_rng<R: Rng>(rng: &mut R) -> Self {
Self([
<Self as FieldExtension<4>>::BaseField::rand_from_rng(rng),
<Self as FieldExtension<4>>::BaseField::rand_from_rng(rng),
<Self as FieldExtension<4>>::BaseField::rand_from_rng(rng),
<Self as FieldExtension<4>>::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<I: Iterator<Item = Self>>(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 + <Self as OEF<4>>::W * (a1 * b3 + a2 * b2 + a3 * b1);
let c1 = a0 * b1 + a1 * b0 + <Self as OEF<4>>::W * (a2 * b3 + a3 * b2);
let c2 = a0 * b2 + a1 * b1 + a2 * b0 + <Self as OEF<4>>::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<I: Iterator<Item = Self>>(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;
}
}

View File

@ -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<const D: usize> ExtensionTarget<D> {
/// `Target`s representing an element of an extension of an extension field.
#[derive(Copy, Clone, Debug)]
pub struct ExtensionExtensionTarget<const D: usize>(pub [ExtensionTarget<D>; D]);
pub struct ExtensionAlgebraTarget<const D: usize>(pub [ExtensionTarget<D>; D]);
impl<const D: usize> ExtensionExtensionTarget<D> {
impl<const D: usize> ExtensionAlgebraTarget<D> {
pub fn to_ext_target_array(&self) -> [ExtensionTarget<D>; D] {
self.0
}
@ -47,19 +48,16 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
ExtensionTarget(parts)
}
pub fn constant_ext_ext(
pub fn constant_ext_algebra(
&mut self,
c: <<F as Extendable<D>>::Extension as Extendable<D>>::Extension,
) -> ExtensionExtensionTarget<D>
where
F::Extension: Extendable<D>,
{
c: ExtensionAlgebra<F::Extension, D>,
) -> ExtensionAlgebraTarget<D> {
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<D> {
@ -74,11 +72,8 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
self.constant_extension(F::Extension::TWO)
}
pub fn zero_ext_ext(&mut self) -> ExtensionExtensionTarget<D>
where
F::Extension: Extendable<D>,
{
self.constant_ext_ext(<<F as Extendable<D>>::Extension as Extendable<D>>::Extension::ZERO)
pub fn zero_ext_algebra(&mut self) -> ExtensionAlgebraTarget<D> {
self.constant_ext_algebra(ExtensionAlgebra::ZERO)
}
pub fn add_extension(
@ -92,11 +87,11 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
a
}
pub fn add_ext_ext(
pub fn add_ext_algebra(
&mut self,
mut a: ExtensionExtensionTarget<D>,
b: ExtensionExtensionTarget<D>,
) -> ExtensionExtensionTarget<D> {
mut a: ExtensionAlgebraTarget<D>,
b: ExtensionAlgebraTarget<D>,
) -> ExtensionAlgebraTarget<D> {
for i in 0..D {
a.0[i] = self.add_extension(a.0[i], b.0[i]);
}
@ -122,11 +117,11 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
a
}
pub fn sub_ext_ext(
pub fn sub_ext_algebra(
&mut self,
mut a: ExtensionExtensionTarget<D>,
b: ExtensionExtensionTarget<D>,
) -> ExtensionExtensionTarget<D> {
mut a: ExtensionAlgebraTarget<D>,
b: ExtensionAlgebraTarget<D>,
) -> ExtensionAlgebraTarget<D> {
for i in 0..D {
a.0[i] = self.sub_extension(a.0[i], b.0[i]);
}
@ -152,29 +147,25 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
ExtensionTarget(res)
}
pub fn mul_ext_ext(
pub fn mul_ext_algebra(
&mut self,
mut a: ExtensionExtensionTarget<D>,
b: ExtensionExtensionTarget<D>,
) -> ExtensionExtensionTarget<D>
where
F::Extension: Extendable<D>,
{
a: ExtensionAlgebraTarget<D>,
b: ExtensionAlgebraTarget<D>,
) -> ExtensionAlgebraTarget<D> {
let mut res = [self.zero_extension(); D];
let w = self
.constant_extension(<<F as Extendable<D>>::Extension as Extendable<D>>::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<D>]) -> ExtensionTarget<D> {
@ -207,14 +198,11 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// 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<D>,
mut b: ExtensionExtensionTarget<D>,
) -> ExtensionExtensionTarget<D>
where
F::Extension: Extendable<D>,
{
mut b: ExtensionAlgebraTarget<D>,
) -> ExtensionAlgebraTarget<D> {
for i in 0..D {
b.0[i] = self.mul_extension(a, b.0[i]);
}

View File

@ -166,7 +166,7 @@ fn fri_combine_initial<F: Field + Extendable<D>, 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))

View File

@ -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<const D: usize> PolynomialCoeffsExtTarget<D> {
}
}
pub struct PolynomialCoeffsExtExtTarget<const D: usize>(pub Vec<ExtensionExtensionTarget<D>>);
pub struct PolynomialCoeffsExtAlgebraTarget<const D: usize>(pub Vec<ExtensionAlgebraTarget<D>>);
impl<const D: usize> PolynomialCoeffsExtExtTarget<D> {
impl<const D: usize> PolynomialCoeffsExtAlgebraTarget<D> {
pub fn eval_scalar<F>(
&self,
builder: &mut CircuitBuilder<F, D>,
point: ExtensionTarget<D>,
) -> ExtensionExtensionTarget<D>
) -> ExtensionAlgebraTarget<D>
where
F: Extendable<D>,
F::Extension: Extendable<D>,
{
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<const D: usize> PolynomialCoeffsExtExtTarget<D> {
pub fn eval<F>(
&self,
builder: &mut CircuitBuilder<F, D>,
point: ExtensionExtensionTarget<D>,
) -> ExtensionExtensionTarget<D>
point: ExtensionAlgebraTarget<D>,
) -> ExtensionAlgebraTarget<D>
where
F: Extendable<D>,
F::Extension: Extendable<D>,
{
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
}

View File

@ -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<F: Extendable<D>, const D: usize>
where
F::Extension: Extendable<D>,
{
pub(crate) struct InterpolationGate<F: Extendable<D>, const D: usize> {
num_points: usize,
_phantom: PhantomData<F>,
}
impl<F: Extendable<D>, const D: usize> InterpolationGate<F, D>
where
F::Extension: Extendable<D>,
{
impl<F: Extendable<D>, const D: usize> InterpolationGate<F, D> {
pub fn new(num_points: usize) -> GateRef<F, D> {
let gate = Self {
num_points,
@ -99,10 +93,7 @@ where
}
}
impl<F: Extendable<D>, const D: usize> Gate<F, D> for InterpolationGate<F, D>
where
F::Extension: Extendable<D>,
{
impl<F: Extendable<D>, const D: usize> Gate<F, D> for InterpolationGate<F, D> {
fn id(&self) -> String {
format!("{:?}<D={}>", 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<F: Extendable<D>, const D: usize>
where
F::Extension: Extendable<D>,
{
struct InterpolationGenerator<F: Extendable<D>, const D: usize> {
gate_index: usize,
gate: InterpolationGate<F, D>,
_phantom: PhantomData<F>,
}
impl<F: Extendable<D>, const D: usize> SimpleGenerator<F> for InterpolationGenerator<F, D>
where
F::Extension: Extendable<D>,
{
impl<F: Extendable<D>, const D: usize> SimpleGenerator<F> for InterpolationGenerator<F, D> {
fn dependencies(&self) -> Vec<Target> {
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::<F, 4>::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<FF>,
points: Vec<F>,
eval_point: FF,
) -> Vec<FF> {
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] = <FF as FieldExtension<D>>::to_basefield_array(
&coeffs.eval(points[j].into()),
)[i];
}
}
for i in 0..D {
v[num_points * 5 + i] =
<FF as FieldExtension<D>>::to_basefield_array(&eval_point)[i];
}
for i in 0..D {
v[num_points * 5 + D + i] =
<FF as FieldExtension<D>>::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] = <FF as FieldExtension<D>>::to_basefield_array(&coeffs.coeffs[i])[j];
}
}
v.iter().map(|&x| x.into()).collect::<Vec<_>>()
}
// 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::<F, D> {
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."
);
}
}

View File

@ -67,7 +67,7 @@ impl<F: Field> Challenger<F> {
{
let OpeningSet {
constants,
plonk_sigmas,
plonk_s_sigmas,
wires,
plonk_zs,
plonk_zs_right,
@ -75,7 +75,7 @@ impl<F: Field> Challenger<F> {
} = os;
for v in &[
constants,
plonk_sigmas,
plonk_s_sigmas,
wires,
plonk_zs,
plonk_zs_right,

View File

@ -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<F: Extendable<D>, const D: usize>(
common_data: &CommonCircuitData<F, D>,
x: F::Extension,
vars: EvaluationVars<F, D>,
local_plonk_zs: &[F::Extension],
next_plonk_zs: &[F::Extension],
s_sigmas: &[F::Extension],
betas: &[F],
gammas: &[F],
alphas: &[F],
) -> Vec<F::Extension> {
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::<Vec<_>>();
reduce_with_powers_multi(&vanishing_terms, alphas)
}
/// Like `eval_vanishing_poly`, but specialized for base field points.
pub(crate) fn eval_vanishing_poly_base<F: Extendable<D>, const D: usize>(
common_data: &CommonCircuitData<F, D>,
x: F,
vars: EvaluationVarsBase<F>,
local_plonk_zs: &[F],
next_plonk_zs: &[F],
s_sigmas: &[F],
betas: &[F],
gammas: &[F],
alphas: &[F],
) -> Vec<F> {
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

View File

@ -126,7 +126,7 @@ impl<F: Field> ListPolynomialCommitment<F> {
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()

View File

@ -147,7 +147,7 @@ pub struct FriProofTarget<const D: usize> {
/// The purported values of each polynomial at a single point.
pub struct OpeningSet<F: Field + Extendable<D>, const D: usize> {
pub constants: Vec<F::Extension>,
pub plonk_sigmas: Vec<F::Extension>,
pub plonk_s_sigmas: Vec<F::Extension>,
pub wires: Vec<F::Extension>,
pub plonk_zs: Vec<F::Extension>,
pub plonk_zs_right: Vec<F::Extension>,
@ -172,7 +172,7 @@ impl<F: Field + Extendable<D>, const D: usize> OpeningSet<F, D> {
};
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),

View File

@ -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<F: Extendable<D>, 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<F: Extendable<D>, 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<F: Extendable<D>, 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<F: Extendable<D>, const D: usize>(
common_data: &CommonCircuitData<F, D>,
x: F,
vars: EvaluationVarsBase<F>,
local_plonk_zs: &[F],
next_plonk_zs: &[F],
s_sigmas: &[F],
betas: &[F],
gammas: &[F],
alphas: &[F],
) -> Vec<F> {
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<F: Field>(
input: usize,
witness: &PartialWitness<F>,

View File

@ -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<D>, const D: usize> EvaluationVars<'a, F, D> {
pub fn get_local_ext_ext(
pub fn get_local_ext_algebra(
&self,
wire_range: Range<usize>,
) -> <<F as Extendable<D>>::Extension as Extendable<D>>::Extension
where
F::Extension: Extendable<D>,
{
) -> ExtensionAlgebra<F::Extension, D> {
debug_assert_eq!(wire_range.len(), D);
let arr = self.local_wires[wire_range].try_into().unwrap();
<<F as Extendable<D>>::Extension as Extendable<D>>::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<usize>) -> ExtensionExtensionTarget<D> {
pub fn get_local_ext_algebra(&self, wire_range: Range<usize>) -> ExtensionAlgebraTarget<D> {
debug_assert_eq!(wire_range.len(), D);
let arr = self.local_wires[wire_range].try_into().unwrap();
ExtensionExtensionTarget(arr)
ExtensionAlgebraTarget(arr)
}
}

View File

@ -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<F: Extendable<D>, const D: usize>(
proof: Proof<F, D>,
@ -29,7 +31,35 @@ pub(crate) fn verify<F: Extendable<D>, 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;