diff --git a/src/field/extension_field/mod.rs b/src/field/extension_field/mod.rs index 67329c3c..46c99f11 100644 --- a/src/field/extension_field/mod.rs +++ b/src/field/extension_field/mod.rs @@ -1,16 +1,43 @@ -use crate::field::extension_field::quadratic::QuadraticFieldExtension; -use crate::field::extension_field::quartic::QuarticFieldExtension; use crate::field::field::Field; pub mod quadratic; pub mod quartic; +pub mod target; + +/// Optimal extension field trait. +/// A degree `d` field extension is optimal if there exists a base field element `W`, +/// such that the extension is `F[X]/(X^d-W)`. +#[allow(clippy::upper_case_acronyms)] +pub trait OEF: FieldExtension { + // Element W of BaseField, such that `X^d - W` is irreducible over BaseField. + const W: Self::BaseField; + + /// Frobenius automorphisms: x -> x^p, where p is the order of BaseField. + fn frobenius(&self) -> Self { + let arr = self.to_basefield_array(); + let k = (Self::BaseField::ORDER - 1) / (D as u64); + let z0 = Self::W.exp(k); + let mut z = Self::BaseField::ONE; + let mut res = [Self::BaseField::ZERO; D]; + for i in 0..D { + res[i] = arr[i] * z; + z *= z0; + } + + Self::from_basefield_array(res) + } +} + +impl OEF<1> for F { + const W: Self::BaseField = F::ZERO; +} pub trait Extendable: Sized { - type Extension: Field + FieldExtension + From; + type Extension: Field + OEF + From; } impl Extendable<1> for F { - type Extension = Self; + type Extension = F; } pub trait FieldExtension: Field { @@ -21,6 +48,10 @@ pub trait FieldExtension: Field { fn from_basefield_array(arr: [Self::BaseField; D]) -> Self; fn from_basefield(x: Self::BaseField) -> Self; + + fn is_in_basefield(&self) -> bool { + self.to_basefield_array()[1..].iter().all(|x| x.is_zero()) + } } impl FieldExtension<1> for F { @@ -39,38 +70,6 @@ impl FieldExtension<1> for F { } } -impl FieldExtension<2> for FE { - type BaseField = FE::BaseField; - - fn to_basefield_array(&self) -> [Self::BaseField; 2] { - self.to_canonical_representation() - } - - fn from_basefield_array(arr: [Self::BaseField; 2]) -> Self { - Self::from_canonical_representation(arr) - } - - fn from_basefield(x: Self::BaseField) -> Self { - x.into() - } -} - -impl FieldExtension<4> for FE { - type BaseField = FE::BaseField; - - fn to_basefield_array(&self) -> [Self::BaseField; 4] { - self.to_canonical_representation() - } - - fn from_basefield_array(arr: [Self::BaseField; 4]) -> Self { - Self::from_canonical_representation(arr) - } - - fn from_basefield(x: Self::BaseField) -> Self { - x.into() - } -} - /// Flatten the slice by sending every extension field element to its D-sized canonical representation. pub fn flatten(l: &[F::Extension]) -> Vec where diff --git a/src/field/extension_field/quadratic.rs b/src/field/extension_field/quadratic.rs index 25b42e49..fc74ec88 100644 --- a/src/field/extension_field/quadratic.rs +++ b/src/field/extension_field/quadratic.rs @@ -1,4 +1,5 @@ use crate::field::crandall_field::CrandallField; +use crate::field::extension_field::{FieldExtension, OEF}; use crate::field::field::Field; use rand::Rng; use std::fmt::{Debug, Display, Formatter}; @@ -6,61 +7,41 @@ use std::hash::{Hash, Hasher}; use std::iter::{Product, Sum}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -pub trait QuadraticFieldExtension: - Field + From<::BaseField> -{ - type BaseField: Field; - - // Element W of BaseField, such that `X^2 - W` is irreducible over BaseField. - const W: Self::BaseField; - - fn to_canonical_representation(&self) -> [Self::BaseField; 2]; - - fn from_canonical_representation(v: [Self::BaseField; 2]) -> Self; - - fn is_in_basefield(&self) -> bool { - self.to_canonical_representation()[1..] - .iter() - .all(|x| x.is_zero()) - } - - /// Frobenius automorphisms: x -> x^p, where p is the order of BaseField. - fn frobenius(&self) -> Self { - let [a0, a1] = self.to_canonical_representation(); - let k = (Self::BaseField::ORDER - 1) / 2; - let z = Self::W.exp(k); - - Self::from_canonical_representation([a0, a1 * z]) - } -} - #[derive(Copy, Clone)] pub struct QuadraticCrandallField([CrandallField; 2]); -impl QuadraticFieldExtension for QuadraticCrandallField { - type BaseField = CrandallField; +impl OEF<2> for QuadraticCrandallField { // Verifiable in Sage with // ``R. = GF(p)[]; assert (x^2 -3).is_irreducible()`. - const W: Self::BaseField = CrandallField(3); + const W: CrandallField = CrandallField(3); +} - fn to_canonical_representation(&self) -> [Self::BaseField; 2] { +impl FieldExtension<2> for QuadraticCrandallField { + type BaseField = CrandallField; + + fn to_basefield_array(&self) -> [Self::BaseField; 2] { self.0 } - fn from_canonical_representation(v: [Self::BaseField; 2]) -> Self { - Self(v) + fn from_basefield_array(arr: [Self::BaseField; 2]) -> Self { + Self(arr) + } + + fn from_basefield(x: Self::BaseField) -> Self { + x.into() } } -impl From<::BaseField> for QuadraticCrandallField { - fn from(x: ::BaseField) -> Self { - Self([x, ::BaseField::ZERO]) +impl From<>::BaseField> for QuadraticCrandallField { + fn from(x: >::BaseField) -> Self { + Self([x, >::BaseField::ZERO]) } } impl PartialEq for QuadraticCrandallField { fn eq(&self, other: &Self) -> bool { - self.to_canonical_representation() == other.to_canonical_representation() + FieldExtension::<2>::to_basefield_array(self) + == FieldExtension::<2>::to_basefield_array(other) } } @@ -68,7 +49,7 @@ impl Eq for QuadraticCrandallField {} impl Hash for QuadraticCrandallField { fn hash(&self, state: &mut H) { - for l in &self.to_canonical_representation() { + for l in &FieldExtension::<2>::to_basefield_array(self) { Hash::hash(l, state); } } @@ -99,9 +80,9 @@ impl Field for QuadraticCrandallField { return None; } - let a_pow_r_minus_1 = self.frobenius(); + let a_pow_r_minus_1 = OEF::<2>::frobenius(self); let a_pow_r = a_pow_r_minus_1 * *self; - debug_assert!(a_pow_r.is_in_basefield()); + debug_assert!(FieldExtension::<2>::is_in_basefield(&a_pow_r)); Some(a_pow_r_minus_1 * a_pow_r.0[0].inverse().into()) } @@ -111,16 +92,13 @@ impl Field for QuadraticCrandallField { } fn from_canonical_u64(n: u64) -> Self { - Self([ - ::BaseField::from_canonical_u64(n), - ::BaseField::ZERO, - ]) + >::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), ]) } } @@ -191,7 +169,7 @@ impl Mul for QuadraticCrandallField { let Self([a0, a1]) = self; let Self([b0, b1]) = rhs; - let c0 = a0 * b0 + Self::W * a1 * b1; + let c0 = a0 * b0 + >::W * a1 * b1; let c1 = a0 * b1 + a1 * b0; Self([c0, c1]) @@ -228,9 +206,8 @@ impl DivAssign for QuadraticCrandallField { #[cfg(test)] mod tests { - use crate::field::extension_field::quadratic::{ - QuadraticCrandallField, QuadraticFieldExtension, - }; + use crate::field::extension_field::quadratic::QuadraticCrandallField; + use crate::field::extension_field::{FieldExtension, OEF}; use crate::field::field::Field; #[test] @@ -241,10 +218,7 @@ mod tests { let z = F::rand(); assert_eq!(x + (-x), F::ZERO); assert_eq!(-x, F::ZERO - x); - assert_eq!( - x + x, - x * ::BaseField::TWO.into() - ); + assert_eq!(x + x, x * >::BaseField::TWO.into()); assert_eq!(x * (-x), -x.square()); assert_eq!(x + y, y + x); assert_eq!(x * y, y * x); @@ -273,8 +247,8 @@ mod tests { type F = QuadraticCrandallField; let x = F::rand(); assert_eq!( - x.exp(::BaseField::ORDER), - x.frobenius() + x.exp(>::BaseField::ORDER), + OEF::<2>::frobenius(&x) ); } @@ -300,10 +274,9 @@ mod tests { F::POWER_OF_TWO_GENERATOR ); assert_eq!( - F::POWER_OF_TWO_GENERATOR.exp( - 1 << (F::TWO_ADICITY - ::BaseField::TWO_ADICITY) - ), - ::BaseField::POWER_OF_TWO_GENERATOR.into() + F::POWER_OF_TWO_GENERATOR + .exp(1 << (F::TWO_ADICITY - >::BaseField::TWO_ADICITY)), + >::BaseField::POWER_OF_TWO_GENERATOR.into() ); } } diff --git a/src/field/extension_field/quartic.rs b/src/field/extension_field/quartic.rs index 121e4c67..f1791d0e 100644 --- a/src/field/extension_field/quartic.rs +++ b/src/field/extension_field/quartic.rs @@ -1,4 +1,5 @@ use crate::field::crandall_field::CrandallField; +use crate::field::extension_field::{FieldExtension, OEF}; use crate::field::field::Field; use rand::Rng; use std::fmt::{Debug, Display, Formatter}; @@ -6,72 +7,46 @@ use std::hash::{Hash, Hasher}; use std::iter::{Product, Sum}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -pub trait QuarticFieldExtension: Field + From<::BaseField> { - type BaseField: Field; - - // Element W of BaseField, such that `X^4 - W` is irreducible over BaseField. - const W: Self::BaseField; - - fn to_canonical_representation(&self) -> [Self::BaseField; 4]; - - fn from_canonical_representation(v: [Self::BaseField; 4]) -> Self; - - fn is_in_basefield(&self) -> bool { - self.to_canonical_representation()[1..] - .iter() - .all(|x| x.is_zero()) - } - - /// Frobenius automorphisms: x -> x^p, where p is the order of BaseField. - fn frobenius(&self) -> Self { - let [a0, a1, a2, a3] = self.to_canonical_representation(); - let k = (Self::BaseField::ORDER - 1) / 4; - let z0 = Self::W.exp(k); - let mut z = Self::BaseField::ONE; - let b0 = a0 * z; - z *= z0; - let b1 = a1 * z; - z *= z0; - let b2 = a2 * z; - z *= z0; - let b3 = a3 * z; - - Self::from_canonical_representation([b0, b1, b2, b3]) - } -} - #[derive(Copy, Clone)] pub struct QuarticCrandallField([CrandallField; 4]); -impl QuarticFieldExtension for QuarticCrandallField { - type BaseField = CrandallField; +impl OEF<4> for QuarticCrandallField { // Verifiable in Sage with // ``R. = GF(p)[]; assert (x^4 -3).is_irreducible()`. - const W: Self::BaseField = CrandallField(3); + const W: CrandallField = CrandallField(3); +} - fn to_canonical_representation(&self) -> [Self::BaseField; 4] { +impl FieldExtension<4> for QuarticCrandallField { + type BaseField = CrandallField; + + fn to_basefield_array(&self) -> [Self::BaseField; 4] { self.0 } - fn from_canonical_representation(v: [Self::BaseField; 4]) -> Self { - Self(v) + fn from_basefield_array(arr: [Self::BaseField; 4]) -> Self { + Self(arr) + } + + fn from_basefield(x: Self::BaseField) -> Self { + x.into() } } -impl From<::BaseField> for QuarticCrandallField { - fn from(x: ::BaseField) -> Self { +impl From<>::BaseField> for QuarticCrandallField { + fn from(x: >::BaseField) -> Self { Self([ x, - ::BaseField::ZERO, - ::BaseField::ZERO, - ::BaseField::ZERO, + >::BaseField::ZERO, + >::BaseField::ZERO, + >::BaseField::ZERO, ]) } } impl PartialEq for QuarticCrandallField { fn eq(&self, other: &Self) -> bool { - self.to_canonical_representation() == other.to_canonical_representation() + FieldExtension::<4>::to_basefield_array(self) + == FieldExtension::<4>::to_basefield_array(other) } } @@ -79,7 +54,7 @@ impl Eq for QuarticCrandallField {} impl Hash for QuarticCrandallField { fn hash(&self, state: &mut H) { - for l in &self.to_canonical_representation() { + for l in &FieldExtension::<4>::to_basefield_array(self) { Hash::hash(l, state); } } @@ -131,12 +106,12 @@ impl Field for QuarticCrandallField { return None; } - let a_pow_p = self.frobenius(); + let a_pow_p = OEF::<4>::frobenius(self); let a_pow_p_plus_1 = a_pow_p * *self; - let a_pow_p3_plus_p2 = a_pow_p_plus_1.frobenius().frobenius(); + let a_pow_p3_plus_p2 = OEF::<4>::frobenius(&OEF::<4>::frobenius(&a_pow_p_plus_1)); let a_pow_r_minus_1 = a_pow_p3_plus_p2 * a_pow_p; let a_pow_r = a_pow_r_minus_1 * *self; - debug_assert!(a_pow_r.is_in_basefield()); + debug_assert!(FieldExtension::<4>::is_in_basefield(&a_pow_r)); Some(a_pow_r_minus_1 * a_pow_r.0[0].inverse().into()) } @@ -146,20 +121,15 @@ impl Field for QuarticCrandallField { } fn from_canonical_u64(n: u64) -> Self { - Self([ - ::BaseField::from_canonical_u64(n), - ::BaseField::ZERO, - ::BaseField::ZERO, - ::BaseField::ZERO, - ]) + >::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), + >::BaseField::rand_from_rng(rng), + >::BaseField::rand_from_rng(rng), + >::BaseField::rand_from_rng(rng), + >::BaseField::rand_from_rng(rng), ]) } } @@ -244,9 +214,9 @@ impl Mul for QuarticCrandallField { let Self([a0, a1, a2, a3]) = self; let Self([b0, b1, b2, b3]) = rhs; - let c0 = a0 * b0 + Self::W * (a1 * b3 + a2 * b2 + a3 * b1); - let c1 = a0 * b1 + a1 * b0 + Self::W * (a2 * b3 + a3 * b2); - let c2 = a0 * b2 + a1 * b1 + a2 * b0 + Self::W * a3 * b3; + 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]) @@ -283,7 +253,8 @@ impl DivAssign for QuarticCrandallField { #[cfg(test)] mod tests { - use crate::field::extension_field::quartic::{QuarticCrandallField, QuarticFieldExtension}; + use crate::field::extension_field::quartic::QuarticCrandallField; + use crate::field::extension_field::{FieldExtension, OEF}; use crate::field::field::Field; fn exp_naive(x: F, power: u128) -> F { @@ -307,10 +278,7 @@ mod tests { let z = F::rand(); assert_eq!(x + (-x), F::ZERO); assert_eq!(-x, F::ZERO - x); - assert_eq!( - x + x, - x * ::BaseField::TWO.into() - ); + assert_eq!(x + x, x * >::BaseField::TWO.into()); assert_eq!(x * (-x), -x.square()); assert_eq!(x + y, y + x); assert_eq!(x * y, y * x); @@ -339,8 +307,8 @@ mod tests { type F = QuarticCrandallField; let x = F::rand(); assert_eq!( - exp_naive(x, ::BaseField::ORDER as u128), - x.frobenius() + exp_naive(x, >::BaseField::ORDER as u128), + OEF::<4>::frobenius(&x) ); } @@ -374,8 +342,8 @@ mod tests { ); assert_eq!( F::POWER_OF_TWO_GENERATOR - .exp(1 << (F::TWO_ADICITY - ::BaseField::TWO_ADICITY)), - ::BaseField::POWER_OF_TWO_GENERATOR.into() + .exp(1 << (F::TWO_ADICITY - >::BaseField::TWO_ADICITY)), + >::BaseField::POWER_OF_TWO_GENERATOR.into() ); } } diff --git a/src/field/extension_field/target.rs b/src/field/extension_field/target.rs new file mode 100644 index 00000000..1927e0d1 --- /dev/null +++ b/src/field/extension_field/target.rs @@ -0,0 +1,88 @@ +use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::{Extendable, FieldExtension, OEF}; +use crate::field::field::Field; +use crate::target::Target; + +#[derive(Copy, Clone, Debug)] +pub struct ExtensionTarget(pub [Target; D]); + +impl ExtensionTarget { + pub fn to_target_array(&self) -> [Target; D] { + self.0 + } +} + +impl CircuitBuilder { + pub fn zero_extension(&mut self) -> ExtensionTarget + where + F: Extendable, + { + ExtensionTarget([self.zero(); D]) + } + + pub fn add_extension( + &mut self, + mut a: ExtensionTarget, + b: ExtensionTarget, + ) -> ExtensionTarget + where + F: Extendable, + { + for i in 0..D { + a.0[i] = self.add(a.0[i], b.0[i]); + } + a + } + + pub fn sub_extension( + &mut self, + mut a: ExtensionTarget, + b: ExtensionTarget, + ) -> ExtensionTarget + where + F: Extendable, + { + for i in 0..D { + a.0[i] = self.sub(a.0[i], b.0[i]); + } + a + } + + pub fn mul_extension( + &mut self, + a: ExtensionTarget, + b: ExtensionTarget, + ) -> ExtensionTarget + where + F: Extendable, + { + let w = self.constant(F::Extension::W); + let mut res = [self.zero(); D]; + for i in 0..D { + for j in 0..D { + res[(i + j) % D] = if i + j < D { + self.mul_add(a.0[i], b.0[j], res[(i + j) % D]) + } else { + let tmp = self.mul(a.0[i], b.0[j]); + self.mul_add(w, tmp, res[(i + j) % D]) + } + } + } + ExtensionTarget(res) + } + + /// Returns a*b where `b` is in the extension field and `a` is in the base field. + pub fn scalar_mul( + &mut self, + a: Target, + mut b: ExtensionTarget, + ) -> ExtensionTarget + where + F: Extendable, + { + for i in 0..D { + b.0[i] = self.mul(a, b.0[i]); + } + b + } +} diff --git a/src/gadgets/mod.rs b/src/gadgets/mod.rs index ed84207e..9a6a728e 100644 --- a/src/gadgets/mod.rs +++ b/src/gadgets/mod.rs @@ -1,3 +1,4 @@ pub mod arithmetic; pub mod hash; +pub mod polynomial; pub(crate) mod split_join; diff --git a/src/gadgets/polynomial.rs b/src/gadgets/polynomial.rs new file mode 100644 index 00000000..64bf4ca3 --- /dev/null +++ b/src/gadgets/polynomial.rs @@ -0,0 +1,35 @@ +use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::target::ExtensionTarget; +use crate::field::extension_field::Extendable; +use crate::field::field::Field; +use crate::target::Target; + +pub struct PolynomialCoeffsTarget(pub Vec>); + +impl PolynomialCoeffsTarget { + pub fn eval_scalar>( + &self, + builder: &mut CircuitBuilder, + point: Target, + ) -> ExtensionTarget { + let mut acc = builder.zero_extension(); + for &c in self.0.iter().rev() { + let tmp = builder.scalar_mul(point, acc); + acc = builder.add_extension(tmp, c); + } + acc + } + + pub fn eval>( + &self, + builder: &mut CircuitBuilder, + point: ExtensionTarget, + ) -> ExtensionTarget { + let mut acc = builder.zero_extension(); + for &c in self.0.iter().rev() { + let tmp = builder.mul_extension(point, acc); + acc = builder.add_extension(tmp, c); + } + acc + } +} diff --git a/src/gates/interpolation.rs b/src/gates/interpolation.rs index adc7055d..a1b9ddd5 100644 --- a/src/gates/interpolation.rs +++ b/src/gates/interpolation.rs @@ -6,6 +6,7 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; use crate::field::lagrange::interpolant; +use crate::gadgets::polynomial::PolynomialCoeffsTarget; use crate::gates::gate::{Gate, GateRef}; use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::polynomial::polynomial::PolynomialCoeffs; @@ -125,7 +126,34 @@ impl, const D: usize> Gate for InterpolationGate, vars: EvaluationTargets, ) -> Vec { - todo!() + let mut constraints = Vec::with_capacity(self.num_constraints()); + + let coeffs = (0..self.num_points) + .map(|i| vars.get_local_ext(self.wires_coeff(i))) + .collect(); + let interpolant = PolynomialCoeffsTarget(coeffs); + + for i in 0..self.num_points { + let point = vars.local_wires[self.wire_point(i)]; + let value = vars.get_local_ext(self.wires_value(i)); + let computed_value = interpolant.eval_scalar(builder, point); + constraints.extend( + &builder + .sub_extension(value, computed_value) + .to_target_array(), + ); + } + + let evaluation_point = vars.get_local_ext(self.wires_evaluation_point()); + let evaluation_value = vars.get_local_ext(self.wires_evaluation_value()); + let computed_evaluation_value = interpolant.eval(builder, evaluation_point); + constraints.extend( + &builder + .sub_extension(evaluation_value, computed_evaluation_value) + .to_target_array(), + ); + + constraints } fn generators( diff --git a/src/vars.rs b/src/vars.rs index 7c0afab9..aa8a3561 100644 --- a/src/vars.rs +++ b/src/vars.rs @@ -1,6 +1,7 @@ use std::convert::TryInto; use std::ops::Range; +use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; use crate::target::Target; @@ -27,3 +28,11 @@ pub struct EvaluationTargets<'a> { pub(crate) local_constants: &'a [Target], pub(crate) local_wires: &'a [Target], } + +impl<'a> EvaluationTargets<'a> { + pub fn get_local_ext(&self, wire_range: Range) -> ExtensionTarget { + debug_assert_eq!(wire_range.len(), D); + let arr = self.local_wires[wire_range].try_into().unwrap(); + ExtensionTarget(arr) + } +}