diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 443bd7c9..aae0963c 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -33,14 +33,13 @@ impl, const D: usize> CircuitBuilder { let g = F::primitive_root_of_unity(arity_bits); let g_inv = g.exp_u64((arity as u64) - 1); - let g_inv_t = self.constant(g_inv); // The evaluation vector needs to be reordered first. let mut evals = evals.to_vec(); reverse_index_bits_in_place(&mut evals); // Want `g^(arity - rev_x_index_within_coset)` as in the out-of-circuit version. Compute it // as `(g^-1)^rev_x_index_within_coset`. - let start = self.exp_from_bits(g_inv_t, x_index_within_coset_bits.iter().rev()); + let start = self.exp_from_bits_const_base(g_inv, x_index_within_coset_bits.iter().rev()); let coset_start = self.mul(start, x); // The answer is gotten by interpolating {(x*g^i, P(x*g^i))} and evaluating at beta. @@ -334,9 +333,8 @@ impl, const D: usize> CircuitBuilder { // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. let (mut subgroup_x, vanish_zeta) = with_context!(self, "compute x from its index", { let g = self.constant(F::coset_shift()); - let phi = self.constant(F::primitive_root_of_unity(n_log)); - - let phi = self.exp_from_bits(phi, x_index_bits.iter().rev()); + let phi = F::primitive_root_of_unity(n_log); + let phi = self.exp_from_bits_const_base(phi, x_index_bits.iter().rev()); let g_ext = self.convert_to_ext(g); let phi_ext = self.convert_to_ext(phi); // `subgroup_x = g*phi, vanish_zeta = g*phi - zeta` diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index da67b131..93387ea7 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -1,7 +1,8 @@ use std::borrow::Borrow; use crate::field::extension_field::Extendable; -use crate::field::field_types::RichField; +use crate::field::field_types::{Field, RichField}; +use crate::gates::arithmetic::ArithmeticExtensionGate; use crate::gates::exponentiation::ExponentiationGate; use crate::iop::target::{BoolTarget, Target}; use crate::plonk::circuit_builder::CircuitBuilder; @@ -115,7 +116,16 @@ impl, const D: usize> CircuitBuilder { /// Exponentiate `base` to the power of `2^power_log`. pub fn exp_power_of_2(&mut self, base: Target, power_log: usize) -> Target { - self.exp_u64(base, 1 << power_log) + if power_log > ArithmeticExtensionGate::::new_from_config(&self.config).num_ops { + // Cheaper to just use `ExponentiateGate`. + return self.exp_u64(base, 1 << power_log); + } + + let mut product = base; + for _ in 0..power_log { + product = self.square(product); + } + product } // TODO: Test @@ -151,6 +161,39 @@ impl, const D: usize> CircuitBuilder { self.exp_from_bits(base, exponent_bits.iter()) } + /// Like `exp_from_bits` but with a constant base. + pub fn exp_from_bits_const_base( + &mut self, + base: F, + exponent_bits: impl IntoIterator>, + ) -> Target { + let base_t = self.constant(base); + let exponent_bits: Vec<_> = exponent_bits.into_iter().map(|b| *b.borrow()).collect(); + + if exponent_bits.len() > ArithmeticExtensionGate::::new_from_config(&self.config).num_ops + { + // Cheaper to just use `ExponentiateGate`. + return self.exp_from_bits(base_t, exponent_bits); + } + + let mut product = self.one(); + for (i, bit) in exponent_bits.iter().enumerate() { + let pow = 1 << i; + // If the bit is on, we multiply product by base^pow. + // We can arithmetize this as: + // product *= 1 + bit (base^pow - 1) + // product = (base^pow - 1) product bit + product + product = self.arithmetic( + base.exp_u64(pow as u64) - F::ONE, + F::ONE, + product, + bit.target, + product, + ) + } + product + } + /// Exponentiate `base` to the power of a known `exponent`. // TODO: Test pub fn exp_u64(&mut self, base: Target, mut exponent: u64) -> Target {