Draw challenge points from the extension field (#51)

* Draw challenge points from the extension field

* Now building

* Misc

* Default eval_unfiltered_base

* fmt

* A few field settings

* Add to Sage

* Display tweak

* eval_filtered_base

* Quartic in bench

* Missing methods

* Fix tests

* PR feedback
This commit is contained in:
Daniel Lubarov 2021-05-30 13:25:53 -07:00 committed by GitHub
parent 15eb25bc35
commit cb7f8c8b8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 827 additions and 617 deletions

View File

@ -19,7 +19,7 @@ fn main() {
// change this to info or warn later. // change this to info or warn later.
env_logger::Builder::from_env(Env::default().default_filter_or("debug")).init(); env_logger::Builder::from_env(Env::default().default_filter_or("debug")).init();
bench_prove::<CrandallField, 2>(); bench_prove::<CrandallField, 4>();
// bench_field_mul::<CrandallField>(); // bench_field_mul::<CrandallField>();
@ -29,7 +29,7 @@ fn main() {
} }
fn bench_prove<F: Field + Extendable<D>, const D: usize>() { fn bench_prove<F: Field + Extendable<D>, const D: usize>() {
let gmimc_gate = GMiMCGate::<F, GMIMC_ROUNDS>::with_automatic_constants(); let gmimc_gate = GMiMCGate::<F, D, GMIMC_ROUNDS>::with_automatic_constants();
let config = CircuitConfig { let config = CircuitConfig {
num_wires: 134, num_wires: 134,
@ -46,7 +46,7 @@ fn bench_prove<F: Field + Extendable<D>, const D: usize>() {
}, },
}; };
let mut builder = CircuitBuilder::<F>::new(config); let mut builder = CircuitBuilder::<F, D>::new(config);
for _ in 0..10000 { for _ in 0..10000 {
builder.add_gate_no_constants(gmimc_gate.clone()); builder.add_gate_no_constants(gmimc_gate.clone());

View File

@ -8,28 +8,28 @@ use crate::circuit_data::{
VerifierCircuitData, VerifierOnlyCircuitData, VerifierCircuitData, VerifierOnlyCircuitData,
}; };
use crate::field::cosets::get_unique_coset_shifts; use crate::field::cosets::get_unique_coset_shifts;
use crate::field::extension_field::Extendable;
use crate::field::field::Field; use crate::field::field::Field;
use crate::gates::constant::ConstantGate; use crate::gates::constant::ConstantGate;
use crate::gates::gate::{GateInstance, GateRef}; use crate::gates::gate::{GateInstance, GateRef};
use crate::gates::noop::NoopGate; use crate::gates::noop::NoopGate;
use crate::generator::{CopyGenerator, WitnessGenerator}; use crate::generator::{CopyGenerator, WitnessGenerator};
use crate::hash::hash_n_to_hash; use crate::hash::hash_n_to_hash;
use crate::merkle_tree::MerkleTree;
use crate::permutation_argument::TargetPartitions; use crate::permutation_argument::TargetPartitions;
use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::commitment::ListPolynomialCommitment;
use crate::polynomial::polynomial::PolynomialValues; use crate::polynomial::polynomial::PolynomialValues;
use crate::target::Target; use crate::target::Target;
use crate::util::{log2_strict, transpose, transpose_poly_values}; use crate::util::{log2_strict, transpose};
use crate::wire::Wire; use crate::wire::Wire;
pub struct CircuitBuilder<F: Field> { pub struct CircuitBuilder<F: Extendable<D>, const D: usize> {
pub(crate) config: CircuitConfig, pub(crate) config: CircuitConfig,
/// The types of gates used in this circuit. /// The types of gates used in this circuit.
gates: HashSet<GateRef<F>>, gates: HashSet<GateRef<F, D>>,
/// The concrete placement of each gate. /// The concrete placement of each gate.
gate_instances: Vec<GateInstance<F>>, gate_instances: Vec<GateInstance<F, D>>,
/// The next available index for a public input. /// The next available index for a public input.
public_input_index: usize, public_input_index: usize,
@ -46,7 +46,7 @@ pub struct CircuitBuilder<F: Field> {
targets_to_constants: HashMap<Target, F>, targets_to_constants: HashMap<Target, F>,
} }
impl<F: Field> CircuitBuilder<F> { impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn new(config: CircuitConfig) -> Self { pub fn new(config: CircuitConfig) -> Self {
CircuitBuilder { CircuitBuilder {
config, config,
@ -89,12 +89,12 @@ impl<F: Field> CircuitBuilder<F> {
(0..n).map(|_i| self.add_virtual_advice_target()).collect() (0..n).map(|_i| self.add_virtual_advice_target()).collect()
} }
pub fn add_gate_no_constants(&mut self, gate_type: GateRef<F>) -> usize { pub fn add_gate_no_constants(&mut self, gate_type: GateRef<F, D>) -> usize {
self.add_gate(gate_type, Vec::new()) self.add_gate(gate_type, Vec::new())
} }
/// Adds a gate to the circuit, and returns its index. /// Adds a gate to the circuit, and returns its index.
pub fn add_gate(&mut self, gate_type: GateRef<F>, constants: Vec<F>) -> usize { pub fn add_gate(&mut self, gate_type: GateRef<F, D>, constants: Vec<F>) -> usize {
// If we haven't seen a gate of this type before, check that it's compatible with our // If we haven't seen a gate of this type before, check that it's compatible with our
// circuit configuration, then register it. // circuit configuration, then register it.
if !self.gates.contains(&gate_type) { if !self.gates.contains(&gate_type) {
@ -113,7 +113,7 @@ impl<F: Field> CircuitBuilder<F> {
index index
} }
fn check_gate_compatibility(&self, gate: &GateRef<F>) { fn check_gate_compatibility(&self, gate: &GateRef<F, D>) {
assert!( assert!(
gate.0.num_wires() <= self.config.num_wires, gate.0.num_wires() <= self.config.num_wires,
"{:?} requires {} wires, but our GateConfig has only {}", "{:?} requires {} wires, but our GateConfig has only {}",
@ -261,7 +261,7 @@ impl<F: Field> CircuitBuilder<F> {
} }
/// Builds a "full circuit", with both prover and verifier data. /// Builds a "full circuit", with both prover and verifier data.
pub fn build(mut self) -> CircuitData<F> { pub fn build(mut self) -> CircuitData<F, D> {
let start = Instant::now(); let start = Instant::now();
info!( info!(
"degree before blinding & padding: {}", "degree before blinding & padding: {}",
@ -335,7 +335,7 @@ impl<F: Field> CircuitBuilder<F> {
} }
/// Builds a "prover circuit", with data needed to generate proofs but not verify them. /// Builds a "prover circuit", with data needed to generate proofs but not verify them.
pub fn build_prover(self) -> ProverCircuitData<F> { pub fn build_prover(self) -> ProverCircuitData<F, D> {
// TODO: Can skip parts of this. // TODO: Can skip parts of this.
let CircuitData { let CircuitData {
prover_only, prover_only,
@ -349,7 +349,7 @@ impl<F: Field> CircuitBuilder<F> {
} }
/// Builds a "verifier circuit", with data needed to verify proofs but not generate them. /// Builds a "verifier circuit", with data needed to verify proofs but not generate them.
pub fn build_verifier(self) -> VerifierCircuitData<F> { pub fn build_verifier(self) -> VerifierCircuitData<F, D> {
// TODO: Can skip parts of this. // TODO: Can skip parts of this.
let CircuitData { let CircuitData {
verifier_only, verifier_only,

View File

@ -5,7 +5,6 @@ use crate::field::field::Field;
use crate::fri::FriConfig; use crate::fri::FriConfig;
use crate::gates::gate::GateRef; use crate::gates::gate::GateRef;
use crate::generator::WitnessGenerator; use crate::generator::WitnessGenerator;
use crate::merkle_tree::MerkleTree;
use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::commitment::ListPolynomialCommitment;
use crate::proof::{Hash, HashTarget, Proof}; use crate::proof::{Hash, HashTarget, Proof};
use crate::prover::prove; use crate::prover::prove;
@ -52,24 +51,18 @@ impl CircuitConfig {
} }
/// Circuit data required by the prover or the verifier. /// Circuit data required by the prover or the verifier.
pub struct CircuitData<F: Field> { pub struct CircuitData<F: Extendable<D>, const D: usize> {
pub(crate) prover_only: ProverOnlyCircuitData<F>, pub(crate) prover_only: ProverOnlyCircuitData<F>,
pub(crate) verifier_only: VerifierOnlyCircuitData<F>, pub(crate) verifier_only: VerifierOnlyCircuitData<F>,
pub(crate) common: CommonCircuitData<F>, pub(crate) common: CommonCircuitData<F, D>,
} }
impl<F: Field> CircuitData<F> { impl<F: Extendable<D>, const D: usize> CircuitData<F, D> {
pub fn prove<const D: usize>(&self, inputs: PartialWitness<F>) -> Proof<F, D> pub fn prove(&self, inputs: PartialWitness<F>) -> Proof<F, D> {
where
F: Extendable<D>,
{
prove(&self.prover_only, &self.common, inputs) prove(&self.prover_only, &self.common, inputs)
} }
pub fn verify<const D: usize>(&self, proof: Proof<F, D>) -> Result<()> pub fn verify(&self, proof: Proof<F, D>) -> Result<()> {
where
F: Extendable<D>,
{
verify(proof, &self.verifier_only, &self.common) verify(proof, &self.verifier_only, &self.common)
} }
} }
@ -81,31 +74,25 @@ impl<F: Field> CircuitData<F> {
/// structure as succinct as we can. Thus we include various precomputed data which isn't strictly /// structure as succinct as we can. Thus we include various precomputed data which isn't strictly
/// required, like LDEs of preprocessed polynomials. If more succinctness was desired, we could /// required, like LDEs of preprocessed polynomials. If more succinctness was desired, we could
/// construct a more minimal prover structure and convert back and forth. /// construct a more minimal prover structure and convert back and forth.
pub struct ProverCircuitData<F: Field> { pub struct ProverCircuitData<F: Extendable<D>, const D: usize> {
pub(crate) prover_only: ProverOnlyCircuitData<F>, pub(crate) prover_only: ProverOnlyCircuitData<F>,
pub(crate) common: CommonCircuitData<F>, pub(crate) common: CommonCircuitData<F, D>,
} }
impl<F: Field> ProverCircuitData<F> { impl<F: Extendable<D>, const D: usize> ProverCircuitData<F, D> {
pub fn prove<const D: usize>(&self, inputs: PartialWitness<F>) -> Proof<F, D> pub fn prove(&self, inputs: PartialWitness<F>) -> Proof<F, D> {
where
F: Extendable<D>,
{
prove(&self.prover_only, &self.common, inputs) prove(&self.prover_only, &self.common, inputs)
} }
} }
/// Circuit data required by the prover. /// Circuit data required by the prover.
pub struct VerifierCircuitData<F: Field> { pub struct VerifierCircuitData<F: Extendable<D>, const D: usize> {
pub(crate) verifier_only: VerifierOnlyCircuitData<F>, pub(crate) verifier_only: VerifierOnlyCircuitData<F>,
pub(crate) common: CommonCircuitData<F>, pub(crate) common: CommonCircuitData<F, D>,
} }
impl<F: Field> VerifierCircuitData<F> { impl<F: Extendable<D>, const D: usize> VerifierCircuitData<F, D> {
pub fn verify<const D: usize>(&self, proof: Proof<F, D>) -> Result<()> pub fn verify(&self, proof: Proof<F, D>) -> Result<()> {
where
F: Extendable<D>,
{
verify(proof, &self.verifier_only, &self.common) verify(proof, &self.verifier_only, &self.common)
} }
} }
@ -129,13 +116,13 @@ pub(crate) struct VerifierOnlyCircuitData<F: Field> {
} }
/// Circuit data required by both the prover and the verifier. /// Circuit data required by both the prover and the verifier.
pub(crate) struct CommonCircuitData<F: Field> { pub(crate) struct CommonCircuitData<F: Extendable<D>, const D: usize> {
pub(crate) config: CircuitConfig, pub(crate) config: CircuitConfig,
pub(crate) degree_bits: usize, pub(crate) degree_bits: usize,
/// The types of gates used in this circuit. /// The types of gates used in this circuit.
pub(crate) gates: Vec<GateRef<F>>, pub(crate) gates: Vec<GateRef<F, D>>,
/// The largest number of constraints imposed by any gate. /// The largest number of constraints imposed by any gate.
pub(crate) num_gate_constraints: usize, pub(crate) num_gate_constraints: usize,
@ -148,7 +135,7 @@ pub(crate) struct CommonCircuitData<F: Field> {
pub(crate) circuit_digest: Hash<F>, pub(crate) circuit_digest: Hash<F>,
} }
impl<F: Field> CommonCircuitData<F> { impl<F: Extendable<D>, const D: usize> CommonCircuitData<F, D> {
pub fn degree(&self) -> usize { pub fn degree(&self) -> usize {
1 << self.degree_bits 1 << self.degree_bits
} }

View File

@ -2,6 +2,7 @@ use crate::field::field::Field;
pub mod quadratic; pub mod quadratic;
pub mod quartic; pub mod quartic;
mod quartic_quartic;
pub mod target; pub mod target;
/// Optimal extension field trait. /// Optimal extension field trait.
@ -32,7 +33,7 @@ impl<F: Field> OEF<1> for F {
const W: Self::BaseField = F::ZERO; const W: Self::BaseField = F::ZERO;
} }
pub trait Extendable<const D: usize>: Sized { pub trait Extendable<const D: usize>: Field + Sized {
type Extension: Field + OEF<D, BaseField = Self> + From<Self>; type Extension: Field + OEF<D, BaseField = Self> + From<Self>;
} }

View File

@ -3,11 +3,11 @@ use crate::field::extension_field::{FieldExtension, OEF};
use crate::field::field::Field; use crate::field::field::Field;
use rand::Rng; use rand::Rng;
use std::fmt::{Debug, Display, Formatter}; use std::fmt::{Debug, Display, Formatter};
use std::hash::{Hash, Hasher}; use std::hash::Hash;
use std::iter::{Product, Sum}; use std::iter::{Product, Sum};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
#[derive(Copy, Clone)] #[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct QuadraticCrandallField([CrandallField; 2]); pub struct QuadraticCrandallField([CrandallField; 2]);
impl OEF<2> for QuadraticCrandallField { impl OEF<2> for QuadraticCrandallField {
@ -38,23 +38,6 @@ impl From<<Self as FieldExtension<2>>::BaseField> for QuadraticCrandallField {
} }
} }
impl PartialEq for QuadraticCrandallField {
fn eq(&self, other: &Self) -> bool {
FieldExtension::<2>::to_basefield_array(self)
== FieldExtension::<2>::to_basefield_array(other)
}
}
impl Eq for QuadraticCrandallField {}
impl Hash for QuadraticCrandallField {
fn hash<H: Hasher>(&self, state: &mut H) {
for l in &FieldExtension::<2>::to_basefield_array(self) {
Hash::hash(l, state);
}
}
}
impl Field for QuadraticCrandallField { impl Field for QuadraticCrandallField {
const ZERO: Self = Self([CrandallField::ZERO; 2]); const ZERO: Self = Self([CrandallField::ZERO; 2]);
const ONE: Self = Self([CrandallField::ONE, CrandallField::ZERO]); const ONE: Self = Self([CrandallField::ONE, CrandallField::ZERO]);

View File

@ -1,18 +1,23 @@
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}; use std::fmt::{Debug, Display, Formatter};
use std::hash::{Hash, Hasher}; use std::hash::Hash;
use std::iter::{Product, Sum}; use std::iter::{Product, Sum};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
#[derive(Copy, Clone)] use rand::Rng;
pub struct QuarticCrandallField([CrandallField; 4]);
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::field::Field;
/// A quartic extension of `CrandallField`.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct QuarticCrandallField(pub(crate) [CrandallField; 4]);
impl OEF<4> for QuarticCrandallField { impl OEF<4> for QuarticCrandallField {
// Verifiable in Sage with // Verifiable in Sage with
// ``R.<x> = GF(p)[]; assert (x^4 -3).is_irreducible()`. // R.<x> = GF(p)[]
// assert (x^4 - 3).is_irreducible()
const W: CrandallField = CrandallField(3); const W: CrandallField = CrandallField(3);
} }
@ -43,23 +48,6 @@ impl From<<Self as FieldExtension<4>>::BaseField> for QuarticCrandallField {
} }
} }
impl PartialEq for QuarticCrandallField {
fn eq(&self, other: &Self) -> bool {
FieldExtension::<4>::to_basefield_array(self)
== FieldExtension::<4>::to_basefield_array(other)
}
}
impl Eq for QuarticCrandallField {}
impl Hash for QuarticCrandallField {
fn hash<H: Hasher>(&self, state: &mut H) {
for l in &FieldExtension::<4>::to_basefield_array(self) {
Hash::hash(l, state);
}
}
}
impl Field for QuarticCrandallField { impl Field for QuarticCrandallField {
const ZERO: Self = Self([CrandallField::ZERO; 4]); const ZERO: Self = Self([CrandallField::ZERO; 4]);
const ONE: Self = Self([ const ONE: Self = Self([
@ -251,6 +239,10 @@ impl DivAssign for QuarticCrandallField {
} }
} }
impl Extendable<4> for QuarticCrandallField {
type Extension = QuarticQuarticCrandallField;
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::field::extension_field::quartic::QuarticCrandallField; use crate::field::extension_field::quartic::QuarticCrandallField;

View File

@ -0,0 +1,259 @@
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

@ -3,6 +3,7 @@ use crate::field::extension_field::{Extendable, FieldExtension, OEF};
use crate::field::field::Field; use crate::field::field::Field;
use crate::target::Target; use crate::target::Target;
/// `Target`s representing an element of an extension field.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct ExtensionTarget<const D: usize>(pub [Target; D]); pub struct ExtensionTarget<const D: usize>(pub [Target; D]);
@ -12,50 +13,117 @@ impl<const D: usize> ExtensionTarget<D> {
} }
} }
impl<F: Field> CircuitBuilder<F> { /// `Target`s representing an element of an extension of an extension field.
pub fn zero_extension<const D: usize>(&mut self) -> ExtensionTarget<D> #[derive(Copy, Clone, Debug)]
where pub struct ExtensionExtensionTarget<const D: usize>(pub [ExtensionTarget<D>; D]);
F: Extendable<D>,
{ impl<const D: usize> ExtensionExtensionTarget<D> {
ExtensionTarget([self.zero(); D]) pub fn to_ext_target_array(&self) -> [ExtensionTarget<D>; D] {
self.0
}
}
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn constant_extension(&mut self, c: F::Extension) -> ExtensionTarget<D> {
let c_parts = c.to_basefield_array();
let mut parts = [self.zero(); D];
for i in 0..D {
parts[i] = self.constant(c_parts[i]);
}
ExtensionTarget(parts)
} }
pub fn add_extension<const D: usize>( pub fn constant_ext_ext(
&mut self,
c: <<F as Extendable<D>>::Extension as Extendable<D>>::Extension,
) -> ExtensionExtensionTarget<D>
where
F::Extension: Extendable<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)
}
pub fn zero_extension(&mut self) -> ExtensionTarget<D> {
self.constant_extension(F::Extension::ZERO)
}
pub fn one_extension(&mut self) -> ExtensionTarget<D> {
self.constant_extension(F::Extension::ONE)
}
pub fn two_extension(&mut self) -> ExtensionTarget<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 add_extension(
&mut self, &mut self,
mut a: ExtensionTarget<D>, mut a: ExtensionTarget<D>,
b: ExtensionTarget<D>, b: ExtensionTarget<D>,
) -> ExtensionTarget<D> ) -> ExtensionTarget<D> {
where
F: Extendable<D>,
{
for i in 0..D { for i in 0..D {
a.0[i] = self.add(a.0[i], b.0[i]); a.0[i] = self.add(a.0[i], b.0[i]);
} }
a a
} }
pub fn sub_extension<const D: usize>( pub fn add_ext_ext(
&mut self,
mut a: ExtensionExtensionTarget<D>,
b: ExtensionExtensionTarget<D>,
) -> ExtensionExtensionTarget<D> {
for i in 0..D {
a.0[i] = self.add_extension(a.0[i], b.0[i]);
}
a
}
pub fn add_many_extension(&mut self, terms: &[ExtensionTarget<D>]) -> ExtensionTarget<D> {
let mut sum = self.zero_extension();
for term in terms {
sum = self.add_extension(sum, *term);
}
sum
}
pub fn sub_extension(
&mut self, &mut self,
mut a: ExtensionTarget<D>, mut a: ExtensionTarget<D>,
b: ExtensionTarget<D>, b: ExtensionTarget<D>,
) -> ExtensionTarget<D> ) -> ExtensionTarget<D> {
where
F: Extendable<D>,
{
for i in 0..D { for i in 0..D {
a.0[i] = self.sub(a.0[i], b.0[i]); a.0[i] = self.sub(a.0[i], b.0[i]);
} }
a a
} }
pub fn mul_extension<const D: usize>( pub fn sub_ext_ext(
&mut self,
mut a: ExtensionExtensionTarget<D>,
b: ExtensionExtensionTarget<D>,
) -> ExtensionExtensionTarget<D> {
for i in 0..D {
a.0[i] = self.sub_extension(a.0[i], b.0[i]);
}
a
}
pub fn mul_extension(
&mut self, &mut self,
a: ExtensionTarget<D>, a: ExtensionTarget<D>,
b: ExtensionTarget<D>, b: ExtensionTarget<D>,
) -> ExtensionTarget<D> ) -> ExtensionTarget<D> {
where
F: Extendable<D>,
{
let mut res = [self.zero(); D]; let mut res = [self.zero(); D];
for i in 0..D { for i in 0..D {
for j in 0..D { for j in 0..D {
@ -70,18 +138,72 @@ impl<F: Field> CircuitBuilder<F> {
ExtensionTarget(res) ExtensionTarget(res)
} }
/// Returns a*b where `b` is in the extension field and `a` is in the base field. pub fn mul_ext_ext(
pub fn scalar_mul<const D: usize>(
&mut self, &mut self,
a: Target, mut a: ExtensionExtensionTarget<D>,
mut b: ExtensionTarget<D>, b: ExtensionExtensionTarget<D>,
) -> ExtensionTarget<D> ) -> ExtensionExtensionTarget<D>
where where
F: Extendable<D>, F::Extension: Extendable<D>,
{ {
let mut res = [self.zero_extension(); D];
let w = self
.constant_extension(<<F as Extendable<D>>::Extension as Extendable<D>>::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);
self.add_extension(w_ai_bi, res[(i + j) % D])
}
}
}
ExtensionExtensionTarget(res)
}
pub fn mul_many_extension(&mut self, terms: &[ExtensionTarget<D>]) -> ExtensionTarget<D> {
let mut product = self.one_extension();
for term in terms {
product = self.mul_extension(product, *term);
}
product
}
/// Like `mul_add`, but for `ExtensionTarget`s. Note that, unlike `mul_add`, this has no
/// performance benefit over separate muls and adds.
pub fn mul_add_extension(
&mut self,
a: ExtensionTarget<D>,
b: ExtensionTarget<D>,
c: ExtensionTarget<D>,
) -> ExtensionTarget<D> {
let product = self.mul_extension(a, b);
self.add_extension(product, c)
}
/// Returns `a * b`, where `b` is in the extension field and `a` is in the base field.
pub fn scalar_mul_ext(&mut self, a: Target, mut b: ExtensionTarget<D>) -> ExtensionTarget<D> {
for i in 0..D { for i in 0..D {
b.0[i] = self.mul(a, b.0[i]); b.0[i] = self.mul(a, b.0[i]);
} }
b b
} }
/// 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(
&mut self,
a: ExtensionTarget<D>,
mut b: ExtensionExtensionTarget<D>,
) -> ExtensionExtensionTarget<D>
where
F::Extension: Extendable<D>,
{
for i in 0..D {
b.0[i] = self.mul_extension(a, b.0[i]);
}
b
}
} }

View File

@ -50,12 +50,12 @@ fn fri_l(codeword_len: usize, rate_log: usize, conjecture: bool) -> f64 {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use anyhow::Result;
use rand::rngs::ThreadRng;
use rand::Rng;
use crate::field::crandall_field::CrandallField; use crate::field::crandall_field::CrandallField;
use crate::field::extension_field::quadratic::QuadraticCrandallField; use crate::field::extension_field::Extendable;
use crate::field::extension_field::quartic::QuarticCrandallField;
use crate::field::extension_field::{flatten, Extendable, FieldExtension};
use crate::field::fft::ifft;
use crate::field::field::Field; use crate::field::field::Field;
use crate::fri::prover::fri_proof; use crate::fri::prover::fri_proof;
use crate::fri::verifier::verify_fri_proof; use crate::fri::verifier::verify_fri_proof;
@ -63,9 +63,8 @@ mod tests {
use crate::plonk_challenger::Challenger; use crate::plonk_challenger::Challenger;
use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues};
use crate::util::reverse_index_bits_in_place; use crate::util::reverse_index_bits_in_place;
use anyhow::Result;
use rand::rngs::ThreadRng; use super::*;
use rand::Rng;
fn check_fri<F: Field + Extendable<D>, const D: usize>( fn check_fri<F: Field + Extendable<D>, const D: usize>(
degree_log: usize, degree_log: usize,

View File

@ -1,4 +1,5 @@
use crate::circuit_builder::CircuitBuilder; use crate::circuit_builder::CircuitBuilder;
use crate::field::extension_field::Extendable;
use crate::field::field::Field; use crate::field::field::Field;
use crate::gates::arithmetic::ArithmeticGate; use crate::gates::arithmetic::ArithmeticGate;
use crate::generator::SimpleGenerator; use crate::generator::SimpleGenerator;
@ -6,7 +7,7 @@ use crate::target::Target;
use crate::wire::Wire; use crate::wire::Wire;
use crate::witness::PartialWitness; use crate::witness::PartialWitness;
impl<F: Field> CircuitBuilder<F> { impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// Computes `-x`. /// Computes `-x`.
pub fn neg(&mut self, x: Target) -> Target { pub fn neg(&mut self, x: Target) -> Target {
let neg_one = self.neg_one(); let neg_one = self.neg_one();

View File

@ -1,21 +1,21 @@
use std::convert::TryInto; use std::convert::TryInto;
use crate::circuit_builder::CircuitBuilder; use crate::circuit_builder::CircuitBuilder;
use crate::field::field::Field; use crate::field::extension_field::Extendable;
use crate::gates::gmimc::GMiMCGate; use crate::gates::gmimc::GMiMCGate;
use crate::hash::GMIMC_ROUNDS; use crate::hash::GMIMC_ROUNDS;
use crate::target::Target; use crate::target::Target;
use crate::wire::Wire; use crate::wire::Wire;
// TODO: Move to be next to native `permute`? // TODO: Move to be next to native `permute`?
impl<F: Field> CircuitBuilder<F> { impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn permute(&mut self, inputs: [Target; 12]) -> [Target; 12] { pub fn permute(&mut self, inputs: [Target; 12]) -> [Target; 12] {
let zero = self.zero(); let zero = self.zero();
let gate = let gate =
self.add_gate_no_constants(GMiMCGate::<F, GMIMC_ROUNDS>::with_automatic_constants()); self.add_gate_no_constants(GMiMCGate::<F, D, GMIMC_ROUNDS>::with_automatic_constants());
// We don't want to swap any inputs, so set that wire to 0. // We don't want to swap any inputs, so set that wire to 0.
let swap_wire = GMiMCGate::<F, GMIMC_ROUNDS>::WIRE_SWAP; let swap_wire = GMiMCGate::<F, D, GMIMC_ROUNDS>::WIRE_SWAP;
let swap_wire = Target::Wire(Wire { let swap_wire = Target::Wire(Wire {
gate, gate,
input: swap_wire, input: swap_wire,
@ -24,7 +24,7 @@ impl<F: Field> CircuitBuilder<F> {
// The old accumulator wire doesn't matter, since we won't read the new accumulator wire. // The old accumulator wire doesn't matter, since we won't read the new accumulator wire.
// We do have to set it to something though, so we'll arbitrary pick 0. // We do have to set it to something though, so we'll arbitrary pick 0.
let old_acc_wire = GMiMCGate::<F, GMIMC_ROUNDS>::WIRE_INDEX_ACCUMULATOR_OLD; let old_acc_wire = GMiMCGate::<F, D, GMIMC_ROUNDS>::WIRE_INDEX_ACCUMULATOR_OLD;
let old_acc_wire = Target::Wire(Wire { let old_acc_wire = Target::Wire(Wire {
gate, gate,
input: old_acc_wire, input: old_acc_wire,
@ -33,7 +33,7 @@ impl<F: Field> CircuitBuilder<F> {
// Route input wires. // Route input wires.
for i in 0..12 { for i in 0..12 {
let in_wire = GMiMCGate::<F, GMIMC_ROUNDS>::wire_input(i); let in_wire = GMiMCGate::<F, D, GMIMC_ROUNDS>::wire_input(i);
let in_wire = Target::Wire(Wire { let in_wire = Target::Wire(Wire {
gate, gate,
input: in_wire, input: in_wire,
@ -46,7 +46,7 @@ impl<F: Field> CircuitBuilder<F> {
.map(|i| { .map(|i| {
Target::Wire(Wire { Target::Wire(Wire {
gate, gate,
input: GMiMCGate::<F, GMIMC_ROUNDS>::wire_output(i), input: GMiMCGate::<F, D, GMIMC_ROUNDS>::wire_output(i),
}) })
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()

View File

@ -1,28 +1,27 @@
use crate::circuit_builder::CircuitBuilder; use crate::circuit_builder::CircuitBuilder;
use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::target::{ExtensionExtensionTarget, ExtensionTarget};
use crate::field::extension_field::Extendable; use crate::field::extension_field::Extendable;
use crate::field::field::Field;
use crate::target::Target; use crate::target::Target;
pub struct PolynomialCoeffsTarget<const D: usize>(pub Vec<ExtensionTarget<D>>); pub struct PolynomialCoeffsExtTarget<const D: usize>(pub Vec<ExtensionTarget<D>>);
impl<const D: usize> PolynomialCoeffsTarget<D> { impl<const D: usize> PolynomialCoeffsExtTarget<D> {
pub fn eval_scalar<F: Field + Extendable<D>>( pub fn eval_scalar<F: Extendable<D>>(
&self, &self,
builder: &mut CircuitBuilder<F>, builder: &mut CircuitBuilder<F, D>,
point: Target, point: Target,
) -> ExtensionTarget<D> { ) -> ExtensionTarget<D> {
let mut acc = builder.zero_extension(); let mut acc = builder.zero_extension();
for &c in self.0.iter().rev() { for &c in self.0.iter().rev() {
let tmp = builder.scalar_mul(point, acc); let tmp = builder.scalar_mul_ext(point, acc);
acc = builder.add_extension(tmp, c); acc = builder.add_extension(tmp, c);
} }
acc acc
} }
pub fn eval<F: Field + Extendable<D>>( pub fn eval<F: Extendable<D>>(
&self, &self,
builder: &mut CircuitBuilder<F>, builder: &mut CircuitBuilder<F, D>,
point: ExtensionTarget<D>, point: ExtensionTarget<D>,
) -> ExtensionTarget<D> { ) -> ExtensionTarget<D> {
let mut acc = builder.zero_extension(); let mut acc = builder.zero_extension();
@ -33,3 +32,41 @@ impl<const D: usize> PolynomialCoeffsTarget<D> {
acc acc
} }
} }
pub struct PolynomialCoeffsExtExtTarget<const D: usize>(pub Vec<ExtensionExtensionTarget<D>>);
impl<const D: usize> PolynomialCoeffsExtExtTarget<D> {
pub fn eval_scalar<F>(
&self,
builder: &mut CircuitBuilder<F, D>,
point: ExtensionTarget<D>,
) -> ExtensionExtensionTarget<D>
where
F: Extendable<D>,
F::Extension: Extendable<D>,
{
let mut acc = builder.zero_ext_ext();
for &c in self.0.iter().rev() {
let tmp = builder.scalar_mul_ext_ext(point, acc);
acc = builder.add_ext_ext(tmp, c);
}
acc
}
pub fn eval<F>(
&self,
builder: &mut CircuitBuilder<F, D>,
point: ExtensionExtensionTarget<D>,
) -> ExtensionExtensionTarget<D>
where
F: Extendable<D>,
F::Extension: Extendable<D>,
{
let mut acc = builder.zero_ext_ext();
for &c in self.0.iter().rev() {
let tmp = builder.mul_ext_ext(point, acc);
acc = builder.add_ext_ext(tmp, c);
}
acc
}
}

View File

@ -1,11 +1,12 @@
use crate::circuit_builder::CircuitBuilder; use crate::circuit_builder::CircuitBuilder;
use crate::field::extension_field::Extendable;
use crate::field::field::Field; use crate::field::field::Field;
use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::generator::{SimpleGenerator, WitnessGenerator};
use crate::target::Target; use crate::target::Target;
use crate::wire::Wire; use crate::wire::Wire;
use crate::witness::PartialWitness; use crate::witness::PartialWitness;
impl<F: Field> CircuitBuilder<F> { impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// Split the given integer into a list of virtual advice targets, where each one represents a /// Split the given integer into a list of virtual advice targets, where each one represents a
/// bit of the integer, with little-endian ordering. /// bit of the integer, with little-endian ordering.
/// ///

View File

@ -1,4 +1,6 @@
use crate::circuit_builder::CircuitBuilder; 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::field::field::Field;
use crate::gates::gate::{Gate, GateRef}; use crate::gates::gate::{Gate, GateRef};
use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::generator::{SimpleGenerator, WitnessGenerator};
@ -16,7 +18,7 @@ use crate::witness::PartialWitness;
pub struct ArithmeticGate; pub struct ArithmeticGate;
impl ArithmeticGate { impl ArithmeticGate {
pub fn new<F: Field>() -> GateRef<F> { pub fn new<F: Extendable<D>, const D: usize>() -> GateRef<F, D> {
GateRef::new(ArithmeticGate) GateRef::new(ArithmeticGate)
} }
@ -26,12 +28,12 @@ impl ArithmeticGate {
pub const WIRE_OUTPUT: usize = 3; pub const WIRE_OUTPUT: usize = 3;
} }
impl<F: Field> Gate<F> for ArithmeticGate { impl<F: Extendable<D>, const D: usize> Gate<F, D> for ArithmeticGate {
fn id(&self) -> String { fn id(&self) -> String {
format!("{:?}", self) format!("{:?}", self)
} }
fn eval_unfiltered(&self, vars: EvaluationVars<F>) -> Vec<F> { fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let const_0 = vars.local_constants[0]; let const_0 = vars.local_constants[0];
let const_1 = vars.local_constants[1]; let const_1 = vars.local_constants[1];
let multiplicand_0 = vars.local_wires[Self::WIRE_MULTIPLICAND_0]; let multiplicand_0 = vars.local_wires[Self::WIRE_MULTIPLICAND_0];
@ -44,9 +46,9 @@ impl<F: Field> Gate<F> for ArithmeticGate {
fn eval_unfiltered_recursively( fn eval_unfiltered_recursively(
&self, &self,
builder: &mut CircuitBuilder<F>, builder: &mut CircuitBuilder<F, D>,
vars: EvaluationTargets, vars: EvaluationTargets<D>,
) -> Vec<Target> { ) -> Vec<ExtensionTarget<D>> {
let const_0 = vars.local_constants[0]; let const_0 = vars.local_constants[0];
let const_1 = vars.local_constants[1]; let const_1 = vars.local_constants[1];
let multiplicand_0 = vars.local_wires[Self::WIRE_MULTIPLICAND_0]; let multiplicand_0 = vars.local_wires[Self::WIRE_MULTIPLICAND_0];
@ -54,10 +56,10 @@ impl<F: Field> Gate<F> for ArithmeticGate {
let addend = vars.local_wires[Self::WIRE_ADDEND]; let addend = vars.local_wires[Self::WIRE_ADDEND];
let output = vars.local_wires[Self::WIRE_OUTPUT]; let output = vars.local_wires[Self::WIRE_OUTPUT];
let product_term = builder.mul_many(&[const_0, multiplicand_0, multiplicand_1]); let product_term = builder.mul_many_extension(&[const_0, multiplicand_0, multiplicand_1]);
let addend_term = builder.mul(const_1, addend); let addend_term = builder.mul_extension(const_1, addend);
let computed_output = builder.add_many(&[product_term, addend_term]); let computed_output = builder.add_many_extension(&[product_term, addend_term]);
vec![builder.sub(computed_output, output)] vec![builder.sub_extension(computed_output, output)]
} }
fn generators( fn generators(
@ -150,6 +152,6 @@ mod tests {
#[test] #[test]
fn low_degree() { fn low_degree() {
test_low_degree(ArithmeticGate::new::<CrandallField>()) test_low_degree(ArithmeticGate::new::<CrandallField, 4>())
} }
} }

View File

@ -1,4 +1,6 @@
use crate::circuit_builder::CircuitBuilder; 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::field::field::Field;
use crate::gates::gate::{Gate, GateRef}; use crate::gates::gate::{Gate, GateRef};
use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::generator::{SimpleGenerator, WitnessGenerator};
@ -11,7 +13,7 @@ use crate::witness::PartialWitness;
pub struct ConstantGate; pub struct ConstantGate;
impl ConstantGate { impl ConstantGate {
pub fn get<F: Field>() -> GateRef<F> { pub fn get<F: Extendable<D>, const D: usize>() -> GateRef<F, D> {
GateRef::new(ConstantGate) GateRef::new(ConstantGate)
} }
@ -20,12 +22,12 @@ impl ConstantGate {
pub const WIRE_OUTPUT: usize = 0; pub const WIRE_OUTPUT: usize = 0;
} }
impl<F: Field> Gate<F> for ConstantGate { impl<F: Extendable<D>, const D: usize> Gate<F, D> for ConstantGate {
fn id(&self) -> String { fn id(&self) -> String {
"ConstantGate".into() "ConstantGate".into()
} }
fn eval_unfiltered(&self, vars: EvaluationVars<F>) -> Vec<F> { fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let input = vars.local_constants[Self::CONST_INPUT]; let input = vars.local_constants[Self::CONST_INPUT];
let output = vars.local_wires[Self::WIRE_OUTPUT]; let output = vars.local_wires[Self::WIRE_OUTPUT];
vec![output - input] vec![output - input]
@ -33,12 +35,12 @@ impl<F: Field> Gate<F> for ConstantGate {
fn eval_unfiltered_recursively( fn eval_unfiltered_recursively(
&self, &self,
builder: &mut CircuitBuilder<F>, builder: &mut CircuitBuilder<F, D>,
vars: EvaluationTargets, vars: EvaluationTargets<D>,
) -> Vec<Target> { ) -> Vec<ExtensionTarget<D>> {
let input = vars.local_constants[Self::CONST_INPUT]; let input = vars.local_constants[Self::CONST_INPUT];
let output = vars.local_wires[Self::WIRE_OUTPUT]; let output = vars.local_wires[Self::WIRE_OUTPUT];
vec![builder.sub(output, input)] vec![builder.sub_extension(output, input)]
} }
fn generators( fn generators(
@ -98,6 +100,6 @@ mod tests {
#[test] #[test]
fn low_degree() { fn low_degree() {
test_low_degree(ConstantGate::get::<CrandallField>()) test_low_degree(ConstantGate::get::<CrandallField, 4>())
} }
} }

View File

@ -2,33 +2,71 @@ use std::hash::{Hash, Hasher};
use std::sync::Arc; use std::sync::Arc;
use crate::circuit_builder::CircuitBuilder; use crate::circuit_builder::CircuitBuilder;
use crate::field::field::Field; use crate::field::extension_field::target::ExtensionTarget;
use crate::field::extension_field::{Extendable, FieldExtension};
use crate::generator::WitnessGenerator; use crate::generator::WitnessGenerator;
use crate::target::Target; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
use crate::vars::{EvaluationTargets, EvaluationVars};
/// A custom gate. /// A custom gate.
pub trait Gate<F: Field>: 'static + Send + Sync { pub trait Gate<F: Extendable<D>, const D: usize>: 'static + Send + Sync {
fn id(&self) -> String; fn id(&self) -> String;
fn eval_unfiltered(&self, vars: EvaluationVars<F>) -> Vec<F>; fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension>;
/// Like `eval_unfiltered`, but specialized for points in the base field.
///
/// By default, this just calls `eval_unfiltered`, which treats the point as an extension field
/// element. This isn't very efficient.
fn eval_unfiltered_base(&self, vars_base: EvaluationVarsBase<F>) -> Vec<F> {
let local_constants = &vars_base
.local_constants
.iter()
.map(|c| F::Extension::from_basefield(*c))
.collect::<Vec<_>>();
let local_wires = &vars_base
.local_wires
.iter()
.map(|w| F::Extension::from_basefield(*w))
.collect::<Vec<_>>();
let vars = EvaluationVars {
local_constants,
local_wires,
};
let values = self.eval_unfiltered(vars);
// Each value should be in the base field, i.e. only the degree-zero part should be nonzero.
values
.into_iter()
.map(|value| {
// TODO: Change to debug-only once our gate code is mostly finished/stable.
assert!(F::Extension::is_in_basefield(&value));
value.to_basefield_array()[0]
})
.collect()
}
fn eval_unfiltered_recursively( fn eval_unfiltered_recursively(
&self, &self,
builder: &mut CircuitBuilder<F>, builder: &mut CircuitBuilder<F, D>,
vars: EvaluationTargets, vars: EvaluationTargets<D>,
) -> Vec<Target>; ) -> Vec<ExtensionTarget<D>>;
fn eval_filtered(&self, vars: EvaluationVars<F>) -> Vec<F> { fn eval_filtered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
// TODO: Filter // TODO: Filter
self.eval_unfiltered(vars) self.eval_unfiltered(vars)
} }
/// Like `eval_filtered`, but specialized for points in the base field.
fn eval_filtered_base(&self, vars: EvaluationVarsBase<F>) -> Vec<F> {
// TODO: Filter
self.eval_unfiltered_base(vars)
}
fn eval_filtered_recursively( fn eval_filtered_recursively(
&self, &self,
builder: &mut CircuitBuilder<F>, builder: &mut CircuitBuilder<F, D>,
vars: EvaluationTargets, vars: EvaluationTargets<D>,
) -> Vec<Target> { ) -> Vec<ExtensionTarget<D>> {
// TODO: Filter // TODO: Filter
self.eval_unfiltered_recursively(builder, vars) self.eval_unfiltered_recursively(builder, vars)
} }
@ -53,30 +91,30 @@ pub trait Gate<F: Field>: 'static + Send + Sync {
/// A wrapper around an `Rc<Gate>` which implements `PartialEq`, `Eq` and `Hash` based on gate IDs. /// A wrapper around an `Rc<Gate>` which implements `PartialEq`, `Eq` and `Hash` based on gate IDs.
#[derive(Clone)] #[derive(Clone)]
pub struct GateRef<F: Field>(pub(crate) Arc<dyn Gate<F>>); pub struct GateRef<F: Extendable<D>, const D: usize>(pub(crate) Arc<dyn Gate<F, D>>);
impl<F: Field> GateRef<F> { impl<F: Extendable<D>, const D: usize> GateRef<F, D> {
pub fn new<G: Gate<F>>(gate: G) -> GateRef<F> { pub fn new<G: Gate<F, D>>(gate: G) -> GateRef<F, D> {
GateRef(Arc::new(gate)) GateRef(Arc::new(gate))
} }
} }
impl<F: Field> PartialEq for GateRef<F> { impl<F: Extendable<D>, const D: usize> PartialEq for GateRef<F, D> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.0.id() == other.0.id() self.0.id() == other.0.id()
} }
} }
impl<F: Field> Hash for GateRef<F> { impl<F: Extendable<D>, const D: usize> Hash for GateRef<F, D> {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.0.id().hash(state) self.0.id().hash(state)
} }
} }
impl<F: Field> Eq for GateRef<F> {} impl<F: Extendable<D>, const D: usize> Eq for GateRef<F, D> {}
/// A gate along with any constants used to configure it. /// A gate along with any constants used to configure it.
pub struct GateInstance<F: Field> { pub struct GateInstance<F: Extendable<D>, const D: usize> {
pub gate_type: GateRef<F>, pub gate_type: GateRef<F, D>,
pub constants: Vec<F>, pub constants: Vec<F>,
} }

View File

@ -1,3 +1,4 @@
use crate::field::extension_field::Extendable;
use crate::field::field::Field; use crate::field::field::Field;
use crate::gates::gate::{Gate, GateRef}; use crate::gates::gate::{Gate, GateRef};
use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues};
@ -9,12 +10,12 @@ const WITNESS_DEGREE: usize = WITNESS_SIZE - 1;
/// Tests that the constraints imposed by the given gate are low-degree by applying them to random /// Tests that the constraints imposed by the given gate are low-degree by applying them to random
/// low-degree witness polynomials. /// low-degree witness polynomials.
pub(crate) fn test_low_degree<F: Field>(gate: GateRef<F>) { pub(crate) fn test_low_degree<F: Extendable<D>, const D: usize>(gate: GateRef<F, D>) {
let gate = gate.0; let gate = gate.0;
let rate_bits = log2_ceil(gate.degree() + 1); let rate_bits = log2_ceil(gate.degree() + 1);
let wire_ldes = random_low_degree_matrix(gate.num_wires(), rate_bits); let wire_ldes = random_low_degree_matrix::<F::Extension>(gate.num_wires(), rate_bits);
let constant_ldes = random_low_degree_matrix::<F>(gate.num_constants(), rate_bits); let constant_ldes = random_low_degree_matrix::<F::Extension>(gate.num_constants(), rate_bits);
assert_eq!(wire_ldes.len(), constant_ldes.len()); assert_eq!(wire_ldes.len(), constant_ldes.len());
let constraint_evals = wire_ldes let constraint_evals = wire_ldes

View File

@ -1,9 +1,10 @@
use std::sync::Arc; use std::sync::Arc;
use crate::circuit_builder::CircuitBuilder; 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::field::field::Field;
use crate::gates::gate::{Gate, GateRef}; use crate::gates::gate::{Gate, GateRef};
use crate::gates::gmimc_eval::GMiMCEvalGate;
use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::generator::{SimpleGenerator, WitnessGenerator};
use crate::gmimc::gmimc_automatic_constants; use crate::gmimc::gmimc_automatic_constants;
use crate::target::Target; use crate::target::Target;
@ -22,17 +23,17 @@ const W: usize = 12;
/// sibling digests. It also has an accumulator that computes the weighted sum of these flags, for /// sibling digests. It also has an accumulator that computes the weighted sum of these flags, for
/// computing the index of the leaf based on these swap bits. /// computing the index of the leaf based on these swap bits.
#[derive(Debug)] #[derive(Debug)]
pub struct GMiMCGate<F: Field, const R: usize> { pub struct GMiMCGate<F: Extendable<D>, const D: usize, const R: usize> {
constants: Arc<[F; R]>, constants: Arc<[F; R]>,
} }
impl<F: Field, const R: usize> GMiMCGate<F, R> { impl<F: Extendable<D>, const D: usize, const R: usize> GMiMCGate<F, D, R> {
pub fn with_constants(constants: Arc<[F; R]>) -> GateRef<F> { pub fn with_constants(constants: Arc<[F; R]>) -> GateRef<F, D> {
let gate = GMiMCGate::<F, R> { constants }; let gate = GMiMCGate::<F, D, R> { constants };
GateRef::new(gate) GateRef::new(gate)
} }
pub fn with_automatic_constants() -> GateRef<F> { pub fn with_automatic_constants() -> GateRef<F, D> {
let constants = Arc::new(gmimc_automatic_constants::<F, R>()); let constants = Arc::new(gmimc_automatic_constants::<F, R>());
Self::with_constants(constants) Self::with_constants(constants)
} }
@ -66,21 +67,21 @@ impl<F: Field, const R: usize> GMiMCGate<F, R> {
} }
} }
impl<F: Field, const R: usize> Gate<F> for GMiMCGate<F, R> { impl<F: Extendable<D>, const D: usize, const R: usize> Gate<F, D> for GMiMCGate<F, D, R> {
fn id(&self) -> String { fn id(&self) -> String {
format!("<R={}> {:?}", R, self) format!("<R={}> {:?}", R, self)
} }
fn eval_unfiltered(&self, vars: EvaluationVars<F>) -> Vec<F> { fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let mut constraints = Vec::with_capacity(self.num_constraints()); let mut constraints = Vec::with_capacity(self.num_constraints());
// Assert that `swap` is binary. // Assert that `swap` is binary.
let swap = vars.local_wires[Self::WIRE_SWAP]; let swap = vars.local_wires[Self::WIRE_SWAP];
constraints.push(swap * (swap - F::ONE)); constraints.push(swap * (swap - F::Extension::ONE));
let old_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_OLD]; let old_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_OLD];
let new_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_NEW]; let new_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_NEW];
let computed_new_index_acc = F::TWO * old_index_acc + swap; let computed_new_index_acc = F::Extension::TWO * old_index_acc + swap;
constraints.push(computed_new_index_acc - new_index_acc); constraints.push(computed_new_index_acc - new_index_acc);
let mut state = Vec::with_capacity(12); let mut state = Vec::with_capacity(12);
@ -100,11 +101,11 @@ impl<F: Field, const R: usize> Gate<F> for GMiMCGate<F, R> {
// Value that is implicitly added to each element. // Value that is implicitly added to each element.
// See https://affine.group/2020/02/starkware-challenge // See https://affine.group/2020/02/starkware-challenge
let mut addition_buffer = F::ZERO; let mut addition_buffer = F::Extension::ZERO;
for r in 0..R { for r in 0..R {
let active = r % W; let active = r % W;
let cubing_input = state[active] + addition_buffer + self.constants[r]; let cubing_input = state[active] + addition_buffer + self.constants[r].into();
let cubing_input_wire = vars.local_wires[Self::wire_cubing_input(r)]; let cubing_input_wire = vars.local_wires[Self::wire_cubing_input(r)];
constraints.push(cubing_input - cubing_input_wire); constraints.push(cubing_input - cubing_input_wire);
let f = cubing_input_wire.cube(); let f = cubing_input_wire.cube();
@ -122,37 +123,36 @@ impl<F: Field, const R: usize> Gate<F> for GMiMCGate<F, R> {
fn eval_unfiltered_recursively( fn eval_unfiltered_recursively(
&self, &self,
builder: &mut CircuitBuilder<F>, builder: &mut CircuitBuilder<F, D>,
vars: EvaluationTargets, vars: EvaluationTargets<D>,
) -> Vec<Target> { ) -> Vec<ExtensionTarget<D>> {
let mut constraints = Vec::with_capacity(self.num_constraints()); let mut constraints = Vec::with_capacity(self.num_constraints());
// Assert that `swap` is binary. Usually we would assert that
// swap(swap - 1) = 0
// but to make it work with a single ArithmeticGate, we will instead write it as
// swap*swap - swap = 0
let swap = vars.local_wires[Self::WIRE_SWAP]; let swap = vars.local_wires[Self::WIRE_SWAP];
constraints.push(builder.mul_sub(swap, swap, swap)); let one_ext = builder.one_extension();
let not_swap = builder.sub_extension(swap, one_ext);
constraints.push(builder.mul_extension(swap, not_swap));
let old_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_OLD]; let old_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_OLD];
let new_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_NEW]; let new_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_NEW];
// computed_new_index_acc = 2 * old_index_acc + swap // computed_new_index_acc = 2 * old_index_acc + swap
let two = builder.two(); let two = builder.two();
let computed_new_index_acc = builder.mul_add(two, old_index_acc, swap); let double_old_index_acc = builder.scalar_mul_ext(two, old_index_acc);
constraints.push(builder.sub(computed_new_index_acc, new_index_acc)); let computed_new_index_acc = builder.add_extension(double_old_index_acc, swap);
constraints.push(builder.sub_extension(computed_new_index_acc, new_index_acc));
let mut state = Vec::with_capacity(12); let mut state = Vec::with_capacity(12);
for i in 0..4 { for i in 0..4 {
let a = vars.local_wires[i]; let a = vars.local_wires[i];
let b = vars.local_wires[i + 4]; let b = vars.local_wires[i + 4];
let delta = builder.sub(b, a); let delta = builder.sub_extension(b, a);
state.push(builder.mul_add(swap, delta, a)); state.push(builder.mul_add_extension(swap, delta, a));
} }
for i in 0..4 { for i in 0..4 {
let a = vars.local_wires[i + 4]; let a = vars.local_wires[i + 4];
let b = vars.local_wires[i]; let b = vars.local_wires[i];
let delta = builder.sub(b, a); let delta = builder.sub_extension(b, a);
state.push(builder.mul_add(swap, delta, a)); state.push(builder.mul_add_extension(swap, delta, a));
} }
for i in 8..12 { for i in 8..12 {
state.push(vars.local_wires[i]); state.push(vars.local_wires[i]);
@ -160,56 +160,24 @@ impl<F: Field, const R: usize> Gate<F> for GMiMCGate<F, R> {
// Value that is implicitly added to each element. // Value that is implicitly added to each element.
// See https://affine.group/2020/02/starkware-challenge // See https://affine.group/2020/02/starkware-challenge
let mut addition_buffer = builder.zero(); let mut addition_buffer = builder.zero_extension();
for r in 0..R { for r in 0..R {
let active = r % W; let active = r % W;
let gate = builder.add_gate(GMiMCEvalGate::get(), vec![self.constants[r]]);
let cubing_input = vars.local_wires[Self::wire_cubing_input(r)]; let constant = builder.constant_extension(self.constants[r].into());
builder.route( let cubing_input =
cubing_input, builder.add_many_extension(&[state[active], addition_buffer, constant]);
Target::Wire(Wire { let square = builder.mul_extension(cubing_input, cubing_input);
gate, let f = builder.mul_extension(square, cubing_input);
input: GMiMCEvalGate::<F>::WIRE_CUBING_INPUT, addition_buffer = builder.add_extension(addition_buffer, f);
}), state[active] = builder.sub_extension(state[active], f);
);
builder.route(
addition_buffer,
Target::Wire(Wire {
gate,
input: GMiMCEvalGate::<F>::WIRE_ADDITION_BUFFER_OLD,
}),
);
builder.route(
state[active],
Target::Wire(Wire {
gate,
input: GMiMCEvalGate::<F>::WIRE_STATE_A_OLD,
}),
);
constraints.push(Target::Wire(Wire {
gate,
input: GMiMCEvalGate::<F>::WIRE_CONSTRAINT,
}));
addition_buffer = Target::Wire(Wire {
gate,
input: GMiMCEvalGate::<F>::WIRE_ADDITION_BUFFER_NEW,
});
state[active] = Target::Wire(Wire {
gate,
input: GMiMCEvalGate::<F>::WIRE_STATE_A_NEW,
});
} }
for i in 0..W { for i in 0..W {
state[i] = builder.add(state[i], addition_buffer); state[i] = builder.add_extension(state[i], addition_buffer);
constraints.push(builder.sub(state[i], vars.local_wires[Self::wire_output(i)])); constraints
.push(builder.sub_extension(state[i], vars.local_wires[Self::wire_output(i)]));
} }
constraints constraints
@ -245,19 +213,21 @@ impl<F: Field, const R: usize> Gate<F> for GMiMCGate<F, R> {
} }
#[derive(Debug)] #[derive(Debug)]
struct GMiMCGenerator<F: Field, const R: usize> { struct GMiMCGenerator<F: Extendable<D>, const D: usize, const R: usize> {
gate_index: usize, gate_index: usize,
constants: Arc<[F; R]>, constants: Arc<[F; R]>,
} }
impl<F: Field, const R: usize> SimpleGenerator<F> for GMiMCGenerator<F, R> { impl<F: Extendable<D>, const D: usize, const R: usize> SimpleGenerator<F>
for GMiMCGenerator<F, D, R>
{
fn dependencies(&self) -> Vec<Target> { fn dependencies(&self) -> Vec<Target> {
let mut dep_input_indices = Vec::with_capacity(W + 2); let mut dep_input_indices = Vec::with_capacity(W + 2);
for i in 0..W { for i in 0..W {
dep_input_indices.push(GMiMCGate::<F, R>::wire_input(i)); dep_input_indices.push(GMiMCGate::<F, D, R>::wire_input(i));
} }
dep_input_indices.push(GMiMCGate::<F, R>::WIRE_SWAP); dep_input_indices.push(GMiMCGate::<F, D, R>::WIRE_SWAP);
dep_input_indices.push(GMiMCGate::<F, R>::WIRE_INDEX_ACCUMULATOR_OLD); dep_input_indices.push(GMiMCGate::<F, D, R>::WIRE_INDEX_ACCUMULATOR_OLD);
dep_input_indices dep_input_indices
.into_iter() .into_iter()
@ -277,14 +247,14 @@ impl<F: Field, const R: usize> SimpleGenerator<F> for GMiMCGenerator<F, R> {
.map(|i| { .map(|i| {
witness.get_wire(Wire { witness.get_wire(Wire {
gate: self.gate_index, gate: self.gate_index,
input: GMiMCGate::<F, R>::wire_input(i), input: GMiMCGate::<F, D, R>::wire_input(i),
}) })
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let swap_value = witness.get_wire(Wire { let swap_value = witness.get_wire(Wire {
gate: self.gate_index, gate: self.gate_index,
input: GMiMCGate::<F, R>::WIRE_SWAP, input: GMiMCGate::<F, D, R>::WIRE_SWAP,
}); });
debug_assert!(swap_value == F::ZERO || swap_value == F::ONE); debug_assert!(swap_value == F::ZERO || swap_value == F::ONE);
if swap_value == F::ONE { if swap_value == F::ONE {
@ -296,13 +266,13 @@ impl<F: Field, const R: usize> SimpleGenerator<F> for GMiMCGenerator<F, R> {
// Update the index accumulator. // Update the index accumulator.
let old_index_acc_value = witness.get_wire(Wire { let old_index_acc_value = witness.get_wire(Wire {
gate: self.gate_index, gate: self.gate_index,
input: GMiMCGate::<F, R>::WIRE_INDEX_ACCUMULATOR_OLD, input: GMiMCGate::<F, D, R>::WIRE_INDEX_ACCUMULATOR_OLD,
}); });
let new_index_acc_value = F::TWO * old_index_acc_value + swap_value; let new_index_acc_value = F::TWO * old_index_acc_value + swap_value;
result.set_wire( result.set_wire(
Wire { Wire {
gate: self.gate_index, gate: self.gate_index,
input: GMiMCGate::<F, R>::WIRE_INDEX_ACCUMULATOR_NEW, input: GMiMCGate::<F, D, R>::WIRE_INDEX_ACCUMULATOR_NEW,
}, },
new_index_acc_value, new_index_acc_value,
); );
@ -317,7 +287,7 @@ impl<F: Field, const R: usize> SimpleGenerator<F> for GMiMCGenerator<F, R> {
result.set_wire( result.set_wire(
Wire { Wire {
gate: self.gate_index, gate: self.gate_index,
input: GMiMCGate::<F, R>::wire_cubing_input(r), input: GMiMCGate::<F, D, R>::wire_cubing_input(r),
}, },
cubing_input, cubing_input,
); );
@ -331,7 +301,7 @@ impl<F: Field, const R: usize> SimpleGenerator<F> for GMiMCGenerator<F, R> {
result.set_wire( result.set_wire(
Wire { Wire {
gate: self.gate_index, gate: self.gate_index,
input: GMiMCGate::<F, R>::wire_output(i), input: GMiMCGate::<F, D, R>::wire_output(i),
}, },
state[i], state[i],
); );
@ -361,7 +331,7 @@ mod tests {
type F = CrandallField; type F = CrandallField;
const R: usize = 101; const R: usize = 101;
let constants = Arc::new([F::TWO; R]); let constants = Arc::new([F::TWO; R]);
type Gate = GMiMCGate<F, R>; type Gate = GMiMCGate<F, 4, R>;
let gate = Gate::with_constants(constants.clone()); let gate = Gate::with_constants(constants.clone());
let config = CircuitConfig { let config = CircuitConfig {
@ -423,7 +393,7 @@ mod tests {
type F = CrandallField; type F = CrandallField;
const R: usize = 101; const R: usize = 101;
let constants = Arc::new([F::TWO; R]); let constants = Arc::new([F::TWO; R]);
type Gate = GMiMCGate<F, R>; type Gate = GMiMCGate<F, 4, R>;
let gate = Gate::with_constants(constants); let gate = Gate::with_constants(constants);
test_low_degree(gate) test_low_degree(gate)
} }

View File

@ -1,239 +0,0 @@
use std::marker::PhantomData;
use crate::circuit_builder::CircuitBuilder;
use crate::field::field::Field;
use crate::gates::gate::{Gate, GateRef};
use crate::generator::{SimpleGenerator, WitnessGenerator};
use crate::target::Target;
use crate::vars::{EvaluationTargets, EvaluationVars};
use crate::wire::Wire;
use crate::witness::PartialWitness;
/// Performs some arithmetic involved in the evaluation of GMiMC's constraint polynomials for one
/// round. In particular, this performs the following computations:
///
/// - `constraint := state_a_old + addition_buffer_old + C_r - cubing_input`
/// - `f := cubing_input^3`
/// - `addition_buffer_new := addition_buffer_old + f`
/// - `state_a_new := state_a_old - f`
///
/// Here `state_a_{old,new}` represent the old and new states of the `a`th element of the GMiMC
/// permutation. `addition_buffer_{old,new}` represents a value that is implicitly added to each
/// element; see https://affine.group/2020/02/starkware-challenge. `C_r` represents the round
/// constant for round `r`.
#[derive(Debug)]
pub struct GMiMCEvalGate<F: Field> {
_phantom: PhantomData<F>,
}
impl<F: Field> GMiMCEvalGate<F> {
pub fn get() -> GateRef<F> {
GateRef::new(GMiMCEvalGate {
_phantom: PhantomData,
})
}
pub const CONST_C_R: usize = 0;
pub const WIRE_CONSTRAINT: usize = 0;
pub const WIRE_STATE_A_OLD: usize = 1;
pub const WIRE_STATE_A_NEW: usize = 2;
pub const WIRE_ADDITION_BUFFER_OLD: usize = 3;
pub const WIRE_ADDITION_BUFFER_NEW: usize = 4;
pub const WIRE_CUBING_INPUT: usize = 5;
const WIRE_F: usize = 6;
}
impl<F: Field> Gate<F> for GMiMCEvalGate<F> {
fn id(&self) -> String {
format!("{:?}", self)
}
fn eval_unfiltered(&self, vars: EvaluationVars<F>) -> Vec<F> {
let c_r = vars.local_constants[Self::CONST_C_R];
let constraint = vars.local_wires[Self::WIRE_CONSTRAINT];
let state_a_old = vars.local_wires[Self::WIRE_STATE_A_OLD];
let state_a_new = vars.local_wires[Self::WIRE_STATE_A_NEW];
let addition_buffer_old = vars.local_wires[Self::WIRE_ADDITION_BUFFER_OLD];
let addition_buffer_new = vars.local_wires[Self::WIRE_ADDITION_BUFFER_NEW];
let cubing_input = vars.local_wires[Self::WIRE_CUBING_INPUT];
let f = vars.local_wires[Self::WIRE_F];
let mut constraints = Vec::with_capacity(self.num_constraints());
// constraint := state_a_old + addition_buffer_old + C_r - cubing_input
let computed_constraint = state_a_old + addition_buffer_old + c_r - cubing_input;
constraints.push(constraint - computed_constraint);
// f := cubing_input^3
let computed_f = cubing_input.cube();
constraints.push(f - computed_f);
// addition_buffer_new := addition_buffer_old + f
let computed_addition_buffer_new = addition_buffer_old + f;
constraints.push(addition_buffer_new - computed_addition_buffer_new);
// state_a_new := state_a_old - f
let computed_state_a_new = state_a_old - f;
constraints.push(state_a_new - computed_state_a_new);
constraints
}
fn eval_unfiltered_recursively(
&self,
builder: &mut CircuitBuilder<F>,
vars: EvaluationTargets,
) -> Vec<Target> {
let c_r = vars.local_constants[Self::CONST_C_R];
let constraint = vars.local_wires[Self::WIRE_CONSTRAINT];
let state_a_old = vars.local_wires[Self::WIRE_STATE_A_OLD];
let state_a_new = vars.local_wires[Self::WIRE_STATE_A_NEW];
let addition_buffer_old = vars.local_wires[Self::WIRE_ADDITION_BUFFER_OLD];
let addition_buffer_new = vars.local_wires[Self::WIRE_ADDITION_BUFFER_NEW];
let cubing_input = vars.local_wires[Self::WIRE_CUBING_INPUT];
let f = vars.local_wires[Self::WIRE_F];
let mut constraints = Vec::with_capacity(self.num_constraints());
// constraint := state_a_old + addition_buffer_old + C_r - cubing_input
let sum = builder.add_many(&[state_a_old, addition_buffer_old, c_r]);
let computed_constraint = builder.sub(sum, cubing_input);
constraints.push(builder.sub(constraint, computed_constraint));
// f := cubing_input^3
let computed_f = builder.cube(cubing_input);
constraints.push(builder.sub(f, computed_f));
// addition_buffer_new := addition_buffer_old + f
let computed_addition_buffer_new = builder.add(addition_buffer_old, f);
constraints.push(builder.sub(addition_buffer_new, computed_addition_buffer_new));
// state_a_new := state_a_old - f
let computed_state_a_new = builder.sub(state_a_old, f);
constraints.push(builder.sub(state_a_new, computed_state_a_new));
constraints
}
fn generators(
&self,
gate_index: usize,
local_constants: &[F],
) -> Vec<Box<dyn WitnessGenerator<F>>> {
let gen = GMiMCEvalGenerator::<F> {
gate_index,
c_r: local_constants[Self::CONST_C_R],
};
vec![Box::new(gen)]
}
fn num_wires(&self) -> usize {
7
}
fn num_constants(&self) -> usize {
1
}
fn degree(&self) -> usize {
3
}
fn num_constraints(&self) -> usize {
4
}
}
#[derive(Debug)]
struct GMiMCEvalGenerator<F: Field> {
gate_index: usize,
c_r: F,
}
impl<F: Field> SimpleGenerator<F> for GMiMCEvalGenerator<F> {
fn dependencies(&self) -> Vec<Target> {
let gate = self.gate_index;
vec![
Target::Wire(Wire {
gate,
input: GMiMCEvalGate::<F>::WIRE_CUBING_INPUT,
}),
Target::Wire(Wire {
gate,
input: GMiMCEvalGate::<F>::WIRE_ADDITION_BUFFER_OLD,
}),
Target::Wire(Wire {
gate,
input: GMiMCEvalGate::<F>::WIRE_STATE_A_OLD,
}),
]
}
fn run_once(&self, witness: &PartialWitness<F>) -> PartialWitness<F> {
let gate = self.gate_index;
let wire_constraint = Wire {
gate,
input: GMiMCEvalGate::<F>::WIRE_CONSTRAINT,
};
let wire_state_a_old = Wire {
gate,
input: GMiMCEvalGate::<F>::WIRE_STATE_A_OLD,
};
let wire_state_a_new = Wire {
gate,
input: GMiMCEvalGate::<F>::WIRE_STATE_A_NEW,
};
let wire_addition_buffer_old = Wire {
gate,
input: GMiMCEvalGate::<F>::WIRE_ADDITION_BUFFER_OLD,
};
let wire_addition_buffer_new = Wire {
gate,
input: GMiMCEvalGate::<F>::WIRE_ADDITION_BUFFER_NEW,
};
let wire_cubing_input = Wire {
gate,
input: GMiMCEvalGate::<F>::WIRE_CUBING_INPUT,
};
let wire_f = Wire {
gate,
input: GMiMCEvalGate::<F>::WIRE_F,
};
let addition_buffer_old = witness.get_wire(wire_addition_buffer_old);
let state_a_old = witness.get_wire(wire_state_a_old);
let cubing_input = witness.get_wire(wire_cubing_input);
// constraint := state_a_old + addition_buffer_old + C_r - cubing_input
let constraint = state_a_old + addition_buffer_old + self.c_r - cubing_input;
// f := cubing_input^3
let f = cubing_input.cube();
// addition_buffer_new := addition_buffer_old + f
let addition_buffer_new = addition_buffer_old + f;
// state_a_new := state_a_old - f
let state_a_new = state_a_old - f;
let mut witness = PartialWitness::new();
witness.set_wire(wire_constraint, constraint);
witness.set_wire(wire_f, f);
witness.set_wire(wire_state_a_new, addition_buffer_new);
witness.set_wire(wire_addition_buffer_new, state_a_new);
witness
}
}
#[cfg(test)]
mod tests {
use crate::field::crandall_field::CrandallField;
use crate::gates::gate_testing::test_low_degree;
use crate::gates::gmimc_eval::GMiMCEvalGate;
#[test]
fn low_degree() {
test_low_degree(GMiMCEvalGate::<CrandallField>::get())
}
}

View File

@ -3,10 +3,10 @@ use std::marker::PhantomData;
use std::ops::Range; use std::ops::Range;
use crate::circuit_builder::CircuitBuilder; use crate::circuit_builder::CircuitBuilder;
use crate::field::extension_field::target::ExtensionTarget;
use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::extension_field::{Extendable, FieldExtension};
use crate::field::field::Field;
use crate::field::lagrange::interpolant; use crate::field::lagrange::interpolant;
use crate::gadgets::polynomial::PolynomialCoeffsTarget; use crate::gadgets::polynomial::PolynomialCoeffsExtExtTarget;
use crate::gates::gate::{Gate, GateRef}; use crate::gates::gate::{Gate, GateRef};
use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::generator::{SimpleGenerator, WitnessGenerator};
use crate::polynomial::polynomial::PolynomialCoeffs; use crate::polynomial::polynomial::PolynomialCoeffs;
@ -21,13 +21,19 @@ use crate::witness::PartialWitness;
/// to evaluate the interpolant at. It computes the interpolant and outputs its evaluation at the /// to evaluate the interpolant at. It computes the interpolant and outputs its evaluation at the
/// given point. /// given point.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct InterpolationGate<F: Field + Extendable<D>, const D: usize> { pub(crate) struct InterpolationGate<F: Extendable<D>, const D: usize>
where
F::Extension: Extendable<D>,
{
num_points: usize, num_points: usize,
_phantom: PhantomData<F>, _phantom: PhantomData<F>,
} }
impl<F: Field + Extendable<D>, const D: usize> InterpolationGate<F, D> { impl<F: Extendable<D>, const D: usize> InterpolationGate<F, D>
pub fn new(num_points: usize) -> GateRef<F> { where
F::Extension: Extendable<D>,
{
pub fn new(num_points: usize) -> GateRef<F, D> {
let gate = Self { let gate = Self {
num_points, num_points,
_phantom: PhantomData, _phantom: PhantomData,
@ -93,28 +99,31 @@ impl<F: Field + Extendable<D>, const D: usize> InterpolationGate<F, D> {
} }
} }
impl<F: Field + Extendable<D>, const D: usize> Gate<F> for InterpolationGate<F, D> { impl<F: Extendable<D>, const D: usize> Gate<F, D> for InterpolationGate<F, D>
where
F::Extension: Extendable<D>,
{
fn id(&self) -> String { fn id(&self) -> String {
format!("{:?}<D={}>", self, D) format!("{:?}<D={}>", self, D)
} }
fn eval_unfiltered(&self, vars: EvaluationVars<F>) -> Vec<F> { fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let mut constraints = Vec::with_capacity(self.num_constraints()); let mut constraints = Vec::with_capacity(self.num_constraints());
let coeffs = (0..self.num_points) let coeffs = (0..self.num_points)
.map(|i| vars.get_local_ext(self.wires_coeff(i))) .map(|i| vars.get_local_ext_ext(self.wires_coeff(i)))
.collect(); .collect();
let interpolant = PolynomialCoeffs::new(coeffs); let interpolant = PolynomialCoeffs::new(coeffs);
for i in 0..self.num_points { for i in 0..self.num_points {
let point = F::Extension::from_basefield(vars.local_wires[self.wire_point(i)]); let point = vars.local_wires[self.wire_point(i)];
let value = vars.get_local_ext(self.wires_value(i)); let value = vars.get_local_ext_ext(self.wires_value(i));
let computed_value = interpolant.eval(point); let computed_value = interpolant.eval(point.into());
constraints.extend(&(value - computed_value).to_basefield_array()); constraints.extend(&(value - computed_value).to_basefield_array());
} }
let evaluation_point = vars.get_local_ext(self.wires_evaluation_point()); let evaluation_point = vars.get_local_ext_ext(self.wires_evaluation_point());
let evaluation_value = vars.get_local_ext(self.wires_evaluation_value()); let evaluation_value = vars.get_local_ext_ext(self.wires_evaluation_value());
let computed_evaluation_value = interpolant.eval(evaluation_point); let computed_evaluation_value = interpolant.eval(evaluation_point);
constraints.extend(&(evaluation_value - computed_evaluation_value).to_basefield_array()); constraints.extend(&(evaluation_value - computed_evaluation_value).to_basefield_array());
@ -123,34 +132,34 @@ impl<F: Field + Extendable<D>, const D: usize> Gate<F> for InterpolationGate<F,
fn eval_unfiltered_recursively( fn eval_unfiltered_recursively(
&self, &self,
builder: &mut CircuitBuilder<F>, builder: &mut CircuitBuilder<F, D>,
vars: EvaluationTargets, vars: EvaluationTargets<D>,
) -> Vec<Target> { ) -> Vec<ExtensionTarget<D>> {
let mut constraints = Vec::with_capacity(self.num_constraints()); let mut constraints = Vec::with_capacity(self.num_constraints());
let coeffs = (0..self.num_points) let coeffs = (0..self.num_points)
.map(|i| vars.get_local_ext(self.wires_coeff(i))) .map(|i| vars.get_local_ext_ext(self.wires_coeff(i)))
.collect(); .collect();
let interpolant = PolynomialCoeffsTarget(coeffs); let interpolant = PolynomialCoeffsExtExtTarget(coeffs);
for i in 0..self.num_points { for i in 0..self.num_points {
let point = vars.local_wires[self.wire_point(i)]; let point = vars.local_wires[self.wire_point(i)];
let value = vars.get_local_ext(self.wires_value(i)); let value = vars.get_local_ext_ext(self.wires_value(i));
let computed_value = interpolant.eval_scalar(builder, point); let computed_value = interpolant.eval_scalar(builder, point);
constraints.extend( constraints.extend(
&builder &builder
.sub_extension(value, computed_value) .sub_ext_ext(value, computed_value)
.to_target_array(), .to_ext_target_array(),
); );
} }
let evaluation_point = vars.get_local_ext(self.wires_evaluation_point()); let evaluation_point = vars.get_local_ext_ext(self.wires_evaluation_point());
let evaluation_value = vars.get_local_ext(self.wires_evaluation_value()); let evaluation_value = vars.get_local_ext_ext(self.wires_evaluation_value());
let computed_evaluation_value = interpolant.eval(builder, evaluation_point); let computed_evaluation_value = interpolant.eval(builder, evaluation_point);
constraints.extend( constraints.extend(
&builder &builder
.sub_extension(evaluation_value, computed_evaluation_value) .sub_ext_ext(evaluation_value, computed_evaluation_value)
.to_target_array(), .to_ext_target_array(),
); );
constraints constraints
@ -190,13 +199,19 @@ impl<F: Field + Extendable<D>, const D: usize> Gate<F> for InterpolationGate<F,
} }
} }
struct InterpolationGenerator<F: Field + Extendable<D>, const D: usize> { struct InterpolationGenerator<F: Extendable<D>, const D: usize>
where
F::Extension: Extendable<D>,
{
gate_index: usize, gate_index: usize,
gate: InterpolationGate<F, D>, gate: InterpolationGate<F, D>,
_phantom: PhantomData<F>, _phantom: PhantomData<F>,
} }
impl<F: Field + Extendable<D>, const D: usize> SimpleGenerator<F> for InterpolationGenerator<F, D> { impl<F: Extendable<D>, const D: usize> SimpleGenerator<F> for InterpolationGenerator<F, D>
where
F::Extension: Extendable<D>,
{
fn dependencies(&self) -> Vec<Target> { fn dependencies(&self) -> Vec<Target> {
let local_target = |input| { let local_target = |input| {
Target::Wire(Wire { Target::Wire(Wire {
@ -293,7 +308,6 @@ mod tests {
#[test] #[test]
fn low_degree() { fn low_degree() {
type F = CrandallField; type F = CrandallField;
test_low_degree(InterpolationGate::<F, 2>::new(4));
test_low_degree(InterpolationGate::<F, 4>::new(4)); test_low_degree(InterpolationGate::<F, 4>::new(4));
} }
} }

View File

@ -2,7 +2,6 @@ pub(crate) mod arithmetic;
pub mod constant; pub mod constant;
pub(crate) mod gate; pub(crate) mod gate;
pub mod gmimc; pub mod gmimc;
pub(crate) mod gmimc_eval;
mod interpolation; mod interpolation;
pub(crate) mod noop; pub(crate) mod noop;

View File

@ -1,33 +1,33 @@
use crate::circuit_builder::CircuitBuilder; use crate::circuit_builder::CircuitBuilder;
use crate::field::field::Field; use crate::field::extension_field::target::ExtensionTarget;
use crate::field::extension_field::Extendable;
use crate::gates::gate::{Gate, GateRef}; use crate::gates::gate::{Gate, GateRef};
use crate::generator::WitnessGenerator; use crate::generator::WitnessGenerator;
use crate::target::Target;
use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::vars::{EvaluationTargets, EvaluationVars};
/// A gate which takes a single constant parameter and outputs that value. /// A gate which takes a single constant parameter and outputs that value.
pub struct NoopGate; pub struct NoopGate;
impl NoopGate { impl NoopGate {
pub fn get<F: Field>() -> GateRef<F> { pub fn get<F: Extendable<D>, const D: usize>() -> GateRef<F, D> {
GateRef::new(NoopGate) GateRef::new(NoopGate)
} }
} }
impl<F: Field> Gate<F> for NoopGate { impl<F: Extendable<D>, const D: usize> Gate<F, D> for NoopGate {
fn id(&self) -> String { fn id(&self) -> String {
"NoopGate".into() "NoopGate".into()
} }
fn eval_unfiltered(&self, _vars: EvaluationVars<F>) -> Vec<F> { fn eval_unfiltered(&self, _vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
Vec::new() Vec::new()
} }
fn eval_unfiltered_recursively( fn eval_unfiltered_recursively(
&self, &self,
_builder: &mut CircuitBuilder<F>, _builder: &mut CircuitBuilder<F, D>,
_vars: EvaluationTargets, _vars: EvaluationTargets<D>,
) -> Vec<Target> { ) -> Vec<ExtensionTarget<D>> {
Vec::new() Vec::new()
} }
@ -64,6 +64,6 @@ mod tests {
#[test] #[test]
fn low_degree() { fn low_degree() {
test_low_degree(NoopGate::get::<CrandallField>()) test_low_degree(NoopGate::get::<CrandallField, 4>())
} }
} }

View File

@ -1,6 +1,7 @@
//! Concrete instantiation of a hash function. //! Concrete instantiation of a hash function.
use crate::circuit_builder::CircuitBuilder; use crate::circuit_builder::CircuitBuilder;
use crate::field::extension_field::Extendable;
use crate::field::field::Field; use crate::field::field::Field;
use crate::gmimc::gmimc_permute_array; use crate::gmimc::gmimc_permute_array;
use crate::proof::{Hash, HashTarget}; use crate::proof::{Hash, HashTarget};
@ -132,7 +133,7 @@ pub fn hash_or_noop<F: Field>(inputs: Vec<F>) -> Hash<F> {
} }
} }
impl<F: Field> CircuitBuilder<F> { impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn hash_or_noop(&mut self, inputs: Vec<Target>) -> HashTarget { pub fn hash_or_noop(&mut self, inputs: Vec<Target>) -> HashTarget {
let zero = self.zero(); let zero = self.zero();
if inputs.len() <= 4 { if inputs.len() <= 4 {

View File

@ -1,4 +1,7 @@
use anyhow::{ensure, Result};
use crate::circuit_builder::CircuitBuilder; use crate::circuit_builder::CircuitBuilder;
use crate::field::extension_field::Extendable;
use crate::field::field::Field; use crate::field::field::Field;
use crate::gates::gmimc::GMiMCGate; use crate::gates::gmimc::GMiMCGate;
use crate::hash::GMIMC_ROUNDS; use crate::hash::GMIMC_ROUNDS;
@ -6,7 +9,6 @@ use crate::hash::{compress, hash_or_noop};
use crate::proof::{Hash, HashTarget}; use crate::proof::{Hash, HashTarget};
use crate::target::Target; use crate::target::Target;
use crate::wire::Wire; use crate::wire::Wire;
use anyhow::{ensure, Result};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct MerkleProof<F: Field> { pub struct MerkleProof<F: Field> {
@ -52,7 +54,7 @@ pub(crate) fn verify_merkle_proof<F: Field>(
Ok(()) Ok(())
} }
impl<F: Field> CircuitBuilder<F> { impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// Verifies that the given leaf data is present at the given index in the Merkle tree with the /// Verifies that the given leaf data is present at the given index in the Merkle tree with the
/// given root. /// given root.
pub(crate) fn verify_merkle_proof( pub(crate) fn verify_merkle_proof(
@ -71,23 +73,23 @@ impl<F: Field> CircuitBuilder<F> {
for (bit, sibling) in purported_index_bits.into_iter().zip(proof.siblings) { for (bit, sibling) in purported_index_bits.into_iter().zip(proof.siblings) {
let gate = self let gate = self
.add_gate_no_constants(GMiMCGate::<F, GMIMC_ROUNDS>::with_automatic_constants()); .add_gate_no_constants(GMiMCGate::<F, D, GMIMC_ROUNDS>::with_automatic_constants());
let swap_wire = GMiMCGate::<F, GMIMC_ROUNDS>::WIRE_SWAP; let swap_wire = GMiMCGate::<F, D, GMIMC_ROUNDS>::WIRE_SWAP;
let swap_wire = Target::Wire(Wire { let swap_wire = Target::Wire(Wire {
gate, gate,
input: swap_wire, input: swap_wire,
}); });
self.generate_copy(bit, swap_wire); self.generate_copy(bit, swap_wire);
let old_acc_wire = GMiMCGate::<F, GMIMC_ROUNDS>::WIRE_INDEX_ACCUMULATOR_OLD; let old_acc_wire = GMiMCGate::<F, D, GMIMC_ROUNDS>::WIRE_INDEX_ACCUMULATOR_OLD;
let old_acc_wire = Target::Wire(Wire { let old_acc_wire = Target::Wire(Wire {
gate, gate,
input: old_acc_wire, input: old_acc_wire,
}); });
self.route(acc_leaf_index, old_acc_wire); self.route(acc_leaf_index, old_acc_wire);
let new_acc_wire = GMiMCGate::<F, GMIMC_ROUNDS>::WIRE_INDEX_ACCUMULATOR_NEW; let new_acc_wire = GMiMCGate::<F, D, GMIMC_ROUNDS>::WIRE_INDEX_ACCUMULATOR_NEW;
let new_acc_wire = Target::Wire(Wire { let new_acc_wire = Target::Wire(Wire {
gate, gate,
input: new_acc_wire, input: new_acc_wire,
@ -98,7 +100,7 @@ impl<F: Field> CircuitBuilder<F> {
.map(|i| { .map(|i| {
Target::Wire(Wire { Target::Wire(Wire {
gate, gate,
input: GMiMCGate::<F, GMIMC_ROUNDS>::wire_input(i), input: GMiMCGate::<F, D, GMIMC_ROUNDS>::wire_input(i),
}) })
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -114,7 +116,7 @@ impl<F: Field> CircuitBuilder<F> {
.map(|i| { .map(|i| {
Target::Wire(Wire { Target::Wire(Wire {
gate, gate,
input: GMiMCGate::<F, GMIMC_ROUNDS>::wire_output(i), input: GMiMCGate::<F, D, GMIMC_ROUNDS>::wire_output(i),
}) })
}) })
.collect(), .collect(),

View File

@ -160,7 +160,9 @@ pub(crate) struct RecursiveChallenger {
} }
impl RecursiveChallenger { impl RecursiveChallenger {
pub(crate) fn new<F: Field>(builder: &mut CircuitBuilder<F>) -> Self { pub(crate) fn new<F: Extendable<D>, const D: usize>(
builder: &mut CircuitBuilder<F, D>,
) -> Self {
let zero = builder.zero(); let zero = builder.zero();
RecursiveChallenger { RecursiveChallenger {
sponge_state: [zero; SPONGE_WIDTH], sponge_state: [zero; SPONGE_WIDTH],
@ -186,7 +188,10 @@ impl RecursiveChallenger {
self.observe_elements(&hash.elements) self.observe_elements(&hash.elements)
} }
pub(crate) fn get_challenge<F: Field>(&mut self, builder: &mut CircuitBuilder<F>) -> Target { pub(crate) fn get_challenge<F: Extendable<D>, const D: usize>(
&mut self,
builder: &mut CircuitBuilder<F, D>,
) -> Target {
self.absorb_buffered_inputs(builder); self.absorb_buffered_inputs(builder);
if self.output_buffer.is_empty() { if self.output_buffer.is_empty() {
@ -200,16 +205,16 @@ impl RecursiveChallenger {
.expect("Output buffer should be non-empty") .expect("Output buffer should be non-empty")
} }
pub(crate) fn get_2_challenges<F: Field>( pub(crate) fn get_2_challenges<F: Extendable<D>, const D: usize>(
&mut self, &mut self,
builder: &mut CircuitBuilder<F>, builder: &mut CircuitBuilder<F, D>,
) -> (Target, Target) { ) -> (Target, Target) {
(self.get_challenge(builder), self.get_challenge(builder)) (self.get_challenge(builder), self.get_challenge(builder))
} }
pub(crate) fn get_3_challenges<F: Field>( pub(crate) fn get_3_challenges<F: Extendable<D>, const D: usize>(
&mut self, &mut self,
builder: &mut CircuitBuilder<F>, builder: &mut CircuitBuilder<F, D>,
) -> (Target, Target, Target) { ) -> (Target, Target, Target) {
( (
self.get_challenge(builder), self.get_challenge(builder),
@ -218,16 +223,19 @@ impl RecursiveChallenger {
) )
} }
pub(crate) fn get_n_challenges<F: Field>( pub(crate) fn get_n_challenges<F: Extendable<D>, const D: usize>(
&mut self, &mut self,
builder: &mut CircuitBuilder<F>, builder: &mut CircuitBuilder<F, D>,
n: usize, n: usize,
) -> Vec<Target> { ) -> Vec<Target> {
(0..n).map(|_| self.get_challenge(builder)).collect() (0..n).map(|_| self.get_challenge(builder)).collect()
} }
/// Absorb any buffered inputs. After calling this, the input buffer will be empty. /// Absorb any buffered inputs. After calling this, the input buffer will be empty.
fn absorb_buffered_inputs<F: Field>(&mut self, builder: &mut CircuitBuilder<F>) { fn absorb_buffered_inputs<F: Extendable<D>, const D: usize>(
&mut self,
builder: &mut CircuitBuilder<F, D>,
) {
if self.input_buffer.is_empty() { if self.input_buffer.is_empty() {
return; return;
} }
@ -308,7 +316,7 @@ mod tests {
num_routed_wires: 27, num_routed_wires: 27,
..CircuitConfig::default() ..CircuitConfig::default()
}; };
let mut builder = CircuitBuilder::<F>::new(config); let mut builder = CircuitBuilder::<F, 4>::new(config);
let mut recursive_challenger = RecursiveChallenger::new(&mut builder); let mut recursive_challenger = RecursiveChallenger::new(&mut builder);
let mut recursive_outputs_per_round: Vec<Vec<Target>> = Vec::new(); let mut recursive_outputs_per_round: Vec<Vec<Target>> = Vec::new();
for (r, inputs) in inputs_per_round.iter().enumerate() { for (r, inputs) in inputs_per_round.iter().enumerate() {

View File

@ -1,20 +1,22 @@
use crate::circuit_builder::CircuitBuilder; 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::field::field::Field;
use crate::gates::gate::GateRef; use crate::gates::gate::GateRef;
use crate::target::Target; use crate::target::Target;
use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
/// Evaluates all gate constraints. /// Evaluates all gate constraints.
/// ///
/// `num_gate_constraints` is the largest number of constraints imposed by any gate. It is not /// `num_gate_constraints` is the largest number of constraints imposed by any gate. It is not
/// strictly necessary, but it helps performance by ensuring that we allocate a vector with exactly /// strictly necessary, but it helps performance by ensuring that we allocate a vector with exactly
/// the capacity that we need. /// the capacity that we need.
pub fn evaluate_gate_constraints<F: Field>( pub fn evaluate_gate_constraints<F: Extendable<D>, const D: usize>(
gates: &[GateRef<F>], gates: &[GateRef<F, D>],
num_gate_constraints: usize, num_gate_constraints: usize,
vars: EvaluationVars<F>, vars: EvaluationVars<F, D>,
) -> Vec<F> { ) -> Vec<F::Extension> {
let mut constraints = vec![F::ZERO; num_gate_constraints]; let mut constraints = vec![F::Extension::ZERO; num_gate_constraints];
for gate in gates { for gate in gates {
let gate_constraints = gate.0.eval_filtered(vars); let gate_constraints = gate.0.eval_filtered(vars);
for (i, c) in gate_constraints.into_iter().enumerate() { for (i, c) in gate_constraints.into_iter().enumerate() {
@ -28,17 +30,36 @@ pub fn evaluate_gate_constraints<F: Field>(
constraints constraints
} }
pub fn evaluate_gate_constraints_recursively<F: Field>( pub fn evaluate_gate_constraints_base<F: Extendable<D>, const D: usize>(
builder: &mut CircuitBuilder<F>, gates: &[GateRef<F, D>],
gates: &[GateRef<F>],
num_gate_constraints: usize, num_gate_constraints: usize,
vars: EvaluationTargets, vars: EvaluationVarsBase<F>,
) -> Vec<Target> { ) -> Vec<F> {
let mut constraints = vec![builder.zero(); num_gate_constraints]; let mut constraints = vec![F::ZERO; num_gate_constraints];
for gate in gates {
let gate_constraints = gate.0.eval_filtered_base(vars);
for (i, c) in gate_constraints.into_iter().enumerate() {
debug_assert!(
i < num_gate_constraints,
"num_constraints() gave too low of a number"
);
constraints[i] += c;
}
}
constraints
}
pub fn evaluate_gate_constraints_recursively<F: Extendable<D>, const D: usize>(
builder: &mut CircuitBuilder<F, D>,
gates: &[GateRef<F, D>],
num_gate_constraints: usize,
vars: EvaluationTargets<D>,
) -> Vec<ExtensionTarget<D>> {
let mut constraints = vec![builder.zero_extension(); num_gate_constraints];
for gate in gates { for gate in gates {
let gate_constraints = gate.0.eval_filtered_recursively(builder, vars); let gate_constraints = gate.0.eval_filtered_recursively(builder, vars);
for (i, c) in gate_constraints.into_iter().enumerate() { for (i, c) in gate_constraints.into_iter().enumerate() {
constraints[i] = builder.add(constraints[i], c); constraints[i] = builder.add_extension(constraints[i], c);
} }
} }
constraints constraints
@ -80,8 +101,8 @@ pub(crate) fn reduce_with_powers<F: Field>(terms: &[F], alpha: F) -> F {
sum sum
} }
pub(crate) fn reduce_with_powers_recursive<F: Field>( pub(crate) fn reduce_with_powers_recursive<F: Extendable<D>, const D: usize>(
builder: &mut CircuitBuilder<F>, builder: &mut CircuitBuilder<F, D>,
terms: Vec<Target>, terms: Vec<Target>,
alpha: Target, alpha: Target,
) -> Target { ) -> Target {

View File

@ -359,8 +359,6 @@ impl<F: Field + Extendable<D>, const D: usize> OpeningProof<F, D> {
mod tests { mod tests {
use anyhow::Result; use anyhow::Result;
use crate::field::crandall_field::CrandallField;
use super::*; use super::*;
fn gen_random_test_case<F: Field + Extendable<D>, const D: usize>( fn gen_random_test_case<F: Field + Extendable<D>, const D: usize>(

View File

@ -9,22 +9,22 @@ use crate::field::fft::ifft;
use crate::field::field::Field; use crate::field::field::Field;
use crate::generator::generate_partial_witness; use crate::generator::generate_partial_witness;
use crate::plonk_challenger::Challenger; use crate::plonk_challenger::Challenger;
use crate::plonk_common::{eval_l_1, evaluate_gate_constraints, reduce_with_powers_multi}; use crate::plonk_common::{eval_l_1, evaluate_gate_constraints_base, reduce_with_powers_multi};
use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::commitment::ListPolynomialCommitment;
use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues};
use crate::proof::Proof; use crate::proof::Proof;
use crate::timed; use crate::timed;
use crate::util::transpose; use crate::util::transpose;
use crate::vars::EvaluationVars; use crate::vars::EvaluationVarsBase;
use crate::wire::Wire; use crate::wire::Wire;
use crate::witness::PartialWitness; use crate::witness::PartialWitness;
/// Corresponds to constants - sigmas - wires - zs - quotient — polynomial commitments. /// Corresponds to constants - sigmas - wires - zs - quotient — polynomial commitments.
pub const PLONK_BLINDING: [bool; 5] = [false, false, true, true, true]; pub const PLONK_BLINDING: [bool; 5] = [false, false, true, true, true];
pub(crate) fn prove<F: Field + Extendable<D>, const D: usize>( pub(crate) fn prove<F: Extendable<D>, const D: usize>(
prover_data: &ProverOnlyCircuitData<F>, prover_data: &ProverOnlyCircuitData<F>,
common_data: &CommonCircuitData<F>, common_data: &CommonCircuitData<F, D>,
inputs: PartialWitness<F>, inputs: PartialWitness<F>,
) -> Proof<F, D> { ) -> Proof<F, D> {
let fri_config = &common_data.config.fri_config; let fri_config = &common_data.config.fri_config;
@ -113,7 +113,7 @@ pub(crate) fn prove<F: Field + Extendable<D>, const D: usize>(
challenger.observe_hash(&quotient_polys_commitment.merkle_tree.root); challenger.observe_hash(&quotient_polys_commitment.merkle_tree.root);
let zetas = challenger.get_n_extension_challenges(config.num_challenges); let zeta = challenger.get_extension_challenge();
let (opening_proof, openings) = timed!( let (opening_proof, openings) = timed!(
ListPolynomialCommitment::batch_open_plonk( ListPolynomialCommitment::batch_open_plonk(
@ -124,7 +124,7 @@ pub(crate) fn prove<F: Field + Extendable<D>, const D: usize>(
&plonk_zs_commitment, &plonk_zs_commitment,
&quotient_polys_commitment, &quotient_polys_commitment,
], ],
&zetas, &[zeta],
&mut challenger, &mut challenger,
&common_data.config.fri_config &common_data.config.fri_config
), ),
@ -145,19 +145,23 @@ pub(crate) fn prove<F: Field + Extendable<D>, const D: usize>(
} }
} }
fn compute_zs<F: Field>(common_data: &CommonCircuitData<F>) -> Vec<PolynomialCoeffs<F>> { fn compute_zs<F: Extendable<D>, const D: usize>(
common_data: &CommonCircuitData<F, D>,
) -> Vec<PolynomialCoeffs<F>> {
(0..common_data.config.num_challenges) (0..common_data.config.num_challenges)
.map(|i| compute_z(common_data, i)) .map(|i| compute_z(common_data, i))
.collect() .collect()
} }
fn compute_z<F: Field>(common_data: &CommonCircuitData<F>, _i: usize) -> PolynomialCoeffs<F> { fn compute_z<F: Extendable<D>, const D: usize>(
common_data: &CommonCircuitData<F, D>,
_i: usize,
) -> PolynomialCoeffs<F> {
PolynomialCoeffs::zero(common_data.degree()) // TODO PolynomialCoeffs::zero(common_data.degree()) // TODO
} }
// TODO: Parallelize. fn compute_vanishing_polys<F: Extendable<D>, const D: usize>(
fn compute_vanishing_polys<F: Field>( common_data: &CommonCircuitData<F, D>,
common_data: &CommonCircuitData<F>,
prover_data: &ProverOnlyCircuitData<F>, prover_data: &ProverOnlyCircuitData<F>,
wires_commitment: &ListPolynomialCommitment<F>, wires_commitment: &ListPolynomialCommitment<F>,
plonk_zs_commitment: &ListPolynomialCommitment<F>, plonk_zs_commitment: &ListPolynomialCommitment<F>,
@ -184,7 +188,7 @@ fn compute_vanishing_polys<F: Field>(
debug_assert_eq!(local_wires.len(), common_data.config.num_wires); debug_assert_eq!(local_wires.len(), common_data.config.num_wires);
debug_assert_eq!(local_plonk_zs.len(), num_challenges); debug_assert_eq!(local_plonk_zs.len(), num_challenges);
let vars = EvaluationVars { let vars = EvaluationVarsBase {
local_constants, local_constants,
local_wires, local_wires,
}; };
@ -211,10 +215,10 @@ fn compute_vanishing_polys<F: Field>(
/// Evaluate the vanishing polynomial at `x`. In this context, the vanishing polynomial is a random /// 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 /// linear combination of gate constraints, plus some other terms relating to the permutation
/// argument. All such terms should vanish on `H`. /// argument. All such terms should vanish on `H`.
fn compute_vanishing_poly_entry<F: Field>( fn compute_vanishing_poly_entry<F: Extendable<D>, const D: usize>(
common_data: &CommonCircuitData<F>, common_data: &CommonCircuitData<F, D>,
x: F, x: F,
vars: EvaluationVars<F>, vars: EvaluationVarsBase<F>,
local_plonk_zs: &[F], local_plonk_zs: &[F],
next_plonk_zs: &[F], next_plonk_zs: &[F],
s_sigmas: &[F], s_sigmas: &[F],
@ -223,7 +227,7 @@ fn compute_vanishing_poly_entry<F: Field>(
alphas: &[F], alphas: &[F],
) -> Vec<F> { ) -> Vec<F> {
let constraint_terms = let constraint_terms =
evaluate_gate_constraints(&common_data.gates, common_data.num_gate_constraints, vars); evaluate_gate_constraints_base(&common_data.gates, common_data.num_gate_constraints, vars);
// The L_1(x) (Z(x) - 1) vanishing terms. // The L_1(x) (Z(x) - 1) vanishing terms.
let mut vanishing_z_1_terms = Vec::new(); let mut vanishing_z_1_terms = Vec::new();

View File

@ -1,6 +1,6 @@
use crate::circuit_builder::CircuitBuilder; use crate::circuit_builder::CircuitBuilder;
use crate::circuit_data::{CircuitConfig, VerifierCircuitTarget}; use crate::circuit_data::{CircuitConfig, VerifierCircuitTarget};
use crate::field::field::Field; use crate::field::extension_field::Extendable;
use crate::gates::gate::GateRef; use crate::gates::gate::GateRef;
use crate::proof::ProofTarget; use crate::proof::ProofTarget;
@ -8,11 +8,11 @@ const MIN_WIRES: usize = 120; // TODO: Double check.
const MIN_ROUTED_WIRES: usize = 8; // TODO: Double check. const MIN_ROUTED_WIRES: usize = 8; // TODO: Double check.
/// Recursively verifies an inner proof. /// Recursively verifies an inner proof.
pub fn add_recursive_verifier<F: Field>( pub fn add_recursive_verifier<F: Extendable<D>, const D: usize>(
builder: &mut CircuitBuilder<F>, builder: &mut CircuitBuilder<F, D>,
inner_config: CircuitConfig, inner_config: CircuitConfig,
inner_circuit: VerifierCircuitTarget, inner_circuit: VerifierCircuitTarget,
inner_gates: Vec<GateRef<F>>, inner_gates: Vec<GateRef<F, D>>,
inner_proof: ProofTarget, inner_proof: ProofTarget,
) { ) {
assert!(builder.config.num_wires >= MIN_WIRES); assert!(builder.config.num_wires >= MIN_WIRES);

View File

@ -1,38 +1,46 @@
use std::convert::TryInto; use std::convert::TryInto;
use std::ops::Range; use std::ops::Range;
use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::target::{ExtensionExtensionTarget, ExtensionTarget};
use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::extension_field::{Extendable, FieldExtension};
use crate::field::field::Field; use crate::field::field::Field;
use crate::target::Target;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct EvaluationVars<'a, F: Field> { pub struct EvaluationVars<'a, F: Extendable<D>, const D: usize> {
pub(crate) local_constants: &'a [F::Extension],
pub(crate) local_wires: &'a [F::Extension],
}
#[derive(Copy, Clone)]
pub struct EvaluationVarsBase<'a, F: Field> {
pub(crate) local_constants: &'a [F], pub(crate) local_constants: &'a [F],
pub(crate) local_wires: &'a [F], pub(crate) local_wires: &'a [F],
} }
impl<'a, F: Field> EvaluationVars<'a, F> { impl<'a, F: Extendable<D>, const D: usize> EvaluationVars<'a, F, D> {
pub fn get_local_ext<const D: usize>(&self, wire_range: Range<usize>) -> F::Extension pub fn get_local_ext_ext(
&self,
wire_range: Range<usize>,
) -> <<F as Extendable<D>>::Extension as Extendable<D>>::Extension
where where
F: Extendable<D>, F::Extension: Extendable<D>,
{ {
debug_assert_eq!(wire_range.len(), D); debug_assert_eq!(wire_range.len(), D);
let arr = self.local_wires[wire_range].try_into().unwrap(); let arr = self.local_wires[wire_range].try_into().unwrap();
F::Extension::from_basefield_array(arr) <<F as Extendable<D>>::Extension as Extendable<D>>::Extension::from_basefield_array(arr)
} }
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct EvaluationTargets<'a> { pub struct EvaluationTargets<'a, const D: usize> {
pub(crate) local_constants: &'a [Target], pub(crate) local_constants: &'a [ExtensionTarget<D>],
pub(crate) local_wires: &'a [Target], pub(crate) local_wires: &'a [ExtensionTarget<D>],
} }
impl<'a> EvaluationTargets<'a> { impl<'a, const D: usize> EvaluationTargets<'a, D> {
pub fn get_local_ext<const D: usize>(&self, wire_range: Range<usize>) -> ExtensionTarget<D> { pub fn get_local_ext_ext(&self, wire_range: Range<usize>) -> ExtensionExtensionTarget<D> {
debug_assert_eq!(wire_range.len(), D); debug_assert_eq!(wire_range.len(), D);
let arr = self.local_wires[wire_range].try_into().unwrap(); let arr = self.local_wires[wire_range].try_into().unwrap();
ExtensionTarget(arr) ExtensionExtensionTarget(arr)
} }
} }

View File

@ -2,14 +2,13 @@ use anyhow::Result;
use crate::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData}; use crate::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData};
use crate::field::extension_field::Extendable; use crate::field::extension_field::Extendable;
use crate::field::field::Field;
use crate::plonk_challenger::Challenger; use crate::plonk_challenger::Challenger;
use crate::proof::Proof; use crate::proof::Proof;
pub(crate) fn verify<F: Field + Extendable<D>, const D: usize>( pub(crate) fn verify<F: Extendable<D>, const D: usize>(
proof: Proof<F, D>, proof: Proof<F, D>,
verifier_data: &VerifierOnlyCircuitData<F>, verifier_data: &VerifierOnlyCircuitData<F>,
common_data: &CommonCircuitData<F>, common_data: &CommonCircuitData<F, D>,
) -> Result<()> { ) -> Result<()> {
let config = &common_data.config; let config = &common_data.config;
let fri_config = &config.fri_config; let fri_config = &config.fri_config;
@ -28,7 +27,7 @@ pub(crate) fn verify<F: Field + Extendable<D>, const D: usize>(
let alphas = challenger.get_n_challenges(num_challenges); let alphas = challenger.get_n_challenges(num_challenges);
challenger.observe_hash(&proof.quotient_polys_root); challenger.observe_hash(&proof.quotient_polys_root);
let zetas = challenger.get_n_extension_challenges(config.num_challenges); let zeta = challenger.get_extension_challenge();
// TODO: Compute PI(zeta), Z_H(zeta), etc. and check the identity at zeta. // TODO: Compute PI(zeta), Z_H(zeta), etc. and check the identity at zeta.
@ -43,7 +42,7 @@ pub(crate) fn verify<F: Field + Extendable<D>, const D: usize>(
]; ];
proof.opening_proof.verify( proof.opening_proof.verify(
&zetas, &[zeta],
evaluations, evaluations,
merkle_roots, merkle_roots,
&mut challenger, &mut challenger,