mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-05 07:13:08 +00:00
Different implementation of RandomAccessGate (#360)
The previous code used an equality test for each index. This variant uses a "MUX tree" instead. If we imagine the items as being the leaves of a binary tree, we can compute the `i`th item by splitting `i` into bits, then performing a "select" operation for each node. The bit used in each select is based on the height of the associated node. This uses fewer wires and is cheaper to evaluate, saving 31 wires in the recursion circuit. A potential disadvantage is that this uses higher-degree constraints (degree 4 with our params), but I don't think this is much of a concern for us since we use a degree-9 constraint system.
This commit is contained in:
parent
9aafa447f8
commit
8ea6c4d392
@ -49,12 +49,12 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Make sure we have enough wires and routed wires to do the FRI checks efficiently. This check
|
||||
/// isn't required -- without it we'd get errors elsewhere in the stack -- but just gives more
|
||||
/// helpful errors.
|
||||
fn check_recursion_config(&self, max_fri_arity: usize) {
|
||||
fn check_recursion_config(&self, max_fri_arity_bits: usize) {
|
||||
let random_access = RandomAccessGate::<F, D>::new_from_config(
|
||||
&self.config,
|
||||
max_fri_arity.max(1 << self.config.cap_height),
|
||||
max_fri_arity_bits.max(self.config.cap_height),
|
||||
);
|
||||
let interpolation_gate = InterpolationGate::<F, D>::new(log2_strict(max_fri_arity));
|
||||
let interpolation_gate = InterpolationGate::<F, D>::new(max_fri_arity_bits);
|
||||
|
||||
let min_wires = random_access
|
||||
.num_wires()
|
||||
@ -65,15 +65,15 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
|
||||
assert!(
|
||||
self.config.num_wires >= min_wires,
|
||||
"To efficiently perform FRI checks with an arity of {}, at least {} wires are needed. Consider reducing arity.",
|
||||
max_fri_arity,
|
||||
"To efficiently perform FRI checks with an arity of 2^{}, at least {} wires are needed. Consider reducing arity.",
|
||||
max_fri_arity_bits,
|
||||
min_wires
|
||||
);
|
||||
|
||||
assert!(
|
||||
self.config.num_routed_wires >= min_routed_wires,
|
||||
"To efficiently perform FRI checks with an arity of {}, at least {} routed wires are needed. Consider reducing arity.",
|
||||
max_fri_arity,
|
||||
"To efficiently perform FRI checks with an arity of 2^{}, at least {} routed wires are needed. Consider reducing arity.",
|
||||
max_fri_arity_bits,
|
||||
min_routed_wires
|
||||
);
|
||||
}
|
||||
@ -107,8 +107,8 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
) {
|
||||
let config = &common_data.config;
|
||||
|
||||
if let Some(max_arity) = common_data.fri_params.max_arity() {
|
||||
self.check_recursion_config(max_arity);
|
||||
if let Some(max_arity_bits) = common_data.fri_params.max_arity_bits() {
|
||||
self.check_recursion_config(max_arity_bits);
|
||||
}
|
||||
|
||||
debug_assert_eq!(
|
||||
|
||||
@ -4,18 +4,20 @@ use crate::field::field_types::RichField;
|
||||
use crate::gates::random_access::RandomAccessGate;
|
||||
use crate::iop::target::Target;
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
use crate::util::log2_strict;
|
||||
|
||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Checks that a `Target` matches a vector at a non-deterministic index.
|
||||
/// Note: `access_index` is not range-checked.
|
||||
pub fn random_access(&mut self, access_index: Target, claimed_element: Target, v: Vec<Target>) {
|
||||
let vec_size = v.len();
|
||||
let bits = log2_strict(vec_size);
|
||||
debug_assert!(vec_size > 0);
|
||||
if vec_size == 1 {
|
||||
return self.connect(claimed_element, v[0]);
|
||||
}
|
||||
let (gate_index, copy) = self.find_random_access_gate(vec_size);
|
||||
let dummy_gate = RandomAccessGate::<F, D>::new_from_config(&self.config, vec_size);
|
||||
let (gate_index, copy) = self.find_random_access_gate(bits);
|
||||
let dummy_gate = RandomAccessGate::<F, D>::new_from_config(&self.config, bits);
|
||||
|
||||
v.iter().enumerate().for_each(|(i, &val)| {
|
||||
self.connect(
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::field::extension_field::target::ExtensionTarget;
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::field_types::{Field, RichField};
|
||||
@ -15,75 +17,64 @@ use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||
/// A gate for checking that a particular element of a list matches a given value.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) struct RandomAccessGate<F: RichField + Extendable<D>, const D: usize> {
|
||||
pub vec_size: usize,
|
||||
pub bits: usize,
|
||||
pub num_copies: usize,
|
||||
_phantom: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<F: RichField + Extendable<D>, const D: usize> RandomAccessGate<F, D> {
|
||||
pub fn new(num_copies: usize, vec_size: usize) -> Self {
|
||||
fn new(num_copies: usize, bits: usize) -> Self {
|
||||
Self {
|
||||
vec_size,
|
||||
bits,
|
||||
num_copies,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_config(config: &CircuitConfig, vec_size: usize) -> Self {
|
||||
let num_copies = Self::max_num_copies(config.num_routed_wires, config.num_wires, vec_size);
|
||||
Self::new(num_copies, vec_size)
|
||||
pub fn new_from_config(config: &CircuitConfig, bits: usize) -> Self {
|
||||
let vec_size = 1 << bits;
|
||||
// Need `(2 + vec_size) * num_copies` routed wires
|
||||
let max_copies = (config.num_routed_wires / (2 + vec_size)).min(
|
||||
// Need `(2 + vec_size + bits) * num_copies` wires
|
||||
config.num_wires / (2 + vec_size + bits),
|
||||
);
|
||||
Self::new(max_copies, bits)
|
||||
}
|
||||
|
||||
pub fn max_num_copies(num_routed_wires: usize, num_wires: usize, vec_size: usize) -> usize {
|
||||
// Need `(2 + vec_size) * num_copies` routed wires
|
||||
(num_routed_wires / (2 + vec_size)).min(
|
||||
// Need `(2 + 3*vec_size) * num_copies` wires
|
||||
num_wires / (2 + 3 * vec_size),
|
||||
)
|
||||
fn vec_size(&self) -> usize {
|
||||
1 << self.bits
|
||||
}
|
||||
|
||||
pub fn wire_access_index(&self, copy: usize) -> usize {
|
||||
debug_assert!(copy < self.num_copies);
|
||||
(2 + self.vec_size) * copy
|
||||
(2 + self.vec_size()) * copy
|
||||
}
|
||||
|
||||
pub fn wire_claimed_element(&self, copy: usize) -> usize {
|
||||
debug_assert!(copy < self.num_copies);
|
||||
(2 + self.vec_size) * copy + 1
|
||||
(2 + self.vec_size()) * copy + 1
|
||||
}
|
||||
|
||||
pub fn wire_list_item(&self, i: usize, copy: usize) -> usize {
|
||||
debug_assert!(i < self.vec_size);
|
||||
debug_assert!(i < self.vec_size());
|
||||
debug_assert!(copy < self.num_copies);
|
||||
(2 + self.vec_size) * copy + 2 + i
|
||||
(2 + self.vec_size()) * copy + 2 + i
|
||||
}
|
||||
|
||||
fn start_of_intermediate_wires(&self) -> usize {
|
||||
(2 + self.vec_size) * self.num_copies
|
||||
(2 + self.vec_size()) * self.num_copies
|
||||
}
|
||||
|
||||
pub(crate) fn num_routed_wires(&self) -> usize {
|
||||
self.start_of_intermediate_wires()
|
||||
}
|
||||
|
||||
/// An intermediate wire for a dummy variable used to show equality.
|
||||
/// The prover sets this to 1/(x-y) if x != y, or to an arbitrary value if
|
||||
/// x == y.
|
||||
pub fn wire_equality_dummy_for_index(&self, i: usize, copy: usize) -> usize {
|
||||
debug_assert!(i < self.vec_size);
|
||||
/// An intermediate wire where the prover gives the (purported) binary decomposition of the
|
||||
/// index.
|
||||
pub fn wire_bit(&self, i: usize, copy: usize) -> usize {
|
||||
debug_assert!(i < self.bits);
|
||||
debug_assert!(copy < self.num_copies);
|
||||
self.start_of_intermediate_wires() + copy * self.vec_size + i
|
||||
}
|
||||
|
||||
/// An intermediate wire for the "index_matches" variable (1 if the current index is the index at
|
||||
/// which to compare, 0 otherwise).
|
||||
pub fn wire_index_matches_for_index(&self, i: usize, copy: usize) -> usize {
|
||||
debug_assert!(i < self.vec_size);
|
||||
debug_assert!(copy < self.num_copies);
|
||||
self.start_of_intermediate_wires()
|
||||
+ self.vec_size * self.num_copies
|
||||
+ self.vec_size * copy
|
||||
+ i
|
||||
self.start_of_intermediate_wires() + copy * self.bits + i
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,23 +88,38 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for RandomAccessGa
|
||||
|
||||
for copy in 0..self.num_copies {
|
||||
let access_index = vars.local_wires[self.wire_access_index(copy)];
|
||||
let list_items = (0..self.vec_size)
|
||||
let mut list_items = (0..self.vec_size())
|
||||
.map(|i| vars.local_wires[self.wire_list_item(i, copy)])
|
||||
.collect::<Vec<_>>();
|
||||
let claimed_element = vars.local_wires[self.wire_claimed_element(copy)];
|
||||
let bits = (0..self.bits)
|
||||
.map(|i| vars.local_wires[self.wire_bit(i, copy)])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for i in 0..self.vec_size {
|
||||
let cur_index = F::Extension::from_canonical_usize(i);
|
||||
let difference = cur_index - access_index;
|
||||
let equality_dummy = vars.local_wires[self.wire_equality_dummy_for_index(i, copy)];
|
||||
let index_matches = vars.local_wires[self.wire_index_matches_for_index(i, copy)];
|
||||
|
||||
// The two index equality constraints.
|
||||
constraints.push(difference * equality_dummy - (F::Extension::ONE - index_matches));
|
||||
constraints.push(index_matches * difference);
|
||||
// Value equality constraint.
|
||||
constraints.push((list_items[i] - claimed_element) * index_matches);
|
||||
// Assert that each bit wire value is indeed boolean.
|
||||
for &b in &bits {
|
||||
constraints.push(b * (b - F::Extension::ONE));
|
||||
}
|
||||
|
||||
// Assert that the binary decomposition was correct.
|
||||
let reconstructed_index = bits
|
||||
.iter()
|
||||
.rev()
|
||||
.fold(F::Extension::ZERO, |acc, &b| acc.double() + b);
|
||||
constraints.push(reconstructed_index - access_index);
|
||||
|
||||
// Repeatedly fold the list, selecting the left or right item from each pair based on
|
||||
// the corresponding bit.
|
||||
for b in bits {
|
||||
list_items = list_items
|
||||
.iter()
|
||||
.tuples()
|
||||
.map(|(&x, &y)| x + b * (y - x))
|
||||
.collect()
|
||||
}
|
||||
|
||||
debug_assert_eq!(list_items.len(), 1);
|
||||
constraints.push(list_items[0] - claimed_element);
|
||||
}
|
||||
|
||||
constraints
|
||||
@ -124,23 +130,35 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for RandomAccessGa
|
||||
|
||||
for copy in 0..self.num_copies {
|
||||
let access_index = vars.local_wires[self.wire_access_index(copy)];
|
||||
let list_items = (0..self.vec_size)
|
||||
let mut list_items = (0..self.vec_size())
|
||||
.map(|i| vars.local_wires[self.wire_list_item(i, copy)])
|
||||
.collect::<Vec<_>>();
|
||||
let claimed_element = vars.local_wires[self.wire_claimed_element(copy)];
|
||||
let bits = (0..self.bits)
|
||||
.map(|i| vars.local_wires[self.wire_bit(i, copy)])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for i in 0..self.vec_size {
|
||||
let cur_index = F::from_canonical_usize(i);
|
||||
let difference = cur_index - access_index;
|
||||
let equality_dummy = vars.local_wires[self.wire_equality_dummy_for_index(i, copy)];
|
||||
let index_matches = vars.local_wires[self.wire_index_matches_for_index(i, copy)];
|
||||
|
||||
// The two index equality constraints.
|
||||
constraints.push(difference * equality_dummy - (F::ONE - index_matches));
|
||||
constraints.push(index_matches * difference);
|
||||
// Value equality constraint.
|
||||
constraints.push((list_items[i] - claimed_element) * index_matches);
|
||||
// Assert that each bit wire value is indeed boolean.
|
||||
for &b in &bits {
|
||||
constraints.push(b * (b - F::ONE));
|
||||
}
|
||||
|
||||
// Assert that the binary decomposition was correct.
|
||||
let reconstructed_index = bits.iter().rev().fold(F::ZERO, |acc, &b| acc.double() + b);
|
||||
constraints.push(reconstructed_index - access_index);
|
||||
|
||||
// Repeatedly fold the list, selecting the left or right item from each pair based on
|
||||
// the corresponding bit.
|
||||
for b in bits {
|
||||
list_items = list_items
|
||||
.iter()
|
||||
.tuples()
|
||||
.map(|(&x, &y)| x + b * (y - x))
|
||||
.collect()
|
||||
}
|
||||
|
||||
debug_assert_eq!(list_items.len(), 1);
|
||||
constraints.push(list_items[0] - claimed_element);
|
||||
}
|
||||
|
||||
constraints
|
||||
@ -151,36 +169,44 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for RandomAccessGa
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
vars: EvaluationTargets<D>,
|
||||
) -> Vec<ExtensionTarget<D>> {
|
||||
let zero = builder.zero_extension();
|
||||
let two = builder.two_extension();
|
||||
let mut constraints = Vec::with_capacity(self.num_constraints());
|
||||
|
||||
for copy in 0..self.num_copies {
|
||||
let access_index = vars.local_wires[self.wire_access_index(copy)];
|
||||
let list_items = (0..self.vec_size)
|
||||
let mut list_items = (0..self.vec_size())
|
||||
.map(|i| vars.local_wires[self.wire_list_item(i, copy)])
|
||||
.collect::<Vec<_>>();
|
||||
let claimed_element = vars.local_wires[self.wire_claimed_element(copy)];
|
||||
let bits = (0..self.bits)
|
||||
.map(|i| vars.local_wires[self.wire_bit(i, copy)])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for i in 0..self.vec_size {
|
||||
let cur_index_ext = F::Extension::from_canonical_usize(i);
|
||||
let cur_index = builder.constant_extension(cur_index_ext);
|
||||
let difference = builder.sub_extension(cur_index, access_index);
|
||||
let equality_dummy = vars.local_wires[self.wire_equality_dummy_for_index(i, copy)];
|
||||
let index_matches = vars.local_wires[self.wire_index_matches_for_index(i, copy)];
|
||||
|
||||
let one = builder.one_extension();
|
||||
let not_index_matches = builder.sub_extension(one, index_matches);
|
||||
let first_equality_constraint =
|
||||
builder.mul_sub_extension(difference, equality_dummy, not_index_matches);
|
||||
constraints.push(first_equality_constraint);
|
||||
|
||||
let second_equality_constraint = builder.mul_extension(index_matches, difference);
|
||||
constraints.push(second_equality_constraint);
|
||||
|
||||
// Output constraint.
|
||||
let diff = builder.sub_extension(list_items[i], claimed_element);
|
||||
let conditional_diff = builder.mul_extension(index_matches, diff);
|
||||
constraints.push(conditional_diff);
|
||||
// Assert that each bit wire value is indeed boolean.
|
||||
for &b in &bits {
|
||||
constraints.push(builder.mul_sub_extension(b, b, b));
|
||||
}
|
||||
|
||||
// Assert that the binary decomposition was correct.
|
||||
let reconstructed_index = bits
|
||||
.iter()
|
||||
.rev()
|
||||
.fold(zero, |acc, &b| builder.mul_add_extension(acc, two, b));
|
||||
constraints.push(builder.sub_extension(reconstructed_index, access_index));
|
||||
|
||||
// Repeatedly fold the list, selecting the left or right item from each pair based on
|
||||
// the corresponding bit.
|
||||
for b in bits {
|
||||
list_items = list_items
|
||||
.iter()
|
||||
.tuples()
|
||||
.map(|(&x, &y)| builder.select_ext_generalized(b, y, x))
|
||||
.collect()
|
||||
}
|
||||
|
||||
debug_assert_eq!(list_items.len(), 1);
|
||||
constraints.push(builder.sub_extension(list_items[0], claimed_element));
|
||||
}
|
||||
|
||||
constraints
|
||||
@ -207,7 +233,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for RandomAccessGa
|
||||
}
|
||||
|
||||
fn num_wires(&self) -> usize {
|
||||
self.wire_index_matches_for_index(self.vec_size - 1, self.num_copies - 1) + 1
|
||||
self.wire_bit(self.bits - 1, self.num_copies - 1) + 1
|
||||
}
|
||||
|
||||
fn num_constants(&self) -> usize {
|
||||
@ -215,11 +241,12 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for RandomAccessGa
|
||||
}
|
||||
|
||||
fn degree(&self) -> usize {
|
||||
2
|
||||
self.bits + 1
|
||||
}
|
||||
|
||||
fn num_constraints(&self) -> usize {
|
||||
3 * self.num_copies * self.vec_size
|
||||
let constraints_per_copy = self.bits + 2;
|
||||
self.num_copies * constraints_per_copy
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,8 +265,7 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
|
||||
let mut deps = Vec::new();
|
||||
deps.push(local_target(self.gate.wire_access_index(self.copy)));
|
||||
deps.push(local_target(self.gate.wire_claimed_element(self.copy)));
|
||||
for i in 0..self.gate.vec_size {
|
||||
for i in 0..self.gate.vec_size() {
|
||||
deps.push(local_target(self.gate.wire_list_item(i, self.copy)));
|
||||
}
|
||||
deps
|
||||
@ -252,11 +278,12 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
};
|
||||
|
||||
let get_local_wire = |input| witness.get_wire(local_wire(input));
|
||||
let mut set_local_wire = |input, value| out_buffer.set_wire(local_wire(input), value);
|
||||
|
||||
// Compute the new vector and the values for equality_dummy and index_matches
|
||||
let vec_size = self.gate.vec_size;
|
||||
let access_index_f = get_local_wire(self.gate.wire_access_index(self.copy));
|
||||
let copy = self.copy;
|
||||
let vec_size = self.gate.vec_size();
|
||||
|
||||
let access_index_f = get_local_wire(self.gate.wire_access_index(copy));
|
||||
let access_index = access_index_f.to_canonical_u64() as usize;
|
||||
debug_assert!(
|
||||
access_index < vec_size,
|
||||
@ -265,22 +292,14 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
|
||||
vec_size
|
||||
);
|
||||
|
||||
for i in 0..vec_size {
|
||||
let equality_dummy_wire =
|
||||
local_wire(self.gate.wire_equality_dummy_for_index(i, self.copy));
|
||||
let index_matches_wire =
|
||||
local_wire(self.gate.wire_index_matches_for_index(i, self.copy));
|
||||
set_local_wire(
|
||||
self.gate.wire_claimed_element(copy),
|
||||
get_local_wire(self.gate.wire_list_item(access_index, copy)),
|
||||
);
|
||||
|
||||
if i == access_index {
|
||||
out_buffer.set_wire(equality_dummy_wire, F::ONE);
|
||||
out_buffer.set_wire(index_matches_wire, F::ONE);
|
||||
} else {
|
||||
out_buffer.set_wire(
|
||||
equality_dummy_wire,
|
||||
(F::from_canonical_usize(i) - F::from_canonical_usize(access_index)).inverse(),
|
||||
);
|
||||
out_buffer.set_wire(index_matches_wire, F::ZERO);
|
||||
}
|
||||
for i in 0..self.gate.bits {
|
||||
let bit = F::from_bool(((access_index >> i) & 1) != 0);
|
||||
set_local_wire(self.gate.wire_bit(i, copy), bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -320,6 +339,7 @@ mod tests {
|
||||
/// Returns the local wires for a random access gate given the vectors, elements to compare,
|
||||
/// and indices.
|
||||
fn get_wires(
|
||||
bits: usize,
|
||||
lists: Vec<Vec<F>>,
|
||||
access_indices: Vec<usize>,
|
||||
claimed_elements: Vec<F>,
|
||||
@ -328,8 +348,7 @@ mod tests {
|
||||
let vec_size = lists[0].len();
|
||||
|
||||
let mut v = Vec::new();
|
||||
let mut equality_dummy_vals = Vec::new();
|
||||
let mut index_matches_vals = Vec::new();
|
||||
let mut bit_vals = Vec::new();
|
||||
for copy in 0..num_copies {
|
||||
let access_index = access_indices[copy];
|
||||
v.push(F::from_canonical_usize(access_index));
|
||||
@ -338,26 +357,17 @@ mod tests {
|
||||
v.push(lists[copy][j]);
|
||||
}
|
||||
|
||||
for i in 0..vec_size {
|
||||
if i == access_index {
|
||||
equality_dummy_vals.push(F::ONE);
|
||||
index_matches_vals.push(F::ONE);
|
||||
} else {
|
||||
equality_dummy_vals.push(
|
||||
(F::from_canonical_usize(i) - F::from_canonical_usize(access_index))
|
||||
.inverse(),
|
||||
);
|
||||
index_matches_vals.push(F::ZERO);
|
||||
}
|
||||
for i in 0..bits {
|
||||
bit_vals.push(F::from_bool(((access_index >> i) & 1) != 0));
|
||||
}
|
||||
}
|
||||
v.extend(equality_dummy_vals);
|
||||
v.extend(index_matches_vals);
|
||||
v.extend(bit_vals);
|
||||
|
||||
v.iter().map(|&x| x.into()).collect::<Vec<_>>()
|
||||
v.iter().map(|&x| x.into()).collect()
|
||||
}
|
||||
|
||||
let vec_size = 3;
|
||||
let bits = 3;
|
||||
let vec_size = 1 << bits;
|
||||
let num_copies = 4;
|
||||
let lists = (0..num_copies)
|
||||
.map(|_| F::rand_vec(vec_size))
|
||||
@ -366,7 +376,7 @@ mod tests {
|
||||
.map(|_| thread_rng().gen_range(0..vec_size))
|
||||
.collect::<Vec<_>>();
|
||||
let gate = RandomAccessGate::<F, D> {
|
||||
vec_size,
|
||||
bits,
|
||||
num_copies,
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
@ -378,13 +388,18 @@ mod tests {
|
||||
.collect();
|
||||
let good_vars = EvaluationVars {
|
||||
local_constants: &[],
|
||||
local_wires: &get_wires(lists.clone(), access_indices.clone(), good_claimed_elements),
|
||||
local_wires: &get_wires(
|
||||
bits,
|
||||
lists.clone(),
|
||||
access_indices.clone(),
|
||||
good_claimed_elements,
|
||||
),
|
||||
public_inputs_hash: &HashOut::rand(),
|
||||
};
|
||||
let bad_claimed_elements = F::rand_vec(4);
|
||||
let bad_vars = EvaluationVars {
|
||||
local_constants: &[],
|
||||
local_wires: &get_wires(lists, access_indices, bad_claimed_elements),
|
||||
local_wires: &get_wires(bits, lists, access_indices, bad_claimed_elements),
|
||||
public_inputs_hash: &HashOut::rand(),
|
||||
};
|
||||
|
||||
|
||||
@ -770,8 +770,8 @@ pub struct BatchedGates<F: RichField + Extendable<D>, const D: usize> {
|
||||
pub(crate) free_arithmetic: HashMap<(F, F), (usize, usize)>,
|
||||
pub(crate) free_base_arithmetic: HashMap<(F, F), (usize, usize)>,
|
||||
|
||||
/// A map `(c0, c1) -> (g, i)` from constants `vec_size` to an available arithmetic gate using
|
||||
/// these constants with gate index `g` and already using `i` random accesses.
|
||||
/// A map `b -> (g, i)` from `b` bits to an available random access gate of that size with gate
|
||||
/// index `g` and already using `i` random accesses.
|
||||
pub(crate) free_random_access: HashMap<usize, (usize, usize)>,
|
||||
|
||||
/// `current_switch_gates[chunk_size - 1]` contains None if we have no switch gates with the value
|
||||
@ -869,32 +869,27 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// Finds the last available random access gate with the given `vec_size` or add one if there aren't any.
|
||||
/// Returns `(g,i)` such that there is a random access gate with the given `vec_size` at index
|
||||
/// `g` and the gate's `i`-th random access is available.
|
||||
pub(crate) fn find_random_access_gate(&mut self, vec_size: usize) -> (usize, usize) {
|
||||
pub(crate) fn find_random_access_gate(&mut self, bits: usize) -> (usize, usize) {
|
||||
let (gate, i) = self
|
||||
.batched_gates
|
||||
.free_random_access
|
||||
.get(&vec_size)
|
||||
.get(&bits)
|
||||
.copied()
|
||||
.unwrap_or_else(|| {
|
||||
let gate = self.add_gate(
|
||||
RandomAccessGate::new_from_config(&self.config, vec_size),
|
||||
RandomAccessGate::new_from_config(&self.config, bits),
|
||||
vec![],
|
||||
);
|
||||
(gate, 0)
|
||||
});
|
||||
|
||||
// Update `free_random_access` with new values.
|
||||
if i < RandomAccessGate::<F, D>::max_num_copies(
|
||||
self.config.num_routed_wires,
|
||||
self.config.num_wires,
|
||||
vec_size,
|
||||
) - 1
|
||||
{
|
||||
if i + 1 < RandomAccessGate::<F, D>::new_from_config(&self.config, bits).num_copies {
|
||||
self.batched_gates
|
||||
.free_random_access
|
||||
.insert(vec_size, (gate, i + 1));
|
||||
.insert(bits, (gate, i + 1));
|
||||
} else {
|
||||
self.batched_gates.free_random_access.remove(&vec_size);
|
||||
self.batched_gates.free_random_access.remove(&bits);
|
||||
}
|
||||
|
||||
(gate, i)
|
||||
@ -1031,14 +1026,11 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
/// `RandomAccessGenerator`s are run.
|
||||
fn fill_random_access_gates(&mut self) {
|
||||
let zero = self.zero();
|
||||
for (vec_size, (_, i)) in self.batched_gates.free_random_access.clone() {
|
||||
let max_copies = RandomAccessGate::<F, D>::max_num_copies(
|
||||
self.config.num_routed_wires,
|
||||
self.config.num_wires,
|
||||
vec_size,
|
||||
);
|
||||
for (bits, (_, i)) in self.batched_gates.free_random_access.clone() {
|
||||
let max_copies =
|
||||
RandomAccessGate::<F, D>::new_from_config(&self.config, bits).num_copies;
|
||||
for _ in i..max_copies {
|
||||
self.random_access(zero, zero, vec![zero; vec_size]);
|
||||
self.random_access(zero, zero, vec![zero; 1 << bits]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user