From 00ce9d9f25214edd0ac06b6b00e53cbd50b064c7 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 18 Oct 2021 11:07:18 +0200 Subject: [PATCH 1/9] Add `num_copies` to RAM gate --- src/gates/random_access.rs | 14 +++++++++++++- src/plonk/recursive_verifier.rs | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/gates/random_access.rs b/src/gates/random_access.rs index 4f086ef0..cc4c0442 100644 --- a/src/gates/random_access.rs +++ b/src/gates/random_access.rs @@ -10,23 +10,35 @@ use crate::iop::target::Target; use crate::iop::wire::Wire; use crate::iop::witness::{PartitionWitness, Witness}; use crate::plonk::circuit_builder::CircuitBuilder; +use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; /// A gate for checking that a particular element of a list matches a given value. #[derive(Clone, Debug)] pub(crate) struct RandomAccessGate, const D: usize> { pub vec_size: usize, + pub num_copies: usize, _phantom: PhantomData, } impl, const D: usize> RandomAccessGate { - pub fn new(vec_size: usize) -> Self { + pub fn new(num_copies: usize, vec_size: usize) -> Self { Self { vec_size, + 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, chunk_size); + Self::new(num_copies, chunk_size) + } + + pub fn max_num_copies(num_routed_wires: usize, vec_size: usize) -> usize { + num_routed_wires / (2 + vec_size) + } + pub fn wire_access_index(&self) -> usize { 0 } diff --git a/src/plonk/recursive_verifier.rs b/src/plonk/recursive_verifier.rs index 7bcc0633..130baa09 100644 --- a/src/plonk/recursive_verifier.rs +++ b/src/plonk/recursive_verifier.rs @@ -378,7 +378,7 @@ mod tests { fn test_recursive_recursive_verifier() -> Result<()> { init_logger(); type F = GoldilocksField; - const D: usize = 4; + const D: usize = 2; let config = CircuitConfig::standard_recursion_config(); From 104fd08e72802296ea28f13e485035de81ad5f0b Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 18 Oct 2021 15:19:09 +0200 Subject: [PATCH 2/9] Working RAM gate --- src/fri/recursive_verifier.rs | 16 +- src/gadgets/random_access.rs | 36 ++-- src/gates/random_access.rs | 327 +++++++++++++++++++--------------- src/hash/merkle_proofs.rs | 4 +- 4 files changed, 209 insertions(+), 174 deletions(-) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index eb30c467..bc1310f9 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -60,15 +60,17 @@ impl, const D: usize> CircuitBuilder { /// isn't required -- without it we'd get errors elsewhere in the stack -- but just gives more /// helpful errors. fn check_config(&self, arity: usize) { - let random_access = RandomAccessGate::::new(arity); + // let random_access = RandomAccessGate::::new(arity); let interpolation_gate = InterpolationGate::::new(arity); - let min_wires = random_access - .num_wires() - .max(interpolation_gate.num_wires()); - let min_routed_wires = random_access - .num_routed_wires() - .max(interpolation_gate.num_routed_wires()); + // let min_wires = random_access + // .num_wires() + // .max(interpolation_gate.num_wires()); + let min_wires = interpolation_gate.num_wires(); + // let min_routed_wires = random_access + // .num_routed_wires() + // .max(interpolation_gate.num_routed_wires()); + let min_routed_wires = interpolation_gate.num_routed_wires(); assert!( self.config.num_wires >= min_wires, diff --git a/src/gadgets/random_access.rs b/src/gadgets/random_access.rs index ab0db68c..bae7c9cd 100644 --- a/src/gadgets/random_access.rs +++ b/src/gadgets/random_access.rs @@ -8,7 +8,7 @@ use crate::plonk::circuit_builder::CircuitBuilder; impl, const D: usize> CircuitBuilder { /// Checks that a `Target` matches a vector at a non-deterministic index. /// Note: `index` is not range-checked. - pub fn random_access( + pub fn random_access_extension( &mut self, access_index: Target, claimed_element: ExtensionTarget, @@ -18,23 +18,25 @@ impl, const D: usize> CircuitBuilder { if v.len() == 1 { return self.connect_extension(claimed_element, v[0]); } - let gate = RandomAccessGate::new(v.len()); + let gate = RandomAccessGate::new(D, v.len()); let gate_index = self.add_gate(gate.clone(), vec![]); - v.iter().enumerate().for_each(|(i, &val)| { - self.connect_extension( - val, - ExtensionTarget::from_range(gate_index, gate.wires_list_item(i)), + 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, + Target::wire(gate_index, gate.wire_access_index(copy)), ); - }); - self.connect( - access_index, - Target::wire(gate_index, gate.wire_access_index()), - ); - self.connect_extension( - claimed_element, - ExtensionTarget::from_range(gate_index, gate.wires_claimed_element()), - ); + 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 @@ -54,7 +56,7 @@ impl, const D: usize> CircuitBuilder { if v.len() < min_length { v.resize(8, zero); } - self.random_access(access_index, claimed_element, v); + self.random_access_extension(access_index, claimed_element, v); } } @@ -83,7 +85,7 @@ mod tests { for i in 0..len { let it = builder.constant(F::from_canonical_usize(i)); let elem = builder.constant_extension(vec[i]); - builder.random_access(it, elem, v.clone()); + builder.random_access_extension(it, elem, v.clone()); } let data = builder.build(); diff --git a/src/gates/random_access.rs b/src/gates/random_access.rs index cc4c0442..8e1618fc 100644 --- a/src/gates/random_access.rs +++ b/src/gates/random_access.rs @@ -30,31 +30,33 @@ impl, const D: usize> RandomAccessGate { } } - pub fn new_from_config(config: CircuitConfig, vec_size: usize) -> Self { - let num_copies = Self::max_num_copies(config.num_routed_wires, chunk_size); - Self::new(num_copies, chunk_size) + pub fn new_from_config(config: &CircuitConfig, vec_size: usize) -> Self { + let num_copies = Self::max_num_copies(config.num_routed_wires, vec_size); + Self::new(num_copies, vec_size) } pub fn max_num_copies(num_routed_wires: usize, vec_size: usize) -> usize { num_routed_wires / (2 + vec_size) } - pub fn wire_access_index(&self) -> usize { - 0 + pub fn wire_access_index(&self, copy: usize) -> usize { + debug_assert!(copy < self.num_copies); + (2 + self.vec_size) * copy } - pub fn wires_claimed_element(&self) -> Range { - 1..D + 1 + pub fn wire_claimed_element(&self, copy: usize) -> usize { + debug_assert!(copy < self.num_copies); + (2 + self.vec_size) * copy + 1 } - pub fn wires_list_item(&self, i: usize) -> Range { + pub fn wire_list_item(&self, i: usize, copy: usize) -> usize { debug_assert!(i < self.vec_size); - let start = (i + 1) * D + 1; - start..start + D + debug_assert!(copy < self.num_copies); + (2 + self.vec_size) * copy + 2 + i } fn start_of_intermediate_wires(&self) -> usize { - (self.vec_size + 1) * D + 1 + (2 + self.vec_size) * self.num_copies } pub(crate) fn num_routed_wires(&self) -> usize { @@ -64,16 +66,21 @@ impl, const D: usize> RandomAccessGate { /// 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) -> usize { + pub fn wire_equality_dummy_for_index(&self, i: usize, copy: usize) -> usize { debug_assert!(i < self.vec_size); - self.start_of_intermediate_wires() + i + 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) -> usize { + pub fn wire_index_matches_for_index(&self, i: usize, copy: usize) -> usize { debug_assert!(i < self.vec_size); - self.start_of_intermediate_wires() + self.vec_size + i + debug_assert!(copy < self.num_copies); + self.start_of_intermediate_wires() + + self.vec_size * self.num_copies + + copy * self.vec_size + + i } } @@ -83,53 +90,55 @@ impl, const D: usize> Gate for RandomAccessGa } fn eval_unfiltered(&self, vars: EvaluationVars) -> Vec { - let access_index = vars.local_wires[self.wire_access_index()]; - let list_items = (0..self.vec_size) - .map(|i| vars.get_local_ext_algebra(self.wires_list_item(i))) - .collect::>(); - let claimed_element = vars.get_local_ext_algebra(self.wires_claimed_element()); - let mut constraints = Vec::with_capacity(self.num_constraints()); - 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)]; - let index_matches = vars.local_wires[self.wire_index_matches_for_index(i)]; - // The two index equality constraints. - constraints.push(difference * equality_dummy - (F::Extension::ONE - index_matches)); - constraints.push(index_matches * difference); - // Value equality constraint. - constraints.extend( - ((list_items[i] - claimed_element).scalar_mul(index_matches)).to_basefield_array(), - ); + 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) + .map(|i| vars.local_wires[self.wire_list_item(i, copy)]) + .collect::>(); + let claimed_element = vars.local_wires[self.wire_claimed_element(copy)]; + + 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. + dbg!(difference, equality_dummy, index_matches); + 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)); + } } constraints } fn eval_unfiltered_base(&self, vars: EvaluationVarsBase) -> Vec { - let access_index = vars.local_wires[self.wire_access_index()]; - let list_items = (0..self.vec_size) - .map(|i| vars.get_local_ext(self.wires_list_item(i))) - .collect::>(); - let claimed_element = vars.get_local_ext(self.wires_claimed_element()); - let mut constraints = Vec::with_capacity(self.num_constraints()); - 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)]; - let index_matches = vars.local_wires[self.wire_index_matches_for_index(i)]; - // The two equality constraints. - constraints.push(difference * equality_dummy - (F::ONE - index_matches)); - constraints.push(index_matches * difference); + 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) + .map(|i| vars.local_wires[self.wire_list_item(i, copy)]) + .collect::>(); + let claimed_element = vars.local_wires[self.wire_claimed_element(copy)]; - // Value equality constraint. - constraints.extend( - ((list_items[i] - claimed_element).scalar_mul(index_matches)).to_basefield_array(), - ); + 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)); + } } constraints @@ -140,35 +149,36 @@ impl, const D: usize> Gate for RandomAccessGa builder: &mut CircuitBuilder, vars: EvaluationTargets, ) -> Vec> { - let access_index = vars.local_wires[self.wire_access_index()]; - let list_items = (0..self.vec_size) - .map(|i| vars.get_local_ext_algebra(self.wires_list_item(i))) - .collect::>(); - let claimed_element = vars.get_local_ext_algebra(self.wires_claimed_element()); - let mut constraints = Vec::with_capacity(self.num_constraints()); - 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)]; - let index_matches = vars.local_wires[self.wire_index_matches_for_index(i)]; + 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) + .map(|i| vars.local_wires[self.wire_list_item(i, copy)]) + .collect::>(); + let claimed_element = vars.local_wires[self.wire_claimed_element(copy)]; - // The two equality constraints. - 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); + 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 second_equality_constraint = builder.mul_extension(index_matches, difference); - constraints.push(second_equality_constraint); + 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); - // Output constraint. - let diff = builder.sub_ext_algebra(list_items[i], claimed_element); - let conditional_diff = builder.scalar_mul_ext_algebra(index_matches, diff); - constraints.extend(conditional_diff.to_ext_target_array()); + 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); + } } constraints @@ -187,7 +197,7 @@ impl, const D: usize> Gate for RandomAccessGa } fn num_wires(&self) -> usize { - self.wire_index_matches_for_index(self.vec_size - 1) + 1 + self.wire_index_matches_for_index(self.vec_size - 1, self.num_copies - 1) + 1 } fn num_constants(&self) -> usize { @@ -199,7 +209,7 @@ impl, const D: usize> Gate for RandomAccessGa } fn num_constraints(&self) -> usize { - self.vec_size * (2 + D) + self.num_copies * self.vec_size * 3 } } @@ -215,13 +225,13 @@ impl, const D: usize> SimpleGenerator fn dependencies(&self) -> Vec { let local_target = |input| Target::wire(self.gate_index, input); - let local_targets = |inputs: Range| inputs.map(local_target); - let mut deps = Vec::new(); - deps.push(local_target(self.gate.wire_access_index())); - deps.extend(local_targets(self.gate.wires_claimed_element())); - for i in 0..self.gate.vec_size { - deps.extend(local_targets(self.gate.wires_list_item(i))); + for copy in 0..self.gate.num_copies { + deps.push(local_target(self.gate.wire_access_index(copy))); + deps.push(local_target(self.gate.wire_claimed_element(copy))); + for i in 0..self.gate.vec_size { + deps.push(local_target(self.gate.wire_list_item(i, copy))); + } } deps } @@ -236,29 +246,34 @@ impl, const D: usize> SimpleGenerator // 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()); + for copy in 0..self.gate.num_copies { + 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, - "Access index {} is larger than the vector size {}", - access_index, - vec_size - ); + let access_index = access_index_f.to_canonical_u64() as usize; + debug_assert!( + access_index < vec_size, + "Access index {} is larger than the vector size {}", + access_index, + vec_size + ); - for i in 0..vec_size { - let equality_dummy_wire = local_wire(self.gate.wire_equality_dummy_for_index(i)); - let index_matches_wire = local_wire(self.gate.wire_index_matches_for_index(i)); + for i in 0..vec_size { + let equality_dummy_wire = + local_wire(self.gate.wire_equality_dummy_for_index(i, copy)); + let index_matches_wire = + local_wire(self.gate.wire_index_matches_for_index(i, 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); + 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); + } } } } @@ -269,6 +284,7 @@ mod tests { use std::marker::PhantomData; use anyhow::Result; + use rand::{thread_rng, Rng}; use crate::field::crandall_field::CrandallField; use crate::field::extension_field::quartic::QuarticExtension; @@ -279,31 +295,31 @@ mod tests { use crate::hash::hash_types::HashOut; use crate::plonk::vars::EvaluationVars; - #[test] - fn wire_indices() { - let gate = RandomAccessGate:: { - vec_size: 3, - _phantom: PhantomData, - }; - - assert_eq!(gate.wire_access_index(), 0); - assert_eq!(gate.wires_claimed_element(), 1..5); - assert_eq!(gate.wires_list_item(0), 5..9); - assert_eq!(gate.wires_list_item(2), 13..17); - assert_eq!(gate.wire_equality_dummy_for_index(0), 17); - assert_eq!(gate.wire_equality_dummy_for_index(2), 19); - assert_eq!(gate.wire_index_matches_for_index(0), 20); - assert_eq!(gate.wire_index_matches_for_index(2), 22); - } + // #[test] + // fn wire_indices() { + // let gate = RandomAccessGate:: { + // vec_size: 3, + // _phantom: PhantomData, + // }; + // + // assert_eq!(gate.wire_access_index(), 0); + // assert_eq!(gate.wires_claimed_element(), 1..5); + // assert_eq!(gate.wires_list_item(0), 5..9); + // assert_eq!(gate.wires_list_item(2), 13..17); + // assert_eq!(gate.wire_equality_dummy_for_index(0), 17); + // assert_eq!(gate.wire_equality_dummy_for_index(2), 19); + // assert_eq!(gate.wire_index_matches_for_index(0), 20); + // assert_eq!(gate.wire_index_matches_for_index(2), 22); + // } #[test] fn low_degree() { - test_low_degree::(RandomAccessGate::new(4)); + test_low_degree::(RandomAccessGate::new(4, 4)); } #[test] fn eval_fns() -> Result<()> { - test_eval_fns::(RandomAccessGate::new(4)) + test_eval_fns::(RandomAccessGate::new(4, 4)) } #[test] @@ -314,64 +330,79 @@ mod tests { /// Returns the local wires for a random access gate given the vector, element to compare, /// and index. - fn get_wires(list: Vec, access_index: usize, claimed_element: FF) -> Vec { - let vec_size = list.len(); + fn get_wires( + lists: Vec>, + access_indices: Vec, + claimed_elements: Vec, + ) -> Vec { + let num_copies = lists.len(); + let vec_size = lists[0].len(); let mut v = Vec::new(); - v.push(F::from_canonical_usize(access_index)); - v.extend(claimed_element.0); - for j in 0..vec_size { - v.extend(list[j].0); - } - let mut equality_dummy_vals = Vec::new(); let mut index_matches_vals = Vec::new(); - 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 copy in 0..num_copies { + let access_index = access_indices[copy]; + v.push(F::from_canonical_usize(access_index)); + v.push(claimed_elements[copy]); + for j in 0..vec_size { + 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); + } } } - v.extend(equality_dummy_vals); v.extend(index_matches_vals); - v.iter().map(|&x| x.into()).collect::>() } - let list = vec![FF::rand(); 3]; - let access_index = 1; + let lists = (0..4).map(|_| F::rand_vec(3)).collect::>(); + let access_indices = (0..4) + .map(|_| thread_rng().gen_range(0..3)) + .collect::>(); let gate = RandomAccessGate:: { vec_size: 3, + num_copies: 4, _phantom: PhantomData, }; - let good_claimed_element = list[access_index]; + let good_claimed_elements = lists + .iter() + .zip(&access_indices) + .map(|(l, &i)| l[i]) + .collect(); + dbg!(&lists, &access_indices, &good_claimed_elements); let good_vars = EvaluationVars { local_constants: &[], - local_wires: &get_wires(list.clone(), access_index, good_claimed_element), + local_wires: &get_wires(lists.clone(), access_indices.clone(), good_claimed_elements), public_inputs_hash: &HashOut::rand(), }; - let bad_claimed_element = FF::rand(); + let bad_claimed_elements = F::rand_vec(4); let bad_vars = EvaluationVars { local_constants: &[], - local_wires: &get_wires(list, access_index, bad_claimed_element), + local_wires: &get_wires(lists, access_indices, bad_claimed_elements), public_inputs_hash: &HashOut::rand(), }; + dbg!(gate.eval_unfiltered(good_vars)); assert!( gate.eval_unfiltered(good_vars).iter().all(|x| x.is_zero()), "Gate constraints are not satisfied." ); assert!( !gate.eval_unfiltered(bad_vars).iter().all(|x| x.is_zero()), - "Gate constraints are satisfied but shouold not be." + "Gate constraints are satisfied but should not be." ); } } diff --git a/src/hash/merkle_proofs.rs b/src/hash/merkle_proofs.rs index ac62d8f0..062ff521 100644 --- a/src/hash/merkle_proofs.rs +++ b/src/hash/merkle_proofs.rs @@ -85,7 +85,7 @@ impl, const D: usize> CircuitBuilder { ExtensionTarget(tmp) }) .collect(); - self.random_access(index, state_ext, cap_ext); + self.random_access_extension(index, state_ext, cap_ext); } /// Same a `verify_merkle_proof` but with the final "cap index" as extra parameter. @@ -122,7 +122,7 @@ impl, const D: usize> CircuitBuilder { ExtensionTarget(tmp) }) .collect(); - self.random_access(cap_index, state_ext, cap_ext); + self.random_access_extension(cap_index, state_ext, cap_ext); } pub fn assert_hashes_equal(&mut self, x: HashOutTarget, y: HashOutTarget) { From a35cd98b03b9533fa7946a58327a5bae3bc9990b Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 18 Oct 2021 15:45:52 +0200 Subject: [PATCH 3/9] New random access gadget --- src/gadgets/random_access.rs | 28 ++++++++++++++++++++++++++-- src/gates/random_access.rs | 3 --- src/hash/merkle_proofs.rs | 29 +++++++---------------------- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/gadgets/random_access.rs b/src/gadgets/random_access.rs index bae7c9cd..f4396466 100644 --- a/src/gadgets/random_access.rs +++ b/src/gadgets/random_access.rs @@ -6,8 +6,32 @@ use crate::iop::target::Target; use crate::plonk::circuit_builder::CircuitBuilder; impl, const D: usize> CircuitBuilder { - /// Checks that a `Target` matches a vector at a non-deterministic index. - /// Note: `index` is not range-checked. + /// Checks that an `ExtensionTarget` 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) { + debug_assert!(!v.is_empty()); + if v.len() == 1 { + return self.connect(claimed_element, v[0]); + } + let gate = RandomAccessGate::new(1, v.len()); + let gate_index = self.add_gate(gate.clone(), vec![]); + + let copy = 0; + v.iter().enumerate().for_each(|(i, &val)| { + self.connect(val, Target::wire(gate_index, gate.wire_list_item(i, copy))); + }); + self.connect( + access_index, + Target::wire(gate_index, gate.wire_access_index(copy)), + ); + self.connect( + claimed_element, + Target::wire(gate_index, gate.wire_claimed_element(copy)), + ); + } + + /// Checks that an `ExtensionTarget` matches a vector at a non-deterministic index. + /// Note: `access_index` is not range-checked. pub fn random_access_extension( &mut self, access_index: Target, diff --git a/src/gates/random_access.rs b/src/gates/random_access.rs index 8e1618fc..e11a33d0 100644 --- a/src/gates/random_access.rs +++ b/src/gates/random_access.rs @@ -106,7 +106,6 @@ impl, const D: usize> Gate for RandomAccessGa let index_matches = vars.local_wires[self.wire_index_matches_for_index(i, copy)]; // The two index equality constraints. - dbg!(difference, equality_dummy, index_matches); constraints.push(difference * equality_dummy - (F::Extension::ONE - index_matches)); constraints.push(index_matches * difference); // Value equality constraint. @@ -382,7 +381,6 @@ mod tests { .zip(&access_indices) .map(|(l, &i)| l[i]) .collect(); - dbg!(&lists, &access_indices, &good_claimed_elements); let good_vars = EvaluationVars { local_constants: &[], local_wires: &get_wires(lists.clone(), access_indices.clone(), good_claimed_elements), @@ -395,7 +393,6 @@ mod tests { public_inputs_hash: &HashOut::rand(), }; - dbg!(gate.eval_unfiltered(good_vars)); assert!( gate.eval_unfiltered(good_vars).iter().all(|x| x.is_zero()), "Gate constraints are not satisfied." diff --git a/src/hash/merkle_proofs.rs b/src/hash/merkle_proofs.rs index 062ff521..3e265979 100644 --- a/src/hash/merkle_proofs.rs +++ b/src/hash/merkle_proofs.rs @@ -75,17 +75,6 @@ impl, const D: usize> CircuitBuilder { } let index = self.le_sum(leaf_index_bits[proof.siblings.len()..].to_vec().into_iter()); - let state_ext = state.elements[..].try_into().expect("requires D = 4"); - let state_ext = ExtensionTarget(state_ext); - let cap_ext = merkle_cap - .0 - .iter() - .map(|h| { - let tmp = h.elements[..].try_into().expect("requires D = 4"); - ExtensionTarget(tmp) - }) - .collect(); - self.random_access_extension(index, state_ext, cap_ext); } /// Same a `verify_merkle_proof` but with the final "cap index" as extra parameter. @@ -112,17 +101,13 @@ impl, const D: usize> CircuitBuilder { }; } - let state_ext = state.elements[..].try_into().expect("requires D = 4"); - let state_ext = ExtensionTarget(state_ext); - let cap_ext = merkle_cap - .0 - .iter() - .map(|h| { - let tmp = h.elements[..].try_into().expect("requires D = 4"); - ExtensionTarget(tmp) - }) - .collect(); - self.random_access_extension(cap_index, state_ext, cap_ext); + for i in 0..4 { + self.random_access( + cap_index, + state.elements[i], + merkle_cap.0.iter().map(|h| h.elements[i]).collect(), + ); + } } pub fn assert_hashes_equal(&mut self, x: HashOutTarget, y: HashOutTarget) { From 3f0b5ab9d3ab8de8db0569d8acd4223d3350a1a4 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 18 Oct 2021 16:48:21 +0200 Subject: [PATCH 4/9] Keep track of the last used RAM gate --- src/fri/recursive_verifier.rs | 7 +-- src/gadgets/random_access.rs | 93 +++++++++++++++++------------------ src/gates/random_access.rs | 12 +++-- src/plonk/circuit_builder.rs | 5 ++ 4 files changed, 59 insertions(+), 58 deletions(-) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index bc1310f9..2a6c5ff9 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -374,12 +374,7 @@ impl, const D: usize> CircuitBuilder { let x_index_within_coset = self.le_sum(x_index_within_coset_bits.iter()); // Check consistency with our old evaluation from the previous round. - self.random_access_padded( - x_index_within_coset, - old_eval, - evals.clone(), - 1 << config.cap_height, - ); + self.random_access_extension(x_index_within_coset, old_eval, evals.clone()); // Infer P(y) from {P(x)}_{x^arity=y}. old_eval = with_context!( diff --git a/src/gadgets/random_access.rs b/src/gadgets/random_access.rs index f4396466..54db3ec9 100644 --- a/src/gadgets/random_access.rs +++ b/src/gadgets/random_access.rs @@ -6,27 +6,60 @@ use crate::iop::target::Target; use crate::plonk::circuit_builder::CircuitBuilder; impl, const D: usize> CircuitBuilder { + /// 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::::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. /// Note: `access_index` is not range-checked. pub fn random_access(&mut self, access_index: Target, claimed_element: Target, v: Vec) { - debug_assert!(!v.is_empty()); - if v.len() == 1 { + let vec_size = v.len(); + debug_assert!(vec_size > 0); + if vec_size == 1 { return self.connect(claimed_element, v[0]); } - let gate = RandomAccessGate::new(1, v.len()); - let gate_index = self.add_gate(gate.clone(), vec![]); + let (gate_index, copy) = self.find_random_acces_gate(vec_size); + let dummy_gate = RandomAccessGate::::new_from_config(&self.config, vec_size); - let copy = 0; 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( access_index, - Target::wire(gate_index, gate.wire_access_index(copy)), + Target::wire(gate_index, dummy_gate.wire_access_index(copy)), ); self.connect( 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, const D: usize> CircuitBuilder { claimed_element: ExtensionTarget, v: Vec>, ) { - debug_assert!(!v.is_empty()); - if v.len() == 1 { - 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( + for i in 0..D { + self.random_access( access_index, - Target::wire(gate_index, gate.wire_access_index(copy)), - ); - self.connect( - claimed_element.0[copy], - Target::wire(gate_index, gate.wire_claimed_element(copy)), + claimed_element.0[i], + v.iter().map(|et| et.0[i]).collect(), ); } } - - /// 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, - mut v: Vec>, - 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)] diff --git a/src/gates/random_access.rs b/src/gates/random_access.rs index e11a33d0..925aa3ea 100644 --- a/src/gates/random_access.rs +++ b/src/gates/random_access.rs @@ -31,12 +31,16 @@ impl, const D: usize> RandomAccessGate { } 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) } - pub fn max_num_copies(num_routed_wires: usize, vec_size: usize) -> usize { - num_routed_wires / (2 + vec_size) + 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 + 4*vec_size) * num_copies` wires + num_wires / (2 + 4 * vec_size), + ) } pub fn wire_access_index(&self, copy: usize) -> usize { @@ -79,7 +83,7 @@ impl, const D: usize> RandomAccessGate { debug_assert!(copy < self.num_copies); self.start_of_intermediate_wires() + self.vec_size * self.num_copies - + copy * self.vec_size + + self.vec_size * copy + i } } diff --git a/src/plonk/circuit_builder.rs b/src/plonk/circuit_builder.rs index ed0bdbdc..96433bce 100644 --- a/src/plonk/circuit_builder.rs +++ b/src/plonk/circuit_builder.rs @@ -73,6 +73,10 @@ pub struct CircuitBuilder, const D: usize> { /// these constants with gate index `g` and already using `i` arithmetic operations. 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, + // `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 // of switches @@ -94,6 +98,7 @@ impl, const D: usize> CircuitBuilder { constants_to_targets: HashMap::new(), targets_to_constants: HashMap::new(), free_arithmetic: HashMap::new(), + free_random_access: HashMap::new(), current_switch_gates: Vec::new(), } } From 5b81006e9aec8ce6cec5e0b0f8ce4dc6e23d0872 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 18 Oct 2021 17:11:59 +0200 Subject: [PATCH 5/9] Fill random access gates to make sure all generators are run --- src/gadgets/random_access.rs | 6 +++--- src/gates/random_access.rs | 2 +- src/plonk/circuit_builder.rs | 23 +++++++++++++++++++++++ src/plonk/recursive_verifier.rs | 2 +- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/gadgets/random_access.rs b/src/gadgets/random_access.rs index 54db3ec9..f8c3acab 100644 --- a/src/gadgets/random_access.rs +++ b/src/gadgets/random_access.rs @@ -9,7 +9,7 @@ impl, const D: usize> CircuitBuilder { /// 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) { + fn find_random_access_gate(&mut self, vec_size: usize) -> (usize, usize) { let (gate, i) = self .free_random_access .get(&vec_size) @@ -36,7 +36,7 @@ impl, const D: usize> CircuitBuilder { (gate, i) } - /// Checks that an `ExtensionTarget` matches a vector at a non-deterministic index. + /// 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) { let vec_size = v.len(); @@ -44,7 +44,7 @@ impl, const D: usize> CircuitBuilder { if vec_size == 1 { return self.connect(claimed_element, v[0]); } - let (gate_index, copy) = self.find_random_acces_gate(vec_size); + let (gate_index, copy) = self.find_random_access_gate(vec_size); let dummy_gate = RandomAccessGate::::new_from_config(&self.config, vec_size); v.iter().enumerate().for_each(|(i, &val)| { diff --git a/src/gates/random_access.rs b/src/gates/random_access.rs index 925aa3ea..191f66e3 100644 --- a/src/gates/random_access.rs +++ b/src/gates/random_access.rs @@ -212,7 +212,7 @@ impl, const D: usize> Gate for RandomAccessGa } fn num_constraints(&self) -> usize { - self.num_copies * self.vec_size * 3 + 3 * self.num_copies * self.vec_size } } diff --git a/src/plonk/circuit_builder.rs b/src/plonk/circuit_builder.rs index 96433bce..4b392be9 100644 --- a/src/plonk/circuit_builder.rs +++ b/src/plonk/circuit_builder.rs @@ -18,6 +18,7 @@ use crate::gates::gate::{Gate, GateInstance, GateRef, PrefixedGate}; use crate::gates::gate_tree::Tree; use crate::gates::noop::NoopGate; use crate::gates::public_input::PublicInputGate; +use crate::gates::random_access::RandomAccessGate; use crate::gates::switch::SwitchGate; use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget}; use crate::hash::hashing::hash_n_to_hash; @@ -535,6 +536,27 @@ impl, const D: usize> CircuitBuilder { } } + /// Fill the remaining unused random access operations with zeros, so that all + /// `RandomAccessGenerator`s are run. + fn fill_random_access_gates(&mut self) { + let zero = self.zero(); + let remaining_random_access_gates = self + .free_random_access + .clone() + .into_iter() + .collect::>(); + for (vec_size, (_, i)) in remaining_random_access_gates { + let max_copies = RandomAccessGate::::max_num_copies( + self.config.num_routed_wires, + self.config.num_wires, + vec_size, + ); + for _ in i..max_copies { + self.random_access(zero, zero, vec![zero; vec_size]); + } + } + } + /// Fill the remaining unused switch gates with dummy values, so that all /// `SwitchGenerator` are run. fn fill_switch_gates(&mut self) { @@ -574,6 +596,7 @@ impl, const D: usize> CircuitBuilder { let start = Instant::now(); self.fill_arithmetic_gates(); + self.fill_random_access_gates(); self.fill_switch_gates(); // Hash the public inputs, and route them to a `PublicInputGate` which will enforce that diff --git a/src/plonk/recursive_verifier.rs b/src/plonk/recursive_verifier.rs index 130baa09..f9bda416 100644 --- a/src/plonk/recursive_verifier.rs +++ b/src/plonk/recursive_verifier.rs @@ -398,7 +398,7 @@ mod tests { fn test_size_optimized_recursion() -> Result<()> { init_logger(); type F = GoldilocksField; - const D: usize = 4; + const D: usize = 2; let normal_config = CircuitConfig::standard_recursion_config(); let final_config = CircuitConfig { From dda14011c5998323b21ee689650866ce74ad3bd5 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 18 Oct 2021 17:23:39 +0200 Subject: [PATCH 6/9] Forgot a random access check --- src/fri/recursive_verifier.rs | 18 +++++++++--------- src/gadgets/random_access.rs | 1 + src/gates/random_access.rs | 36 ++++++++++++----------------------- src/hash/merkle_proofs.rs | 12 +++++++++--- 4 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 2a6c5ff9..0115277e 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -60,17 +60,17 @@ impl, const D: usize> CircuitBuilder { /// isn't required -- without it we'd get errors elsewhere in the stack -- but just gives more /// helpful errors. fn check_config(&self, arity: usize) { - // let random_access = RandomAccessGate::::new(arity); + // TODO: It would be nice to remove the hardcoded 8 here and replace it with the maximum arity bits + // used in FRI. + let random_access = RandomAccessGate::::new_from_config(&self.config, 8); let interpolation_gate = InterpolationGate::::new(arity); - // let min_wires = random_access - // .num_wires() - // .max(interpolation_gate.num_wires()); - let min_wires = interpolation_gate.num_wires(); - // let min_routed_wires = random_access - // .num_routed_wires() - // .max(interpolation_gate.num_routed_wires()); - let min_routed_wires = interpolation_gate.num_routed_wires(); + let min_wires = random_access + .num_wires() + .max(interpolation_gate.num_wires()); + let min_routed_wires = random_access + .num_routed_wires() + .max(interpolation_gate.num_routed_wires()); assert!( self.config.num_wires >= min_wires, diff --git a/src/gadgets/random_access.rs b/src/gadgets/random_access.rs index f8c3acab..9875385f 100644 --- a/src/gadgets/random_access.rs +++ b/src/gadgets/random_access.rs @@ -36,6 +36,7 @@ impl, const D: usize> CircuitBuilder { (gate, i) } + /// 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) { diff --git a/src/gates/random_access.rs b/src/gates/random_access.rs index 191f66e3..5bddf63c 100644 --- a/src/gates/random_access.rs +++ b/src/gates/random_access.rs @@ -298,23 +298,6 @@ mod tests { use crate::hash::hash_types::HashOut; use crate::plonk::vars::EvaluationVars; - // #[test] - // fn wire_indices() { - // let gate = RandomAccessGate:: { - // vec_size: 3, - // _phantom: PhantomData, - // }; - // - // assert_eq!(gate.wire_access_index(), 0); - // assert_eq!(gate.wires_claimed_element(), 1..5); - // assert_eq!(gate.wires_list_item(0), 5..9); - // assert_eq!(gate.wires_list_item(2), 13..17); - // assert_eq!(gate.wire_equality_dummy_for_index(0), 17); - // assert_eq!(gate.wire_equality_dummy_for_index(2), 19); - // assert_eq!(gate.wire_index_matches_for_index(0), 20); - // assert_eq!(gate.wire_index_matches_for_index(2), 22); - // } - #[test] fn low_degree() { test_low_degree::(RandomAccessGate::new(4, 4)); @@ -331,8 +314,8 @@ mod tests { type FF = QuarticExtension; const D: usize = 4; - /// Returns the local wires for a random access gate given the vector, element to compare, - /// and index. + /// Returns the local wires for a random access gate given the vectors, elements to compare, + /// and indices. fn get_wires( lists: Vec>, access_indices: Vec, @@ -367,16 +350,21 @@ mod tests { } v.extend(equality_dummy_vals); v.extend(index_matches_vals); + v.iter().map(|&x| x.into()).collect::>() } - let lists = (0..4).map(|_| F::rand_vec(3)).collect::>(); - let access_indices = (0..4) - .map(|_| thread_rng().gen_range(0..3)) + let vec_size = 3; + let num_copies = 4; + let lists = (0..num_copies) + .map(|_| F::rand_vec(vec_size)) + .collect::>(); + let access_indices = (0..num_copies) + .map(|_| thread_rng().gen_range(0..vec_size)) .collect::>(); let gate = RandomAccessGate:: { - vec_size: 3, - num_copies: 4, + vec_size, + num_copies, _phantom: PhantomData, }; diff --git a/src/hash/merkle_proofs.rs b/src/hash/merkle_proofs.rs index 3e265979..3391bbff 100644 --- a/src/hash/merkle_proofs.rs +++ b/src/hash/merkle_proofs.rs @@ -55,7 +55,6 @@ pub(crate) fn verify_merkle_proof( impl, const D: usize> CircuitBuilder { /// Verifies that the given leaf data is present at the given index in the Merkle tree with the /// given cap. The index is given by it's little-endian bits. - /// Note: Works only for D=4. pub(crate) fn verify_merkle_proof( &mut self, leaf_data: Vec, @@ -75,10 +74,17 @@ impl, const D: usize> CircuitBuilder { } let index = self.le_sum(leaf_index_bits[proof.siblings.len()..].to_vec().into_iter()); + + for i in 0..4 { + self.random_access( + index, + state.elements[i], + merkle_cap.0.iter().map(|h| h.elements[i]).collect(), + ); + } } - /// Same a `verify_merkle_proof` but with the final "cap index" as extra parameter. - /// Note: Works only for D=4. + /// Same as `verify_merkle_proof` but with the final "cap index" as extra parameter. pub(crate) fn verify_merkle_proof_with_cap_index( &mut self, leaf_data: Vec, From c7674b24ba43345eb572e997f71acf05aca1022f Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 18 Oct 2021 17:27:22 +0200 Subject: [PATCH 7/9] Unused imports --- src/gates/random_access.rs | 7 +++---- src/hash/merkle_proofs.rs | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/gates/random_access.rs b/src/gates/random_access.rs index 5bddf63c..9cc283de 100644 --- a/src/gates/random_access.rs +++ b/src/gates/random_access.rs @@ -1,8 +1,7 @@ use std::marker::PhantomData; -use std::ops::Range; use crate::field::extension_field::target::ExtensionTarget; -use crate::field::extension_field::{Extendable, FieldExtension}; +use crate::field::extension_field::Extendable; use crate::field::field_types::{Field, RichField}; use crate::gates::gate::Gate; use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator}; @@ -113,7 +112,7 @@ impl, const D: usize> Gate for RandomAccessGa 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)); + constraints.push((list_items[i] - claimed_element) * index_matches); } } @@ -140,7 +139,7 @@ impl, const D: usize> Gate for RandomAccessGa 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)); + constraints.push((list_items[i] - claimed_element) * index_matches); } } diff --git a/src/hash/merkle_proofs.rs b/src/hash/merkle_proofs.rs index 3391bbff..ac40ffef 100644 --- a/src/hash/merkle_proofs.rs +++ b/src/hash/merkle_proofs.rs @@ -3,7 +3,6 @@ use std::convert::TryInto; use anyhow::{ensure, Result}; use serde::{Deserialize, Serialize}; -use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field_types::{Field, RichField}; use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget}; From 5f4a2442409eb4770052931a1a0ddf17ba7c364a Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 18 Oct 2021 21:38:57 +0200 Subject: [PATCH 8/9] PR feedback --- src/fri/recursive_verifier.rs | 15 ++++++++------- src/gates/random_access.rs | 4 ++-- src/plonk/circuit_builder.rs | 7 +------ 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index 0115277e..b268e831 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -59,11 +59,12 @@ impl, const D: usize> CircuitBuilder { /// 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_config(&self, arity: usize) { - // TODO: It would be nice to remove the hardcoded 8 here and replace it with the maximum arity bits - // used in FRI. - let random_access = RandomAccessGate::::new_from_config(&self.config, 8); - let interpolation_gate = InterpolationGate::::new(arity); + fn check_config(&self, max_fri_arity: usize) { + let random_access = RandomAccessGate::::new_from_config( + &self.config, + max_fri_arity.max(1 << self.config.cap_height), + ); + let interpolation_gate = InterpolationGate::::new(max_fri_arity); let min_wires = random_access .num_wires() @@ -75,14 +76,14 @@ impl, const D: usize> CircuitBuilder { assert!( self.config.num_wires >= min_wires, "To efficiently perform FRI checks with an arity of {}, at least {} wires are needed. Consider reducing arity.", - arity, + max_fri_arity, 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.", - arity, + max_fri_arity, min_routed_wires ); } diff --git a/src/gates/random_access.rs b/src/gates/random_access.rs index 9cc283de..ea4aff97 100644 --- a/src/gates/random_access.rs +++ b/src/gates/random_access.rs @@ -37,8 +37,8 @@ impl, const D: usize> RandomAccessGate { 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 + 4*vec_size) * num_copies` wires - num_wires / (2 + 4 * vec_size), + // Need `(2 + 3*vec_size) * num_copies` wires + num_wires / (2 + 3 * vec_size), ) } diff --git a/src/plonk/circuit_builder.rs b/src/plonk/circuit_builder.rs index 4b392be9..ac8f01f1 100644 --- a/src/plonk/circuit_builder.rs +++ b/src/plonk/circuit_builder.rs @@ -540,12 +540,7 @@ impl, const D: usize> CircuitBuilder { /// `RandomAccessGenerator`s are run. fn fill_random_access_gates(&mut self) { let zero = self.zero(); - let remaining_random_access_gates = self - .free_random_access - .clone() - .into_iter() - .collect::>(); - for (vec_size, (_, i)) in remaining_random_access_gates { + for (vec_size, (_, i)) in self.free_random_access.clone() { let max_copies = RandomAccessGate::::max_num_copies( self.config.num_routed_wires, self.config.num_wires, From e24285c3b0fdb9b94b298561e4b32e7bd4c2f68e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 18 Oct 2021 21:48:40 +0200 Subject: [PATCH 9/9] Separate random access generators --- src/gates/random_access.rs | 78 ++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/src/gates/random_access.rs b/src/gates/random_access.rs index ea4aff97..71851725 100644 --- a/src/gates/random_access.rs +++ b/src/gates/random_access.rs @@ -13,7 +13,7 @@ use crate::plonk::circuit_data::CircuitConfig; use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; /// A gate for checking that a particular element of a list matches a given value. -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub(crate) struct RandomAccessGate, const D: usize> { pub vec_size: usize, pub num_copies: usize, @@ -191,11 +191,19 @@ impl, const D: usize> Gate for RandomAccessGa gate_index: usize, _local_constants: &[F], ) -> Vec>> { - let gen = RandomAccessGenerator:: { - gate_index, - gate: self.clone(), - }; - vec![Box::new(gen.adapter())] + (0..self.num_copies) + .map(|copy| { + let g: Box> = Box::new( + RandomAccessGenerator { + gate_index, + gate: *self, + copy, + } + .adapter(), + ); + g + }) + .collect::>() } fn num_wires(&self) -> usize { @@ -219,6 +227,7 @@ impl, const D: usize> Gate for RandomAccessGa struct RandomAccessGenerator, const D: usize> { gate_index: usize, gate: RandomAccessGate, + copy: usize, } impl, const D: usize> SimpleGenerator @@ -228,12 +237,10 @@ impl, const D: usize> SimpleGenerator let local_target = |input| Target::wire(self.gate_index, input); let mut deps = Vec::new(); - for copy in 0..self.gate.num_copies { - deps.push(local_target(self.gate.wire_access_index(copy))); - deps.push(local_target(self.gate.wire_claimed_element(copy))); - for i in 0..self.gate.vec_size { - deps.push(local_target(self.gate.wire_list_item(i, copy))); - } + 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 { + deps.push(local_target(self.gate.wire_list_item(i, self.copy))); } deps } @@ -248,34 +255,31 @@ impl, const D: usize> SimpleGenerator // Compute the new vector and the values for equality_dummy and index_matches let vec_size = self.gate.vec_size; - for copy in 0..self.gate.num_copies { - let access_index_f = get_local_wire(self.gate.wire_access_index(copy)); + let access_index_f = get_local_wire(self.gate.wire_access_index(self.copy)); - let access_index = access_index_f.to_canonical_u64() as usize; - debug_assert!( - access_index < vec_size, - "Access index {} is larger than the vector size {}", - access_index, - vec_size - ); + let access_index = access_index_f.to_canonical_u64() as usize; + debug_assert!( + access_index < vec_size, + "Access index {} is larger than the vector size {}", + access_index, + vec_size + ); - for i in 0..vec_size { - let equality_dummy_wire = - local_wire(self.gate.wire_equality_dummy_for_index(i, copy)); - let index_matches_wire = - local_wire(self.gate.wire_index_matches_for_index(i, copy)); + 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)); - 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); - } + 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); } } }