diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 2ae287f4..7c84d765 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -21,8 +21,15 @@ use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; impl, const D: usize> CircuitBuilder { /// Computes P'(x^arity) from {P(x*g^i)}_(i=0..arity), where g is a `arity`-th root of unity /// and P' is the FRI reduced polynomial. - fn compute_evaluation(&mut self) { - todo!(); + fn compute_evaluation( + &mut self, + x: Target, + old_x_index: Target, + arity_bits: usize, + last_evals: &[ExtensionTarget], + beta: ExtensionTarget, + ) -> ExtensionTarget { + todo!() // debug_assert_eq!(last_evals.len(), 1 << arity_bits); // // let g = F::primitive_root_of_unity(arity_bits); @@ -272,10 +279,11 @@ impl, const D: usize> CircuitBuilder { // TODO: The verifier will need to check these constants at some point (out of circuit). let g = self.constant(F::MULTIPLICATIVE_GROUP_GENERATOR); let phi = self.constant(F::primitive_root_of_unity(log_n)); - // TODO: Gate for this. - let reversed_x = self.reverse_bits(x_index, log_n); + + let reversed_x = self.reverse_bits::<2>(x_index, log_n); let phi = self.exp(phi, reversed_x); let mut subgroup_x = self.mul(g, phi); + for (i, &arity_bits) in config.reduction_arity_bits.iter().enumerate() { let arity = 1 << arity_bits; let next_domain_size = domain_size >> arity_bits; diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index 98c4d0d0..6767c70d 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -172,6 +172,21 @@ impl, const D: usize> CircuitBuilder { product } + // TODO: Optimize this, maybe with a new gate. + pub fn exp(&mut self, base: Target, exponent: Target) -> Target { + let mut current = base; + let one = self.one(); + let mut product = one; + let exponent_bits = self.split_le(exponent); + + for bit in exponent_bits.into_iter() { + product = self.mul_many(&[bit, current, product]); + current = self.mul(current, current); + } + + product + } + /// Computes `q = x / y` by witnessing `q` and requiring that `q * y = x`. This can be unsafe in /// some cases, as it allows `0 / 0 = `. pub fn div_unsafe(&mut self, x: Target, y: Target) -> Target { diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index 810939fb..776c3091 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -4,20 +4,17 @@ use crate::field::field::Field; use crate::gates::base_sum::{BaseSplitGenerator, BaseSumGate}; use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::target::Target; -use crate::util::ceil_div_usize; +use crate::util::{ceil_div_usize, log2_strict}; use crate::wire::Wire; use crate::witness::PartialWitness; impl, const D: usize> CircuitBuilder { - /// Split the given element into a list of 11 targets, where each one represents a - /// base-64 limb of the element, with little-endian ordering. + /// Split the given element into a list of targets, where each one represents a + /// base-B limb of the element, with little-endian ordering. pub(crate) fn split_le_base(&mut self, x: Target) -> Vec { let num_limbs = num_limbs_to_check(64, B); let gate = self.add_gate(BaseSumGate::::new(num_limbs), vec![]); - let sum = Target::Wire(Wire { - gate, - input: BaseSumGate::::WIRE_SUM, - }); + let sum = Target::wire(gate, BaseSumGate::::WIRE_SUM); self.route(x, sum); Target::wires_from_range( @@ -38,12 +35,21 @@ impl, const D: usize> CircuitBuilder { self.assert_zero(limbs[i]); } } + + pub(crate) fn reverse_bits(&mut self, x: Target, num_limbs: usize) -> Target { + let gate = self.add_gate(BaseSumGate::::new(num_limbs), vec![]); + let sum = Target::wire(gate, BaseSumGate::::WIRE_SUM); + self.route(x, sum); + + Target::wire(gate, BaseSumGate::::WIRE_REVERSED_SUM) + } } /// Returns `k` such that any number with `k` trailing zeros in base `base` has at least /// `n` trailing zeros in base 2. -fn num_limbs_to_check(n: u32, base: usize) -> usize { - (n as f64 * (2.0_f64.log(base as f64))).ceil() as usize +const fn num_limbs_to_check(n: u32, base: usize) -> usize { + assert_eq!(base % 2, 0, "Base should be even."); + ceil_div_usize(n as usize, base.trailing_zeros() as usize) } #[cfg(test)] diff --git a/src/gadgets/split_join.rs b/src/gadgets/split_join.rs index e65198ec..c8f75efa 100644 --- a/src/gadgets/split_join.rs +++ b/src/gadgets/split_join.rs @@ -1,8 +1,10 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::Extendable; use crate::field::field::Field; +use crate::gates::base_sum::BaseSumGate; use crate::generator::{SimpleGenerator, WitnessGenerator}; use crate::target::Target; +use crate::util::ceil_div_usize; use crate::wire::Wire; use crate::witness::PartialWitness; @@ -21,6 +23,48 @@ impl, const D: usize> CircuitBuilder { }); bit_targets } + + /// Split the given integer into a list of wires, where each one represents a + /// bit of the integer, with little-endian ordering. + /// Verifies that the decomposition is correct by using `k` `BaseSum<2>` gates + /// with `k` such that `k*num_routed_bits>=64`. + pub(crate) fn split_le(&mut self, integer: Target) -> Vec { + let num_limbs = self.config.num_routed_wires - BaseSumGate::WIRE_LIMBS_START; + let k = ceil_div_usize(64, num_limbs); + let gates = (0..k) + .map(|_| self.add_gate_no_constants(BaseSumGate::new(num_limbs))) + .collect::>(); + + let mut bits = Vec::with_capacity(64); + for &gate in &gates { + bits.extend(Target::wires_from_range( + gate, + BaseSumGate::WIRE_LIMBS_START..BaseSumGate::WIRE_LIMBS_START + num_limbs, + )); + } + + let zero = self.zero(); + let mut acc = zero; + for &gate in gates.iter().rev() { + let sum = Target::wire(gate, BaseSumGate::WIRE_SUM); + acc = self.arithmetic( + F::from_canonical_usize(1 << num_limbs), + acc, + zero, + F::ONE, + sum, + ); + } + self.assert_equal(acc, integer); + + self.add_generator(WireSplitGenerator { + integer, + gates, + num_limbs, + }); + + bits + } } /// Generator for a little-endian split. @@ -79,3 +123,39 @@ impl SimpleGenerator for SplitGenerator { result } } + +#[derive(Debug)] +struct WireSplitGenerator { + integer: Target, + gates: Vec, + num_limbs: usize, +} + +impl SimpleGenerator for WireSplitGenerator { + fn dependencies(&self) -> Vec { + vec![self.integer] + } + + fn run_once(&self, witness: &PartialWitness) -> PartialWitness { + let mut integer_value = witness.get_target(self.integer).to_canonical_u64(); + + let mut result = PartialWitness::new(); + for gate in self.gates { + let sum = Target::wire(gate, BaseSumGate::WIRE_SUM); + result.set_target( + sum, + F::from_canonical_u64(integer_value & ((1 << self.num_limbs) - 1)), + ); + integer_value >> self.num_limbs; + } + + debug_assert_eq!( + integer_value, + 0, + "Integer too large to fit in {} many `BaseSumGate`s", + self.gates.len() + ); + + result + } +} diff --git a/src/gates/base_sum.rs b/src/gates/base_sum.rs index f56519e1..fe9068e0 100644 --- a/src/gates/base_sum.rs +++ b/src/gates/base_sum.rs @@ -12,7 +12,7 @@ use crate::wire::Wire; use crate::witness::PartialWitness; use std::ops::Range; -/// A gate which can sum base W limbs. +/// A gate which can sum base W limbs and the reversed limbs. #[derive(Debug)] pub struct BaseSumGate { num_limbs: usize, @@ -24,7 +24,8 @@ impl BaseSumGate { } pub const WIRE_SUM: usize = 0; - pub const WIRE_LIMBS_START: usize = 1; + pub const WIRE_REVERSED_SUM: usize = 1; + pub const WIRE_LIMBS_START: usize = 2; /// Returns the index of the `i`th limb wire. pub fn limbs(&self) -> Range { @@ -39,9 +40,13 @@ impl, const D: usize, const B: usize> Gate for BaseSumGat fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { let sum = vars.local_wires[Self::WIRE_SUM]; - let limbs = vars.local_wires[self.limbs()].to_vec(); + 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::Extension::from_canonical_usize(B)); - let mut constraints = vec![computed_sum - sum]; + limbs.reverse(); + let computed_reversed_sum = + reduce_with_powers(&limbs, F::Extension::from_canonical_usize(B)); + let mut constraints = vec![computed_sum - sum, computed_reversed_sum - reversed_sum]; for limb in limbs { constraints.push( (0..B) @@ -59,10 +64,15 @@ impl, const D: usize, const B: usize> Gate for BaseSumGat ) -> Vec> { let base = builder.constant(F::from_canonical_usize(B)); let sum = vars.local_wires[Self::WIRE_SUM]; - let limbs = vars.local_wires[self.limbs()].to_vec(); - let computed_sum = - reduce_with_powers_recursive(builder, &vars.local_wires[self.limbs()], base); - let mut constraints = vec![builder.sub_extension(computed_sum, 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_recursive(builder, &limbs, base); + limbs.reverse(); + let reversed_computed_sum = reduce_with_powers_recursive(builder, &limbs, base); + let mut constraints = vec![ + builder.sub_extension(computed_sum, sum), + builder.sub_extension(reversed_computed_sum, computed_sum), + ]; for limb in limbs { constraints.push({ let mut acc = builder.one_extension(); @@ -89,20 +99,23 @@ impl, const D: usize, const B: usize> Gate for BaseSumGat vec![Box::new(gen)] } + // 2 for the sum and reversed sum, then `num_limbs` for the limbs. fn num_wires(&self) -> usize { - self.num_limbs + 1 + self.num_limbs + 2 } fn num_constants(&self) -> usize { 0 } + // Bounded by the range-check (x-0)*(x-1)*...*(x-B+1). fn degree(&self) -> usize { B } + // 2 for checking the sum and reversed sum, then `num_limbs` for range-checking the limbs. fn num_constraints(&self) -> usize { - 1 + self.num_limbs + 2 + self.num_limbs } } @@ -121,26 +134,29 @@ impl SimpleGenerator for BaseSplitGenerator { } fn run_once(&self, witness: &PartialWitness) -> PartialWitness { - let mut sum_value = witness - .get_target(Target::Wire(Wire { - gate: self.gate_index, - input: BaseSumGate::::WIRE_SUM, - })) + let sum_value = witness + .get_target(Target::wire(self.gate_index, BaseSumGate::::WIRE_SUM)) .to_canonical_u64() as usize; let limbs = (BaseSumGate::::WIRE_LIMBS_START ..BaseSumGate::::WIRE_LIMBS_START + self.num_limbs) - .map(|i| { - Target::Wire(Wire { - gate: self.gate_index, - input: i, - }) - }); + .map(|i| Target::wire(self.gate_index, i)); + let limbs_value = (0..self.num_limbs) + .scan(sum_value, |acc, _| { + let tmp = *acc % B; + *acc /= B; + Some(tmp) + }) + .collect::>(); + + let reversed_sum = limbs_value.iter().rev().fold(0, |acc, &x| acc * B + x); let mut result = PartialWitness::new(); - for b in limbs { - let b_value = sum_value % B; + result.set_target( + Target::wire(self.gate_index, BaseSumGate::::WIRE_REVERSED_SUM), + F::from_canonical_usize(reversed_sum), + ); + for (b, b_value) in limbs.zip(limbs_value) { result.set_target(b, F::from_canonical_usize(b_value)); - sum_value /= B; } debug_assert_eq!( diff --git a/src/util/mod.rs b/src/util/mod.rs index 7ab9bc5b..09bd4e72 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -7,7 +7,7 @@ pub(crate) fn bits_u64(n: u64) -> usize { (64 - n.leading_zeros()) as usize } -pub(crate) fn ceil_div_usize(a: usize, b: usize) -> usize { +pub(crate) const fn ceil_div_usize(a: usize, b: usize) -> usize { (a + b - 1) / b }