From 0c91739b3b551685f26fe1207c0b88f81b420a79 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Mon, 17 May 2021 09:06:17 -0700 Subject: [PATCH 01/13] [DRAFT] Interpolation gate Over quartic field extension (for now). This would be used in our FRI recursive verifier later, for the consistency check. To summarize the wires, - `n` inputs for the `n` points to interpolate (don't need `4n` since they'll be in the subgroup of the base field) - `4n` inputs for the `n` (extension field) values to interpolate - `4` inputs for the point to evaluate the interpolant at (beta, which will be drawn from the extension field right?) - `4` outputs for the interpolated value - `4n` internal wires for the interpolant's coefficients This definitely isn't the most optimal approach, e.g. we could route in a single "base" point and derive its neighboring points, but just wanted to keep it simple for now. --- src/gates/interpolation_quartic.rs | 169 +++++++++++++++++++++++++++++ src/gates/mod.rs | 1 + 2 files changed, 170 insertions(+) create mode 100644 src/gates/interpolation_quartic.rs diff --git a/src/gates/interpolation_quartic.rs b/src/gates/interpolation_quartic.rs new file mode 100644 index 00000000..57b271ff --- /dev/null +++ b/src/gates/interpolation_quartic.rs @@ -0,0 +1,169 @@ +use std::marker::PhantomData; + +use crate::circuit_builder::CircuitBuilder; +use crate::field::extension_field::quartic::QuarticFieldExtension; +use crate::gates::gate::{Gate, GateRef}; +use crate::generator::{SimpleGenerator, WitnessGenerator}; +use crate::target::Target; +use crate::vars::{EvaluationTargets, EvaluationVars}; +use crate::witness::PartialWitness; + +/// The size of the field extension, in terms of number of base elements per extension element. +const EXT_SIZE: usize = 4; + +/// Evaluates the interpolant of some given elements from a quartic field extension. +/// +/// In particular, this gate takes as inputs `num_points` points, `num_points` values, and the point +/// to evaluate the interpolant at. It computes the interpolant and outputs its evaluation at the +/// given point. +#[derive(Debug)] +pub(crate) struct QuarticInterpolationGate { + num_points: usize, + _phantom: PhantomData, +} + +impl QuarticInterpolationGate { + pub fn new(num_points: usize) -> GateRef { + let gate = Self { + num_points, + _phantom: PhantomData, + }; + GateRef::new(gate) + } + + fn start_points(&self) -> usize { + 0 + } + + /// Wire indices of the `i`th interpolant point. + pub fn wire_point(&self, i: usize) -> usize { + debug_assert!(i < self.num_points); + self.start_points() + i + } + + fn start_values(&self) -> usize { + self.start_points() + self.num_points + } + + /// Wire indices of the `i`th interpolant value. + pub fn wires_value(&self, i: usize) -> Vec { + debug_assert!(i < self.num_points); + (0..EXT_SIZE) + .map(|j| self.start_values() + i * EXT_SIZE + j) + .collect() + } + + fn start_interpolated_point(&self) -> usize { + self.start_values() + self.num_points * EXT_SIZE + } + + /// Wire indices of the point to evaluate the interpolant at. + pub fn wires_interpolated_point(&self) -> Vec { + (0..EXT_SIZE).map(|j| self.start_interpolated_point() + j).collect() + } + + fn start_interpolated_value(&self) -> usize { + self.start_interpolated_point() + EXT_SIZE + } + + /// Wire indices of the interpolated value. + pub fn wires_interpolated_value(&self) -> Vec { + (0..EXT_SIZE) + .map(|j| self.start_interpolated_value() + j) + .collect() + } + + fn start_coeffs(&self) -> usize { + self.start_interpolated_value() + EXT_SIZE + } + + /// Wire indices of the interpolant's `i`th coefficient. + pub fn wires_coeff(&self, i: usize) -> Vec { + debug_assert!(i < self.num_points); + (0..EXT_SIZE) + .map(|j| self.start_coeffs() + i * EXT_SIZE + j) + .collect() + } +} + +impl Gate for QuarticInterpolationGate { + fn id(&self) -> String { + let qfe_name = std::any::type_name::(); + format!("{} {:?}", qfe_name, self) + } + + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { + todo!() + } + + fn eval_unfiltered_recursively( + &self, + builder: &mut CircuitBuilder, + vars: EvaluationTargets, + ) -> Vec { + todo!() + } + + fn generators( + &self, + gate_index: usize, + local_constants: &[QFE::BaseField], + ) -> Vec>> { + todo!() + } + + fn num_wires(&self) -> usize { + todo!() + } + + fn num_constants(&self) -> usize { + todo!() + } + + fn degree(&self) -> usize { + todo!() + } + + fn num_constraints(&self) -> usize { + todo!() + } +} + +struct QuarticInterpolationGenerator { + _phantom: PhantomData, +} + +impl SimpleGenerator + for QuarticInterpolationGenerator +{ + fn dependencies(&self) -> Vec { + todo!() + } + + fn run_once(&self, witness: &PartialWitness) -> PartialWitness { + todo!() + } +} + +#[cfg(test)] +mod tests { + use std::marker::PhantomData; + + use crate::gates::interpolation_quartic::QuarticInterpolationGate; + use crate::field::extension_field::quartic::QuarticCrandallField; + + #[test] + fn wire_indices() { + let gate = QuarticInterpolationGate:: { num_points: 2, _phantom: PhantomData }; + // The exact indices aren't really important, but we want to make sure we don't have any + // overlaps or gaps. + assert_eq!(gate.wire_point(0), 0); + assert_eq!(gate.wire_point(1), 1); + assert_eq!(gate.wires_value(0), vec![2, 3, 4, 5]); + assert_eq!(gate.wires_value(1), vec![6, 7, 8, 9]); + assert_eq!(gate.wires_interpolated_point(), vec![10, 11, 12, 13]); + assert_eq!(gate.wires_interpolated_value(), vec![14, 15, 16, 17]); + assert_eq!(gate.wires_coeff(0), vec![18, 19, 20, 21]); + assert_eq!(gate.wires_coeff(1), vec![22, 23, 24, 25]); + } +} diff --git a/src/gates/mod.rs b/src/gates/mod.rs index f222549d..e35a542d 100644 --- a/src/gates/mod.rs +++ b/src/gates/mod.rs @@ -4,4 +4,5 @@ pub(crate) mod fri_consistency_gate; pub(crate) mod gate; pub mod gmimc; pub(crate) mod gmimc_eval; +mod interpolation_quartic; pub(crate) mod noop; From c6fa7eb18e28f19ab932926677fd157dfab1010b Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Mon, 17 May 2021 11:47:33 -0700 Subject: [PATCH 02/13] Minor --- src/gates/interpolation_quartic.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/gates/interpolation_quartic.rs b/src/gates/interpolation_quartic.rs index 57b271ff..3d1c3f54 100644 --- a/src/gates/interpolation_quartic.rs +++ b/src/gates/interpolation_quartic.rs @@ -107,21 +107,26 @@ impl Gate for QuarticInterpolationGa fn generators( &self, gate_index: usize, - local_constants: &[QFE::BaseField], + _local_constants: &[QFE::BaseField], ) -> Vec>> { - todo!() + let gen = QuarticInterpolationGenerator:: { + gate_index, + num_points: self.num_points, + _phantom: PhantomData, + }; + vec![Box::new(gen)] } fn num_wires(&self) -> usize { - todo!() + self.start_coeffs() + self.num_points * EXT_SIZE } fn num_constants(&self) -> usize { - todo!() + 0 } fn degree(&self) -> usize { - todo!() + self.num_points - 1 } fn num_constraints(&self) -> usize { @@ -130,6 +135,8 @@ impl Gate for QuarticInterpolationGa } struct QuarticInterpolationGenerator { + gate_index: usize, + num_points: usize, _phantom: PhantomData, } @@ -149,8 +156,9 @@ impl SimpleGenerator mod tests { use std::marker::PhantomData; - use crate::gates::interpolation_quartic::QuarticInterpolationGate; use crate::field::extension_field::quartic::QuarticCrandallField; + use crate::gates::gate::Gate; + use crate::gates::interpolation_quartic::QuarticInterpolationGate; #[test] fn wire_indices() { @@ -165,5 +173,6 @@ mod tests { assert_eq!(gate.wires_interpolated_value(), vec![14, 15, 16, 17]); assert_eq!(gate.wires_coeff(0), vec![18, 19, 20, 21]); assert_eq!(gate.wires_coeff(1), vec![22, 23, 24, 25]); + assert_eq!(gate.num_wires(), 26); } } From 227c80c82e7aad2b00cf68c74873cf1890499bc9 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Mon, 17 May 2021 11:47:44 -0700 Subject: [PATCH 03/13] fmt --- src/gates/interpolation_quartic.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/gates/interpolation_quartic.rs b/src/gates/interpolation_quartic.rs index 3d1c3f54..14e58970 100644 --- a/src/gates/interpolation_quartic.rs +++ b/src/gates/interpolation_quartic.rs @@ -59,7 +59,9 @@ impl QuarticInterpolationGate { /// Wire indices of the point to evaluate the interpolant at. pub fn wires_interpolated_point(&self) -> Vec { - (0..EXT_SIZE).map(|j| self.start_interpolated_point() + j).collect() + (0..EXT_SIZE) + .map(|j| self.start_interpolated_point() + j) + .collect() } fn start_interpolated_value(&self) -> usize { @@ -162,7 +164,10 @@ mod tests { #[test] fn wire_indices() { - let gate = QuarticInterpolationGate:: { num_points: 2, _phantom: PhantomData }; + let gate = QuarticInterpolationGate:: { + num_points: 2, + _phantom: PhantomData, + }; // The exact indices aren't really important, but we want to make sure we don't have any // overlaps or gaps. assert_eq!(gate.wire_point(0), 0); From b535bf239a6aafd8d2543d4b703a3134fcebc5fe Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Wed, 19 May 2021 12:06:42 -0700 Subject: [PATCH 04/13] Minor --- src/gates/interpolation_quartic.rs | 116 +++++++++++++++++------------ 1 file changed, 68 insertions(+), 48 deletions(-) diff --git a/src/gates/interpolation_quartic.rs b/src/gates/interpolation_quartic.rs index 14e58970..7694d856 100644 --- a/src/gates/interpolation_quartic.rs +++ b/src/gates/interpolation_quartic.rs @@ -1,7 +1,10 @@ +use std::convert::TryInto; use std::marker::PhantomData; +use std::ops::Range; use crate::circuit_builder::CircuitBuilder; -use crate::field::extension_field::quartic::QuarticFieldExtension; +use crate::field::extension_field::{Extendable, FieldExtension}; +use crate::field::field::Field; use crate::gates::gate::{Gate, GateRef}; use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::target::Target; @@ -17,13 +20,13 @@ const EXT_SIZE: usize = 4; /// to evaluate the interpolant at. It computes the interpolant and outputs its evaluation at the /// given point. #[derive(Debug)] -pub(crate) struct QuarticInterpolationGate { +pub(crate) struct QuarticInterpolationGate, const D: usize> { num_points: usize, - _phantom: PhantomData, + _phantom: PhantomData, } -impl QuarticInterpolationGate { - pub fn new(num_points: usize) -> GateRef { +impl, const D: usize> QuarticInterpolationGate { + pub fn new(num_points: usize) -> GateRef { let gate = Self { num_points, _phantom: PhantomData, @@ -46,61 +49,69 @@ impl QuarticInterpolationGate { } /// Wire indices of the `i`th interpolant value. - pub fn wires_value(&self, i: usize) -> Vec { + pub fn wires_value(&self, i: usize) -> Range { debug_assert!(i < self.num_points); - (0..EXT_SIZE) - .map(|j| self.start_values() + i * EXT_SIZE + j) - .collect() + let start = self.start_values() + i * EXT_SIZE; + start..start + EXT_SIZE } - fn start_interpolated_point(&self) -> usize { + fn start_evaluation_point(&self) -> usize { self.start_values() + self.num_points * EXT_SIZE } /// Wire indices of the point to evaluate the interpolant at. - pub fn wires_interpolated_point(&self) -> Vec { - (0..EXT_SIZE) - .map(|j| self.start_interpolated_point() + j) - .collect() + pub fn wires_evaluation_point(&self) -> Range { + let start = self.start_evaluation_point(); + start..start + EXT_SIZE } - fn start_interpolated_value(&self) -> usize { - self.start_interpolated_point() + EXT_SIZE + fn start_evaluation_value(&self) -> usize { + self.start_evaluation_point() + EXT_SIZE } /// Wire indices of the interpolated value. - pub fn wires_interpolated_value(&self) -> Vec { - (0..EXT_SIZE) - .map(|j| self.start_interpolated_value() + j) - .collect() + pub fn wires_evaluation_value(&self) -> Range { + let start = self.start_evaluation_value(); + start..start + EXT_SIZE } fn start_coeffs(&self) -> usize { - self.start_interpolated_value() + EXT_SIZE + self.start_evaluation_value() + EXT_SIZE } /// Wire indices of the interpolant's `i`th coefficient. - pub fn wires_coeff(&self, i: usize) -> Vec { + pub fn wires_coeff(&self, i: usize) -> Range { debug_assert!(i < self.num_points); - (0..EXT_SIZE) - .map(|j| self.start_coeffs() + i * EXT_SIZE + j) - .collect() + let start = self.start_coeffs() + i * EXT_SIZE; + start..start + EXT_SIZE + } + + fn end(&self) -> usize { + self.start_coeffs() + self.num_points * EXT_SIZE } } -impl Gate for QuarticInterpolationGate { +impl, const D: usize> Gate for QuarticInterpolationGate { fn id(&self) -> String { - let qfe_name = std::any::type_name::(); + let qfe_name = std::any::type_name::(); format!("{} {:?}", qfe_name, self) } - fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { - todo!() + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { + let mut constraints = Vec::with_capacity(self.num_constraints()); + + let x_eval = F::Extension::from_basefield_array( + vars.local_wires[self.wires_evaluation_point()].try_into().unwrap()); + let x_eval_powers = x_eval.powers().take(self.num_points); + + // TODO + + constraints } fn eval_unfiltered_recursively( &self, - builder: &mut CircuitBuilder, + builder: &mut CircuitBuilder, vars: EvaluationTargets, ) -> Vec { todo!() @@ -109,9 +120,9 @@ impl Gate for QuarticInterpolationGa fn generators( &self, gate_index: usize, - _local_constants: &[QFE::BaseField], - ) -> Vec>> { - let gen = QuarticInterpolationGenerator:: { + _local_constants: &[F], + ) -> Vec>> { + let gen = QuarticInterpolationGenerator:: { gate_index, num_points: self.num_points, _phantom: PhantomData, @@ -120,7 +131,7 @@ impl Gate for QuarticInterpolationGa } fn num_wires(&self) -> usize { - self.start_coeffs() + self.num_points * EXT_SIZE + self.end() } fn num_constants(&self) -> usize { @@ -136,20 +147,20 @@ impl Gate for QuarticInterpolationGa } } -struct QuarticInterpolationGenerator { +struct QuarticInterpolationGenerator, const D: usize> { gate_index: usize, num_points: usize, - _phantom: PhantomData, + _phantom: PhantomData, } -impl SimpleGenerator - for QuarticInterpolationGenerator +impl, const D: usize> SimpleGenerator + for QuarticInterpolationGenerator { fn dependencies(&self) -> Vec { todo!() } - fn run_once(&self, witness: &PartialWitness) -> PartialWitness { + fn run_once(&self, witness: &PartialWitness) -> PartialWitness { todo!() } } @@ -158,13 +169,13 @@ impl SimpleGenerator mod tests { use std::marker::PhantomData; - use crate::field::extension_field::quartic::QuarticCrandallField; + use crate::field::crandall_field::CrandallField; use crate::gates::gate::Gate; use crate::gates::interpolation_quartic::QuarticInterpolationGate; #[test] - fn wire_indices() { - let gate = QuarticInterpolationGate:: { + fn wire_indices_2_points() { + let gate = QuarticInterpolationGate:: { num_points: 2, _phantom: PhantomData, }; @@ -172,12 +183,21 @@ mod tests { // overlaps or gaps. assert_eq!(gate.wire_point(0), 0); assert_eq!(gate.wire_point(1), 1); - assert_eq!(gate.wires_value(0), vec![2, 3, 4, 5]); - assert_eq!(gate.wires_value(1), vec![6, 7, 8, 9]); - assert_eq!(gate.wires_interpolated_point(), vec![10, 11, 12, 13]); - assert_eq!(gate.wires_interpolated_value(), vec![14, 15, 16, 17]); - assert_eq!(gate.wires_coeff(0), vec![18, 19, 20, 21]); - assert_eq!(gate.wires_coeff(1), vec![22, 23, 24, 25]); + assert_eq!(gate.wires_value(0), 2..6); + assert_eq!(gate.wires_value(1), 6..10); + assert_eq!(gate.wires_evaluation_point(), 10..14); + assert_eq!(gate.wires_evaluation_value(), 14..18); + assert_eq!(gate.wires_coeff(0), 18..22); + assert_eq!(gate.wires_coeff(1), 22..26); assert_eq!(gate.num_wires(), 26); } + + #[test] + fn wire_indices_4_points() { + let gate = QuarticInterpolationGate:: { + num_points: 4, + _phantom: PhantomData, + }; + assert_eq!(gate.num_wires(), 44); + } } From 110763fa7927aab3459c9e5c99d1933e03b4e4e8 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Wed, 19 May 2021 15:00:35 -0700 Subject: [PATCH 05/13] Minor --- src/gates/interpolation_quartic.rs | 51 +++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/src/gates/interpolation_quartic.rs b/src/gates/interpolation_quartic.rs index 7694d856..f3056f01 100644 --- a/src/gates/interpolation_quartic.rs +++ b/src/gates/interpolation_quartic.rs @@ -5,10 +5,13 @@ use std::ops::Range; use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; +use crate::field::lagrange::interpolant; use crate::gates::gate::{Gate, GateRef}; use crate::generator::{SimpleGenerator, WitnessGenerator}; +use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; use crate::vars::{EvaluationTargets, EvaluationVars}; +use crate::wire::Wire; use crate::witness::PartialWitness; /// The size of the field extension, in terms of number of base elements per extension element. @@ -19,7 +22,7 @@ const EXT_SIZE: usize = 4; /// In particular, this gate takes as inputs `num_points` points, `num_points` values, and the point /// to evaluate the interpolant at. It computes the interpolant and outputs its evaluation at the /// given point. -#[derive(Debug)] +#[derive(Clone, Debug)] pub(crate) struct QuarticInterpolationGate, const D: usize> { num_points: usize, _phantom: PhantomData, @@ -98,10 +101,19 @@ impl, const D: usize> Gate for QuarticInterpolationG } fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { + let lookup_fe = |wire_range: Range| { + debug_assert_eq!(wire_range.len(), D); + let arr = vars.local_wires[wire_range].try_into().unwrap(); + F::Extension::from_basefield_array(arr) + }; + let mut constraints = Vec::with_capacity(self.num_constraints()); - let x_eval = F::Extension::from_basefield_array( - vars.local_wires[self.wires_evaluation_point()].try_into().unwrap()); + let coeffs = (0..self.num_points) + .map(|i| lookup_fe(self.wires_coeff(i))) + .collect(); + let interpolant = PolynomialCoeffs::new(coeffs); + let x_eval = lookup_fe(self.wires_evaluation_point()); let x_eval_powers = x_eval.powers().take(self.num_points); // TODO @@ -124,7 +136,7 @@ impl, const D: usize> Gate for QuarticInterpolationG ) -> Vec>> { let gen = QuarticInterpolationGenerator:: { gate_index, - num_points: self.num_points, + gate: self.clone(), _phantom: PhantomData, }; vec![Box::new(gen)] @@ -149,7 +161,7 @@ impl, const D: usize> Gate for QuarticInterpolationG struct QuarticInterpolationGenerator, const D: usize> { gate_index: usize, - num_points: usize, + gate: QuarticInterpolationGate, _phantom: PhantomData, } @@ -161,6 +173,35 @@ impl, const D: usize> SimpleGenerator } fn run_once(&self, witness: &PartialWitness) -> PartialWitness { + let n = self.gate.num_points; + let lookup_fe = |wire_range: Range| { + debug_assert_eq!(wire_range.len(), D); + let values = wire_range + .map(|input| { + witness.get_wire(Wire { + gate: self.gate_index, + input, + }) + }) + .collect::>(); + let arr = values.try_into().unwrap(); + F::Extension::from_basefield_array(arr) + }; + + // Compute the interpolant. + let points = (0..n) + .map(|i| { + ( + F::Extension::from_basefield(witness.get_wire(Wire { + gate: self.gate_index, + input: self.gate.wire_point(i), + })), + lookup_fe(self.gate.wires_value(i)), + ) + }) + .collect::>(); + let interpolant = interpolant(&points); + todo!() } } From 0ce1a4c5eb818cc11e9bce7b59874c7a6a90ab7d Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Wed, 19 May 2021 15:13:33 -0700 Subject: [PATCH 06/13] Minor --- src/gates/interpolation_quartic.rs | 17 +++++++++++++---- src/witness.rs | 14 ++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/gates/interpolation_quartic.rs b/src/gates/interpolation_quartic.rs index f3056f01..0dcf22eb 100644 --- a/src/gates/interpolation_quartic.rs +++ b/src/gates/interpolation_quartic.rs @@ -174,14 +174,16 @@ impl, const D: usize> SimpleGenerator fn run_once(&self, witness: &PartialWitness) -> PartialWitness { let n = self.gate.num_points; + + let local_wire = |input| { + Wire { gate: self.gate_index, input } + }; + let lookup_fe = |wire_range: Range| { debug_assert_eq!(wire_range.len(), D); let values = wire_range .map(|input| { - witness.get_wire(Wire { - gate: self.gate_index, - input, - }) + witness.get_wire(local_wire(input)) }) .collect::>(); let arr = values.try_into().unwrap(); @@ -202,6 +204,13 @@ impl, const D: usize> SimpleGenerator .collect::>(); let interpolant = interpolant(&points); + let mut result = PartialWitness::::new(); + for (i, &coeff) in interpolant.coeffs.iter().enumerate() { + let wire_range = self.gate.wires_coeff(i); + let wires = wire_range.map(|i| local_wire(i)).collect::>(); + result.set_ext_wires(&wires, coeff); + } + todo!() } } diff --git a/src/witness.rs b/src/witness.rs index 42b7150c..e0afb77c 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use crate::field::field::Field; use crate::target::Target; use crate::wire::Wire; +use crate::field::extension_field::{Extendable, FieldExtension}; #[derive(Clone, Debug)] pub struct PartialWitness { @@ -73,6 +74,19 @@ impl PartialWitness { self.set_target(Target::Wire(wire), value) } + pub fn set_wires(&mut self, wires: &[Wire], values: &[F]) { + debug_assert_eq!(wires.len(), values.len()); + for (&wire, &value) in wires.iter().zip(values) { + self.set_wire(wire, value); + } + } + + pub fn set_ext_wires(&mut self, wires: &[Wire], value: F::Extension) + where F: Extendable { + debug_assert_eq!(wires.len(), D); + self.set_wires(wires, &value.to_basefield_array()); + } + pub fn extend(&mut self, other: PartialWitness) { for (target, value) in other.target_values { self.set_target(target, value); From 3311981fc4de5a3d274d8bda4261d7aaea3aa274 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Wed, 19 May 2021 15:57:28 -0700 Subject: [PATCH 07/13] Minor --- src/gates/interpolation_quartic.rs | 66 +++++++++++++++++------------- src/vars.rs | 15 +++++++ src/witness.rs | 19 +++++---- 3 files changed, 65 insertions(+), 35 deletions(-) diff --git a/src/gates/interpolation_quartic.rs b/src/gates/interpolation_quartic.rs index 0dcf22eb..6b37a48f 100644 --- a/src/gates/interpolation_quartic.rs +++ b/src/gates/interpolation_quartic.rs @@ -96,24 +96,17 @@ impl, const D: usize> QuarticInterpolationGate { impl, const D: usize> Gate for QuarticInterpolationGate { fn id(&self) -> String { - let qfe_name = std::any::type_name::(); - format!("{} {:?}", qfe_name, self) + format!("{:?}", self, D) } fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { - let lookup_fe = |wire_range: Range| { - debug_assert_eq!(wire_range.len(), D); - let arr = vars.local_wires[wire_range].try_into().unwrap(); - F::Extension::from_basefield_array(arr) - }; - let mut constraints = Vec::with_capacity(self.num_constraints()); let coeffs = (0..self.num_points) - .map(|i| lookup_fe(self.wires_coeff(i))) + .map(|i| vars.get_local_ext(self.wires_coeff(i))) .collect(); let interpolant = PolynomialCoeffs::new(coeffs); - let x_eval = lookup_fe(self.wires_evaluation_point()); + let x_eval = vars.get_local_ext(self.wires_evaluation_point()); let x_eval_powers = x_eval.powers().take(self.num_points); // TODO @@ -169,23 +162,39 @@ impl, const D: usize> SimpleGenerator for QuarticInterpolationGenerator { fn dependencies(&self) -> Vec { - todo!() + let local_target = |input| { + Target::Wire(Wire { + gate: self.gate_index, + input, + }) + }; + + let local_targets = |inputs: Range| inputs.map(|i| local_target(i)); + + let mut deps = Vec::new(); + deps.extend(local_targets(self.gate.wires_evaluation_point())); + deps.extend(local_targets(self.gate.wires_evaluation_value())); + for i in 0..self.gate.num_points { + deps.push(local_target(self.gate.wire_point(i))); + deps.extend(local_targets(self.gate.wires_value(i))); + deps.extend(local_targets(self.gate.wires_coeff(i))); + } + deps } fn run_once(&self, witness: &PartialWitness) -> PartialWitness { let n = self.gate.num_points; - let local_wire = |input| { - Wire { gate: self.gate_index, input } + let local_wire = |input| Wire { + gate: self.gate_index, + input, }; - let lookup_fe = |wire_range: Range| { + let get_local_wire = |input| witness.get_wire(local_wire(input)); + + let get_local_ext = |wire_range: Range| { debug_assert_eq!(wire_range.len(), D); - let values = wire_range - .map(|input| { - witness.get_wire(local_wire(input)) - }) - .collect::>(); + let values = wire_range.map(get_local_wire).collect::>(); let arr = values.try_into().unwrap(); F::Extension::from_basefield_array(arr) }; @@ -194,11 +203,8 @@ impl, const D: usize> SimpleGenerator let points = (0..n) .map(|i| { ( - F::Extension::from_basefield(witness.get_wire(Wire { - gate: self.gate_index, - input: self.gate.wire_point(i), - })), - lookup_fe(self.gate.wires_value(i)), + F::Extension::from_basefield(get_local_wire(self.gate.wire_point(i))), + get_local_ext(self.gate.wires_value(i)), ) }) .collect::>(); @@ -206,12 +212,16 @@ impl, const D: usize> SimpleGenerator let mut result = PartialWitness::::new(); for (i, &coeff) in interpolant.coeffs.iter().enumerate() { - let wire_range = self.gate.wires_coeff(i); - let wires = wire_range.map(|i| local_wire(i)).collect::>(); - result.set_ext_wires(&wires, coeff); + let wires = self.gate.wires_coeff(i).map(local_wire); + result.set_ext_wires(wires, coeff); } - todo!() + let evaluation_point = get_local_ext(self.gate.wires_evaluation_point()); + let evaluation_value = interpolant.eval(evaluation_point); + let evaluation_value_wires = self.gate.wires_evaluation_value().map(local_wire); + result.set_ext_wires(evaluation_value_wires, evaluation_value); + + result } } diff --git a/src/vars.rs b/src/vars.rs index f2744e8f..7c0afab9 100644 --- a/src/vars.rs +++ b/src/vars.rs @@ -1,3 +1,7 @@ +use std::convert::TryInto; +use std::ops::Range; + +use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; use crate::target::Target; @@ -7,6 +11,17 @@ pub struct EvaluationVars<'a, F: Field> { pub(crate) local_wires: &'a [F], } +impl<'a, F: Field> EvaluationVars<'a, F> { + pub fn get_local_ext(&self, wire_range: Range) -> F::Extension + where + F: Extendable, + { + debug_assert_eq!(wire_range.len(), D); + let arr = self.local_wires[wire_range].try_into().unwrap(); + F::Extension::from_basefield_array(arr) + } +} + #[derive(Copy, Clone)] pub struct EvaluationTargets<'a> { pub(crate) local_constants: &'a [Target], diff --git a/src/witness.rs b/src/witness.rs index e0afb77c..a0b4b2a4 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -1,9 +1,9 @@ use std::collections::HashMap; +use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; use crate::target::Target; use crate::wire::Wire; -use crate::field::extension_field::{Extendable, FieldExtension}; #[derive(Clone, Debug)] pub struct PartialWitness { @@ -74,16 +74,21 @@ impl PartialWitness { self.set_target(Target::Wire(wire), value) } - pub fn set_wires(&mut self, wires: &[Wire], values: &[F]) { - debug_assert_eq!(wires.len(), values.len()); - for (&wire, &value) in wires.iter().zip(values) { + pub fn set_wires(&mut self, wires: W, values: &[F]) + where + W: IntoIterator, + { + // If we used itertools, we could use zip_eq for extra safety. + for (wire, &value) in wires.into_iter().zip(values) { self.set_wire(wire, value); } } - pub fn set_ext_wires(&mut self, wires: &[Wire], value: F::Extension) - where F: Extendable { - debug_assert_eq!(wires.len(), D); + pub fn set_ext_wires(&mut self, wires: W, value: F::Extension) + where + F: Extendable, + W: IntoIterator, + { self.set_wires(wires, &value.to_basefield_array()); } From 6e83d956e9c2f76938fb13995ded21748652e74b Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Wed, 19 May 2021 23:03:52 -0700 Subject: [PATCH 08/13] Finish up --- src/gates/gate_testing.rs | 67 ++++++++++++++++++++++++++++++ src/gates/interpolation_quartic.rs | 33 +++++++++++---- src/gates/mod.rs | 3 ++ src/polynomial/polynomial.rs | 11 +++++ src/util/mod.rs | 4 ++ 5 files changed, 109 insertions(+), 9 deletions(-) create mode 100644 src/gates/gate_testing.rs diff --git a/src/gates/gate_testing.rs b/src/gates/gate_testing.rs new file mode 100644 index 00000000..2b0cb84b --- /dev/null +++ b/src/gates/gate_testing.rs @@ -0,0 +1,67 @@ +use crate::field::field::Field; +use crate::gates::gate::Gate; +use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; +use crate::util::{log2_ceil, transpose}; +use crate::vars::EvaluationVars; + +const WITNESS_SIZE: usize = 1 << 5; +const WITNESS_DEGREE: usize = WITNESS_SIZE - 1; + +/// Tests that the constraints imposed by the given gate are low-degree by applying them to random +/// low-degree witness polynomials. +pub(crate) fn test_low_degree>(gate: G) { + let rate_bits = log2_ceil(gate.degree() + 1); + + let wire_ldes = random_low_degree_matrix(gate.num_wires(), rate_bits); + let constant_ldes = random_low_degree_matrix::(gate.num_constants(), rate_bits); + assert_eq!(wire_ldes.len(), constant_ldes.len()); + + let constraint_evals = wire_ldes + .iter() + .zip(constant_ldes.iter()) + .map(|(local_wires, local_constants)| EvaluationVars { + local_constants, + local_wires, + }) + .map(|vars| gate.eval_unfiltered(vars)) + .collect::>(); + + let constraint_eval_degrees = transpose(&constraint_evals) + .into_iter() + .map(PolynomialValues::new) + .map(|p| p.degree()) + .collect::>(); + + let expected_eval_degree = WITNESS_DEGREE * gate.degree(); + + assert!( + constraint_eval_degrees + .iter() + .all(|°| deg <= expected_eval_degree), + "Expected degrees at most {} * {} = {}, actual {:?}", + WITNESS_SIZE, + gate.degree(), + expected_eval_degree, + constraint_eval_degrees + ); +} + +fn random_low_degree_matrix(num_polys: usize, rate_bits: usize) -> Vec> { + let polys = (0..num_polys) + .map(|_| random_low_degree_values(rate_bits)) + .collect::>(); + + if polys.is_empty() { + // We want a Vec of many empty Vecs, whereas transpose would just give an empty Vec. + vec![Vec::new(); WITNESS_SIZE << rate_bits] + } else { + transpose(&polys) + } +} + +fn random_low_degree_values(rate_bits: usize) -> Vec { + PolynomialCoeffs::new(F::rand_vec(WITNESS_SIZE)) + .lde(rate_bits) + .fft() + .values +} diff --git a/src/gates/interpolation_quartic.rs b/src/gates/interpolation_quartic.rs index 6b37a48f..68ad0fd2 100644 --- a/src/gates/interpolation_quartic.rs +++ b/src/gates/interpolation_quartic.rs @@ -106,10 +106,18 @@ impl, const D: usize> Gate for QuarticInterpolationG .map(|i| vars.get_local_ext(self.wires_coeff(i))) .collect(); let interpolant = PolynomialCoeffs::new(coeffs); - let x_eval = vars.get_local_ext(self.wires_evaluation_point()); - let x_eval_powers = x_eval.powers().take(self.num_points); - // TODO + for i in 0..self.num_points { + let point = F::Extension::from_basefield(vars.local_wires[self.wire_point(i)]); + let value = vars.get_local_ext(self.wires_value(i)); + let computed_value = interpolant.eval(point); + constraints.extend(&(value - computed_value).to_basefield_array()); + } + + let evaluation_point = vars.get_local_ext(self.wires_evaluation_point()); + let evaluation_value = vars.get_local_ext(self.wires_evaluation_value()); + let computed_evaluation_value = interpolant.eval(evaluation_point); + constraints.extend(&(evaluation_value - computed_evaluation_value).to_basefield_array()); constraints } @@ -144,11 +152,15 @@ impl, const D: usize> Gate for QuarticInterpolationG } fn degree(&self) -> usize { - self.num_points - 1 + // The highest power of x is `num_points - 1`, and then multiplication by the coefficient + // adds 1. + self.num_points } fn num_constraints(&self) -> usize { - todo!() + // num_points * D constraints to check for consistency between the coefficients and the + // point-value pairs, plus D constraints for the evaluation value. + self.num_points * D + D } } @@ -231,14 +243,16 @@ mod tests { use crate::field::crandall_field::CrandallField; use crate::gates::gate::Gate; + use crate::gates::gate_testing::test_low_degree; use crate::gates::interpolation_quartic::QuarticInterpolationGate; #[test] - fn wire_indices_2_points() { + fn wire_indices() { let gate = QuarticInterpolationGate:: { num_points: 2, _phantom: PhantomData, }; + // The exact indices aren't really important, but we want to make sure we don't have any // overlaps or gaps. assert_eq!(gate.wire_point(0), 0); @@ -253,11 +267,12 @@ mod tests { } #[test] - fn wire_indices_4_points() { - let gate = QuarticInterpolationGate:: { + fn low_degree() { + type F = CrandallField; + let gate = QuarticInterpolationGate:: { num_points: 4, _phantom: PhantomData, }; - assert_eq!(gate.num_wires(), 44); + test_low_degree(gate); } } diff --git a/src/gates/mod.rs b/src/gates/mod.rs index e35a542d..e0d990b9 100644 --- a/src/gates/mod.rs +++ b/src/gates/mod.rs @@ -6,3 +6,6 @@ pub mod gmimc; pub(crate) mod gmimc_eval; mod interpolation_quartic; pub(crate) mod noop; + +#[cfg(test)] +mod gate_testing; diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index 1d47dea4..5e3dd9f0 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -32,6 +32,7 @@ impl PolynomialValues { pub fn ifft(self) -> PolynomialCoeffs { ifft(self) } + pub fn lde_multiple(polys: Vec, rate_bits: usize) -> Vec { polys.into_iter().map(|p| p.lde(rate_bits)).collect() } @@ -40,6 +41,16 @@ impl PolynomialValues { let coeffs = ifft(self).lde(rate_bits); fft(coeffs) } + + pub fn degree(&self) -> usize { + self.degree_plus_one() + .checked_sub(1) + .expect("deg(0) is undefined") + } + + pub fn degree_plus_one(&self) -> usize { + self.clone().ifft().degree_plus_one() + } } impl From> for PolynomialValues { diff --git a/src/util/mod.rs b/src/util/mod.rs index 2b459c92..7ab9bc5b 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -32,6 +32,10 @@ pub(crate) fn transpose_poly_values(polys: Vec>) - } pub(crate) fn transpose(matrix: &[Vec]) -> Vec> { + if matrix.is_empty() { + return Vec::new(); + } + let old_rows = matrix.len(); let old_cols = matrix[0].len(); let mut transposed = vec![Vec::with_capacity(old_rows); old_cols]; From d05513475ce190c2a99046eb53a0be0c30a43a8d Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Wed, 19 May 2021 23:07:24 -0700 Subject: [PATCH 09/13] Not just quartic --- ...erpolation_quartic.rs => interpolation.rs} | 22 +++++++++---------- src/gates/mod.rs | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) rename src/gates/{interpolation_quartic.rs => interpolation.rs} (91%) diff --git a/src/gates/interpolation_quartic.rs b/src/gates/interpolation.rs similarity index 91% rename from src/gates/interpolation_quartic.rs rename to src/gates/interpolation.rs index 68ad0fd2..04e149e3 100644 --- a/src/gates/interpolation_quartic.rs +++ b/src/gates/interpolation.rs @@ -17,18 +17,18 @@ use crate::witness::PartialWitness; /// The size of the field extension, in terms of number of base elements per extension element. const EXT_SIZE: usize = 4; -/// Evaluates the interpolant of some given elements from a quartic field extension. +/// Evaluates the interpolant of some given elements from a field extension. /// /// In particular, this gate takes as inputs `num_points` points, `num_points` values, and the point /// to evaluate the interpolant at. It computes the interpolant and outputs its evaluation at the /// given point. #[derive(Clone, Debug)] -pub(crate) struct QuarticInterpolationGate, const D: usize> { +pub(crate) struct InterpolationGate, const D: usize> { num_points: usize, _phantom: PhantomData, } -impl, const D: usize> QuarticInterpolationGate { +impl, const D: usize> InterpolationGate { pub fn new(num_points: usize) -> GateRef { let gate = Self { num_points, @@ -94,7 +94,7 @@ impl, const D: usize> QuarticInterpolationGate { } } -impl, const D: usize> Gate for QuarticInterpolationGate { +impl, const D: usize> Gate for InterpolationGate { fn id(&self) -> String { format!("{:?}", self, D) } @@ -135,7 +135,7 @@ impl, const D: usize> Gate for QuarticInterpolationG gate_index: usize, _local_constants: &[F], ) -> Vec>> { - let gen = QuarticInterpolationGenerator:: { + let gen = InterpolationGenerator:: { gate_index, gate: self.clone(), _phantom: PhantomData, @@ -164,14 +164,14 @@ impl, const D: usize> Gate for QuarticInterpolationG } } -struct QuarticInterpolationGenerator, const D: usize> { +struct InterpolationGenerator, const D: usize> { gate_index: usize, - gate: QuarticInterpolationGate, + gate: InterpolationGate, _phantom: PhantomData, } impl, const D: usize> SimpleGenerator - for QuarticInterpolationGenerator + for InterpolationGenerator { fn dependencies(&self) -> Vec { let local_target = |input| { @@ -244,11 +244,11 @@ mod tests { use crate::field::crandall_field::CrandallField; use crate::gates::gate::Gate; use crate::gates::gate_testing::test_low_degree; - use crate::gates::interpolation_quartic::QuarticInterpolationGate; + use crate::gates::interpolation::InterpolationGate; #[test] fn wire_indices() { - let gate = QuarticInterpolationGate:: { + let gate = InterpolationGate:: { num_points: 2, _phantom: PhantomData, }; @@ -269,7 +269,7 @@ mod tests { #[test] fn low_degree() { type F = CrandallField; - let gate = QuarticInterpolationGate:: { + let gate = InterpolationGate:: { num_points: 4, _phantom: PhantomData, }; diff --git a/src/gates/mod.rs b/src/gates/mod.rs index e0d990b9..0c8b04b8 100644 --- a/src/gates/mod.rs +++ b/src/gates/mod.rs @@ -4,7 +4,7 @@ pub(crate) mod fri_consistency_gate; pub(crate) mod gate; pub mod gmimc; pub(crate) mod gmimc_eval; -mod interpolation_quartic; +mod interpolation; pub(crate) mod noop; #[cfg(test)] From 621b097a70e3b6401abfad81d5453c0f1df67657 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Thu, 20 May 2021 05:15:25 -0700 Subject: [PATCH 10/13] Address most feedback --- src/gates/interpolation.rs | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/gates/interpolation.rs b/src/gates/interpolation.rs index 04e149e3..1e6006c5 100644 --- a/src/gates/interpolation.rs +++ b/src/gates/interpolation.rs @@ -14,9 +14,6 @@ use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::wire::Wire; use crate::witness::PartialWitness; -/// The size of the field extension, in terms of number of base elements per extension element. -const EXT_SIZE: usize = 4; - /// Evaluates the interpolant of some given elements from a field extension. /// /// In particular, this gate takes as inputs `num_points` points, `num_points` values, and the point @@ -54,43 +51,43 @@ impl, const D: usize> InterpolationGate { /// Wire indices of the `i`th interpolant value. pub fn wires_value(&self, i: usize) -> Range { debug_assert!(i < self.num_points); - let start = self.start_values() + i * EXT_SIZE; - start..start + EXT_SIZE + let start = self.start_values() + i * D; + start..start + D } fn start_evaluation_point(&self) -> usize { - self.start_values() + self.num_points * EXT_SIZE + self.start_values() + self.num_points * D } /// Wire indices of the point to evaluate the interpolant at. pub fn wires_evaluation_point(&self) -> Range { let start = self.start_evaluation_point(); - start..start + EXT_SIZE + start..start + D } fn start_evaluation_value(&self) -> usize { - self.start_evaluation_point() + EXT_SIZE + self.start_evaluation_point() + D } /// Wire indices of the interpolated value. pub fn wires_evaluation_value(&self) -> Range { let start = self.start_evaluation_value(); - start..start + EXT_SIZE + start..start + D } fn start_coeffs(&self) -> usize { - self.start_evaluation_value() + EXT_SIZE + self.start_evaluation_value() + D } /// Wire indices of the interpolant's `i`th coefficient. pub fn wires_coeff(&self, i: usize) -> Range { debug_assert!(i < self.num_points); - let start = self.start_coeffs() + i * EXT_SIZE; - start..start + EXT_SIZE + let start = self.start_coeffs() + i * D; + start..start + D } fn end(&self) -> usize { - self.start_coeffs() + self.num_points * EXT_SIZE + self.start_coeffs() + self.num_points * D } } @@ -181,7 +178,7 @@ impl, const D: usize> SimpleGenerator }) }; - let local_targets = |inputs: Range| inputs.map(|i| local_target(i)); + let local_targets = |inputs: Range| inputs.map(local_target); let mut deps = Vec::new(); deps.extend(local_targets(self.gate.wires_evaluation_point())); From 229784e574629e2c92156322b8e7b4370605e77c Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Thu, 20 May 2021 05:27:47 -0700 Subject: [PATCH 11/13] Delete old FRI gate --- src/gates/fri_consistency_gate.rs | 372 ------------------------------ 1 file changed, 372 deletions(-) delete mode 100644 src/gates/fri_consistency_gate.rs diff --git a/src/gates/fri_consistency_gate.rs b/src/gates/fri_consistency_gate.rs deleted file mode 100644 index 6c9c8aaf..00000000 --- a/src/gates/fri_consistency_gate.rs +++ /dev/null @@ -1,372 +0,0 @@ -// use crate::circuit_data::CircuitConfig; -// use crate::constraint_polynomial::ConstraintPolynomial; -// use crate::field::fft::coset_ifft; -// use crate::field::field::Field; -// use crate::gadgets::split_join::{split_le_constraints, split_le_generator_local_wires}; -// use crate::gates::deterministic_gate::{DeterministicGate, DeterministicGateAdapter}; -// use crate::gates::gate::GateRef; -// use crate::gates::output_graph::{OutputGraph, GateOutputLocation, ExpandableOutputGraph}; -// use crate::generator::{SimpleGenerator, WitnessGenerator}; -// use crate::target::Target; -// use crate::wire::Wire; -// use crate::witness::PartialWitness; -// use crate::gadgets::conditionals::conditional_multiply_poly; -// -// /// Performs a FRI consistency check. The goal is to check consistency between polynomials `f^(i)` -// /// and `f^(i + 1)`, where `f^(i + 1)` is supposed to be an `arity`-to-one reduction of `f^(i)`. See -// /// the FRI paper for details. -// /// -// /// This check involves `arity` openings of `f^(i)` and a single opening of `f^(i + 1)`. Let's call -// /// the set of `f^(i)` opening locations `{s_j}`, and the `f^(i + 1)` opening location `y`. Note -// /// that all of these points can be derived from the query path. So, we take as input an integer -// /// whose binary decomposition represents the left and right turns in the query path. -// /// -// /// Since our protocol uses a variant of FRI with `k` commit phases, this gate takes `k` opening -// /// sets for `f^(i)`, and `k` openings for `f^(i + 1)`. -// /// -// /// Putting it together, this gate does the following: -// /// - Computes the binary decomposition of the input representing the query path. -// /// - Computes `{s_j}` and `y` from the query path. -// /// - For each commit phase: -// /// - Non-deterministically interpolates a polynomial through each `(s_j, f^(i))`. -// /// - Evaluates the purported interpolant at each `s_j`, and checks that the result matches the -// /// associated opening of `f^(i)(s_j)`. -// /// - Evaluates the purported interpolant at `y`, and checks that the result matches the opening -// /// of `f^(i + 1)(y)`. -// #[derive(Debug, Copy, Clone)] -// pub(crate) struct FriConsistencyGate { -// /// The arity of this reduction step. -// arity_bits: usize, -// -// /// The number of commit phases. -// num_commits: usize, -// -// /// The maximum number of bits in any query path. -// max_path_bits: usize, -// } -// -// impl FriConsistencyGate { -// pub fn new( -// arity_bits: usize, -// num_commits: usize, -// max_path_bits: usize, -// ) -> GateRef { -// let gate = Self { arity_bits, num_commits, max_path_bits }; -// GateRef::new(DeterministicGateAdapter::new(gate)) -// } -// -// fn arity(&self) -> usize { -// 1 << self.arity_bits -// } -// -// /// Generator for the `i`'th layer of FRI. -// pub const CONST_GENERATOR_I: usize = 0; -// -// // Note: These methods relating to wire indices are ordered by wire index. The index -// // calculations are a little hairy since there are quite a few different "sections" of wires, -// // and each section has to calculate where the previous sections left off. To make it more -// // manageable, we have separate functions to calculate the start of each section. There is also -// // a test to make sure that they behave as expected, with no overlapping indices or what not. -// -// /// An integer representing the location of the `f^(i + 1)` node in the reduction tree. Its -// /// `i`th bit in little-endian form represents the direction of the `i`th turn in the query -// /// path, starting from the root. -// pub const WIRE_PATH: usize = 0; -// -// fn start_wire_f_i(&self) -> usize { -// 1 -// } -// -// pub fn wire_f_i(&self, commit_idx: usize, j: usize) -> usize { -// debug_assert!(commit_idx < self.num_commits); -// debug_assert!(j < self.arity()); -// self.start_wire_f_i() + self.arity() * commit_idx + j -// } -// -// fn start_wire_f_i_plus_1(&self) -> usize { -// self.start_wire_f_i() + self.arity() * self.num_commits -// } -// -// pub fn wire_f_i_plus_1(&self, commit_idx: usize) -> usize { -// debug_assert!(commit_idx < self.num_commits); -// self.start_wire_f_i_plus_1() + commit_idx -// } -// -// fn start_wire_path_bits(&self) -> usize { -// self.start_wire_f_i_plus_1() + self.num_commits -// } -// -// /// The `i`th bit of the path. -// fn wire_path_bit_i(&self, i: usize) -> usize { -// self.start_wire_path_bits() + i -// } -// -// fn start_wire_s_j(&self) -> usize { -// self.start_wire_path_bits() + self.max_path_bits -// } -// -// /// The input index of `s_j` (see the FRI paper). -// fn wire_s_j(&self, j: usize) -> usize { -// debug_assert!(j < self.arity()); -// self.start_wire_s_j() + j -// } -// -// fn start_wire_y(&self) -> usize { -// self.start_wire_s_j() + self.arity() -// } -// -// /// The input index of `y` (see the FRI paper). -// fn wire_y(&self) -> usize { -// self.start_wire_y() -// } -// -// fn start_wire_coefficient(&self) -> usize { -// self.start_wire_y() + 1 -// } -// -// /// The wire input index of the j'th coefficient of the interpolant. -// fn wire_coefficient(&self, commit_idx: usize, j: usize) -> usize { -// debug_assert!(commit_idx < self.num_commits); -// debug_assert!(j < self.arity()); -// self.start_wire_coefficient() + commit_idx * self.arity() + j -// } -// -// fn start_unnamed_wires(&self) -> usize { -// self.start_wire_coefficient() + self.num_commits * self.arity() -// } -// -// fn add_s_j_outputs(&self, output_graph: &mut ExpandableOutputGraph) { -// // Each s_j = g^path, where g is the generator for s_j's layer (see CONST_GENERATOR), and -// // path is an integer representing the location of s_j in the reduction tree. This assumes -// // that path is encoded such that its less significant bits are closer to the root of the -// // tree. -// -// // Note about bit ordering: in a FRI reduction tree, the `j`th node in a layer can be -// // written as `g^rev(j)`, where `rev` reverses the bits of its inputs. One way to think of -// // this is that squaring left-shifts the exponent, so for adjacent nodes to have the same -// // square, they must differ only in the left-most bit (which will "overflow" after the -// // left-shift). FFT trees have the same property. -// -// // We start by computing g^0, g^10, g^100, g^1000, ... -// let mut squares = vec![ConstraintPolynomial::local_constant(0)]; -// for _ in 1..self.max_path_bits { -// let prev_square = squares.last().unwrap(); -// let next_square = output_graph.add(prev_square.square()); -// squares.push(next_square) -// } -// -// // We can think of path as having two parts: a less significant part that is common to all -// // {s_j}, and a more significant part that depends on j. We start by computing -// // g^path_common: -// let mut g_exp_path_common = ConstraintPolynomial::zero(); -// let shared_path_bits = self.max_path_bits - self.arity_bits; -// for i in 0..shared_path_bits { -// let bit = ConstraintPolynomial::local_wire(self.wire_path_bit_i(i)); -// g_exp_path_common = conditional_multiply_poly(&g_exp_path_common, &squares[i], &bit); -// g_exp_path_common = output_graph.add(g_exp_path_common); -// } -// -// // Then, we factor in the "extra" powers of g specific to each child. -// for j in 0..self.arity() { -// let mut s_j = g_exp_path_common.clone(); -// for bit_index in 0..self.arity_bits { -// let bit = (j >> bit_index & 1) != 0; -// if bit { -// // See the comment near the top about bit ordering. -// s_j *= &squares[shared_path_bits + self.arity_bits - 1 - bit_index]; -// } -// } -// let s_j_loc = GateOutputLocation::LocalWire(self.wire_s_j(j)); -// output_graph.output_graph.add(s_j_loc, s_j); -// } -// } -// -// fn add_y_output(&self, output_graph: &mut ExpandableOutputGraph) { -// let loc = GateOutputLocation::LocalWire(self.wire_y()); -// // We can start with any s_j and repeatedly square it. We arbitrary pick s_0. -// let mut out = ConstraintPolynomial::local_wire(self.wire_s_j(0)); -// for _ in 0..self.arity_bits { -// out = out.square(); -// } -// output_graph.output_graph.add(loc, out); -// } -// -// fn evaluate_each_poly(&self, commit_idx: usize) -> Vec> { -// let coefficients = (0..self.arity()) -// .map(|i| ConstraintPolynomial::local_wire(self.wire_coefficient(commit_idx, i))) -// .collect::>>(); -// let mut constraints = Vec::new(); -// -// for j in 0..self.arity() { -// // Check the evaluation of f^(i) at s_j. -// let expected = ConstraintPolynomial::local_wire(self.wire_f_i(commit_idx, j)); -// let actual = self.evaluate_poly(&coefficients, -// ConstraintPolynomial::local_wire(self.wire_s_j(j))); -// constraints.push(actual - expected); -// } -// -// // Check the evaluation of f^(i + 1) at y. -// let expected = ConstraintPolynomial::local_wire(self.wire_f_i_plus_1(commit_idx)); -// let actual = self.evaluate_poly(&coefficients, -// ConstraintPolynomial::local_wire(self.wire_y())); -// constraints.push(actual - expected); -// -// constraints -// } -// -// /// Given a polynomial's coefficients, naively evaluate it at a point. -// fn evaluate_poly( -// &self, -// coefficients: &[ConstraintPolynomial], -// point: ConstraintPolynomial, -// ) -> ConstraintPolynomial { -// coefficients.iter() -// .enumerate() -// .map(|(i, coeff)| coeff * point.exp(i)) -// .sum() -// } -// } -// -// impl DeterministicGate for FriConsistencyGate { -// fn id(&self) -> String { -// format!("{:?}", self) -// } -// -// fn outputs(&self, _config: CircuitConfig) -> OutputGraph { -// let mut output_graph = ExpandableOutputGraph::new(self.start_unnamed_wires()); -// self.add_s_j_outputs(&mut output_graph); -// self.add_y_output(&mut output_graph); -// output_graph.output_graph -// } -// -// fn additional_constraints(&self, _config: CircuitConfig) -> Vec> { -// let mut constraints = Vec::new(); -// -// // Add constraints for splitting the path into its binary representation. -// let bits = (0..self.max_path_bits) -// .map(|i| ConstraintPolynomial::local_wire(self.wire_path_bit_i(i))) -// .collect::>(); -// let split_constraints = split_le_constraints( -// ConstraintPolynomial::local_wire(Self::WIRE_PATH), -// &bits); -// constraints.extend(split_constraints); -// -// // Add constraints for checking each polynomial evaluation. -// for commit_idx in 0..self.num_commits { -// constraints.extend(self.evaluate_each_poly(commit_idx)); -// } -// -// constraints -// } -// -// fn additional_generators( -// &self, -// _config: CircuitConfig, -// gate_index: usize, -// local_constants: Vec, -// _next_constants: Vec, -// ) -> Vec>> { -// let interpolant_generator = Box::new( -// InterpolantGenerator { -// gate: *self, -// gate_index, -// generator_i: local_constants[Self::CONST_GENERATOR_I], -// } -// ); -// -// let bit_input_indices = (0..self.max_path_bits) -// .map(|i| self.wire_path_bit_i(i)) -// .collect::>(); -// let split_generator = split_le_generator_local_wires( -// gate_index, Self::WIRE_PATH, &bit_input_indices); -// -// vec![interpolant_generator, split_generator] -// } -// } -// -// #[derive(Debug)] -// struct InterpolantGenerator { -// gate: FriConsistencyGate, -// gate_index: usize, -// generator_i: F, -// } -// -// impl InterpolantGenerator { -// /// Convenience method for converting a wire input index into a Target with our gate index. -// fn local_wire(&self, input: usize) -> Target { -// Target::Wire(Wire { gate: self.gate_index, input }) -// } -// } -// -// impl SimpleGenerator for InterpolantGenerator { -// fn dependencies(&self) -> Vec { -// let mut deps = vec![self.local_wire(FriConsistencyGate::WIRE_PATH)]; -// for i in 0..self.gate.arity() { -// deps.push(self.local_wire(self.gate.wire_s_j(i))); -// for commit_idx in 0..self.gate.num_commits { -// deps.push(self.local_wire(self.gate.wire_f_i(commit_idx, i))); -// } -// } -// deps -// } -// -// fn run_once(&self, witness: &PartialWitness) -> PartialWitness { -// let mut result = PartialWitness::new(); -// -// for commit_idx in 0..self.gate.num_commits { -// let values = (0..self.gate.arity()) -// .map(|j| witness.get_target(self.local_wire(self.gate.wire_f_i(commit_idx, j)))) -// .collect(); -// -// let path = witness.get_target(self.local_wire(FriConsistencyGate::WIRE_PATH)); -// let shift = self.generator_i.exp(path); -// let coeffs = coset_ifft(values, shift); -// -// for (i, coeff) in coeffs.into_iter().enumerate() { -// result.set_target( -// self.local_wire(self.gate.wire_coefficient(commit_idx, i)), -// coeff); -// } -// } -// -// result -// } -// } -// -// #[cfg(test)] -// mod tests { -// use crate::gates::fri_consistency_gate::FriConsistencyGate; -// -// #[test] -// fn wire_indices() { -// let gate = FriConsistencyGate { -// arity_bits: 1, -// num_commits: 2, -// max_path_bits: 4, -// }; -// -// // The actual indices aren't really important, but we want to make sure that -// // - there are no overlaps -// // - there are no gaps -// // - the routed inputs come first -// assert_eq!(0, FriConsistencyGate::WIRE_PATH); -// assert_eq!(1, gate.wire_f_i(0, 0)); -// assert_eq!(2, gate.wire_f_i(0, 1)); -// assert_eq!(3, gate.wire_f_i(1, 0)); -// assert_eq!(4, gate.wire_f_i(1, 1)); -// assert_eq!(5, gate.wire_f_i_plus_1(0)); -// assert_eq!(6, gate.wire_f_i_plus_1(1)); -// assert_eq!(7, gate.wire_path_bit_i(0)); -// assert_eq!(8, gate.wire_path_bit_i(1)); -// assert_eq!(9, gate.wire_path_bit_i(2)); -// assert_eq!(10, gate.wire_path_bit_i(3)); -// assert_eq!(11, gate.wire_s_j(0)); -// assert_eq!(12, gate.wire_s_j(1)); -// assert_eq!(13, gate.wire_y()); -// assert_eq!(14, gate.wire_coefficient(0, 0)); -// assert_eq!(15, gate.wire_coefficient(0, 1)); -// assert_eq!(16, gate.wire_coefficient(1, 0)); -// assert_eq!(17, gate.wire_coefficient(1, 1)); -// assert_eq!(18, gate.start_unnamed_wires()); -// } -// } From 747974558f227e8ef66ad8d5d17f24cfec6169e7 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Thu, 20 May 2021 05:27:56 -0700 Subject: [PATCH 12/13] Add test_low_degree for other gates --- src/gates/arithmetic.rs | 21 +++++++++++---------- src/gates/constant.rs | 12 ++++++++++++ src/gates/gate_testing.rs | 5 +++-- src/gates/gmimc.rs | 11 +++++++++++ src/gates/gmimc_eval.rs | 15 ++++++++++++++- src/gates/interpolation.rs | 7 ++----- src/gates/mod.rs | 1 - src/gates/noop.rs | 12 ++++++++++++ 8 files changed, 65 insertions(+), 19 deletions(-) diff --git a/src/gates/arithmetic.rs b/src/gates/arithmetic.rs index 4a2f63b7..f24ea7c1 100644 --- a/src/gates/arithmetic.rs +++ b/src/gates/arithmetic.rs @@ -142,13 +142,14 @@ impl SimpleGenerator for ArithmeticGenerator { } } -// #[cfg(test)] -// mod tests { -// use crate::{test_gate_low_degree, ArithmeticGate, Tweedledum}; -// -// test_gate_low_degree!( -// low_degree_ArithmeticGate, -// Tweedledum, -// ArithmeticGate -// ); -// } +#[cfg(test)] +mod tests { + use crate::field::crandall_field::CrandallField; + use crate::gates::arithmetic::ArithmeticGate; + use crate::gates::gate_testing::test_low_degree; + + #[test] + fn low_degree() { + test_low_degree(ArithmeticGate::new::()) + } +} diff --git a/src/gates/constant.rs b/src/gates/constant.rs index 8482a6de..a0a7685e 100644 --- a/src/gates/constant.rs +++ b/src/gates/constant.rs @@ -89,3 +89,15 @@ impl SimpleGenerator for ConstantGenerator { PartialWitness::singleton_target(Target::Wire(wire), self.constant) } } + +#[cfg(test)] +mod tests { + use crate::field::crandall_field::CrandallField; + use crate::gates::constant::ConstantGate; + use crate::gates::gate_testing::test_low_degree; + + #[test] + fn low_degree() { + test_low_degree(ConstantGate::get::()) + } +} diff --git a/src/gates/gate_testing.rs b/src/gates/gate_testing.rs index 2b0cb84b..b7a62c7e 100644 --- a/src/gates/gate_testing.rs +++ b/src/gates/gate_testing.rs @@ -1,5 +1,5 @@ use crate::field::field::Field; -use crate::gates::gate::Gate; +use crate::gates::gate::{Gate, GateRef}; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::util::{log2_ceil, transpose}; use crate::vars::EvaluationVars; @@ -9,7 +9,8 @@ const WITNESS_DEGREE: usize = WITNESS_SIZE - 1; /// Tests that the constraints imposed by the given gate are low-degree by applying them to random /// low-degree witness polynomials. -pub(crate) fn test_low_degree>(gate: G) { +pub(crate) fn test_low_degree(gate: GateRef) { + let gate = gate.0; let rate_bits = log2_ceil(gate.degree() + 1); let wire_ldes = random_low_degree_matrix(gate.num_wires(), rate_bits); diff --git a/src/gates/gmimc.rs b/src/gates/gmimc.rs index de957deb..8263c46a 100644 --- a/src/gates/gmimc.rs +++ b/src/gates/gmimc.rs @@ -349,6 +349,7 @@ mod tests { use crate::gmimc::gmimc_permute_naive; use crate::wire::Wire; use crate::witness::PartialWitness; + use crate::gates::gate_testing::test_low_degree; #[test] fn generated_output() { @@ -411,4 +412,14 @@ mod tests { }); assert_eq!(acc_new, F::from_canonical_usize(7 * 2)); } + + #[test] + fn low_degree() { + type F = CrandallField; + const R: usize = 101; + let constants = Arc::new([F::TWO; R]); + type Gate = GMiMCGate; + let gate = Gate::with_constants(constants); + test_low_degree(gate) + } } diff --git a/src/gates/gmimc_eval.rs b/src/gates/gmimc_eval.rs index 413ccd70..57d9206c 100644 --- a/src/gates/gmimc_eval.rs +++ b/src/gates/gmimc_eval.rs @@ -1,3 +1,5 @@ +use std::marker::PhantomData; + use crate::circuit_builder::CircuitBuilder; use crate::field::field::Field; use crate::gates::gate::{Gate, GateRef}; @@ -6,7 +8,6 @@ use crate::target::Target; use crate::vars::{EvaluationTargets, EvaluationVars}; use crate::wire::Wire; use crate::witness::PartialWitness; -use std::marker::PhantomData; /// Performs some arithmetic involved in the evaluation of GMiMC's constraint polynomials for one /// round. In particular, this performs the following computations: @@ -224,3 +225,15 @@ impl SimpleGenerator for GMiMCEvalGenerator { 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::::get()) + } +} diff --git a/src/gates/interpolation.rs b/src/gates/interpolation.rs index 1e6006c5..272c0a05 100644 --- a/src/gates/interpolation.rs +++ b/src/gates/interpolation.rs @@ -266,10 +266,7 @@ mod tests { #[test] fn low_degree() { type F = CrandallField; - let gate = InterpolationGate:: { - num_points: 4, - _phantom: PhantomData, - }; - test_low_degree(gate); + test_low_degree(InterpolationGate::::new(4)); + test_low_degree(InterpolationGate::::new(4)); } } diff --git a/src/gates/mod.rs b/src/gates/mod.rs index 0c8b04b8..afc0b904 100644 --- a/src/gates/mod.rs +++ b/src/gates/mod.rs @@ -1,6 +1,5 @@ pub(crate) mod arithmetic; pub mod constant; -pub(crate) mod fri_consistency_gate; pub(crate) mod gate; pub mod gmimc; pub(crate) mod gmimc_eval; diff --git a/src/gates/noop.rs b/src/gates/noop.rs index edd4e5dd..fdde6ec6 100644 --- a/src/gates/noop.rs +++ b/src/gates/noop.rs @@ -55,3 +55,15 @@ impl Gate for NoopGate { 0 } } + +#[cfg(test)] +mod tests { + use crate::field::crandall_field::CrandallField; + use crate::gates::gate_testing::test_low_degree; + use crate::gates::noop::NoopGate; + + #[test] + fn low_degree() { + test_low_degree(NoopGate::get::()) + } +} From a4be58557ea6cfddc2af3efff432a3e556b5dbc5 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Thu, 20 May 2021 05:35:16 -0700 Subject: [PATCH 13/13] Fix GMiMCGate --- src/gates/gmimc.rs | 7 ++++++- src/gates/interpolation.rs | 1 + src/plonk_challenger.rs | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/gates/gmimc.rs b/src/gates/gmimc.rs index 8263c46a..da8ce670 100644 --- a/src/gates/gmimc.rs +++ b/src/gates/gmimc.rs @@ -59,6 +59,11 @@ impl GMiMCGate { fn wire_cubing_input(i: usize) -> usize { 2 * W + 3 + i } + + /// End of wire indices, exclusive. + fn end() -> usize { + 2 * W + 3 + R + } } impl Gate for GMiMCGate { @@ -223,7 +228,7 @@ impl Gate for GMiMCGate { } fn num_wires(&self) -> usize { - W + 1 + R + Self::end() } fn num_constants(&self) -> usize { diff --git a/src/gates/interpolation.rs b/src/gates/interpolation.rs index 272c0a05..732f28ba 100644 --- a/src/gates/interpolation.rs +++ b/src/gates/interpolation.rs @@ -86,6 +86,7 @@ impl, const D: usize> InterpolationGate { start..start + D } + /// End of wire indices, exclusive. fn end(&self) -> usize { self.start_coeffs() + self.num_points * D } diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index 67957e13..6a1a8888 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -304,7 +304,7 @@ mod tests { } let config = CircuitConfig { - num_wires: 114, + num_wires: 12 + 12 + 3 + 101, num_routed_wires: 27, ..CircuitConfig::default() };