mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-07 00:03:10 +00:00
cleanup for interpolation
This commit is contained in:
parent
ba28919d66
commit
336046d872
@ -10,7 +10,7 @@ use crate::fri::structure::{FriBatchInfoTarget, FriInstanceInfoTarget, FriOpenin
|
|||||||
use crate::fri::{FriConfig, FriParams};
|
use crate::fri::{FriConfig, FriParams};
|
||||||
use crate::gadgets::interpolation::InterpolationGate;
|
use crate::gadgets::interpolation::InterpolationGate;
|
||||||
use crate::gates::gate::Gate;
|
use crate::gates::gate::Gate;
|
||||||
use crate::gates::interpolation::HighDegreeInterpolationGate;
|
use crate::gates::high_degree_interpolation::HighDegreeInterpolationGate;
|
||||||
use crate::gates::low_degree_interpolation::LowDegreeInterpolationGate;
|
use crate::gates::low_degree_interpolation::LowDegreeInterpolationGate;
|
||||||
use crate::gates::random_access::RandomAccessGate;
|
use crate::gates::random_access::RandomAccessGate;
|
||||||
use crate::hash::hash_types::MerkleCapTarget;
|
use crate::hash::hash_types::MerkleCapTarget;
|
||||||
|
|||||||
@ -1,178 +0,0 @@
|
|||||||
use std::ops::Range;
|
|
||||||
|
|
||||||
use plonky2_field::extension::Extendable;
|
|
||||||
|
|
||||||
use crate::gates::gate::Gate;
|
|
||||||
use crate::hash::hash_types::RichField;
|
|
||||||
use crate::iop::ext_target::ExtensionTarget;
|
|
||||||
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<F: RichField + Extendable<D>, const D: usize>:
|
|
||||||
Gate<F, D> + 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<usize> {
|
|
||||||
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<usize> {
|
|
||||||
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<usize> {
|
|
||||||
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<usize> {
|
|
||||||
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<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|
||||||
/// 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<G: InterpolationGate<F, D>>(
|
|
||||||
&mut self,
|
|
||||||
subgroup_bits: usize,
|
|
||||||
coset_shift: Target,
|
|
||||||
values: &[ExtensionTarget<D>],
|
|
||||||
evaluation_point: ExtensionTarget<D>,
|
|
||||||
) -> ExtensionTarget<D> {
|
|
||||||
let gate = G::new(subgroup_bits);
|
|
||||||
let row = self.add_gate(gate, vec![]);
|
|
||||||
self.connect(coset_shift, Target::wire(row, gate.wire_shift()));
|
|
||||||
for (i, &v) in values.iter().enumerate() {
|
|
||||||
self.connect_extension(v, ExtensionTarget::from_range(row, gate.wires_value(i)));
|
|
||||||
}
|
|
||||||
self.connect_extension(
|
|
||||||
evaluation_point,
|
|
||||||
ExtensionTarget::from_range(row, gate.wires_evaluation_point()),
|
|
||||||
);
|
|
||||||
|
|
||||||
ExtensionTarget::from_range(row, gate.wires_evaluation_value())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use anyhow::Result;
|
|
||||||
use plonky2_field::extension::FieldExtension;
|
|
||||||
use plonky2_field::interpolation::interpolant;
|
|
||||||
use plonky2_field::types::Field;
|
|
||||||
|
|
||||||
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::config::{GenericConfig, PoseidonGoldilocksConfig};
|
|
||||||
use crate::plonk::verifier::verify;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_interpolate() -> Result<()> {
|
|
||||||
const D: usize = 2;
|
|
||||||
type C = PoseidonGoldilocksConfig;
|
|
||||||
type F = <C as GenericConfig<D>>::F;
|
|
||||||
type FF = <C as GenericConfig<D>>::FE;
|
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
|
||||||
let pw = PartialWitness::new();
|
|
||||||
let mut builder = CircuitBuilder::<F, D>::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)| (<FF as FieldExtension<D>>::from_basefield(a), b))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
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::<Vec<_>>();
|
|
||||||
|
|
||||||
let zt = builder.constant_extension(z);
|
|
||||||
|
|
||||||
let eval_hd = builder.interpolate_coset::<HighDegreeInterpolationGate<F, D>>(
|
|
||||||
subgroup_bits,
|
|
||||||
coset_shift_target,
|
|
||||||
&value_targets,
|
|
||||||
zt,
|
|
||||||
);
|
|
||||||
let eval_ld = builder.interpolate_coset::<LowDegreeInterpolationGate<F, D>>(
|
|
||||||
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::<C>();
|
|
||||||
let proof = data.prove(pw)?;
|
|
||||||
|
|
||||||
verify(proof, &data.verifier_only, &data.common)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +1,6 @@
|
|||||||
pub mod arithmetic;
|
pub mod arithmetic;
|
||||||
pub mod arithmetic_extension;
|
pub mod arithmetic_extension;
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
pub mod interpolation;
|
|
||||||
pub mod polynomial;
|
pub mod polynomial;
|
||||||
pub mod random_access;
|
pub mod random_access;
|
||||||
pub mod range_check;
|
pub mod range_check;
|
||||||
|
|||||||
363
plonky2/src/gates/high_degree_interpolation.rs
Normal file
363
plonky2/src/gates/high_degree_interpolation.rs
Normal file
@ -0,0 +1,363 @@
|
|||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
use plonky2_field::extension::algebra::PolynomialCoeffsAlgebra;
|
||||||
|
use plonky2_field::extension::{Extendable, FieldExtension};
|
||||||
|
use plonky2_field::interpolation::interpolant;
|
||||||
|
use plonky2_field::polynomial::PolynomialCoeffs;
|
||||||
|
|
||||||
|
use crate::gadgets::polynomial::PolynomialCoeffsExtAlgebraTarget;
|
||||||
|
use crate::gates::gate::Gate;
|
||||||
|
use crate::gates::interpolation::InterpolationGate;
|
||||||
|
use crate::gates::util::StridedConstraintConsumer;
|
||||||
|
use crate::hash::hash_types::RichField;
|
||||||
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
|
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};
|
||||||
|
|
||||||
|
/// One of the instantiations of `InterpolationGate`: allows constraints of variable
|
||||||
|
/// degree, up to `1<<subgroup_bits`.
|
||||||
|
/// The higher degree is a tradeoff for less gates (`eval_unfiltered_recursively` for
|
||||||
|
/// this version uses less gates than `LowDegreeInterpolationGate`).
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct HighDegreeInterpolationGate<F: RichField + Extendable<D>, const D: usize> {
|
||||||
|
pub subgroup_bits: usize,
|
||||||
|
_phantom: PhantomData<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: RichField + Extendable<D>, const D: usize> InterpolationGate<F, D>
|
||||||
|
for HighDegreeInterpolationGate<F, D>
|
||||||
|
{
|
||||||
|
fn new(subgroup_bits: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
subgroup_bits,
|
||||||
|
_phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_points(&self) -> usize {
|
||||||
|
1 << self.subgroup_bits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: RichField + Extendable<D>, const D: usize> HighDegreeInterpolationGate<F, D> {
|
||||||
|
/// End of wire indices, exclusive.
|
||||||
|
fn end(&self) -> usize {
|
||||||
|
self.start_coeffs() + self.num_points() * D
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The domain of the points we're interpolating.
|
||||||
|
fn coset(&self, shift: F) -> impl Iterator<Item = F> {
|
||||||
|
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<Item = F::Extension> {
|
||||||
|
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_circuit(
|
||||||
|
&self,
|
||||||
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
|
shift: ExtensionTarget<D>,
|
||||||
|
) -> Vec<ExtensionTarget<D>> {
|
||||||
|
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);
|
||||||
|
builder.scalar_mul_ext(subgroup_element, shift)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D>
|
||||||
|
for HighDegreeInterpolationGate<F, D>
|
||||||
|
{
|
||||||
|
fn id(&self) -> String {
|
||||||
|
format!("{:?}<D={}>", self, D)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||||
|
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 interpolant = PolynomialCoeffsAlgebra::new(coeffs);
|
||||||
|
|
||||||
|
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 = interpolant.eval_base(point);
|
||||||
|
constraints.extend(&(value - computed_value).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(evaluation_point);
|
||||||
|
constraints.extend(&(evaluation_value - computed_evaluation_value).to_basefield_array());
|
||||||
|
|
||||||
|
constraints
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_unfiltered_base_one(
|
||||||
|
&self,
|
||||||
|
vars: EvaluationVarsBase<F>,
|
||||||
|
mut yield_constr: StridedConstraintConsumer<F>,
|
||||||
|
) {
|
||||||
|
let coeffs = (0..self.num_points())
|
||||||
|
.map(|i| vars.get_local_ext(self.wires_coeff(i)))
|
||||||
|
.collect();
|
||||||
|
let interpolant = PolynomialCoeffs::new(coeffs);
|
||||||
|
|
||||||
|
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 = interpolant.eval_base(point);
|
||||||
|
yield_constr.many((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);
|
||||||
|
yield_constr.many((evaluation_value - computed_evaluation_value).to_basefield_array());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_unfiltered_circuit(
|
||||||
|
&self,
|
||||||
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
|
vars: EvaluationTargets<D>,
|
||||||
|
) -> Vec<ExtensionTarget<D>> {
|
||||||
|
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 interpolant = PolynomialCoeffsExtAlgebraTarget(coeffs);
|
||||||
|
|
||||||
|
let coset = self.coset_ext_circuit(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 computed_value = interpolant.eval_scalar(builder, point);
|
||||||
|
constraints.extend(
|
||||||
|
&builder
|
||||||
|
.sub_ext_algebra(value, computed_value)
|
||||||
|
.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(builder, evaluation_point);
|
||||||
|
constraints.extend(
|
||||||
|
&builder
|
||||||
|
.sub_ext_algebra(evaluation_value, computed_evaluation_value)
|
||||||
|
.to_ext_target_array(),
|
||||||
|
);
|
||||||
|
|
||||||
|
constraints
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
||||||
|
let gen = InterpolationGenerator::<F, D> {
|
||||||
|
row,
|
||||||
|
gate: *self,
|
||||||
|
_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.
|
||||||
|
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.
|
||||||
|
self.num_points() * D + D
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct InterpolationGenerator<F: RichField + Extendable<D>, const D: usize> {
|
||||||
|
row: usize,
|
||||||
|
gate: HighDegreeInterpolationGate<F, D>,
|
||||||
|
_phantom: PhantomData<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||||
|
for InterpolationGenerator<F, D>
|
||||||
|
{
|
||||||
|
fn dependencies(&self) -> Vec<Target> {
|
||||||
|
let local_target = |column| {
|
||||||
|
Target::Wire(Wire {
|
||||||
|
row: self.row,
|
||||||
|
column,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let local_targets = |columns: Range<usize>| columns.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<F>, out_buffer: &mut GeneratedValues<F>) {
|
||||||
|
let local_wire = |column| Wire {
|
||||||
|
row: self.row,
|
||||||
|
column,
|
||||||
|
};
|
||||||
|
|
||||||
|
let get_local_wire = |column| witness.get_wire(local_wire(column));
|
||||||
|
|
||||||
|
let get_local_ext = |wire_range: Range<usize>| {
|
||||||
|
debug_assert_eq!(wire_range.len(), D);
|
||||||
|
let values = wire_range.map(get_local_wire).collect::<Vec<_>>();
|
||||||
|
let arr = values.try_into().unwrap();
|
||||||
|
F::Extension::from_basefield_array(arr)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compute the interpolant.
|
||||||
|
let points = self.gate.coset(get_local_wire(self.gate.wire_shift()));
|
||||||
|
let points = points
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, point)| (point.into(), get_local_ext(self.gate.wires_value(i))))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
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());
|
||||||
|
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 plonky2_field::goldilocks_field::GoldilocksField;
|
||||||
|
use plonky2_field::polynomial::PolynomialCoeffs;
|
||||||
|
use plonky2_field::types::Field;
|
||||||
|
|
||||||
|
use crate::gadgets::interpolation::InterpolationGate;
|
||||||
|
use crate::gates::gate::Gate;
|
||||||
|
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
||||||
|
use crate::gates::high_degree_interpolation::HighDegreeInterpolationGate;
|
||||||
|
use crate::hash::hash_types::HashOut;
|
||||||
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||||
|
use crate::plonk::vars::EvaluationVars;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wire_indices() {
|
||||||
|
let gate = HighDegreeInterpolationGate::<GoldilocksField, 4> {
|
||||||
|
subgroup_bits: 1,
|
||||||
|
_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_shift(), 0);
|
||||||
|
assert_eq!(gate.wires_value(0), 1..5);
|
||||||
|
assert_eq!(gate.wires_value(1), 5..9);
|
||||||
|
assert_eq!(gate.wires_evaluation_point(), 9..13);
|
||||||
|
assert_eq!(gate.wires_evaluation_value(), 13..17);
|
||||||
|
assert_eq!(gate.wires_coeff(0), 17..21);
|
||||||
|
assert_eq!(gate.wires_coeff(1), 21..25);
|
||||||
|
assert_eq!(gate.num_wires(), 25);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn low_degree() {
|
||||||
|
test_low_degree::<GoldilocksField, _, 4>(HighDegreeInterpolationGate::new(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eval_fns() -> Result<()> {
|
||||||
|
const D: usize = 2;
|
||||||
|
type C = PoseidonGoldilocksConfig;
|
||||||
|
type F = <C as GenericConfig<D>>::F;
|
||||||
|
test_eval_fns::<F, C, _, D>(HighDegreeInterpolationGate::new(2))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gate_constraint() {
|
||||||
|
const D: usize = 2;
|
||||||
|
type C = PoseidonGoldilocksConfig;
|
||||||
|
type F = <C as GenericConfig<D>>::F;
|
||||||
|
type FF = <C as GenericConfig<D>>::FE;
|
||||||
|
|
||||||
|
/// Returns the local wires for an interpolation gate for given coeffs, points and eval point.
|
||||||
|
fn get_wires(
|
||||||
|
gate: &HighDegreeInterpolationGate<F, D>,
|
||||||
|
shift: F,
|
||||||
|
coeffs: PolynomialCoeffs<FF>,
|
||||||
|
eval_point: FF,
|
||||||
|
) -> Vec<FF> {
|
||||||
|
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.iter().map(|&x| x.into()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a working row for InterpolationGate.
|
||||||
|
let shift = F::rand();
|
||||||
|
let coeffs = PolynomialCoeffs::new(vec![FF::rand(), FF::rand()]);
|
||||||
|
let eval_point = FF::rand();
|
||||||
|
let gate = HighDegreeInterpolationGate::<F, D>::new(1);
|
||||||
|
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."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,361 +1,178 @@
|
|||||||
use std::marker::PhantomData;
|
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use plonky2_field::extension::algebra::PolynomialCoeffsAlgebra;
|
use plonky2_field::extension::Extendable;
|
||||||
use plonky2_field::extension::{Extendable, FieldExtension};
|
|
||||||
use plonky2_field::interpolation::interpolant;
|
|
||||||
use plonky2_field::polynomial::PolynomialCoeffs;
|
|
||||||
|
|
||||||
use crate::gadgets::interpolation::InterpolationGate;
|
|
||||||
use crate::gadgets::polynomial::PolynomialCoeffsExtAlgebraTarget;
|
|
||||||
use crate::gates::gate::Gate;
|
use crate::gates::gate::Gate;
|
||||||
use crate::gates::util::StridedConstraintConsumer;
|
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
|
||||||
use crate::iop::target::Target;
|
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::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
|
||||||
|
|
||||||
/// Interpolation gate with constraints of degree at most `1<<subgroup_bits`.
|
/// Trait for gates which interpolate a polynomial, whose points are a (base field) coset of the multiplicative subgroup
|
||||||
/// `eval_unfiltered_recursively` uses less gates than `LowDegreeInterpolationGate`.
|
/// with the given size, and whose values are extension field elements, given by input wires.
|
||||||
#[derive(Copy, Clone, Debug)]
|
/// Outputs the evaluation of the interpolant at a given (extension field) evaluation point.
|
||||||
pub struct HighDegreeInterpolationGate<F: RichField + Extendable<D>, const D: usize> {
|
pub(crate) trait InterpolationGate<F: RichField + Extendable<D>, const D: usize>:
|
||||||
pub subgroup_bits: usize,
|
Gate<F, D> + Copy
|
||||||
_phantom: PhantomData<F>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, const D: usize> InterpolationGate<F, D>
|
|
||||||
for HighDegreeInterpolationGate<F, D>
|
|
||||||
{
|
{
|
||||||
fn new(subgroup_bits: usize) -> Self {
|
fn new(subgroup_bits: usize) -> Self;
|
||||||
Self {
|
|
||||||
subgroup_bits,
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn num_points(&self) -> usize {
|
fn num_points(&self) -> usize;
|
||||||
1 << self.subgroup_bits
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, const D: usize> HighDegreeInterpolationGate<F, D> {
|
/// Wire index of the coset shift.
|
||||||
/// End of wire indices, exclusive.
|
fn wire_shift(&self) -> usize {
|
||||||
fn end(&self) -> usize {
|
|
||||||
self.start_coeffs() + self.num_points() * D
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The domain of the points we're interpolating.
|
|
||||||
fn coset(&self, shift: F) -> impl Iterator<Item = F> {
|
|
||||||
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<Item = F::Extension> {
|
|
||||||
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_circuit(
|
|
||||||
&self,
|
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
|
||||||
shift: ExtensionTarget<D>,
|
|
||||||
) -> Vec<ExtensionTarget<D>> {
|
|
||||||
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);
|
|
||||||
builder.scalar_mul_ext(subgroup_element, shift)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D>
|
|
||||||
for HighDegreeInterpolationGate<F, D>
|
|
||||||
{
|
|
||||||
fn id(&self) -> String {
|
|
||||||
format!("{:?}<D={}>", self, D)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
|
||||||
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 interpolant = PolynomialCoeffsAlgebra::new(coeffs);
|
|
||||||
|
|
||||||
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 = interpolant.eval_base(point);
|
|
||||||
constraints.extend((value - computed_value).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(evaluation_point);
|
|
||||||
constraints.extend((evaluation_value - computed_evaluation_value).to_basefield_array());
|
|
||||||
|
|
||||||
constraints
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eval_unfiltered_base_one(
|
|
||||||
&self,
|
|
||||||
vars: EvaluationVarsBase<F>,
|
|
||||||
mut yield_constr: StridedConstraintConsumer<F>,
|
|
||||||
) {
|
|
||||||
let coeffs = (0..self.num_points())
|
|
||||||
.map(|i| vars.get_local_ext(self.wires_coeff(i)))
|
|
||||||
.collect();
|
|
||||||
let interpolant = PolynomialCoeffs::new(coeffs);
|
|
||||||
|
|
||||||
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 = interpolant.eval_base(point);
|
|
||||||
yield_constr.many((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);
|
|
||||||
yield_constr.many((evaluation_value - computed_evaluation_value).to_basefield_array());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eval_unfiltered_circuit(
|
|
||||||
&self,
|
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
|
||||||
vars: EvaluationTargets<D>,
|
|
||||||
) -> Vec<ExtensionTarget<D>> {
|
|
||||||
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 interpolant = PolynomialCoeffsExtAlgebraTarget(coeffs);
|
|
||||||
|
|
||||||
let coset = self.coset_ext_circuit(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 computed_value = interpolant.eval_scalar(builder, point);
|
|
||||||
constraints.extend(
|
|
||||||
builder
|
|
||||||
.sub_ext_algebra(value, computed_value)
|
|
||||||
.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(builder, evaluation_point);
|
|
||||||
constraints.extend(
|
|
||||||
builder
|
|
||||||
.sub_ext_algebra(evaluation_value, computed_evaluation_value)
|
|
||||||
.to_ext_target_array(),
|
|
||||||
);
|
|
||||||
|
|
||||||
constraints
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<Box<dyn WitnessGenerator<F>>> {
|
|
||||||
let gen = InterpolationGenerator::<F, D> {
|
|
||||||
row,
|
|
||||||
gate: *self,
|
|
||||||
_phantom: PhantomData,
|
|
||||||
};
|
|
||||||
vec![Box::new(gen.adapter())]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn num_wires(&self) -> usize {
|
|
||||||
self.end()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn num_constants(&self) -> usize {
|
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn degree(&self) -> usize {
|
fn start_values(&self) -> usize {
|
||||||
// The highest power of x is `num_points - 1`, and then multiplication by the coefficient
|
1
|
||||||
// adds 1.
|
|
||||||
self.num_points()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn num_constraints(&self) -> usize {
|
/// Wire indices of the `i`th interpolant value.
|
||||||
// num_points * D constraints to check for consistency between the coefficients and the
|
fn wires_value(&self, i: usize) -> Range<usize> {
|
||||||
// point-value pairs, plus D constraints for the evaluation value.
|
debug_assert!(i < self.num_points());
|
||||||
self.num_points() * D + D
|
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<usize> {
|
||||||
|
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<usize> {
|
||||||
|
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<usize> {
|
||||||
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||||
struct InterpolationGenerator<F: RichField + Extendable<D>, const D: usize> {
|
/// Interpolates a polynomial, whose points are a coset of the multiplicative subgroup with the
|
||||||
row: usize,
|
/// given size, and whose values are given. Returns the evaluation of the interpolant at
|
||||||
gate: HighDegreeInterpolationGate<F, D>,
|
/// `evaluation_point`.
|
||||||
_phantom: PhantomData<F>,
|
pub(crate) fn interpolate_coset<G: InterpolationGate<F, D>>(
|
||||||
}
|
&mut self,
|
||||||
|
subgroup_bits: usize,
|
||||||
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
coset_shift: Target,
|
||||||
for InterpolationGenerator<F, D>
|
values: &[ExtensionTarget<D>],
|
||||||
{
|
evaluation_point: ExtensionTarget<D>,
|
||||||
fn dependencies(&self) -> Vec<Target> {
|
) -> ExtensionTarget<D> {
|
||||||
let local_target = |column| {
|
let gate = G::new(subgroup_bits);
|
||||||
Target::Wire(Wire {
|
let row = self.add_gate(gate, vec![]);
|
||||||
row: self.row,
|
self.connect(coset_shift, Target::wire(row, gate.wire_shift()));
|
||||||
column,
|
for (i, &v) in values.iter().enumerate() {
|
||||||
})
|
self.connect_extension(v, ExtensionTarget::from_range(row, gate.wires_value(i)));
|
||||||
};
|
|
||||||
|
|
||||||
let local_targets = |columns: Range<usize>| columns.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
|
self.connect_extension(
|
||||||
}
|
evaluation_point,
|
||||||
|
ExtensionTarget::from_range(row, gate.wires_evaluation_point()),
|
||||||
|
);
|
||||||
|
|
||||||
fn run_once(&self, witness: &PartitionWitness<F>, out_buffer: &mut GeneratedValues<F>) {
|
ExtensionTarget::from_range(row, gate.wires_evaluation_value())
|
||||||
let local_wire = |column| Wire {
|
|
||||||
row: self.row,
|
|
||||||
column,
|
|
||||||
};
|
|
||||||
|
|
||||||
let get_local_wire = |column| witness.get_wire(local_wire(column));
|
|
||||||
|
|
||||||
let get_local_ext = |wire_range: Range<usize>| {
|
|
||||||
debug_assert_eq!(wire_range.len(), D);
|
|
||||||
let values = wire_range.map(get_local_wire).collect::<Vec<_>>();
|
|
||||||
let arr = values.try_into().unwrap();
|
|
||||||
F::Extension::from_basefield_array(arr)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Compute the interpolant.
|
|
||||||
let points = self.gate.coset(get_local_wire(self.gate.wire_shift()));
|
|
||||||
let points = points
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, point)| (point.into(), get_local_ext(self.gate.wires_value(i))))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
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());
|
|
||||||
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use plonky2_field::goldilocks_field::GoldilocksField;
|
use plonky2_field::extension::FieldExtension;
|
||||||
use plonky2_field::polynomial::PolynomialCoeffs;
|
use plonky2_field::interpolation::interpolant;
|
||||||
use plonky2_field::types::Field;
|
use plonky2_field::types::Field;
|
||||||
|
|
||||||
use crate::gadgets::interpolation::InterpolationGate;
|
use crate::gates::high_degree_interpolation::HighDegreeInterpolationGate;
|
||||||
use crate::gates::gate::Gate;
|
use crate::gates::low_degree_interpolation::LowDegreeInterpolationGate;
|
||||||
use crate::gates::gate_testing::{test_eval_fns, test_low_degree};
|
use crate::iop::witness::PartialWitness;
|
||||||
use crate::gates::interpolation::HighDegreeInterpolationGate;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::hash::hash_types::HashOut;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||||
use crate::plonk::vars::EvaluationVars;
|
use crate::plonk::verifier::verify;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn wire_indices() {
|
fn test_interpolate() -> Result<()> {
|
||||||
let gate = HighDegreeInterpolationGate::<GoldilocksField, 4> {
|
|
||||||
subgroup_bits: 1,
|
|
||||||
_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_shift(), 0);
|
|
||||||
assert_eq!(gate.wires_value(0), 1..5);
|
|
||||||
assert_eq!(gate.wires_value(1), 5..9);
|
|
||||||
assert_eq!(gate.wires_evaluation_point(), 9..13);
|
|
||||||
assert_eq!(gate.wires_evaluation_value(), 13..17);
|
|
||||||
assert_eq!(gate.wires_coeff(0), 17..21);
|
|
||||||
assert_eq!(gate.wires_coeff(1), 21..25);
|
|
||||||
assert_eq!(gate.num_wires(), 25);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn low_degree() {
|
|
||||||
test_low_degree::<GoldilocksField, _, 4>(HighDegreeInterpolationGate::new(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn eval_fns() -> Result<()> {
|
|
||||||
const D: usize = 2;
|
|
||||||
type C = PoseidonGoldilocksConfig;
|
|
||||||
type F = <C as GenericConfig<D>>::F;
|
|
||||||
test_eval_fns::<F, C, _, D>(HighDegreeInterpolationGate::new(2))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_gate_constraint() {
|
|
||||||
const D: usize = 2;
|
const D: usize = 2;
|
||||||
type C = PoseidonGoldilocksConfig;
|
type C = PoseidonGoldilocksConfig;
|
||||||
type F = <C as GenericConfig<D>>::F;
|
type F = <C as GenericConfig<D>>::F;
|
||||||
type FF = <C as GenericConfig<D>>::FE;
|
type FF = <C as GenericConfig<D>>::FE;
|
||||||
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
|
let pw = PartialWitness::new();
|
||||||
|
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||||
|
|
||||||
/// Returns the local wires for an interpolation gate for given coeffs, points and eval point.
|
let subgroup_bits = 2;
|
||||||
fn get_wires(
|
let len = 1 << subgroup_bits;
|
||||||
gate: &HighDegreeInterpolationGate<F, D>,
|
let coset_shift = F::rand();
|
||||||
shift: F,
|
let g = F::primitive_root_of_unity(subgroup_bits);
|
||||||
coeffs: PolynomialCoeffs<FF>,
|
let points = F::cyclic_subgroup_coset_known_order(g, coset_shift, len);
|
||||||
eval_point: FF,
|
let values = FF::rand_vec(len);
|
||||||
) -> Vec<FF> {
|
|
||||||
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.iter().map(|&x| x.into()).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a working row for InterpolationGate.
|
let homogeneous_points = points
|
||||||
let shift = F::rand();
|
.iter()
|
||||||
let coeffs = PolynomialCoeffs::new(vec![FF::rand(), FF::rand()]);
|
.zip(values.iter())
|
||||||
let eval_point = FF::rand();
|
.map(|(&a, &b)| (<FF as FieldExtension<D>>::from_basefield(a), b))
|
||||||
let gate = HighDegreeInterpolationGate::<F, D>::new(1);
|
.collect::<Vec<_>>();
|
||||||
let vars = EvaluationVars {
|
|
||||||
local_constants: &[],
|
|
||||||
local_wires: &get_wires(&gate, shift, coeffs, eval_point),
|
|
||||||
public_inputs_hash: &HashOut::rand(),
|
|
||||||
};
|
|
||||||
|
|
||||||
assert!(
|
let true_interpolant = interpolant(&homogeneous_points);
|
||||||
gate.eval_unfiltered(vars).iter().all(|x| x.is_zero()),
|
|
||||||
"Gate constraints are not satisfied."
|
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::<Vec<_>>();
|
||||||
|
|
||||||
|
let zt = builder.constant_extension(z);
|
||||||
|
|
||||||
|
let eval_hd = builder.interpolate_coset::<HighDegreeInterpolationGate<F, D>>(
|
||||||
|
subgroup_bits,
|
||||||
|
coset_shift_target,
|
||||||
|
&value_targets,
|
||||||
|
zt,
|
||||||
);
|
);
|
||||||
|
let eval_ld = builder.interpolate_coset::<LowDegreeInterpolationGate<F, D>>(
|
||||||
|
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::<C>();
|
||||||
|
let proof = data.prove(pw)?;
|
||||||
|
|
||||||
|
verify(proof, &data.verifier_only, &data.common)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,9 +7,9 @@ use plonky2_field::interpolation::interpolant;
|
|||||||
use plonky2_field::polynomial::PolynomialCoeffs;
|
use plonky2_field::polynomial::PolynomialCoeffs;
|
||||||
use plonky2_field::types::Field;
|
use plonky2_field::types::Field;
|
||||||
|
|
||||||
use crate::gadgets::interpolation::InterpolationGate;
|
|
||||||
use crate::gadgets::polynomial::PolynomialCoeffsExtAlgebraTarget;
|
use crate::gadgets::polynomial::PolynomialCoeffsExtAlgebraTarget;
|
||||||
use crate::gates::gate::Gate;
|
use crate::gates::gate::Gate;
|
||||||
|
use crate::gates::interpolation::InterpolationGate;
|
||||||
use crate::gates::util::StridedConstraintConsumer;
|
use crate::gates::util::StridedConstraintConsumer;
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
|
|||||||
@ -7,6 +7,7 @@ pub mod base_sum;
|
|||||||
pub mod constant;
|
pub mod constant;
|
||||||
pub mod exponentiation;
|
pub mod exponentiation;
|
||||||
pub mod gate;
|
pub mod gate;
|
||||||
|
pub mod high_degree_interpolation;
|
||||||
pub mod interpolation;
|
pub mod interpolation;
|
||||||
pub mod low_degree_interpolation;
|
pub mod low_degree_interpolation;
|
||||||
pub mod multiplication_extension;
|
pub mod multiplication_extension;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user