use std::ops::Range; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field_types::RichField; use crate::gates::gate::Gate; use crate::iop::target::Target; use crate::plonk::circuit_builder::CircuitBuilder; /// Trait for gates which interpolate 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. pub(crate) trait InterpolationGate, const D: usize>: Gate + Copy { fn new(subgroup_bits: usize) -> Self; fn num_points(&self) -> usize; /// Wire index of the coset shift. fn wire_shift(&self) -> usize { 0 } fn start_values(&self) -> usize { 1 } /// Wire indices of the `i`th interpolant value. 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. 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. 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. fn num_routed_wires(&self) -> usize { self.start_coeffs() } /// Wire indices of the interpolant's `i`th coefficient. 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() } } impl, const D: usize> CircuitBuilder { /// Interpolates a polynomial, whose points are a coset of the multiplicative subgroup with the /// given size, and whose values are given. Returns the evaluation of the interpolant at /// `evaluation_point`. pub(crate) fn interpolate_coset>( &mut self, subgroup_bits: usize, coset_shift: Target, values: &[ExtensionTarget], evaluation_point: ExtensionTarget, ) -> ExtensionTarget { let gate = G::new(subgroup_bits); let gate_index = self.add_gate(gate, vec![]); self.connect(coset_shift, Target::wire(gate_index, gate.wire_shift())); for (i, &v) in values.iter().enumerate() { self.connect_extension( v, ExtensionTarget::from_range(gate_index, gate.wires_value(i)), ); } self.connect_extension( evaluation_point, ExtensionTarget::from_range(gate_index, gate.wires_evaluation_point()), ); ExtensionTarget::from_range(gate_index, gate.wires_evaluation_value()) } } #[cfg(test)] mod tests { use anyhow::Result; use crate::field::extension_field::quadratic::QuadraticExtension; use crate::field::extension_field::FieldExtension; use crate::field::field_types::Field; use crate::field::goldilocks_field::GoldilocksField; use crate::field::interpolation::interpolant; use crate::gates::interpolation::HighDegreeInterpolationGate; use crate::gates::low_degree_interpolation::LowDegreeInterpolationGate; use crate::iop::witness::PartialWitness; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::verifier::verify; #[test] fn test_interpolate() -> Result<()> { type F = GoldilocksField; const D: usize = 2; type FF = QuadraticExtension; let config = CircuitConfig::standard_recursion_config(); let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); let subgroup_bits = 2; let len = 1 << subgroup_bits; let coset_shift = F::rand(); let g = F::primitive_root_of_unity(subgroup_bits); let points = F::cyclic_subgroup_coset_known_order(g, coset_shift, len); let values = FF::rand_vec(len); let homogeneous_points = points .iter() .zip(values.iter()) .map(|(&a, &b)| (>::from_basefield(a), b)) .collect::>(); let true_interpolant = interpolant(&homogeneous_points); let z = FF::rand(); let true_eval = true_interpolant.eval(z); let coset_shift_target = builder.constant(coset_shift); let value_targets = values .iter() .map(|&v| (builder.constant_extension(v))) .collect::>(); let zt = builder.constant_extension(z); let eval_hd = builder.interpolate_coset::>( subgroup_bits, coset_shift_target, &value_targets, zt, ); let eval_ld = builder.interpolate_coset::>( subgroup_bits, coset_shift_target, &value_targets, zt, ); let true_eval_target = builder.constant_extension(true_eval); builder.connect_extension(eval_hd, true_eval_target); builder.connect_extension(eval_ld, true_eval_target); let data = builder.build(); let proof = data.prove(pw)?; verify(proof, &data.verifier_only, &data.common) } }