diff --git a/src/gates/arithmetic.rs b/src/gates/arithmetic.rs index cf39e09b..a3739ee5 100644 --- a/src/gates/arithmetic.rs +++ b/src/gates/arithmetic.rs @@ -2,11 +2,11 @@ use std::ops::Range; use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; -use crate::field::extension_field::Extendable; +use crate::field::extension_field::{Extendable, FieldExtension}; use crate::gates::gate::{Gate, GateRef}; use crate::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; use crate::target::Target; -use crate::vars::{EvaluationTargets, EvaluationVars}; +use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; use crate::witness::PartialWitness; /// A gate which can a linear combination `c0*x*y+c1*z` twice with the same `x`. @@ -74,6 +74,31 @@ impl, const D: usize> Gate for ArithmeticExtensionGate constraints } + fn eval_unfiltered_base(&self, vars: EvaluationVarsBase) -> Vec { + let const_0 = vars.local_constants[0]; + let const_1 = vars.local_constants[1]; + + let first_multiplicand_0 = vars.get_local_ext(Self::wires_first_multiplicand_0()); + let first_multiplicand_1 = vars.get_local_ext(Self::wires_first_multiplicand_1()); + let first_addend = vars.get_local_ext(Self::wires_first_addend()); + let second_multiplicand_0 = vars.get_local_ext(Self::wires_second_multiplicand_0()); + let second_multiplicand_1 = vars.get_local_ext(Self::wires_second_multiplicand_1()); + let second_addend = vars.get_local_ext(Self::wires_second_addend()); + let first_output = vars.get_local_ext(Self::wires_first_output()); + let second_output = vars.get_local_ext(Self::wires_second_output()); + + let first_computed_output = first_multiplicand_0 * first_multiplicand_1 * const_0.into() + + first_addend * const_1.into(); + let second_computed_output = second_multiplicand_0 * second_multiplicand_1 * const_0.into() + + second_addend * const_1.into(); + + let mut constraints = (first_output - first_computed_output) + .to_basefield_array() + .to_vec(); + constraints.extend((second_output - second_computed_output).to_basefield_array()); + constraints + } + fn eval_unfiltered_recursively( &self, builder: &mut CircuitBuilder, diff --git a/src/gates/base_sum.rs b/src/gates/base_sum.rs index 8ad189ee..b6645959 100644 --- a/src/gates/base_sum.rs +++ b/src/gates/base_sum.rs @@ -8,7 +8,7 @@ use crate::gates::gate::{Gate, GateRef}; use crate::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; use crate::plonk_common::{reduce_with_powers, reduce_with_powers_recursive}; use crate::target::Target; -use crate::vars::{EvaluationTargets, EvaluationVars}; +use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; use crate::witness::PartialWitness; /// A gate which can decompose a number into base B little-endian limbs, @@ -57,6 +57,20 @@ impl, const D: usize, const B: usize> Gate for BaseSumGat constraints } + fn eval_unfiltered_base(&self, vars: EvaluationVarsBase) -> Vec { + let sum = vars.local_wires[Self::WIRE_SUM]; + let reversed_sum = vars.local_wires[Self::WIRE_REVERSED_SUM]; + let mut limbs = vars.local_wires[self.limbs()].to_vec(); + let computed_sum = reduce_with_powers(&limbs, F::from_canonical_usize(B)); + limbs.reverse(); + let computed_reversed_sum = reduce_with_powers(&limbs, F::from_canonical_usize(B)); + let mut constraints = vec![computed_sum - sum, computed_reversed_sum - reversed_sum]; + for limb in limbs { + constraints.push((0..B).map(|i| limb - F::from_canonical_usize(i)).product()); + } + constraints + } + fn eval_unfiltered_recursively( &self, builder: &mut CircuitBuilder, diff --git a/src/gates/constant.rs b/src/gates/constant.rs index 4049d058..4a5c4373 100644 --- a/src/gates/constant.rs +++ b/src/gates/constant.rs @@ -5,7 +5,7 @@ use crate::field::field::Field; use crate::gates::gate::{Gate, GateRef}; use crate::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; use crate::target::Target; -use crate::vars::{EvaluationTargets, EvaluationVars}; +use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; use crate::wire::Wire; use crate::witness::PartialWitness; @@ -33,6 +33,12 @@ impl, const D: usize> Gate for ConstantGate { vec![output - input] } + fn eval_unfiltered_base(&self, vars: EvaluationVarsBase) -> Vec { + let input = vars.local_constants[Self::CONST_INPUT]; + let output = vars.local_wires[Self::WIRE_OUTPUT]; + vec![output - input] + } + fn eval_unfiltered_recursively( &self, builder: &mut CircuitBuilder, diff --git a/src/gates/gmimc.rs b/src/gates/gmimc.rs index 0404884b..12e43b3c 100644 --- a/src/gates/gmimc.rs +++ b/src/gates/gmimc.rs @@ -8,7 +8,7 @@ use crate::gates::gate::{Gate, GateRef}; use crate::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; use crate::gmimc::gmimc_automatic_constants; use crate::target::Target; -use crate::vars::{EvaluationTargets, EvaluationVars}; +use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; use crate::wire::Wire; use crate::witness::PartialWitness; @@ -121,6 +121,55 @@ impl, const D: usize, const R: usize> Gate for GMiMCGate< constraints } + fn eval_unfiltered_base(&self, vars: EvaluationVarsBase) -> Vec { + let mut constraints = Vec::with_capacity(self.num_constraints()); + + // Assert that `swap` is binary. + let swap = vars.local_wires[Self::WIRE_SWAP]; + constraints.push(swap * (swap - F::ONE)); + + let old_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_OLD]; + let new_index_acc = vars.local_wires[Self::WIRE_INDEX_ACCUMULATOR_NEW]; + let computed_new_index_acc = F::TWO * old_index_acc + swap; + constraints.push(computed_new_index_acc - new_index_acc); + + let mut state = Vec::with_capacity(12); + for i in 0..4 { + let a = vars.local_wires[i]; + let b = vars.local_wires[i + 4]; + state.push(a + swap * (b - a)); + } + for i in 0..4 { + let a = vars.local_wires[i + 4]; + let b = vars.local_wires[i]; + state.push(a + swap * (b - a)); + } + for i in 8..12 { + state.push(vars.local_wires[i]); + } + + // Value that is implicitly added to each element. + // See https://affine.group/2020/02/starkware-challenge + let mut addition_buffer = F::ZERO; + + for r in 0..R { + let active = r % W; + let cubing_input = state[active] + addition_buffer + self.constants[r].into(); + let cubing_input_wire = vars.local_wires[Self::wire_cubing_input(r)]; + constraints.push(cubing_input - cubing_input_wire); + let f = cubing_input_wire.cube(); + addition_buffer += f; + state[active] -= f; + } + + for i in 0..W { + state[i] += addition_buffer; + constraints.push(state[i] - vars.local_wires[Self::wire_output(i)]); + } + + constraints + } + fn eval_unfiltered_recursively( &self, builder: &mut CircuitBuilder, diff --git a/src/gates/insertion.rs b/src/gates/insertion.rs index 1bc0b454..4bfe97a9 100644 --- a/src/gates/insertion.rs +++ b/src/gates/insertion.rs @@ -9,7 +9,7 @@ use crate::field::field::Field; use crate::gates::gate::{Gate, GateRef}; use crate::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; use crate::target::Target; -use crate::vars::{EvaluationTargets, EvaluationVars}; +use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; use crate::wire::Wire; use crate::witness::PartialWitness; @@ -114,6 +114,44 @@ impl, const D: usize> Gate for InsertionGate { constraints } + fn eval_unfiltered_base(&self, vars: EvaluationVarsBase) -> Vec { + let insertion_index = vars.local_wires[self.wires_insertion_index()]; + let list_items = (0..self.vec_size) + .map(|i| vars.get_local_ext(self.wires_original_list_item(i))) + .collect::>(); + let output_list_items = (0..=self.vec_size) + .map(|i| vars.get_local_ext(self.wires_output_list_item(i))) + .collect::>(); + let element_to_insert = vars.get_local_ext(self.wires_element_to_insert()); + + let mut constraints = Vec::new(); + let mut already_inserted = F::ZERO; + for r in 0..=self.vec_size { + let cur_index = F::from_canonical_usize(r); + let difference = cur_index - insertion_index; + let equality_dummy = vars.local_wires[self.wires_equality_dummy_for_round_r(r)]; + let insert_here = vars.local_wires[self.wires_insert_here_for_round_r(r)]; + + // The two equality constraints. + constraints.push(difference * equality_dummy - (F::ONE - insert_here)); + constraints.push(insert_here * difference); + + let mut new_item = element_to_insert * insert_here.into(); + if r > 0 { + new_item += list_items[r - 1] * already_inserted.into(); + } + already_inserted += insert_here; + if r < self.vec_size { + new_item += list_items[r] * (F::ONE - already_inserted).into(); + } + + // Output constraint. + constraints.extend((new_item - output_list_items[r]).to_basefield_array()); + } + + constraints + } + fn eval_unfiltered_recursively( &self, builder: &mut CircuitBuilder, diff --git a/src/gates/interpolation.rs b/src/gates/interpolation.rs index 17d34e3a..ed04c322 100644 --- a/src/gates/interpolation.rs +++ b/src/gates/interpolation.rs @@ -10,8 +10,9 @@ use crate::field::interpolation::interpolant; use crate::gadgets::polynomial::PolynomialCoeffsExtAlgebraTarget; use crate::gates::gate::{Gate, GateRef}; use crate::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; +use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; -use crate::vars::{EvaluationTargets, EvaluationVars}; +use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; use crate::wire::Wire; use crate::witness::PartialWitness; @@ -121,6 +122,29 @@ impl, const D: usize> Gate for InterpolationGate { 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 interpolant = PolynomialCoeffs::new(coeffs); + + for i in 0..self.num_points { + let point = vars.local_wires[self.wire_point(i)]; + let value = vars.get_local_ext(self.wires_value(i)); + let computed_value = interpolant.eval(point.into()); + 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 + } + fn eval_unfiltered_recursively( &self, builder: &mut CircuitBuilder, diff --git a/src/gates/noop.rs b/src/gates/noop.rs index a12df932..c27b22bf 100644 --- a/src/gates/noop.rs +++ b/src/gates/noop.rs @@ -3,7 +3,7 @@ use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::gates::gate::{Gate, GateRef}; use crate::generator::WitnessGenerator; -use crate::vars::{EvaluationTargets, EvaluationVars}; +use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; /// A gate which does nothing. pub struct NoopGate; @@ -23,6 +23,10 @@ impl, const D: usize> Gate for NoopGate { Vec::new() } + fn eval_unfiltered_base(&self, _vars: EvaluationVarsBase) -> Vec { + Vec::new() + } + fn eval_unfiltered_recursively( &self, _builder: &mut CircuitBuilder, diff --git a/src/gates/public_input.rs b/src/gates/public_input.rs index a86b78d5..e1ce9271 100644 --- a/src/gates/public_input.rs +++ b/src/gates/public_input.rs @@ -5,7 +5,7 @@ use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::gates::gate::{Gate, GateRef}; use crate::generator::WitnessGenerator; -use crate::vars::{EvaluationTargets, EvaluationVars}; +use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; /// A gate whose first four wires will be equal to a hash of public inputs. pub struct PublicInputGate; @@ -32,6 +32,13 @@ impl, const D: usize> Gate for PublicInputGate { .collect() } + fn eval_unfiltered_base(&self, vars: EvaluationVarsBase) -> Vec { + Self::wires_public_inputs_hash() + .zip(vars.public_inputs_hash.elements) + .map(|(wire, hash_part)| vars.local_wires[wire] - hash_part) + .collect() + } + fn eval_unfiltered_recursively( &self, builder: &mut CircuitBuilder, diff --git a/src/vars.rs b/src/vars.rs index 8e98d41f..66ce2efb 100644 --- a/src/vars.rs +++ b/src/vars.rs @@ -3,7 +3,7 @@ use std::ops::Range; use crate::field::extension_field::algebra::ExtensionAlgebra; use crate::field::extension_field::target::{ExtensionAlgebraTarget, ExtensionTarget}; -use crate::field::extension_field::Extendable; +use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; use crate::proof::{Hash, HashTarget}; @@ -37,6 +37,15 @@ impl<'a, F: Extendable, const D: usize> EvaluationVars<'a, F, D> { } impl<'a, F: Field> EvaluationVarsBase<'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) + } + pub fn remove_prefix(&mut self, prefix: &[bool]) { self.local_constants = &self.local_constants[prefix.len()..]; }