mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-07 08:13:11 +00:00
Added in-circuit reverse_bits and exp.
This commit is contained in:
parent
9adf5bb43f
commit
89761ef22a
@ -21,8 +21,15 @@ use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place};
|
||||
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// 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<D>],
|
||||
beta: ExtensionTarget<D>,
|
||||
) -> ExtensionTarget<D> {
|
||||
todo!()
|
||||
// debug_assert_eq!(last_evals.len(), 1 << arity_bits);
|
||||
//
|
||||
// let g = F::primitive_root_of_unity(arity_bits);
|
||||
@ -272,10 +279,11 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
// 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;
|
||||
|
||||
@ -172,6 +172,21 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
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 = <anything>`.
|
||||
pub fn div_unsafe(&mut self, x: Target, y: Target) -> Target {
|
||||
|
||||
@ -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<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// 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<const B: usize>(&mut self, x: Target) -> Vec<Target> {
|
||||
let num_limbs = num_limbs_to_check(64, B);
|
||||
let gate = self.add_gate(BaseSumGate::<B>::new(num_limbs), vec![]);
|
||||
let sum = Target::Wire(Wire {
|
||||
gate,
|
||||
input: BaseSumGate::<B>::WIRE_SUM,
|
||||
});
|
||||
let sum = Target::wire(gate, BaseSumGate::<B>::WIRE_SUM);
|
||||
self.route(x, sum);
|
||||
|
||||
Target::wires_from_range(
|
||||
@ -38,12 +35,21 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
self.assert_zero(limbs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn reverse_bits<const B: usize>(&mut self, x: Target, num_limbs: usize) -> Target {
|
||||
let gate = self.add_gate(BaseSumGate::<B>::new(num_limbs), vec![]);
|
||||
let sum = Target::wire(gate, BaseSumGate::<B>::WIRE_SUM);
|
||||
self.route(x, sum);
|
||||
|
||||
Target::wire(gate, BaseSumGate::<B>::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)]
|
||||
|
||||
@ -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<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
});
|
||||
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<Target> {
|
||||
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::<Vec<_>>();
|
||||
|
||||
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<F: Field> SimpleGenerator<F> for SplitGenerator {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct WireSplitGenerator {
|
||||
integer: Target,
|
||||
gates: Vec<usize>,
|
||||
num_limbs: usize,
|
||||
}
|
||||
|
||||
impl<F: Field> SimpleGenerator<F> for WireSplitGenerator {
|
||||
fn dependencies(&self) -> Vec<Target> {
|
||||
vec![self.integer]
|
||||
}
|
||||
|
||||
fn run_once(&self, witness: &PartialWitness<F>) -> PartialWitness<F> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<const B: usize> {
|
||||
num_limbs: usize,
|
||||
@ -24,7 +24,8 @@ impl<const B: usize> BaseSumGate<B> {
|
||||
}
|
||||
|
||||
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<usize> {
|
||||
@ -39,9 +40,13 @@ impl<F: Extendable<D>, const D: usize, const B: usize> Gate<F, D> for BaseSumGat
|
||||
|
||||
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
|
||||
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<F: Extendable<D>, const D: usize, const B: usize> Gate<F, D> for BaseSumGat
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
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<F: Extendable<D>, const D: usize, const B: usize> Gate<F, D> 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<F: Field, const B: usize> SimpleGenerator<F> for BaseSplitGenerator<B> {
|
||||
}
|
||||
|
||||
fn run_once(&self, witness: &PartialWitness<F>) -> PartialWitness<F> {
|
||||
let mut sum_value = witness
|
||||
.get_target(Target::Wire(Wire {
|
||||
gate: self.gate_index,
|
||||
input: BaseSumGate::<B>::WIRE_SUM,
|
||||
}))
|
||||
let sum_value = witness
|
||||
.get_target(Target::wire(self.gate_index, BaseSumGate::<B>::WIRE_SUM))
|
||||
.to_canonical_u64() as usize;
|
||||
let limbs = (BaseSumGate::<B>::WIRE_LIMBS_START
|
||||
..BaseSumGate::<B>::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::<Vec<_>>();
|
||||
|
||||
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::<B>::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!(
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user