Keep track of the last used RAM gate

This commit is contained in:
wborgeaud 2021-10-18 16:48:21 +02:00
parent a35cd98b03
commit 3f0b5ab9d3
4 changed files with 59 additions and 58 deletions

View File

@ -374,12 +374,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
let x_index_within_coset = self.le_sum(x_index_within_coset_bits.iter()); let x_index_within_coset = self.le_sum(x_index_within_coset_bits.iter());
// Check consistency with our old evaluation from the previous round. // Check consistency with our old evaluation from the previous round.
self.random_access_padded( self.random_access_extension(x_index_within_coset, old_eval, evals.clone());
x_index_within_coset,
old_eval,
evals.clone(),
1 << config.cap_height,
);
// Infer P(y) from {P(x)}_{x^arity=y}. // Infer P(y) from {P(x)}_{x^arity=y}.
old_eval = with_context!( old_eval = with_context!(

View File

@ -6,27 +6,60 @@ use crate::iop::target::Target;
use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_builder::CircuitBuilder;
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> { 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.
fn find_random_acces_gate(&mut self, vec_size: usize) -> (usize, usize) {
let (gate, i) = self
.free_random_access
.get(&vec_size)
.copied()
.unwrap_or_else(|| {
let gate = self.add_gate(
RandomAccessGate::new_from_config(&self.config, vec_size),
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
{
self.free_random_access.insert(vec_size, (gate, i + 1));
} else {
self.free_random_access.remove(&vec_size);
}
(gate, i)
}
/// Checks that an `ExtensionTarget` matches a vector at a non-deterministic index. /// Checks that an `ExtensionTarget` matches a vector at a non-deterministic index.
/// Note: `access_index` is not range-checked. /// Note: `access_index` is not range-checked.
pub fn random_access(&mut self, access_index: Target, claimed_element: Target, v: Vec<Target>) { pub fn random_access(&mut self, access_index: Target, claimed_element: Target, v: Vec<Target>) {
debug_assert!(!v.is_empty()); let vec_size = v.len();
if v.len() == 1 { debug_assert!(vec_size > 0);
if vec_size == 1 {
return self.connect(claimed_element, v[0]); return self.connect(claimed_element, v[0]);
} }
let gate = RandomAccessGate::new(1, v.len()); let (gate_index, copy) = self.find_random_acces_gate(vec_size);
let gate_index = self.add_gate(gate.clone(), vec![]); let dummy_gate = RandomAccessGate::<F, D>::new_from_config(&self.config, vec_size);
let copy = 0;
v.iter().enumerate().for_each(|(i, &val)| { v.iter().enumerate().for_each(|(i, &val)| {
self.connect(val, Target::wire(gate_index, gate.wire_list_item(i, copy))); self.connect(
val,
Target::wire(gate_index, dummy_gate.wire_list_item(i, copy)),
);
}); });
self.connect( self.connect(
access_index, access_index,
Target::wire(gate_index, gate.wire_access_index(copy)), Target::wire(gate_index, dummy_gate.wire_access_index(copy)),
); );
self.connect( self.connect(
claimed_element, claimed_element,
Target::wire(gate_index, gate.wire_claimed_element(copy)), Target::wire(gate_index, dummy_gate.wire_claimed_element(copy)),
); );
} }
@ -38,50 +71,14 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
claimed_element: ExtensionTarget<D>, claimed_element: ExtensionTarget<D>,
v: Vec<ExtensionTarget<D>>, v: Vec<ExtensionTarget<D>>,
) { ) {
debug_assert!(!v.is_empty()); for i in 0..D {
if v.len() == 1 { self.random_access(
return self.connect_extension(claimed_element, v[0]);
}
let gate = RandomAccessGate::new(D, v.len());
let gate_index = self.add_gate(gate.clone(), vec![]);
for copy in 0..D {
v.iter().enumerate().for_each(|(i, &val)| {
self.connect(
val.0[copy],
Target::wire(gate_index, gate.wire_list_item(i, copy)),
);
});
self.connect(
access_index, access_index,
Target::wire(gate_index, gate.wire_access_index(copy)), claimed_element.0[i],
); v.iter().map(|et| et.0[i]).collect(),
self.connect(
claimed_element.0[copy],
Target::wire(gate_index, gate.wire_claimed_element(copy)),
); );
} }
} }
/// Like `random_access`, but first pads `v` to a given minimum length. This can help to avoid
/// having multiple `RandomAccessGate`s with different sizes.
pub fn random_access_padded(
&mut self,
access_index: Target,
claimed_element: ExtensionTarget<D>,
mut v: Vec<ExtensionTarget<D>>,
min_length: usize,
) {
debug_assert!(!v.is_empty());
if v.len() == 1 {
return self.connect_extension(claimed_element, v[0]);
}
let zero = self.zero_extension();
if v.len() < min_length {
v.resize(8, zero);
}
self.random_access_extension(access_index, claimed_element, v);
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -31,12 +31,16 @@ impl<F: RichField + Extendable<D>, const D: usize> RandomAccessGate<F, D> {
} }
pub fn new_from_config(config: &CircuitConfig, vec_size: usize) -> Self { pub fn new_from_config(config: &CircuitConfig, vec_size: usize) -> Self {
let num_copies = Self::max_num_copies(config.num_routed_wires, vec_size); let num_copies = Self::max_num_copies(config.num_routed_wires, config.num_wires, vec_size);
Self::new(num_copies, vec_size) Self::new(num_copies, vec_size)
} }
pub fn max_num_copies(num_routed_wires: usize, vec_size: usize) -> usize { pub fn max_num_copies(num_routed_wires: usize, num_wires: usize, vec_size: usize) -> usize {
num_routed_wires / (2 + vec_size) // Need `(2 + vec_size) * num_copies` routed wires
(num_routed_wires / (2 + vec_size)).min(
// Need `(2 + 4*vec_size) * num_copies` wires
num_wires / (2 + 4 * vec_size),
)
} }
pub fn wire_access_index(&self, copy: usize) -> usize { pub fn wire_access_index(&self, copy: usize) -> usize {
@ -79,7 +83,7 @@ impl<F: RichField + Extendable<D>, const D: usize> RandomAccessGate<F, D> {
debug_assert!(copy < self.num_copies); debug_assert!(copy < self.num_copies);
self.start_of_intermediate_wires() self.start_of_intermediate_wires()
+ self.vec_size * self.num_copies + self.vec_size * self.num_copies
+ copy * self.vec_size + self.vec_size * copy
+ i + i
} }
} }

View File

@ -73,6 +73,10 @@ pub struct CircuitBuilder<F: RichField + Extendable<D>, const D: usize> {
/// these constants with gate index `g` and already using `i` arithmetic operations. /// these constants with gate index `g` and already using `i` arithmetic operations.
pub(crate) free_arithmetic: HashMap<(F, F), (usize, usize)>, pub(crate) free_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.
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 // `current_switch_gates[chunk_size - 1]` contains None if we have no switch gates with the value
// chunk_size, and contains `(g, i, c)`, if the gate `g`, at index `i`, already contains `c` copies // chunk_size, and contains `(g, i, c)`, if the gate `g`, at index `i`, already contains `c` copies
// of switches // of switches
@ -94,6 +98,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
constants_to_targets: HashMap::new(), constants_to_targets: HashMap::new(),
targets_to_constants: HashMap::new(), targets_to_constants: HashMap::new(),
free_arithmetic: HashMap::new(), free_arithmetic: HashMap::new(),
free_random_access: HashMap::new(),
current_switch_gates: Vec::new(), current_switch_gates: Vec::new(),
} }
} }