From 8522026c36810ac3a242873a2877130a0ef8d7e6 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 22 Nov 2021 11:39:56 +0100 Subject: [PATCH] Change file structure --- src/gates/interpolation.rs | 177 ++------- src/gates/low_degree_interpolation.rs | 525 ++++++++++++++++++++++++++ src/gates/mod.rs | 1 + 3 files changed, 553 insertions(+), 150 deletions(-) create mode 100644 src/gates/low_degree_interpolation.rs diff --git a/src/gates/interpolation.rs b/src/gates/interpolation.rs index 9f7fa216..d97eb009 100644 --- a/src/gates/interpolation.rs +++ b/src/gates/interpolation.rs @@ -1,10 +1,10 @@ use std::marker::PhantomData; use std::ops::Range; -use crate::field::extension_field::algebra::{ExtensionAlgebra, PolynomialCoeffsAlgebra}; +use crate::field::extension_field::algebra::PolynomialCoeffsAlgebra; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; -use crate::field::field_types::{Field, RichField}; +use crate::field::field_types::RichField; use crate::field::interpolation::interpolant; use crate::gadgets::polynomial::PolynomialCoeffsExtAlgebraTarget; use crate::gates::gate::Gate; @@ -90,26 +90,9 @@ impl, const D: usize> InterpolationGate { start..start + D } - pub fn powers_init(&self, i: usize) -> usize { - debug_assert!(0 < i && i < self.num_points()); - if i == 1 { - return self.wire_shift(); - } - self.start_coeffs() + self.num_points() * D + i - } - - pub fn powers_eval(&self, i: usize) -> Range { - debug_assert!(0 < i && i < self.num_points()); - if i == 1 { - return self.wires_evaluation_point(); - } - let start = self.start_coeffs() + self.num_points() * D + self.num_points() - 1 + i * D; - start..start + D - } - /// End of wire indices, exclusive. fn end(&self) -> usize { - self.powers_eval(self.num_points() - 1).end + self.start_coeffs() + self.num_points() * D } /// The domain of the points we're interpolating. @@ -155,45 +138,19 @@ impl, const D: usize> Gate for InterpolationG let coeffs = (0..self.num_points()) .map(|i| vars.get_local_ext_algebra(self.wires_coeff(i))) - .collect::>(); - - let mut powers_init = (1..self.num_points()) - .map(|i| vars.local_wires[self.powers_init(i)]) - .collect::>(); - powers_init.insert(0, F::Extension::ONE); - let wire_shift = powers_init[1]; - for i in 2..self.num_points() { - constraints.push(powers_init[i - 1] * wire_shift - powers_init[i]); - } - let ocoeffs = coeffs - .iter() - .zip(powers_init) - .map(|(&c, p)| c.scalar_mul(p)) - .collect::>(); + .collect(); let interpolant = PolynomialCoeffsAlgebra::new(coeffs); - let ointerpolant = PolynomialCoeffsAlgebra::new(ocoeffs); - for (i, point) in F::Extension::two_adic_subgroup(self.subgroup_bits) - .into_iter() - .enumerate() - { + let coset = self.coset_ext(vars.local_wires[self.wire_shift()]); + for (i, point) in coset.into_iter().enumerate() { let value = vars.get_local_ext_algebra(self.wires_value(i)); - let computed_value = ointerpolant.eval_base(point); + let computed_value = interpolant.eval_base(point); constraints.extend(&(value - computed_value).to_basefield_array()); } - let mut evaluation_point_powers = (1..self.num_points()) - .map(|i| vars.get_local_ext_algebra(self.powers_eval(i))) - .collect::>(); - let evaluation_point = evaluation_point_powers[0]; - for i in 1..self.num_points() - 1 { - constraints.extend( - (evaluation_point_powers[i - 1] * evaluation_point - evaluation_point_powers[i]) - .to_basefield_array(), - ); - } + let evaluation_point = vars.get_local_ext_algebra(self.wires_evaluation_point()); let evaluation_value = vars.get_local_ext_algebra(self.wires_evaluation_value()); - let computed_evaluation_value = interpolant.eval_with_powers(&evaluation_point_powers); + let computed_evaluation_value = interpolant.eval(evaluation_point); constraints.extend(&(evaluation_value - computed_evaluation_value).to_basefield_array()); constraints @@ -204,44 +161,19 @@ impl, const D: usize> Gate for InterpolationG let coeffs = (0..self.num_points()) .map(|i| vars.get_local_ext(self.wires_coeff(i))) - .collect::>(); - let mut powers_init = (1..self.num_points()) - .map(|i| vars.local_wires[self.powers_init(i)]) - .collect::>(); - powers_init.insert(0, F::ONE); - let wire_shift = powers_init[1]; - for i in 2..self.num_points() { - constraints.push(powers_init[i - 1] * wire_shift - powers_init[i]); - } - let ocoeffs = coeffs - .iter() - .zip(powers_init) - .map(|(&c, p)| c.scalar_mul(p)) - .collect::>(); + .collect(); let interpolant = PolynomialCoeffs::new(coeffs); - let ointerpolant = PolynomialCoeffs::new(ocoeffs); - for (i, point) in F::two_adic_subgroup(self.subgroup_bits) - .into_iter() - .enumerate() - { + let coset = self.coset(vars.local_wires[self.wire_shift()]); + for (i, point) in coset.into_iter().enumerate() { let value = vars.get_local_ext(self.wires_value(i)); - let computed_value = ointerpolant.eval_base(point); + let computed_value = interpolant.eval_base(point); constraints.extend(&(value - computed_value).to_basefield_array()); } - let evaluation_point_powers = (1..self.num_points()) - .map(|i| vars.get_local_ext(self.powers_eval(i))) - .collect::>(); - let evaluation_point = evaluation_point_powers[0]; - for i in 1..self.num_points() - 1 { - constraints.extend( - (evaluation_point_powers[i - 1] * evaluation_point - evaluation_point_powers[i]) - .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_with_powers(&evaluation_point_powers); + let computed_evaluation_value = interpolant.eval(evaluation_point); constraints.extend(&(evaluation_value - computed_evaluation_value).to_basefield_array()); constraints @@ -256,34 +188,13 @@ impl, const D: usize> Gate for InterpolationG let coeffs = (0..self.num_points()) .map(|i| vars.get_local_ext_algebra(self.wires_coeff(i))) - .collect::>(); - let mut powers_init = (1..self.num_points()) - .map(|i| vars.local_wires[self.powers_init(i)]) - .collect::>(); - powers_init.insert(0, builder.one_extension()); - let wire_shift = powers_init[1]; - for i in 2..self.num_points() { - constraints.push(builder.mul_sub_extension( - powers_init[i - 1], - wire_shift, - powers_init[i], - )); - } - let ocoeffs = coeffs - .iter() - .zip(powers_init) - .map(|(&c, p)| builder.scalar_mul_ext_algebra(p, c)) - .collect::>(); + .collect(); let interpolant = PolynomialCoeffsExtAlgebraTarget(coeffs); - let ointerpolant = PolynomialCoeffsExtAlgebraTarget(ocoeffs); - for (i, point) in F::Extension::two_adic_subgroup(self.subgroup_bits) - .into_iter() - .enumerate() - { + let coset = self.coset_ext_recursive(builder, vars.local_wires[self.wire_shift()]); + for (i, point) in coset.into_iter().enumerate() { let value = vars.get_local_ext_algebra(self.wires_value(i)); - let point = builder.constant_extension(point); - let computed_value = ointerpolant.eval_scalar(builder, point); + let computed_value = interpolant.eval_scalar(builder, point); constraints.extend( &builder .sub_ext_algebra(value, computed_value) @@ -291,27 +202,9 @@ impl, const D: usize> Gate for InterpolationG ); } - let evaluation_point_powers = (1..self.num_points()) - .map(|i| vars.get_local_ext_algebra(self.powers_eval(i))) - .collect::>(); - let evaluation_point = evaluation_point_powers[0]; - for i in 1..self.num_points() - 1 { - let neg_one_ext = builder.neg_one_extension(); - let neg_new_power = - builder.scalar_mul_ext_algebra(neg_one_ext, evaluation_point_powers[i]); - let constraint = builder.mul_add_ext_algebra( - evaluation_point, - evaluation_point_powers[i - 1], - neg_new_power, - ); - constraints.extend(constraint.to_ext_target_array()); - } + let evaluation_point = vars.get_local_ext_algebra(self.wires_evaluation_point()); let evaluation_value = vars.get_local_ext_algebra(self.wires_evaluation_value()); - let computed_evaluation_value = - interpolant.eval_with_powers(builder, &evaluation_point_powers); - // let evaluation_point = vars.get_local_ext_algebra(self.wires_evaluation_point()); - // let evaluation_value = vars.get_local_ext_algebra(self.wires_evaluation_value()); - // let computed_evaluation_value = interpolant.eval(builder, evaluation_point); + let computed_evaluation_value = interpolant.eval(builder, evaluation_point); constraints.extend( &builder .sub_ext_algebra(evaluation_value, computed_evaluation_value) @@ -345,14 +238,13 @@ impl, const D: usize> Gate for InterpolationG fn degree(&self) -> usize { // The highest power of x is `num_points - 1`, and then multiplication by the coefficient // adds 1. - 2 + self.num_points() } fn num_constraints(&self) -> usize { - // `num_points * D` constraints to check for consistency between the coefficients and the - // point-value pairs, plus `D` constraints for the evaluation value, plus `(D+1)*(num_points-2)` - // to check power constraints for evaluation point and wire shift. - self.num_points() * D + D + (D + 1) * (self.num_points() - 2) + // 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 } } @@ -402,17 +294,8 @@ impl, const D: usize> SimpleGenerator F::Extension::from_basefield_array(arr) }; - let wire_shift = get_local_wire(self.gate.wire_shift()); - - for i in 2..self.gate.num_points() { - out_buffer.set_wire( - local_wire(self.gate.powers_init(i)), - wire_shift.exp_u64(i as u64), - ); - } - // Compute the interpolant. - let points = self.gate.coset(wire_shift); + let points = self.gate.coset(get_local_wire(self.gate.wire_shift())); let points = points .into_iter() .enumerate() @@ -426,12 +309,6 @@ impl, const D: usize> SimpleGenerator } let evaluation_point = get_local_ext(self.gate.wires_evaluation_point()); - for i in 2..self.gate.num_points() { - out_buffer.set_extension_target( - ExtensionTarget::from_range(self.gate_index, self.gate.powers_eval(i)), - evaluation_point.exp_u64(i as u64), - ); - } let evaluation_value = interpolant.eval(evaluation_point); let evaluation_value_wires = self.gate.wires_evaluation_value().map(local_wire); out_buffer.set_ext_wires(evaluation_value_wires, evaluation_value); @@ -480,7 +357,7 @@ mod tests { #[test] fn eval_fns() -> Result<()> { - test_eval_fns::(InterpolationGate::new(3)) + test_eval_fns::(InterpolationGate::new(2)) } #[test] diff --git a/src/gates/low_degree_interpolation.rs b/src/gates/low_degree_interpolation.rs new file mode 100644 index 00000000..95b168f7 --- /dev/null +++ b/src/gates/low_degree_interpolation.rs @@ -0,0 +1,525 @@ +use std::marker::PhantomData; +use std::ops::Range; + +use crate::field::extension_field::algebra::{ExtensionAlgebra, PolynomialCoeffsAlgebra}; +use crate::field::extension_field::target::ExtensionTarget; +use crate::field::extension_field::{Extendable, FieldExtension}; +use crate::field::field_types::{Field, RichField}; +use crate::field::interpolation::interpolant; +use crate::gadgets::polynomial::PolynomialCoeffsExtAlgebraTarget; +use crate::gates::gate::Gate; +use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; +use crate::iop::target::Target; +use crate::iop::wire::Wire; +use crate::iop::witness::{PartitionWitness, Witness}; +use crate::plonk::circuit_builder::CircuitBuilder; +use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; +use crate::polynomial::polynomial::PolynomialCoeffs; + +/// Interpolates a polynomial, whose points are a (base field) coset of the multiplicative subgroup +/// with the given size, and whose values are extension field elements, given by input wires. +/// Outputs the evaluation of the interpolant at a given (extension field) evaluation point. +#[derive(Clone, Debug)] +pub(crate) struct LowDegreeInterpolationGate, const D: usize> { + pub subgroup_bits: usize, + _phantom: PhantomData, +} + +impl, const D: usize> LowDegreeInterpolationGate { + pub fn new(subgroup_bits: usize) -> Self { + Self { + subgroup_bits, + _phantom: PhantomData, + } + } + + fn num_points(&self) -> usize { + 1 << self.subgroup_bits + } + + /// Wire index of the coset shift. + pub fn wire_shift(&self) -> usize { + 0 + } + + fn start_values(&self) -> usize { + 1 + } + + /// 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 * D; + start..start + D + } + + fn start_evaluation_point(&self) -> usize { + 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 + D + } + + fn start_evaluation_value(&self) -> usize { + 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 + D + } + + fn start_coeffs(&self) -> usize { + self.start_evaluation_value() + D + } + + /// The number of routed wires required in the typical usage of this gate, where the points to + /// interpolate, the evaluation point, and the corresponding value are all routed. + pub(crate) fn num_routed_wires(&self) -> usize { + self.start_coeffs() + } + + /// 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 * D; + start..start + D + } + + fn end_coeffs(&self) -> usize { + self.start_coeffs() + D * self.num_points() + } + + pub fn powers_init(&self, i: usize) -> usize { + debug_assert!(0 < i && i < self.num_points()); + if i == 1 { + return self.wire_shift(); + } + self.end_coeffs() + i - 2 + } + + pub fn powers_eval(&self, i: usize) -> Range { + debug_assert!(0 < i && i < self.num_points()); + if i == 1 { + return self.wires_evaluation_point(); + } + let start = self.end_coeffs() + self.num_points() - 2 + (i - 2) * D; + start..start + D + } + + /// End of wire indices, exclusive. + fn end(&self) -> usize { + self.powers_eval(self.num_points() - 1).end + } + + /// The domain of the points we're interpolating. + fn coset(&self, shift: F) -> impl Iterator { + let g = F::primitive_root_of_unity(self.subgroup_bits); + let size = 1 << self.subgroup_bits; + // Speed matters here, so we avoid `cyclic_subgroup_coset_known_order` which allocates. + g.powers().take(size).map(move |x| x * shift) + } + + /// The domain of the points we're interpolating. + fn coset_ext(&self, shift: F::Extension) -> impl Iterator { + let g = F::primitive_root_of_unity(self.subgroup_bits); + let size = 1 << self.subgroup_bits; + g.powers().take(size).map(move |x| shift.scalar_mul(x)) + } + + /// The domain of the points we're interpolating. + fn coset_ext_recursive( + &self, + builder: &mut CircuitBuilder, + shift: ExtensionTarget, + ) -> Vec> { + let g = F::primitive_root_of_unity(self.subgroup_bits); + let size = 1 << self.subgroup_bits; + g.powers() + .take(size) + .map(move |x| { + let subgroup_element = builder.constant(x.into()); + builder.scalar_mul_ext(subgroup_element, shift) + }) + .collect() + } +} + +impl, const D: usize> Gate for LowDegreeInterpolationGate { + fn id(&self) -> String { + format!("{:?}", self, D) + } + + fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { + let mut constraints = Vec::with_capacity(self.num_constraints()); + + let coeffs = (0..self.num_points()) + .map(|i| vars.get_local_ext_algebra(self.wires_coeff(i))) + .collect::>(); + + let mut powers_init = (1..self.num_points()) + .map(|i| vars.local_wires[self.powers_init(i)]) + .collect::>(); + powers_init.insert(0, F::Extension::ONE); + let wire_shift = powers_init[1]; + for i in 2..self.num_points() { + constraints.push(powers_init[i - 1] * wire_shift - powers_init[i]); + } + let ocoeffs = coeffs + .iter() + .zip(powers_init) + .map(|(&c, p)| c.scalar_mul(p)) + .collect::>(); + let interpolant = PolynomialCoeffsAlgebra::new(coeffs); + let ointerpolant = PolynomialCoeffsAlgebra::new(ocoeffs); + + for (i, point) in F::Extension::two_adic_subgroup(self.subgroup_bits) + .into_iter() + .enumerate() + { + let value = vars.get_local_ext_algebra(self.wires_value(i)); + let computed_value = ointerpolant.eval_base(point); + constraints.extend(&(value - computed_value).to_basefield_array()); + } + + let mut evaluation_point_powers = (1..self.num_points()) + .map(|i| vars.get_local_ext_algebra(self.powers_eval(i))) + .collect::>(); + let evaluation_point = evaluation_point_powers[0]; + for i in 1..self.num_points() - 1 { + constraints.extend( + (evaluation_point_powers[i - 1] * evaluation_point - evaluation_point_powers[i]) + .to_basefield_array(), + ); + } + let evaluation_value = vars.get_local_ext_algebra(self.wires_evaluation_value()); + let computed_evaluation_value = interpolant.eval_with_powers(&evaluation_point_powers); + constraints.extend(&(evaluation_value - computed_evaluation_value).to_basefield_array()); + + constraints + } + + fn eval_unfiltered_base(&self, vars: EvaluationVarsBase) -> Vec { + let mut constraints = Vec::with_capacity(self.num_constraints()); + + let coeffs = (0..self.num_points()) + .map(|i| vars.get_local_ext(self.wires_coeff(i))) + .collect::>(); + let mut powers_init = (1..self.num_points()) + .map(|i| vars.local_wires[self.powers_init(i)]) + .collect::>(); + powers_init.insert(0, F::ONE); + let wire_shift = powers_init[1]; + for i in 2..self.num_points() { + constraints.push(powers_init[i - 1] * wire_shift - powers_init[i]); + } + let ocoeffs = coeffs + .iter() + .zip(powers_init) + .map(|(&c, p)| c.scalar_mul(p)) + .collect::>(); + let interpolant = PolynomialCoeffs::new(coeffs); + let ointerpolant = PolynomialCoeffs::new(ocoeffs); + + for (i, point) in F::two_adic_subgroup(self.subgroup_bits) + .into_iter() + .enumerate() + { + let value = vars.get_local_ext(self.wires_value(i)); + let computed_value = ointerpolant.eval_base(point); + constraints.extend(&(value - computed_value).to_basefield_array()); + } + + let evaluation_point_powers = (1..self.num_points()) + .map(|i| vars.get_local_ext(self.powers_eval(i))) + .collect::>(); + let evaluation_point = evaluation_point_powers[0]; + for i in 1..self.num_points() - 1 { + constraints.extend( + (evaluation_point_powers[i - 1] * evaluation_point - evaluation_point_powers[i]) + .to_basefield_array(), + ); + } + let evaluation_value = vars.get_local_ext(self.wires_evaluation_value()); + let computed_evaluation_value = interpolant.eval_with_powers(&evaluation_point_powers); + constraints.extend(&(evaluation_value - computed_evaluation_value).to_basefield_array()); + + constraints + } + + fn eval_unfiltered_recursively( + &self, + builder: &mut CircuitBuilder, + vars: EvaluationTargets, + ) -> Vec> { + let mut constraints = Vec::with_capacity(self.num_constraints()); + + let coeffs = (0..self.num_points()) + .map(|i| vars.get_local_ext_algebra(self.wires_coeff(i))) + .collect::>(); + let mut powers_init = (1..self.num_points()) + .map(|i| vars.local_wires[self.powers_init(i)]) + .collect::>(); + powers_init.insert(0, builder.one_extension()); + let wire_shift = powers_init[1]; + for i in 2..self.num_points() { + constraints.push(builder.mul_sub_extension( + powers_init[i - 1], + wire_shift, + powers_init[i], + )); + } + let ocoeffs = coeffs + .iter() + .zip(powers_init) + .map(|(&c, p)| builder.scalar_mul_ext_algebra(p, c)) + .collect::>(); + let interpolant = PolynomialCoeffsExtAlgebraTarget(coeffs); + let ointerpolant = PolynomialCoeffsExtAlgebraTarget(ocoeffs); + + for (i, point) in F::Extension::two_adic_subgroup(self.subgroup_bits) + .into_iter() + .enumerate() + { + let value = vars.get_local_ext_algebra(self.wires_value(i)); + let point = builder.constant_extension(point); + let computed_value = ointerpolant.eval_scalar(builder, point); + constraints.extend( + &builder + .sub_ext_algebra(value, computed_value) + .to_ext_target_array(), + ); + } + + let evaluation_point_powers = (1..self.num_points()) + .map(|i| vars.get_local_ext_algebra(self.powers_eval(i))) + .collect::>(); + let evaluation_point = evaluation_point_powers[0]; + for i in 1..self.num_points() - 1 { + let neg_one_ext = builder.neg_one_extension(); + let neg_new_power = + builder.scalar_mul_ext_algebra(neg_one_ext, evaluation_point_powers[i]); + let constraint = builder.mul_add_ext_algebra( + evaluation_point, + evaluation_point_powers[i - 1], + neg_new_power, + ); + constraints.extend(constraint.to_ext_target_array()); + } + let evaluation_value = vars.get_local_ext_algebra(self.wires_evaluation_value()); + let computed_evaluation_value = + interpolant.eval_with_powers(builder, &evaluation_point_powers); + // let evaluation_point = vars.get_local_ext_algebra(self.wires_evaluation_point()); + // let evaluation_value = vars.get_local_ext_algebra(self.wires_evaluation_value()); + // let computed_evaluation_value = interpolant.eval(builder, evaluation_point); + constraints.extend( + &builder + .sub_ext_algebra(evaluation_value, computed_evaluation_value) + .to_ext_target_array(), + ); + + constraints + } + + fn generators( + &self, + gate_index: usize, + _local_constants: &[F], + ) -> Vec>> { + let gen = InterpolationGenerator:: { + gate_index, + gate: self.clone(), + _phantom: PhantomData, + }; + vec![Box::new(gen.adapter())] + } + + fn num_wires(&self) -> usize { + self.end() + } + + fn num_constants(&self) -> usize { + 0 + } + + fn degree(&self) -> usize { + // The highest power of x is `num_points - 1`, and then multiplication by the coefficient + // adds 1. + 2 + } + + fn num_constraints(&self) -> usize { + // `num_points * D` constraints to check for consistency between the coefficients and the + // point-value pairs, plus `D` constraints for the evaluation value, plus `(D+1)*(num_points-2)` + // to check power constraints for evaluation point and wire shift. + self.num_points() * D + D + (D + 1) * (self.num_points() - 2) + } +} + +#[derive(Debug)] +struct InterpolationGenerator, const D: usize> { + gate_index: usize, + gate: LowDegreeInterpolationGate, + _phantom: PhantomData, +} + +impl, const D: usize> SimpleGenerator + for InterpolationGenerator +{ + fn dependencies(&self) -> Vec { + let local_target = |input| { + Target::Wire(Wire { + gate: self.gate_index, + input, + }) + }; + + let local_targets = |inputs: Range| inputs.map(local_target); + + let num_points = self.gate.num_points(); + let mut deps = Vec::with_capacity(1 + D + num_points * D); + + deps.push(local_target(self.gate.wire_shift())); + deps.extend(local_targets(self.gate.wires_evaluation_point())); + for i in 0..num_points { + deps.extend(local_targets(self.gate.wires_value(i))); + } + deps + } + + fn run_once(&self, witness: &PartitionWitness, out_buffer: &mut GeneratedValues) { + let local_wire = |input| Wire { + gate: self.gate_index, + input, + }; + + 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(get_local_wire).collect::>(); + let arr = values.try_into().unwrap(); + F::Extension::from_basefield_array(arr) + }; + + let wire_shift = get_local_wire(self.gate.wire_shift()); + + for i in 2..self.gate.num_points() { + out_buffer.set_wire( + local_wire(self.gate.powers_init(i)), + wire_shift.exp_u64(i as u64), + ); + } + + // Compute the interpolant. + let points = self.gate.coset(wire_shift); + let points = points + .into_iter() + .enumerate() + .map(|(i, point)| (point.into(), get_local_ext(self.gate.wires_value(i)))) + .collect::>(); + let interpolant = interpolant(&points); + + for (i, &coeff) in interpolant.coeffs.iter().enumerate() { + let wires = self.gate.wires_coeff(i).map(local_wire); + out_buffer.set_ext_wires(wires, coeff); + } + + let evaluation_point = get_local_ext(self.gate.wires_evaluation_point()); + for i in 2..self.gate.num_points() { + out_buffer.set_extension_target( + ExtensionTarget::from_range(self.gate_index, self.gate.powers_eval(i)), + evaluation_point.exp_u64(i as u64), + ); + } + let evaluation_value = interpolant.eval(evaluation_point); + let evaluation_value_wires = self.gate.wires_evaluation_value().map(local_wire); + out_buffer.set_ext_wires(evaluation_value_wires, evaluation_value); + } +} + +#[cfg(test)] +mod tests { + use std::marker::PhantomData; + + use anyhow::Result; + + use crate::field::extension_field::quadratic::QuadraticExtension; + use crate::field::extension_field::quartic::QuarticExtension; + use crate::field::field_types::Field; + use crate::field::goldilocks_field::GoldilocksField; + use crate::gates::gate::Gate; + use crate::gates::gate_testing::{test_eval_fns, test_low_degree}; + use crate::gates::low_degree_interpolation::LowDegreeInterpolationGate; + use crate::hash::hash_types::HashOut; + use crate::plonk::vars::EvaluationVars; + use crate::polynomial::polynomial::PolynomialCoeffs; + + #[test] + fn low_degree() { + test_low_degree::(LowDegreeInterpolationGate::new(4)); + } + + #[test] + fn eval_fns() -> Result<()> { + test_eval_fns::(LowDegreeInterpolationGate::new(4)) + } + + #[test] + fn test_gate_constraint() { + type F = GoldilocksField; + type FF = QuadraticExtension; + const D: usize = 2; + + /// Returns the local wires for an interpolation gate for given coeffs, points and eval point. + fn get_wires( + gate: &LowDegreeInterpolationGate, + shift: F, + coeffs: PolynomialCoeffs, + eval_point: FF, + ) -> Vec { + let points = gate.coset(shift); + let mut v = vec![shift]; + for x in points { + v.extend(coeffs.eval(x.into()).0); + } + v.extend(eval_point.0); + v.extend(coeffs.eval(eval_point).0); + for i in 0..coeffs.len() { + v.extend(coeffs.coeffs[i].0); + } + v.extend(shift.powers().skip(2).take(gate.num_points() - 2)); + v.extend( + eval_point + .powers() + .skip(2) + .take(gate.num_points() - 2) + .flat_map(|ff| ff.0), + ); + v.iter().map(|&x| x.into()).collect::>() + } + + // Get a working row for LowDegreeInterpolationGate. + let subgroup_bits = 4; + let shift = F::rand(); + let coeffs = PolynomialCoeffs::new(FF::rand_vec(1 << subgroup_bits)); + let eval_point = FF::rand(); + let gate = LowDegreeInterpolationGate::::new(subgroup_bits); + dbg!(gate.end_coeffs()); + dbg!(gate.powers_eval(15)); + let vars = EvaluationVars { + local_constants: &[], + local_wires: &get_wires(&gate, shift, coeffs, eval_point), + public_inputs_hash: &HashOut::rand(), + }; + + assert!( + gate.eval_unfiltered(vars).iter().all(|x| x.is_zero()), + "Gate constraints are not satisfied." + ); + } +} diff --git a/src/gates/mod.rs b/src/gates/mod.rs index 93de5e97..1663fdd0 100644 --- a/src/gates/mod.rs +++ b/src/gates/mod.rs @@ -14,6 +14,7 @@ pub mod gate_tree; pub mod gmimc; pub mod insertion; pub mod interpolation; +pub mod low_degree_interpolation; pub mod noop; pub mod poseidon; pub(crate) mod poseidon_mds;