From 322556260c250bd165187efa19be540279104507 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 14 Jun 2021 16:06:41 +0200 Subject: [PATCH 01/28] Cosmetic changes --- src/generator.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/generator.rs b/src/generator.rs index 443809b6..b6534921 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -17,17 +17,13 @@ pub(crate) fn generate_partial_witness( for watch in generator.watch_list() { generator_indices_by_watches .entry(watch) - .or_insert_with(Vec::new) - .push(i); + .or_insert_with(|| vec![i]); } } // Build a list of "pending" generators which are queued to be run. Initially, all generators // are queued. - let mut pending_generator_indices = HashSet::new(); - for i in 0..generators.len() { - pending_generator_indices.insert(i); - } + let mut pending_generator_indices: HashSet<_> = (0..generators.len()).collect(); // We also track a list of "expired" generators which have already returned false. let mut expired_generator_indices = HashSet::new(); From ea6a724560392b2422be85cce1988093c6debfbf Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 14 Jun 2021 17:02:52 +0200 Subject: [PATCH 02/28] Enforce copy constraints in partial witness generation. --- src/circuit_builder.rs | 11 ++++++++--- src/circuit_data.rs | 3 +++ src/gates/gmimc.rs | 10 +++++++++- src/generator.rs | 9 ++++++++- src/permutation_argument.rs | 21 +++++++++++++++++++++ src/plonk_challenger.rs | 9 +++++++-- src/prover.rs | 6 +++++- src/witness.rs | 4 ++++ 8 files changed, 65 insertions(+), 8 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 4150bb68..b9b2e533 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -236,7 +236,7 @@ impl, const D: usize> CircuitBuilder { .collect() } - fn sigma_vecs(&self, k_is: &[F]) -> Vec> { + fn sigma_vecs(&self, k_is: &[F]) -> (Vec>, TargetPartitions) { let degree = self.gate_instances.len(); let degree_log = log2_strict(degree); let mut target_partitions = TargetPartitions::new(); @@ -256,7 +256,11 @@ impl, const D: usize> CircuitBuilder { } let wire_partitions = target_partitions.to_wire_partitions(); - wire_partitions.get_sigma_polys(degree_log, k_is) + + ( + wire_partitions.get_sigma_polys(degree_log, k_is), + target_partitions, + ) } /// Builds a "full circuit", with both prover and verifier data. @@ -278,7 +282,7 @@ impl, const D: usize> CircuitBuilder { ); let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires); - let sigma_vecs = self.sigma_vecs(&k_is); + let (sigma_vecs, targets_partition) = self.sigma_vecs(&k_is); let sigmas_commitment = ListPolynomialCommitment::new( sigma_vecs.into_iter().map(|v| v.ifft()).collect(), self.config.fri_config.rate_bits, @@ -297,6 +301,7 @@ impl, const D: usize> CircuitBuilder { generators, constants_commitment, sigmas_commitment, + targets_partition, }; // The HashSet of gates will have a non-deterministic order. When converting to a Vec, we diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 4d9a7110..6b6d614a 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -5,6 +5,7 @@ use crate::field::field::Field; use crate::fri::FriConfig; use crate::gates::gate::GateRef; use crate::generator::WitnessGenerator; +use crate::permutation_argument::TargetPartitions; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::proof::{Hash, HashTarget, Proof}; use crate::prover::prove; @@ -104,6 +105,8 @@ pub(crate) struct ProverOnlyCircuitData { pub constants_commitment: ListPolynomialCommitment, /// Commitments to the sigma polynomial. pub sigmas_commitment: ListPolynomialCommitment, + /// Partition of the targets into copy-constrained sets. + pub targets_partition: TargetPartitions, } /// Circuit data required by the verifier, but not the prover. diff --git a/src/gates/gmimc.rs b/src/gates/gmimc.rs index 19042d57..ac5741e4 100644 --- a/src/gates/gmimc.rs +++ b/src/gates/gmimc.rs @@ -323,6 +323,8 @@ mod tests { use crate::gates::gmimc::{GMiMCGate, W}; use crate::generator::generate_partial_witness; use crate::gmimc::gmimc_permute_naive; + use crate::permutation_argument::TargetPartitions; + use crate::target::Target; use crate::wire::Wire; use crate::witness::PartialWitness; @@ -368,7 +370,13 @@ mod tests { } let generators = gate.0.generators(0, &[]); - generate_partial_witness(&mut witness, &generators); + let mut tp = TargetPartitions::new(); + for g in 0..10 { + for i in 0..config.num_routed_wires { + tp.add_partition(Target::wire(g, i)); + } + } + generate_partial_witness(&mut witness, &generators, &tp); let expected_outputs: [F; W] = gmimc_permute_naive(permutation_inputs.try_into().unwrap(), constants); diff --git a/src/generator.rs b/src/generator.rs index b6534921..b04442b5 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use crate::field::field::Field; +use crate::permutation_argument::TargetPartitions; use crate::target::Target; use crate::witness::PartialWitness; @@ -10,6 +11,7 @@ use crate::witness::PartialWitness; pub(crate) fn generate_partial_witness( witness: &mut PartialWitness, generators: &[Box>], + target_partition: &TargetPartitions, ) { // Index generator indices by their watched targets. let mut generator_indices_by_watches = HashMap::new(); @@ -21,6 +23,8 @@ pub(crate) fn generate_partial_witness( } } + target_partition.generate_copies(witness, &witness.all_populated_targets()); + // Build a list of "pending" generators which are queued to be run. Initially, all generators // are queued. let mut pending_generator_indices: HashSet<_> = (0..generators.len()).collect(); @@ -33,11 +37,14 @@ pub(crate) fn generate_partial_witness( let mut next_pending_generator_indices = HashSet::new(); for &generator_idx in &pending_generator_indices { - let (result, finished) = generators[generator_idx].run(&witness); + let (mut result, finished) = generators[generator_idx].run(&witness); if finished { expired_generator_indices.insert(generator_idx); } + let new_targets = result.all_populated_targets(); + target_partition.generate_copies(&mut result, &new_targets); + // Enqueue unfinished generators that were watching one of the newly populated targets. for watch in result.target_values.keys() { if let Some(watching_generator_indices) = generator_indices_by_watches.get(watch) { diff --git a/src/permutation_argument.rs b/src/permutation_argument.rs index 62ee63d4..f2739756 100644 --- a/src/permutation_argument.rs +++ b/src/permutation_argument.rs @@ -6,6 +6,7 @@ use crate::field::field::Field; use crate::polynomial::polynomial::PolynomialValues; use crate::target::Target; use crate::wire::Wire; +use crate::witness::PartialWitness; #[derive(Debug, Clone)] pub struct TargetPartitions { @@ -82,6 +83,26 @@ impl TargetPartitions { indices, } } + /// For the given set of targets, find any copy constraints involving those targets and populate + /// the witness with copies as needed. + pub fn generate_copies(&self, witness: &mut PartialWitness, targets: &[Target]) { + let mut result = PartialWitness::new(); + + for &target in targets { + let value = witness.get_target(target); + let partition = self.get_partition(target); + + for &sibling in partition { + if witness.contains(sibling) { + // This sibling's value was already set; make sure it has the same value. + assert_eq!(witness.get_target(sibling), value); + } else { + result.set_target(sibling, value); + } + } + } + witness.extend(result); + } } pub struct WirePartitions { diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index fd21ee0d..30d4cf70 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -289,6 +289,7 @@ mod tests { use crate::field::crandall_field::CrandallField; use crate::field::field::Field; use crate::generator::generate_partial_witness; + use crate::permutation_argument::TargetPartitions; use crate::plonk_challenger::{Challenger, RecursiveChallenger}; use crate::target::Target; use crate::witness::PartialWitness; @@ -337,7 +338,7 @@ mod tests { let config = CircuitConfig { num_wires: 12 + 12 + 3 + 101, - num_routed_wires: 27, + num_routed_wires: 200, ..CircuitConfig::default() }; let mut builder = CircuitBuilder::::new(config); @@ -351,7 +352,11 @@ mod tests { } let circuit = builder.build(); let mut witness = PartialWitness::new(); - generate_partial_witness(&mut witness, &circuit.prover_only.generators); + generate_partial_witness( + &mut witness, + &circuit.prover_only.generators, + &circuit.prover_only.targets_partition, + ); let recursive_output_values_per_round: Vec> = recursive_outputs_per_round .iter() .map(|outputs| witness.get_targets(outputs)) diff --git a/src/prover.rs b/src/prover.rs index 7a492eaa..fc4e6f38 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -34,7 +34,11 @@ pub(crate) fn prove, const D: usize>( let mut witness = inputs; info!("Running {} generators", prover_data.generators.len()); timed!( - generate_partial_witness(&mut witness, &prover_data.generators), + generate_partial_witness( + &mut witness, + &prover_data.generators, + &prover_data.targets_partition + ), "to generate witness" ); diff --git a/src/witness.rs b/src/witness.rs index a0b4b2a4..e71eebc9 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -59,6 +59,10 @@ impl PartialWitness { targets.iter().all(|&t| self.contains(t)) } + pub fn all_populated_targets(&self) -> Vec { + self.target_values.keys().cloned().collect() + } + pub fn set_target(&mut self, target: Target, value: F) { let opt_old_value = self.target_values.insert(target, value); if let Some(old_value) = opt_old_value { From 86b4b0ab41295df4d133c5803ccca8125dd25e98 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 14 Jun 2021 17:06:53 +0200 Subject: [PATCH 03/28] Add check that all generators were run. --- src/generator.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/generator.rs b/src/generator.rs index b04442b5..a5ba4eaf 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -61,6 +61,11 @@ pub(crate) fn generate_partial_witness( pending_generator_indices = next_pending_generator_indices; } + assert_eq!( + expired_generator_indices.len(), + generators.len(), + "Some generators weren't run." + ); } /// A generator participates in the generation of the witness. From c22bc6261ed9c36288300acbf833fdaca0cefb84 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 14 Jun 2021 21:03:50 +0200 Subject: [PATCH 04/28] Revert watch insertion --- src/generator.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/generator.rs b/src/generator.rs index a5ba4eaf..5b6a258a 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -19,7 +19,8 @@ pub(crate) fn generate_partial_witness( for watch in generator.watch_list() { generator_indices_by_watches .entry(watch) - .or_insert_with(|| vec![i]); + .or_insert_with(Vec::new) + .push(i) } } From da3d34a0d4ecb83fb3a430250710609c2ac25c90 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 22 Jun 2021 14:31:46 +0200 Subject: [PATCH 05/28] Working gate tree generation --- src/circuit_builder.rs | 7 ++ src/gates/gate.rs | 7 ++ src/gates/gate_tree.rs | 156 +++++++++++++++++++++++++++++++++++++++++ src/gates/mod.rs | 1 + 4 files changed, 171 insertions(+) create mode 100644 src/gates/gate_tree.rs diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 3a26497e..232c9c20 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -12,6 +12,7 @@ use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::gates::constant::ConstantGate; use crate::gates::gate::{GateInstance, GateRef}; +use crate::gates::gate_tree::{GatePrefixes, Tree}; use crate::gates::noop::NoopGate; use crate::generator::{CopyGenerator, WitnessGenerator}; use crate::hash::hash_n_to_hash; @@ -279,6 +280,12 @@ impl, const D: usize> CircuitBuilder { /// Builds a "full circuit", with both prover and verifier data. pub fn build(mut self) -> CircuitData { + let gates = self.gates.iter().cloned().collect(); + let tree = Tree::from_gates(gates); + let prefixes = GatePrefixes::from(tree); + for (g, p) in &prefixes.prefixes { + println!("{}: {:?}", g.0.id(), p); + } let start = Instant::now(); info!( "degree before blinding & padding: {}", diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 1765191e..d27c8f8b 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -1,3 +1,4 @@ +use std::fmt::{Debug, Error, Formatter}; use std::hash::{Hash, Hasher}; use std::sync::Arc; @@ -113,6 +114,12 @@ impl, const D: usize> Hash for GateRef { impl, const D: usize> Eq for GateRef {} +impl, const D: usize> Debug for GateRef { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + write!(f, "{}", self.0.id()) + } +} + /// A gate along with any constants used to configure it. pub struct GateInstance, const D: usize> { pub gate_type: GateRef, diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs new file mode 100644 index 00000000..39b3b9c2 --- /dev/null +++ b/src/gates/gate_tree.rs @@ -0,0 +1,156 @@ +use std::collections::HashMap; +use std::iter::FromIterator; + +use crate::field::extension_field::Extendable; +use crate::field::field::Field; +use crate::gates::gate::GateRef; + +#[derive(Debug, Clone)] +enum Node { + Terminus(T), + Bifurcation, +} + +#[derive(Debug, Clone)] +pub struct Tree { + node: Node, + left: Option>>, + right: Option>>, +} + +impl Default for Tree { + fn default() -> Self { + Self { + node: Node::Bifurcation, + left: None, + right: None, + } + } +} + +impl Tree { + pub fn preorder_traversal(&self) -> Vec<(T, Vec)> { + let mut res = Vec::new(); + let prefix = []; + self.traverse(&prefix, &mut res); + res + } + + fn traverse(&self, prefix: &[bool], current: &mut Vec<(T, Vec)>) { + if let Node::Terminus(t) = &self.node { + current.push((t.clone(), prefix.to_vec())); + } else { + if let Some(l) = &self.left { + let mut left_prefix = prefix.to_vec(); + left_prefix.push(false); + l.traverse(&left_prefix, current); + } + if let Some(r) = &self.right { + let mut right_prefix = prefix.to_vec(); + right_prefix.push(true); + r.traverse(&right_prefix, current); + } + } + } +} + +#[derive(Clone)] +pub struct GatePrefixes, const D: usize> { + pub prefixes: HashMap, Vec>, +} + +impl, const D: usize> From>> for GatePrefixes { + fn from(tree: Tree>) -> Self { + GatePrefixes { + prefixes: HashMap::from_iter(tree.preorder_traversal()), + } + } +} + +impl, const D: usize> Tree> { + pub fn from_gates(mut gates: Vec>) -> Self { + let timer = std::time::Instant::now(); + gates.sort_unstable_by_key(|g| -((g.0.degree() + g.0.num_constants()) as isize)); + + for max_degree in 1..100 { + if let Some(mut tree) = Self::find_tree(&gates, max_degree) { + tree.prune(); + println!( + "Found tree with max degree {} in {}s.", + max_degree, + timer.elapsed().as_secs_f32() + ); + return tree; + } + } + + panic!("Can't find a tree.") + } + + fn find_tree(gates: &[GateRef], max_degree: usize) -> Option { + let mut tree = Tree::default(); + + for g in gates { + tree.try_add_gate(g, max_degree)?; + } + Some(tree) + } + + fn try_add_gate(&mut self, g: &GateRef, max_degree: usize) -> Option<()> { + let depth = max_degree.checked_sub(g.0.num_constants() + g.0.degree())?; + self.try_add_gate_at_depth(g, depth).is_err().then(|| ()) + } + + fn try_add_gate_at_depth(&mut self, g: &GateRef, depth: usize) -> Result<(), GateAdded> { + if depth == 0 { + return if let Node::Bifurcation = self.node { + self.node = Node::Terminus(g.clone()); + Err(GateAdded) + } else { + Ok(()) + }; + } + + if let Node::Terminus(_) = self.node { + return Ok(()); + } + + if let Some(left) = &mut self.left { + left.try_add_gate_at_depth(g, depth - 1)?; + } else { + let mut left = Tree::default(); + if left.try_add_gate_at_depth(g, depth - 1).is_err() { + self.left = Some(Box::new(left)); + return Err(GateAdded); + } + } + if let Some(right) = &mut self.right { + right.try_add_gate_at_depth(g, depth - 1)?; + } else { + let mut right = Tree::default(); + if right.try_add_gate_at_depth(g, depth - 1).is_err() { + self.right = Some(Box::new(right)); + return Err(GateAdded); + } + } + + Ok(()) + } + + fn prune(&mut self) { + if let (Some(left), None) = (&self.left, &self.right) { + debug_assert!(matches!(self.node, Node::Bifurcation)); + let mut new = *left.clone(); + new.prune(); + *self = new; + } + if let Some(left) = &mut self.left { + left.prune(); + } + if let Some(right) = &mut self.right { + right.prune(); + } + } +} + +struct GateAdded; diff --git a/src/gates/mod.rs b/src/gates/mod.rs index d013c7cd..bb8b178b 100644 --- a/src/gates/mod.rs +++ b/src/gates/mod.rs @@ -2,6 +2,7 @@ pub(crate) mod arithmetic; pub mod base_sum; pub mod constant; pub(crate) mod gate; +pub mod gate_tree; pub mod gmimc; pub mod interpolation; pub mod mul_extension; From 1983600169bb728fea8abaf26df48299052807b3 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 22 Jun 2021 14:50:08 +0200 Subject: [PATCH 06/28] Change tree from struct to enum --- src/gates/gate_tree.rs | 115 ++++++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 54 deletions(-) diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs index 39b3b9c2..fd3d8c33 100644 --- a/src/gates/gate_tree.rs +++ b/src/gates/gate_tree.rs @@ -12,19 +12,14 @@ enum Node { } #[derive(Debug, Clone)] -pub struct Tree { - node: Node, - left: Option>>, - right: Option>>, +pub enum Tree { + Node(T), + Bifurcation(Option>>, Option>>), } impl Default for Tree { fn default() -> Self { - Self { - node: Node::Bifurcation, - left: None, - right: None, - } + Self::Bifurcation(None, None) } } @@ -37,18 +32,21 @@ impl Tree { } fn traverse(&self, prefix: &[bool], current: &mut Vec<(T, Vec)>) { - if let Node::Terminus(t) = &self.node { - current.push((t.clone(), prefix.to_vec())); - } else { - if let Some(l) = &self.left { - let mut left_prefix = prefix.to_vec(); - left_prefix.push(false); - l.traverse(&left_prefix, current); + match &self { + Tree::Node(t) => { + current.push((t.clone(), prefix.to_vec())); } - if let Some(r) = &self.right { - let mut right_prefix = prefix.to_vec(); - right_prefix.push(true); - r.traverse(&right_prefix, current); + Tree::Bifurcation(left, right) => { + if let Some(l) = left { + let mut left_prefix = prefix.to_vec(); + left_prefix.push(false); + l.traverse(&left_prefix, current); + } + if let Some(r) = right { + let mut right_prefix = prefix.to_vec(); + right_prefix.push(true); + r.traverse(&right_prefix, current); + } } } } @@ -98,57 +96,66 @@ impl, const D: usize> Tree> { fn try_add_gate(&mut self, g: &GateRef, max_degree: usize) -> Option<()> { let depth = max_degree.checked_sub(g.0.num_constants() + g.0.degree())?; - self.try_add_gate_at_depth(g, depth).is_err().then(|| ()) + self.try_add_gate_at_depth(g, depth) } - fn try_add_gate_at_depth(&mut self, g: &GateRef, depth: usize) -> Result<(), GateAdded> { + fn try_add_gate_at_depth(&mut self, g: &GateRef, depth: usize) -> Option<()> { if depth == 0 { - return if let Node::Bifurcation = self.node { - self.node = Node::Terminus(g.clone()); - Err(GateAdded) + return if let Tree::Bifurcation(_, _) = self { + *self = Tree::Node(g.clone()); + Some(()) } else { - Ok(()) + None }; } - if let Node::Terminus(_) = self.node { - return Ok(()); + if let Tree::Node(_) = self { + return None; } - if let Some(left) = &mut self.left { - left.try_add_gate_at_depth(g, depth - 1)?; - } else { - let mut left = Tree::default(); - if left.try_add_gate_at_depth(g, depth - 1).is_err() { - self.left = Some(Box::new(left)); - return Err(GateAdded); + if let Tree::Bifurcation(left, right) = self { + if let Some(left) = left { + if left.try_add_gate_at_depth(g, depth - 1).is_some() { + return Some(()); + } + } else { + let mut new_left = Tree::default(); + if new_left.try_add_gate_at_depth(g, depth - 1).is_some() { + *left = Some(Box::new(new_left)); + return Some(()); + } } - } - if let Some(right) = &mut self.right { - right.try_add_gate_at_depth(g, depth - 1)?; - } else { - let mut right = Tree::default(); - if right.try_add_gate_at_depth(g, depth - 1).is_err() { - self.right = Some(Box::new(right)); - return Err(GateAdded); + if let Some(right) = right { + if right.try_add_gate_at_depth(g, depth - 1).is_some() { + return Some(()); + } + } else { + let mut new_right = Tree::default(); + if new_right.try_add_gate_at_depth(g, depth - 1).is_some() { + *right = Some(Box::new(new_right)); + return Some(()); + } } } - Ok(()) + None } fn prune(&mut self) { - if let (Some(left), None) = (&self.left, &self.right) { - debug_assert!(matches!(self.node, Node::Bifurcation)); - let mut new = *left.clone(); - new.prune(); - *self = new; + if let Tree::Bifurcation(left, right) = self { + if let (Some(left), None) = (left, right) { + let mut new = *left.clone(); + new.prune(); + *self = new; + } } - if let Some(left) = &mut self.left { - left.prune(); - } - if let Some(right) = &mut self.right { - right.prune(); + if let Tree::Bifurcation(left, right) = self { + if let Some(left) = left { + left.prune(); + } + if let Some(right) = right { + right.prune(); + } } } } From cfa3d3a6609401d52d1acefd5541bceeee5dd862 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 22 Jun 2021 15:34:50 +0200 Subject: [PATCH 07/28] Added comments --- src/circuit_builder.rs | 4 +- src/field/extension_field/mod.rs | 3 +- src/gates/gate.rs | 17 +++++++ src/gates/gate_tree.rs | 76 +++++++++++++++++--------------- src/plonk_challenger.rs | 3 +- src/target.rs | 3 +- src/wire.rs | 3 +- src/witness.rs | 2 +- 8 files changed, 69 insertions(+), 42 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 232c9c20..c29fda79 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -11,8 +11,8 @@ use crate::field::cosets::get_unique_coset_shifts; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::gates::constant::ConstantGate; -use crate::gates::gate::{GateInstance, GateRef}; -use crate::gates::gate_tree::{GatePrefixes, Tree}; +use crate::gates::gate::{GateInstance, GatePrefixes, GateRef}; +use crate::gates::gate_tree::Tree; use crate::gates::noop::NoopGate; use crate::generator::{CopyGenerator, WitnessGenerator}; use crate::hash::hash_n_to_hash; diff --git a/src/field/extension_field/mod.rs b/src/field/extension_field/mod.rs index 9caa7dc8..2a176fe9 100644 --- a/src/field/extension_field/mod.rs +++ b/src/field/extension_field/mod.rs @@ -1,6 +1,7 @@ -use crate::field::field::Field; use std::convert::TryInto; +use crate::field::field::Field; + pub mod algebra; pub mod quadratic; pub mod quartic; diff --git a/src/gates/gate.rs b/src/gates/gate.rs index d27c8f8b..9219d5dc 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -1,10 +1,13 @@ +use std::collections::HashMap; use std::fmt::{Debug, Error, Formatter}; use std::hash::{Hash, Hasher}; +use std::iter::FromIterator; use std::sync::Arc; use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; +use crate::gates::gate_tree::Tree; use crate::generator::WitnessGenerator; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; @@ -125,3 +128,17 @@ pub struct GateInstance, const D: usize> { pub gate_type: GateRef, pub constants: Vec, } + +/// Map each gate to a boolean prefix used to construct the gate's selector polynomial. +#[derive(Debug, Clone)] +pub struct GatePrefixes, const D: usize> { + pub prefixes: HashMap, Vec>, +} + +impl, const D: usize> From>> for GatePrefixes { + fn from(tree: Tree>) -> Self { + GatePrefixes { + prefixes: HashMap::from_iter(tree.traversal()), + } + } +} diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs index fd3d8c33..7ece765e 100644 --- a/src/gates/gate_tree.rs +++ b/src/gates/gate_tree.rs @@ -1,19 +1,10 @@ -use std::collections::HashMap; -use std::iter::FromIterator; - use crate::field::extension_field::Extendable; -use crate::field::field::Field; use crate::gates::gate::GateRef; -#[derive(Debug, Clone)] -enum Node { - Terminus(T), - Bifurcation, -} - +/// A binary tree where leaves hold some type `T` and other nodes are empty. #[derive(Debug, Clone)] pub enum Tree { - Node(T), + Leaf(T), Bifurcation(Option>>, Option>>), } @@ -24,18 +15,23 @@ impl Default for Tree { } impl Tree { - pub fn preorder_traversal(&self) -> Vec<(T, Vec)> { + /// Traverse a tree using a depth-first traversal and collect data and position for each leaf. + /// A leaf's position is represented by its left/right path, where `false` means left and `true` means right. + pub fn traversal(&self) -> Vec<(T, Vec)> { let mut res = Vec::new(); let prefix = []; self.traverse(&prefix, &mut res); res } + /// Utility function to traverse the tree. fn traverse(&self, prefix: &[bool], current: &mut Vec<(T, Vec)>) { match &self { - Tree::Node(t) => { + // If node is a leaf, collect the data and position. + Tree::Leaf(t) => { current.push((t.clone(), prefix.to_vec())); } + // Otherwise, traverse the left subtree and then the right subtree. Tree::Bifurcation(left, right) => { if let Some(l) = left { let mut left_prefix = prefix.to_vec(); @@ -52,27 +48,22 @@ impl Tree { } } -#[derive(Clone)] -pub struct GatePrefixes, const D: usize> { - pub prefixes: HashMap, Vec>, -} - -impl, const D: usize> From>> for GatePrefixes { - fn from(tree: Tree>) -> Self { - GatePrefixes { - prefixes: HashMap::from_iter(tree.preorder_traversal()), - } - } -} - impl, const D: usize> Tree> { + /// Construct a binary tree of gates using the following greedy algorithm: + /// We want a tree where the maximum `M` of + /// `F(gate) = gate.degree() + gate.num_constants() + tree.depth(gate)` + /// over all gates is minimized. Such a tree is constructed by iterating over possible values of `M` + /// (from 1 to 99, then we give up) and then looking for a tree with this value of `M` + /// using `Self::find_tree`. This latter function greedily adds gates at the depth where + /// `F(gate)=M` to ensure no space is wasted. We return the first tree found in this manner, + /// i.e., the one with minimal `M` value. pub fn from_gates(mut gates: Vec>) -> Self { let timer = std::time::Instant::now(); gates.sort_unstable_by_key(|g| -((g.0.degree() + g.0.num_constants()) as isize)); for max_degree in 1..100 { if let Some(mut tree) = Self::find_tree(&gates, max_degree) { - tree.prune(); + tree.shorten(); println!( "Found tree with max degree {} in {}s.", max_degree, @@ -85,6 +76,7 @@ impl, const D: usize> Tree> { panic!("Can't find a tree.") } + /// Greedily add gates wherever possible. Returns `None` if this fails. fn find_tree(gates: &[GateRef], max_degree: usize) -> Option { let mut tree = Tree::default(); @@ -94,31 +86,39 @@ impl, const D: usize> Tree> { Some(tree) } + /// Try to add a gate in the tree. Returns `None` if this fails. fn try_add_gate(&mut self, g: &GateRef, max_degree: usize) -> Option<()> { let depth = max_degree.checked_sub(g.0.num_constants() + g.0.degree())?; self.try_add_gate_at_depth(g, depth) } + /// Try to add a gate in the tree at a specified depth. Returns `None` if this fails. fn try_add_gate_at_depth(&mut self, g: &GateRef, depth: usize) -> Option<()> { + // If depth is 0, we have to insert the gate here. if depth == 0 { return if let Tree::Bifurcation(_, _) = self { - *self = Tree::Node(g.clone()); + // Insert the gate as a new leaf. + *self = Tree::Leaf(g.clone()); Some(()) } else { + // A leaf is already here. None }; } - if let Tree::Node(_) = self { + // A leaf is already here so we cannot go deeper. + if let Tree::Leaf(_) = self { return None; } if let Tree::Bifurcation(left, right) = self { if let Some(left) = left { + // Try to add the gate to the left if there's already a left subtree. if left.try_add_gate_at_depth(g, depth - 1).is_some() { return Some(()); } } else { + // Add a new left subtree and try to add the gate to it. let mut new_left = Tree::default(); if new_left.try_add_gate_at_depth(g, depth - 1).is_some() { *left = Some(Box::new(new_left)); @@ -126,10 +126,12 @@ impl, const D: usize> Tree> { } } if let Some(right) = right { + // Try to add the gate to the right if there's already a right subtree. if right.try_add_gate_at_depth(g, depth - 1).is_some() { return Some(()); } } else { + // Add a new right subtree and try to add the gate to it. let mut new_right = Tree::default(); if new_right.try_add_gate_at_depth(g, depth - 1).is_some() { *right = Some(Box::new(new_right)); @@ -141,23 +143,27 @@ impl, const D: usize> Tree> { None } - fn prune(&mut self) { + /// `Self::find_tree` returns a tree where each gate has `F(gate)=M` (see `Self::from_gates` comment). + /// This can produce subtrees with more nodes than necessary. This function removes useless nodes, + /// i.e., nodes that have a left but no right subtree. + fn shorten(&mut self) { if let Tree::Bifurcation(left, right) = self { if let (Some(left), None) = (left, right) { + // If the node has a left but no right subtree, set the node to its (shortened) left subtree. let mut new = *left.clone(); - new.prune(); + new.shorten(); *self = new; } } if let Tree::Bifurcation(left, right) = self { if let Some(left) = left { - left.prune(); + // Shorten the left subtree if there is one. + left.shorten(); } if let Some(right) = right { - right.prune(); + // Shorten the right subtree if there is one. + right.shorten(); } } } } - -struct GateAdded; diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index b24bbde8..b66b422b 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; @@ -5,7 +7,6 @@ use crate::field::field::Field; use crate::hash::{permute, SPONGE_RATE, SPONGE_WIDTH}; use crate::proof::{Hash, HashTarget, OpeningSet}; use crate::target::Target; -use std::convert::TryInto; /// Observes prover messages, and generates challenges by hashing the transcript. #[derive(Clone)] diff --git a/src/target.rs b/src/target.rs index 423865fa..8aec0b5a 100644 --- a/src/target.rs +++ b/src/target.rs @@ -1,6 +1,7 @@ +use std::ops::Range; + use crate::circuit_data::CircuitConfig; use crate::wire::Wire; -use std::ops::Range; /// A location in the witness. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] diff --git a/src/wire.rs b/src/wire.rs index 02d43029..f63a19c1 100644 --- a/src/wire.rs +++ b/src/wire.rs @@ -1,6 +1,7 @@ -use crate::circuit_data::CircuitConfig; use std::ops::Range; +use crate::circuit_data::CircuitConfig; + /// Represents a wire in the circuit. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct Wire { diff --git a/src/witness.rs b/src/witness.rs index e71b1cfb..5df8b238 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -1,11 +1,11 @@ use std::collections::HashMap; +use std::convert::TryInto; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; use crate::target::Target; use crate::wire::Wire; -use std::convert::TryInto; #[derive(Clone, Debug)] pub struct PartialWitness { From aec6f217922a81ad79a46e74aade7c1c255fd0d7 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 22 Jun 2021 16:07:35 +0200 Subject: [PATCH 08/28] Test with tree from all gates. --- src/circuit_builder.rs | 9 +----- src/gates/gate_tree.rs | 68 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index c29fda79..3a26497e 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -11,8 +11,7 @@ use crate::field::cosets::get_unique_coset_shifts; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::gates::constant::ConstantGate; -use crate::gates::gate::{GateInstance, GatePrefixes, GateRef}; -use crate::gates::gate_tree::Tree; +use crate::gates::gate::{GateInstance, GateRef}; use crate::gates::noop::NoopGate; use crate::generator::{CopyGenerator, WitnessGenerator}; use crate::hash::hash_n_to_hash; @@ -280,12 +279,6 @@ impl, const D: usize> CircuitBuilder { /// Builds a "full circuit", with both prover and verifier data. pub fn build(mut self) -> CircuitData { - let gates = self.gates.iter().cloned().collect(); - let tree = Tree::from_gates(gates); - let prefixes = GatePrefixes::from(tree); - for (g, p) in &prefixes.prefixes { - println!("{}: {:?}", g.0.id(), p); - } let start = Instant::now(); info!( "degree before blinding & padding: {}", diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs index 7ece765e..8081ab25 100644 --- a/src/gates/gate_tree.rs +++ b/src/gates/gate_tree.rs @@ -167,3 +167,71 @@ impl, const D: usize> Tree> { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::field::crandall_field::CrandallField; + use crate::gates::arithmetic::ArithmeticGate; + use crate::gates::base_sum::BaseSumGate; + use crate::gates::constant::ConstantGate; + use crate::gates::gmimc::GMiMCGate; + use crate::gates::interpolation::InterpolationGate; + use crate::gates::mul_extension::MulExtensionGate; + use crate::gates::noop::NoopGate; + use crate::hash::GMIMC_ROUNDS; + + #[test] + fn test_prefix_generation() { + type F = CrandallField; + const D: usize = 4; + + let gates = vec![ + NoopGate::get::(), + ConstantGate::get(), + ArithmeticGate::new(), + BaseSumGate::<4>::new(4), + GMiMCGate::::with_automatic_constants(), + InterpolationGate::new(4), + MulExtensionGate::new(), + ]; + let len = gates.len(); + + let tree = Tree::from_gates(gates.clone()); + let mut gates_with_prefix = tree.traversal(); + for (g, p) in &gates_with_prefix { + println!("{}: {:?}", g.0.id(), p); + } + + assert_eq!( + gates_with_prefix.len(), + gates.len(), + "The tree has too much or too little gates." + ); + assert!( + gates.iter().all(|g| gates_with_prefix + .iter() + .map(|(gg, _)| gg) + .find(|gg| *gg == g) + .is_some()), + "Some gates are not in the tree." + ); + assert!( + gates_with_prefix + .iter() + .all(|(g, p)| g.0.degree() + g.0.num_constants() + p.len() <= 8), + "Total degree is larger than 8." + ); + + gates_with_prefix.sort_unstable_by_key(|(g, p)| p.len()); + for i in 0..gates_with_prefix.len() { + for j in i + 1..gates_with_prefix.len() { + assert_ne!( + &gates_with_prefix[i].1, + &gates_with_prefix[j].1[0..gates_with_prefix[i].1.len()], + "Some gates share the same prefix" + ); + } + } + } +} From 5acbb674ad1ad13e6baf28a2c19180f7c43a1550 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 22 Jun 2021 16:54:20 +0200 Subject: [PATCH 09/28] Add prefix to constant polys --- src/circuit_builder.rs | 28 +++++++++++++++++++--------- src/gates/gate.rs | 14 ++++++++++++++ src/gates/gate_tree.rs | 7 +++---- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 3a26497e..c91ef470 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -11,7 +11,8 @@ use crate::field::cosets::get_unique_coset_shifts; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::gates::constant::ConstantGate; -use crate::gates::gate::{GateInstance, GateRef}; +use crate::gates::gate::{GateInstance, GatePrefixes, GateRef}; +use crate::gates::gate_tree::Tree; use crate::gates::noop::NoopGate; use crate::generator::{CopyGenerator, WitnessGenerator}; use crate::hash::hash_n_to_hash; @@ -229,22 +230,28 @@ impl, const D: usize> CircuitBuilder { } } - fn constant_polys(&self) -> Vec> { + fn constant_polys(&self, prefixes: &GatePrefixes) -> Vec> { let num_constants = self .gate_instances .iter() - .map(|gate_inst| gate_inst.constants.len()) + .map(|gate_inst| gate_inst.constants.len() + prefixes[&gate_inst.gate_type].len()) .max() .unwrap(); let constants_per_gate = self .gate_instances .iter() .map(|gate_inst| { - let mut padded_constants = gate_inst.constants.clone(); - for _ in padded_constants.len()..num_constants { - padded_constants.push(F::ZERO); - } - padded_constants + let mut prefixed_constants = Vec::new(); + prefixed_constants.extend(prefixes[&gate_inst.gate_type].iter().map(|&b| { + if b { + F::ONE + } else { + F::ZERO + } + })); + prefixed_constants.extend_from_slice(&gate_inst.constants); + prefixed_constants.resize(num_constants, F::ZERO); + prefixed_constants }) .collect::>(); @@ -288,7 +295,10 @@ impl, const D: usize> CircuitBuilder { let degree = self.gate_instances.len(); info!("degree after blinding & padding: {}", degree); - let constant_vecs = self.constant_polys(); + let gates = self.gates.iter().cloned().collect(); + let gate_tree = Tree::from_gates(gates); + + let constant_vecs = self.constant_polys(&gate_tree.into()); let constants_commitment = ListPolynomialCommitment::new( constant_vecs.into_iter().map(|v| v.ifft()).collect(), self.config.fri_config.rate_bits, diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 9219d5dc..cf939d9d 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -1,7 +1,9 @@ +use std::borrow::Borrow; use std::collections::HashMap; use std::fmt::{Debug, Error, Formatter}; use std::hash::{Hash, Hasher}; use std::iter::FromIterator; +use std::ops::Index; use std::sync::Arc; use crate::circuit_builder::CircuitBuilder; @@ -142,3 +144,15 @@ impl, const D: usize> From>> for GatePrefixe } } } + +impl, T: Borrow>, const D: usize> Index for GatePrefixes { + type Output = Vec; + + fn index(&self, index: T) -> &Self::Output { + &self.prefixes[index.borrow()] + } +} + +// impl, const D: usize> GatePrefixes { +// pub fn prefix_len() +// } diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs index 8081ab25..9195e30c 100644 --- a/src/gates/gate_tree.rs +++ b/src/gates/gate_tree.rs @@ -1,3 +1,5 @@ +use log::info; + use crate::field::extension_field::Extendable; use crate::gates::gate::GateRef; @@ -64,7 +66,7 @@ impl, const D: usize> Tree> { for max_degree in 1..100 { if let Some(mut tree) = Self::find_tree(&gates, max_degree) { tree.shorten(); - println!( + info!( "Found tree with max degree {} in {}s.", max_degree, timer.elapsed().as_secs_f32() @@ -199,9 +201,6 @@ mod tests { let tree = Tree::from_gates(gates.clone()); let mut gates_with_prefix = tree.traversal(); - for (g, p) in &gates_with_prefix { - println!("{}: {:?}", g.0.id(), p); - } assert_eq!( gates_with_prefix.len(), From 680d7a6389a7e6553c0433a44e60973ec32c5751 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 22 Jun 2021 17:10:36 +0200 Subject: [PATCH 10/28] Add `eval_filtered` methods --- src/circuit_builder.rs | 4 +++- src/circuit_data.rs | 5 ++++- src/gates/gate.rs | 35 +++++++++++++++++++++++++++++++---- src/plonk_common.rs | 24 +++++++++++++++++------- 4 files changed, 55 insertions(+), 13 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index c91ef470..e6a1e296 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -297,8 +297,9 @@ impl, const D: usize> CircuitBuilder { let gates = self.gates.iter().cloned().collect(); let gate_tree = Tree::from_gates(gates); + let gate_prefixes = gate_tree.into(); - let constant_vecs = self.constant_polys(&gate_tree.into()); + let constant_vecs = self.constant_polys(&gate_prefixes); let constants_commitment = ListPolynomialCommitment::new( constant_vecs.into_iter().map(|v| v.ifft()).collect(), self.config.fri_config.rate_bits, @@ -348,6 +349,7 @@ impl, const D: usize> CircuitBuilder { config: self.config, degree_bits, gates, + gate_prefixes, num_gate_constraints, k_is, circuit_digest, diff --git a/src/circuit_data.rs b/src/circuit_data.rs index c8477464..3b314281 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -3,7 +3,7 @@ use anyhow::Result; use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::fri::FriConfig; -use crate::gates::gate::GateRef; +use crate::gates::gate::{GatePrefixes, GateRef}; use crate::generator::WitnessGenerator; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::proof::{Hash, HashTarget, Proof}; @@ -139,6 +139,9 @@ pub(crate) struct CommonCircuitData, const D: usize> { /// The types of gates used in this circuit. pub(crate) gates: Vec>, + /// The gate prefixes used to construct the selector polynomials. + pub(crate) gate_prefixes: GatePrefixes, + /// The largest number of constraints imposed by any gate. pub(crate) num_gate_constraints: usize, diff --git a/src/gates/gate.rs b/src/gates/gate.rs index cf939d9d..2ab410d4 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -9,6 +9,7 @@ use std::sync::Arc; use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; +use crate::field::field::Field; use crate::gates::gate_tree::Tree; use crate::generator::WitnessGenerator; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; @@ -57,15 +58,41 @@ pub trait Gate, const D: usize>: 'static + Send + Sync { vars: EvaluationTargets, ) -> Vec>; - fn eval_filtered(&self, vars: EvaluationVars) -> Vec { - // TODO: Filter + fn eval_filtered(&self, vars: EvaluationVars, prefix: &[bool]) -> Vec { + let filter: F::Extension = prefix + .iter() + .enumerate() + .map(|(i, &b)| { + if b { + vars.local_constants[i] + } else { + F::Extension::ONE - vars.local_constants[i] + } + }) + .product(); self.eval_unfiltered(vars) + .into_iter() + .map(|c| filter * c) + .collect() } /// Like `eval_filtered`, but specialized for points in the base field. - fn eval_filtered_base(&self, vars: EvaluationVarsBase) -> Vec { - // TODO: Filter + fn eval_filtered_base(&self, vars: EvaluationVarsBase, prefix: &[bool]) -> Vec { + let filter = prefix + .iter() + .enumerate() + .map(|(i, &b)| { + if b { + vars.local_constants[i] + } else { + F::ONE - vars.local_constants[i] + } + }) + .product(); self.eval_unfiltered_base(vars) + .into_iter() + .map(|c| c * filter) + .collect() } fn eval_filtered_recursively( diff --git a/src/plonk_common.rs b/src/plonk_common.rs index c9f11b74..2edd2add 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -5,7 +5,7 @@ use crate::circuit_data::CommonCircuitData; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field::Field; -use crate::gates::gate::GateRef; +use crate::gates::gate::{GatePrefixes, GateRef}; use crate::polynomial::commitment::SALT_SIZE; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; @@ -76,8 +76,12 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( gammas: &[F], alphas: &[F], ) -> Vec { - let constraint_terms = - evaluate_gate_constraints(&common_data.gates, common_data.num_gate_constraints, vars); + let constraint_terms = evaluate_gate_constraints( + &common_data.gates, + common_data.num_gate_constraints, + vars, + &common_data.gate_prefixes, + ); // The L_1(x) (Z(x) - 1) vanishing terms. let mut vanishing_z_1_terms = Vec::new(); @@ -125,8 +129,12 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( gammas: &[F], alphas: &[F], ) -> Vec { - let constraint_terms = - evaluate_gate_constraints_base(&common_data.gates, common_data.num_gate_constraints, vars); + let constraint_terms = evaluate_gate_constraints_base( + &common_data.gates, + common_data.num_gate_constraints, + vars, + &common_data.gate_prefixes, + ); // The L_1(x) (Z(x) - 1) vanishing terms. let mut vanishing_z_1_terms = Vec::new(); @@ -170,10 +178,11 @@ pub fn evaluate_gate_constraints, const D: usize>( gates: &[GateRef], num_gate_constraints: usize, vars: EvaluationVars, + prefixes: &GatePrefixes, ) -> Vec { let mut constraints = vec![F::Extension::ZERO; num_gate_constraints]; for gate in gates { - let gate_constraints = gate.0.eval_filtered(vars); + let gate_constraints = gate.0.eval_filtered(vars, &prefixes[gate]); for (i, c) in gate_constraints.into_iter().enumerate() { debug_assert!( i < num_gate_constraints, @@ -189,10 +198,11 @@ pub fn evaluate_gate_constraints_base, const D: usize>( gates: &[GateRef], num_gate_constraints: usize, vars: EvaluationVarsBase, + prefixes: &GatePrefixes, ) -> Vec { let mut constraints = vec![F::ZERO; num_gate_constraints]; for gate in gates { - let gate_constraints = gate.0.eval_filtered_base(vars); + let gate_constraints = gate.0.eval_filtered_base(vars, &prefixes[gate]); for (i, c) in gate_constraints.into_iter().enumerate() { debug_assert!( i < num_gate_constraints, From 3bc27c65ef49a149cdfb4e68dfe7d4dff8d13823 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 13:46:19 +0200 Subject: [PATCH 11/28] Rollback to previous semantics --- src/circuit_builder.rs | 10 +++------- src/circuit_data.rs | 3 --- src/gates/gmimc.rs | 8 +------- src/generator.rs | 10 ++-------- src/permutation_argument.rs | 21 --------------------- src/plonk_challenger.rs | 6 +----- src/prover.rs | 6 +----- src/witness.rs | 4 ---- 8 files changed, 8 insertions(+), 60 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index b9b2e533..8c7d0a72 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -236,7 +236,7 @@ impl, const D: usize> CircuitBuilder { .collect() } - fn sigma_vecs(&self, k_is: &[F]) -> (Vec>, TargetPartitions) { + fn sigma_vecs(&self, k_is: &[F]) -> Vec> { let degree = self.gate_instances.len(); let degree_log = log2_strict(degree); let mut target_partitions = TargetPartitions::new(); @@ -257,10 +257,7 @@ impl, const D: usize> CircuitBuilder { let wire_partitions = target_partitions.to_wire_partitions(); - ( - wire_partitions.get_sigma_polys(degree_log, k_is), - target_partitions, - ) + wire_partitions.get_sigma_polys(degree_log, k_is) } /// Builds a "full circuit", with both prover and verifier data. @@ -282,7 +279,7 @@ impl, const D: usize> CircuitBuilder { ); let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires); - let (sigma_vecs, targets_partition) = self.sigma_vecs(&k_is); + let sigma_vecs = self.sigma_vecs(&k_is); let sigmas_commitment = ListPolynomialCommitment::new( sigma_vecs.into_iter().map(|v| v.ifft()).collect(), self.config.fri_config.rate_bits, @@ -301,7 +298,6 @@ impl, const D: usize> CircuitBuilder { generators, constants_commitment, sigmas_commitment, - targets_partition, }; // The HashSet of gates will have a non-deterministic order. When converting to a Vec, we diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 6b6d614a..4d9a7110 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -5,7 +5,6 @@ use crate::field::field::Field; use crate::fri::FriConfig; use crate::gates::gate::GateRef; use crate::generator::WitnessGenerator; -use crate::permutation_argument::TargetPartitions; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::proof::{Hash, HashTarget, Proof}; use crate::prover::prove; @@ -105,8 +104,6 @@ pub(crate) struct ProverOnlyCircuitData { pub constants_commitment: ListPolynomialCommitment, /// Commitments to the sigma polynomial. pub sigmas_commitment: ListPolynomialCommitment, - /// Partition of the targets into copy-constrained sets. - pub targets_partition: TargetPartitions, } /// Circuit data required by the verifier, but not the prover. diff --git a/src/gates/gmimc.rs b/src/gates/gmimc.rs index ac5741e4..bdfade7c 100644 --- a/src/gates/gmimc.rs +++ b/src/gates/gmimc.rs @@ -370,13 +370,7 @@ mod tests { } let generators = gate.0.generators(0, &[]); - let mut tp = TargetPartitions::new(); - for g in 0..10 { - for i in 0..config.num_routed_wires { - tp.add_partition(Target::wire(g, i)); - } - } - generate_partial_witness(&mut witness, &generators, &tp); + generate_partial_witness(&mut witness, &generators); let expected_outputs: [F; W] = gmimc_permute_naive(permutation_inputs.try_into().unwrap(), constants); diff --git a/src/generator.rs b/src/generator.rs index 5b6a258a..db81172f 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -11,7 +11,6 @@ use crate::witness::PartialWitness; pub(crate) fn generate_partial_witness( witness: &mut PartialWitness, generators: &[Box>], - target_partition: &TargetPartitions, ) { // Index generator indices by their watched targets. let mut generator_indices_by_watches = HashMap::new(); @@ -20,12 +19,10 @@ pub(crate) fn generate_partial_witness( generator_indices_by_watches .entry(watch) .or_insert_with(Vec::new) - .push(i) + .push(i); } } - target_partition.generate_copies(witness, &witness.all_populated_targets()); - // Build a list of "pending" generators which are queued to be run. Initially, all generators // are queued. let mut pending_generator_indices: HashSet<_> = (0..generators.len()).collect(); @@ -38,14 +35,11 @@ pub(crate) fn generate_partial_witness( let mut next_pending_generator_indices = HashSet::new(); for &generator_idx in &pending_generator_indices { - let (mut result, finished) = generators[generator_idx].run(&witness); + let (result, finished) = generators[generator_idx].run(&witness); if finished { expired_generator_indices.insert(generator_idx); } - let new_targets = result.all_populated_targets(); - target_partition.generate_copies(&mut result, &new_targets); - // Enqueue unfinished generators that were watching one of the newly populated targets. for watch in result.target_values.keys() { if let Some(watching_generator_indices) = generator_indices_by_watches.get(watch) { diff --git a/src/permutation_argument.rs b/src/permutation_argument.rs index f2739756..62ee63d4 100644 --- a/src/permutation_argument.rs +++ b/src/permutation_argument.rs @@ -6,7 +6,6 @@ use crate::field::field::Field; use crate::polynomial::polynomial::PolynomialValues; use crate::target::Target; use crate::wire::Wire; -use crate::witness::PartialWitness; #[derive(Debug, Clone)] pub struct TargetPartitions { @@ -83,26 +82,6 @@ impl TargetPartitions { indices, } } - /// For the given set of targets, find any copy constraints involving those targets and populate - /// the witness with copies as needed. - pub fn generate_copies(&self, witness: &mut PartialWitness, targets: &[Target]) { - let mut result = PartialWitness::new(); - - for &target in targets { - let value = witness.get_target(target); - let partition = self.get_partition(target); - - for &sibling in partition { - if witness.contains(sibling) { - // This sibling's value was already set; make sure it has the same value. - assert_eq!(witness.get_target(sibling), value); - } else { - result.set_target(sibling, value); - } - } - } - witness.extend(result); - } } pub struct WirePartitions { diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index 30d4cf70..290b5231 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -352,11 +352,7 @@ mod tests { } let circuit = builder.build(); let mut witness = PartialWitness::new(); - generate_partial_witness( - &mut witness, - &circuit.prover_only.generators, - &circuit.prover_only.targets_partition, - ); + generate_partial_witness(&mut witness, &circuit.prover_only.generators); let recursive_output_values_per_round: Vec> = recursive_outputs_per_round .iter() .map(|outputs| witness.get_targets(outputs)) diff --git a/src/prover.rs b/src/prover.rs index fc4e6f38..81b02674 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -34,11 +34,7 @@ pub(crate) fn prove, const D: usize>( let mut witness = inputs; info!("Running {} generators", prover_data.generators.len()); timed!( - generate_partial_witness( - &mut witness, - &prover_data.generators, - &prover_data.targets_partition - ), + generate_partial_witness(&mut witness, &prover_data.generators,), "to generate witness" ); diff --git a/src/witness.rs b/src/witness.rs index e71eebc9..a0b4b2a4 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -59,10 +59,6 @@ impl PartialWitness { targets.iter().all(|&t| self.contains(t)) } - pub fn all_populated_targets(&self) -> Vec { - self.target_values.keys().cloned().collect() - } - pub fn set_target(&mut self, target: Target, value: F) { let opt_old_value = self.target_values.insert(target, value); if let Some(old_value) = opt_old_value { From bc90909fa34ca0c81b73d42545edecddc53ec01f Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 14:16:05 +0200 Subject: [PATCH 12/28] Add check of copy constraints after witness generation --- src/circuit_builder.rs | 5 +++-- src/circuit_data.rs | 13 +++++++++---- src/prover.rs | 11 +++++++++-- src/witness.rs | 27 +++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 8c7d0a72..9be203bb 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -293,11 +293,12 @@ impl, const D: usize> CircuitBuilder { sigmas_root, }; - let generators = self.generators; let prover_only = ProverOnlyCircuitData { - generators, + generators: self.generators, constants_commitment, sigmas_commitment, + copy_constraints: self.copy_constraints, + gate_instances: self.gate_instances, }; // The HashSet of gates will have a non-deterministic order. When converting to a Vec, we diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 4d9a7110..37925c0c 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -3,11 +3,12 @@ use anyhow::Result; use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::fri::FriConfig; -use crate::gates::gate::GateRef; +use crate::gates::gate::{GateInstance, GateRef}; use crate::generator::WitnessGenerator; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::proof::{Hash, HashTarget, Proof}; use crate::prover::prove; +use crate::target::Target; use crate::verifier::verify; use crate::witness::PartialWitness; @@ -52,7 +53,7 @@ impl CircuitConfig { /// Circuit data required by the prover or the verifier. pub struct CircuitData, const D: usize> { - pub(crate) prover_only: ProverOnlyCircuitData, + pub(crate) prover_only: ProverOnlyCircuitData, pub(crate) verifier_only: VerifierOnlyCircuitData, pub(crate) common: CommonCircuitData, } @@ -75,7 +76,7 @@ impl, const D: usize> CircuitData { /// required, like LDEs of preprocessed polynomials. If more succinctness was desired, we could /// construct a more minimal prover structure and convert back and forth. pub struct ProverCircuitData, const D: usize> { - pub(crate) prover_only: ProverOnlyCircuitData, + pub(crate) prover_only: ProverOnlyCircuitData, pub(crate) common: CommonCircuitData, } @@ -98,12 +99,16 @@ impl, const D: usize> VerifierCircuitData { } /// Circuit data required by the prover, but not the verifier. -pub(crate) struct ProverOnlyCircuitData { +pub(crate) struct ProverOnlyCircuitData, const D: usize> { pub generators: Vec>>, /// Commitments to the constants polynomial. pub constants_commitment: ListPolynomialCommitment, /// Commitments to the sigma polynomial. pub sigmas_commitment: ListPolynomialCommitment, + /// The circuit's copy constraints. + pub copy_constraints: Vec<(Target, Target)>, + /// The concrete placement of each gate in the circuit. + pub gate_instances: Vec>, } /// Circuit data required by the verifier, but not the prover. diff --git a/src/prover.rs b/src/prover.rs index 81b02674..0215aa13 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -23,7 +23,7 @@ use crate::witness::PartialWitness; pub const PLONK_BLINDING: [bool; 5] = [false, false, true, true, true]; pub(crate) fn prove, const D: usize>( - prover_data: &ProverOnlyCircuitData, + prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, inputs: PartialWitness, ) -> Proof { @@ -38,6 +38,13 @@ pub(crate) fn prove, const D: usize>( "to generate witness" ); + timed!( + witness + .check_copy_constraints(&prover_data.copy_constraints, &prover_data.gate_instances) + .unwrap(), // TODO: Change return value to `Result` and use `?` here. + "to check copy constraints" + ); + let config = &common_data.config; let num_wires = config.num_wires; let num_challenges = config.num_challenges; @@ -162,7 +169,7 @@ fn compute_z, const D: usize>( fn compute_vanishing_polys, const D: usize>( common_data: &CommonCircuitData, - prover_data: &ProverOnlyCircuitData, + prover_data: &ProverOnlyCircuitData, wires_commitment: &ListPolynomialCommitment, plonk_zs_commitment: &ListPolynomialCommitment, betas: &[F], diff --git a/src/witness.rs b/src/witness.rs index a0b4b2a4..ff6a8a50 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -1,7 +1,10 @@ use std::collections::HashMap; +use anyhow::{ensure, Result}; + use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; +use crate::gates::gate::GateInstance; use crate::target::Target; use crate::wire::Wire; @@ -97,6 +100,30 @@ impl PartialWitness { self.set_target(target, value); } } + + /// Checks that the copy constraints are satisfied in the witness. + pub fn check_copy_constraints( + &self, + copy_constraints: &[(Target, Target)], + gate_instances: &[GateInstance], + ) -> Result<()> + where + F: Extendable, + { + for &(a, b) in copy_constraints { + if let (Target::Wire(wa), Target::Wire(wb)) = (a, b) { + let va = self.target_values.get(&a).copied().unwrap_or(F::ZERO); + let vb = self.target_values.get(&b).copied().unwrap_or(F::ZERO); + ensure!( + va == vb, + "Copy constraint between wire {} of gate #{} (`{}`) and wire {} of gate #{} (`{}`) is not satisfied.\ + Got values of {} and {} respectively.", + wa.input, wa.gate, gate_instances[wa.gate].gate_type.0.id(), wb.input, wb.gate, + gate_instances[wb.gate].gate_type.0.id(), va, vb); + } + } + Ok(()) + } } impl Default for PartialWitness { From 8ae664d94fba60ea38d96dc55ca5353c3a40e72c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 14:22:42 +0200 Subject: [PATCH 13/28] Minor --- src/gadgets/arithmetic.rs | 2 -- src/witness.rs | 5 +++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/gadgets/arithmetic.rs b/src/gadgets/arithmetic.rs index 520f8cd9..7a9fd441 100644 --- a/src/gadgets/arithmetic.rs +++ b/src/gadgets/arithmetic.rs @@ -385,8 +385,6 @@ mod tests { let x = FF::rand(); let y = FF::rand(); - let x = FF::TWO; - let y = FF::ONE; let z = x / y; let xt = builder.constant_extension(x); let yt = builder.constant_extension(y); diff --git a/src/witness.rs b/src/witness.rs index f55d4297..aff0192a 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::convert::TryInto; use anyhow::{ensure, Result}; @@ -139,8 +140,8 @@ impl PartialWitness { let vb = self.target_values.get(&b).copied().unwrap_or(F::ZERO); ensure!( va == vb, - "Copy constraint between wire {} of gate #{} (`{}`) and wire {} of gate #{} (`{}`) is not satisfied.\ - Got values of {} and {} respectively.", + "Copy constraint between wire {} of gate #{} (`{}`) and wire {} of gate #{} (`{}`) is not satisfied. \ + Got values of {} and {} respectively.", wa.input, wa.gate, gate_instances[wa.gate].gate_type.0.id(), wb.input, wb.gate, gate_instances[wb.gate].gate_type.0.id(), va, vb); } From 747f1875af0e76b3b44ed6c91dc19430bff7ac4a Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 14:26:05 +0200 Subject: [PATCH 14/28] Add todo for public inputs --- src/circuit_builder.rs | 1 - src/witness.rs | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 640e17a5..0a7041e5 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -274,7 +274,6 @@ impl, const D: usize> CircuitBuilder { } let wire_partitions = target_partitions.to_wire_partitions(); - wire_partitions.get_sigma_polys(degree_log, k_is) } diff --git a/src/witness.rs b/src/witness.rs index aff0192a..989ec2bc 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -135,6 +135,7 @@ impl PartialWitness { F: Extendable, { for &(a, b) in copy_constraints { + // TODO: Take care of public inputs once they land. if let (Target::Wire(wa), Target::Wire(wb)) = (a, b) { let va = self.target_values.get(&a).copied().unwrap_or(F::ZERO); let vb = self.target_values.get(&b).copied().unwrap_or(F::ZERO); From c01e772fd80526de8a4e803971498b5668fbee0b Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 15:41:39 +0200 Subject: [PATCH 15/28] Simplify filter computation --- src/gates/gate.rs | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 2ab410d4..1e73e37d 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -59,17 +59,7 @@ pub trait Gate, const D: usize>: 'static + Send + Sync { ) -> Vec>; fn eval_filtered(&self, vars: EvaluationVars, prefix: &[bool]) -> Vec { - let filter: F::Extension = prefix - .iter() - .enumerate() - .map(|(i, &b)| { - if b { - vars.local_constants[i] - } else { - F::Extension::ONE - vars.local_constants[i] - } - }) - .product(); + let filter = compute_filter(prefix, vars.local_constants); self.eval_unfiltered(vars) .into_iter() .map(|c| filter * c) @@ -78,17 +68,7 @@ pub trait Gate, const D: usize>: 'static + Send + Sync { /// Like `eval_filtered`, but specialized for points in the base field. fn eval_filtered_base(&self, vars: EvaluationVarsBase, prefix: &[bool]) -> Vec { - let filter = prefix - .iter() - .enumerate() - .map(|(i, &b)| { - if b { - vars.local_constants[i] - } else { - F::ONE - vars.local_constants[i] - } - }) - .product(); + let filter = compute_filter(prefix, vars.local_constants); self.eval_unfiltered_base(vars) .into_iter() .map(|c| c * filter) @@ -180,6 +160,16 @@ impl, T: Borrow>, const D: usize> Index for Ga } } -// impl, const D: usize> GatePrefixes { -// pub fn prefix_len() -// } +fn compute_filter(prefix: &[bool], constants: &[K]) -> K { + prefix + .iter() + .enumerate() + .map(|(i, &b)| { + if b { + constants[i] + } else { + K::ONE - constants[i] + } + }) + .product() +} From 0a75dcdb95fee4d00f14fbfd24a3be4e8ff58375 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 16:06:30 +0200 Subject: [PATCH 16/28] Remove prefix before calling `eval_unfiltered_*` --- src/gates/gate.rs | 6 ++++-- src/vars.rs | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 1e73e37d..24d0c062 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -58,8 +58,9 @@ pub trait Gate, const D: usize>: 'static + Send + Sync { vars: EvaluationTargets, ) -> Vec>; - fn eval_filtered(&self, vars: EvaluationVars, prefix: &[bool]) -> Vec { + fn eval_filtered(&self, mut vars: EvaluationVars, prefix: &[bool]) -> Vec { let filter = compute_filter(prefix, vars.local_constants); + vars.remove_prefix(prefix); self.eval_unfiltered(vars) .into_iter() .map(|c| filter * c) @@ -67,8 +68,9 @@ pub trait Gate, const D: usize>: 'static + Send + Sync { } /// Like `eval_filtered`, but specialized for points in the base field. - fn eval_filtered_base(&self, vars: EvaluationVarsBase, prefix: &[bool]) -> Vec { + fn eval_filtered_base(&self, mut vars: EvaluationVarsBase, prefix: &[bool]) -> Vec { let filter = compute_filter(prefix, vars.local_constants); + vars.remove_prefix(prefix); self.eval_unfiltered_base(vars) .into_iter() .map(|c| c * filter) diff --git a/src/vars.rs b/src/vars.rs index 74f15f23..a8fa6527 100644 --- a/src/vars.rs +++ b/src/vars.rs @@ -27,6 +27,16 @@ impl<'a, F: Extendable, const D: usize> EvaluationVars<'a, F, D> { let arr = self.local_wires[wire_range].try_into().unwrap(); ExtensionAlgebra::from_basefield_array(arr) } + + pub fn remove_prefix(&mut self, prefix: &[bool]) { + self.local_constants = &self.local_constants[prefix.len()..]; + } +} + +impl<'a, F: Field> EvaluationVarsBase<'a, F> { + pub fn remove_prefix(&mut self, prefix: &[bool]) { + self.local_constants = &self.local_constants[prefix.len()..]; + } } #[derive(Copy, Clone)] From 1cfffcc919734300767457ead43a540ed5a9569b Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 16:45:30 +0200 Subject: [PATCH 17/28] Add comment on `compute_filter` --- src/gates/gate.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 24d0c062..831927e1 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -162,6 +162,8 @@ impl, T: Borrow>, const D: usize> Index for Ga } } +/// A gate's filter is computed as `prod b_i*c_i + (1-b_i)*(1-c_i)`, with `(b_i)` the prefix and +/// `(c_i)` the local constants, which is one if the prefix of `constants` matches `prefix`. fn compute_filter(prefix: &[bool], constants: &[K]) -> K { prefix .iter() From d69f11794e45034964a95d6f5069d88aaa4b9bc6 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 23 Jun 2021 19:15:52 +0200 Subject: [PATCH 18/28] Revert `num_routed_wires` to 27 --- src/plonk_challenger.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index d1141672..9af5e590 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; @@ -5,7 +7,6 @@ use crate::field::field::Field; use crate::hash::{permute, SPONGE_RATE, SPONGE_WIDTH}; use crate::proof::{Hash, HashTarget, OpeningSet}; use crate::target::Target; -use std::convert::TryInto; /// Observes prover messages, and generates challenges by hashing the transcript. #[derive(Clone)] @@ -369,7 +370,7 @@ mod tests { let config = CircuitConfig { num_wires: 12 + 12 + 3 + 101, - num_routed_wires: 200, + num_routed_wires: 27, ..CircuitConfig::default() }; let mut builder = CircuitBuilder::::new(config); From ac1179255a95aefd0dfacd3cd2c8e5d6403148ee Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Wed, 23 Jun 2021 15:45:48 -0700 Subject: [PATCH 19/28] Delete coset [I]FFT methods (#72) I think they had a mistake, and in any case we have a similar method in `polynomial.rs` now which has tests. --- src/field/fft.rs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/field/fft.rs b/src/field/fft.rs index 8bcde967..e1c1a097 100644 --- a/src/field/fft.rs +++ b/src/field/fft.rs @@ -126,32 +126,11 @@ pub(crate) fn fft_with_precomputation_power_of_2( PolynomialValues { values } } -pub(crate) fn coset_fft(poly: PolynomialCoeffs, shift: F) -> PolynomialValues { - let mut points = fft(poly); - let mut shift_exp_i = F::ONE; - for p in points.values.iter_mut() { - *p *= shift_exp_i; - shift_exp_i *= shift; - } - points -} - pub(crate) fn ifft(poly: PolynomialValues) -> PolynomialCoeffs { let precomputation = fft_precompute(poly.len()); ifft_with_precomputation_power_of_2(poly, &precomputation) } -pub(crate) fn coset_ifft(poly: PolynomialValues, shift: F) -> PolynomialCoeffs { - let shift_inv = shift.inverse(); - let mut shift_inv_exp_i = F::ONE; - let mut coeffs = ifft(poly); - for c in coeffs.coeffs.iter_mut() { - *c *= shift_inv_exp_i; - shift_inv_exp_i *= shift_inv; - } - coeffs -} - #[cfg(test)] mod tests { use crate::field::crandall_field::CrandallField; From 24af1f8c0ad2441f38d87d4b0e8f009c44d03f12 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 17:03:07 +0200 Subject: [PATCH 20/28] New heuristic --- src/gates/gate_tree.rs | 49 +++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs index 9195e30c..ac1c7d37 100644 --- a/src/gates/gate_tree.rs +++ b/src/gates/gate_tree.rs @@ -61,17 +61,20 @@ impl, const D: usize> Tree> { /// i.e., the one with minimal `M` value. pub fn from_gates(mut gates: Vec>) -> Self { let timer = std::time::Instant::now(); - gates.sort_unstable_by_key(|g| -((g.0.degree() + g.0.num_constants()) as isize)); + gates.sort_unstable_by_key(|g| (-(g.0.degree() as isize), -(g.0.num_constants() as isize))); - for max_degree in 1..100 { - if let Some(mut tree) = Self::find_tree(&gates, max_degree) { - tree.shorten(); - info!( - "Found tree with max degree {} in {}s.", - max_degree, - timer.elapsed().as_secs_f32() - ); - return tree; + for max_degree_bits in 1..10 { + let max_degree = 1 << max_degree_bits; + for max_wires in 1..100 { + if let Some(mut tree) = Self::find_tree(&gates, max_degree, max_wires) { + tree.shorten(); + info!( + "Found tree with max degree {} in {}s.", + max_degree, + timer.elapsed().as_secs_f32() + ); + return tree; + } } } @@ -79,18 +82,25 @@ impl, const D: usize> Tree> { } /// Greedily add gates wherever possible. Returns `None` if this fails. - fn find_tree(gates: &[GateRef], max_degree: usize) -> Option { + fn find_tree(gates: &[GateRef], max_degree: usize, max_wires: usize) -> Option { let mut tree = Tree::default(); for g in gates { - tree.try_add_gate(g, max_degree)?; + tree.try_add_gate(g, max_degree, max_wires)?; } Some(tree) } /// Try to add a gate in the tree. Returns `None` if this fails. - fn try_add_gate(&mut self, g: &GateRef, max_degree: usize) -> Option<()> { - let depth = max_degree.checked_sub(g.0.num_constants() + g.0.degree())?; + fn try_add_gate( + &mut self, + g: &GateRef, + max_degree: usize, + max_wires: usize, + ) -> Option<()> { + let depth = max_degree + .checked_sub(g.0.degree())? + .min(max_wires.checked_sub(g.0.num_constants())?); self.try_add_gate_at_depth(g, depth) } @@ -98,7 +108,7 @@ impl, const D: usize> Tree> { fn try_add_gate_at_depth(&mut self, g: &GateRef, depth: usize) -> Option<()> { // If depth is 0, we have to insert the gate here. if depth == 0 { - return if let Tree::Bifurcation(_, _) = self { + return if let Tree::Bifurcation(None, None) = self { // Insert the gate as a new leaf. *self = Tree::Leaf(g.clone()); Some(()) @@ -185,6 +195,7 @@ mod tests { #[test] fn test_prefix_generation() { + env_logger::init(); type F = CrandallField; const D: usize = 4; @@ -201,6 +212,14 @@ mod tests { let tree = Tree::from_gates(gates.clone()); let mut gates_with_prefix = tree.traversal(); + for (g, p) in &gates_with_prefix { + println!("{} {:?}", &g.0.id()[..20.min(g.0.id().len())], p); + println!( + "{} {}", + g.0.degree() + p.len(), + g.0.num_constants() + p.len() + ); + } assert_eq!( gates_with_prefix.len(), From 8c008ce4fb99915110656059246daa06d0612349 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 17:43:53 +0200 Subject: [PATCH 21/28] Add comments --- src/gates/gate_tree.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs index ac1c7d37..f0fb0414 100644 --- a/src/gates/gate_tree.rs +++ b/src/gates/gate_tree.rs @@ -51,19 +51,20 @@ impl Tree { } impl, const D: usize> Tree> { - /// Construct a binary tree of gates using the following greedy algorithm: - /// We want a tree where the maximum `M` of - /// `F(gate) = gate.degree() + gate.num_constants() + tree.depth(gate)` - /// over all gates is minimized. Such a tree is constructed by iterating over possible values of `M` - /// (from 1 to 99, then we give up) and then looking for a tree with this value of `M` - /// using `Self::find_tree`. This latter function greedily adds gates at the depth where - /// `F(gate)=M` to ensure no space is wasted. We return the first tree found in this manner, - /// i.e., the one with minimal `M` value. + /// The binary gate tree influences the degree `D` of the constraint polynomial and the number `C` + /// of constant wires in the circuit. We want to construct a tree minimizing both values. To do so + /// we iterate over possible values of `(D, C)` and try to construct a tree with these values. + /// For this construction, we use the greedy algorithm in `Self::find_tree`. + /// This latter function greedily adds gates at the depth where + /// `filtered_deg(gate)=D, constant_wires(gate)=C` to ensure no space is wasted. + /// We return the first tree found in this manner. pub fn from_gates(mut gates: Vec>) -> Self { let timer = std::time::Instant::now(); gates.sort_unstable_by_key(|g| (-(g.0.degree() as isize), -(g.0.num_constants() as isize))); for max_degree_bits in 1..10 { + // The constraint polynomials are padded to the next power in `compute_vanishig_polys`. + // So we can restrict our search space by setting `max_degree` to a power of 2. let max_degree = 1 << max_degree_bits; for max_wires in 1..100 { if let Some(mut tree) = Self::find_tree(&gates, max_degree, max_wires) { @@ -98,6 +99,7 @@ impl, const D: usize> Tree> { max_degree: usize, max_wires: usize, ) -> Option<()> { + // We want `gate.degree + depth <= max_degree` and `gate.num_constants + depth <= max_wires`. let depth = max_degree .checked_sub(g.0.degree())? .min(max_wires.checked_sub(g.0.num_constants())?); @@ -213,9 +215,11 @@ mod tests { let tree = Tree::from_gates(gates.clone()); let mut gates_with_prefix = tree.traversal(); for (g, p) in &gates_with_prefix { - println!("{} {:?}", &g.0.id()[..20.min(g.0.id().len())], p); - println!( - "{} {}", + info!( + "\nGate: {}, prefix: {:?}.\n\ + Filtered constraint degree: {}, Num constant wires: {}", + &g.0.id()[..20.min(g.0.id().len())], + p, g.0.degree() + p.len(), g.0.num_constants() + p.len() ); From b4258976b10129e2bc5e41f90bdf9eb868a3e1cf Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 17:49:30 +0200 Subject: [PATCH 22/28] PR fixes --- src/gates/gate_tree.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs index f0fb0414..f99ae076 100644 --- a/src/gates/gate_tree.rs +++ b/src/gates/gate_tree.rs @@ -231,11 +231,9 @@ mod tests { "The tree has too much or too little gates." ); assert!( - gates.iter().all(|g| gates_with_prefix + gates .iter() - .map(|(gg, _)| gg) - .find(|gg| *gg == g) - .is_some()), + .all(|g| gates_with_prefix.iter().map(|(gg, _)| gg).any(|gg| gg == g)), "Some gates are not in the tree." ); assert!( From 54315d173535f7128c9c8c12615b5f48b51807b3 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 18:06:48 +0200 Subject: [PATCH 23/28] Remove `GatePrefixes` to avoid using a `HashMap` --- src/circuit_builder.rs | 33 +++++++++++++++------------------ src/circuit_data.rs | 11 ++++------- src/gates/gate.rs | 24 +++++++++--------------- src/plonk_common.rs | 28 +++++++++------------------- 4 files changed, 37 insertions(+), 59 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index e6a1e296..221aa27b 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -11,7 +11,7 @@ use crate::field::cosets::get_unique_coset_shifts; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::gates::constant::ConstantGate; -use crate::gates::gate::{GateInstance, GatePrefixes, GateRef}; +use crate::gates::gate::{GateInstance, GateRef, PrefixedGate}; use crate::gates::gate_tree::Tree; use crate::gates::noop::NoopGate; use crate::generator::{CopyGenerator, WitnessGenerator}; @@ -230,26 +230,24 @@ impl, const D: usize> CircuitBuilder { } } - fn constant_polys(&self, prefixes: &GatePrefixes) -> Vec> { - let num_constants = self - .gate_instances + fn constant_polys(&self, gates: &[PrefixedGate]) -> Vec> { + let num_constants = gates .iter() - .map(|gate_inst| gate_inst.constants.len() + prefixes[&gate_inst.gate_type].len()) + .map(|gate| gate.gate.0.num_constants() + gate.prefix.len()) .max() .unwrap(); let constants_per_gate = self .gate_instances .iter() - .map(|gate_inst| { + .map(|gate| { + let prefix = &gates + .iter() + .find(|g| g.gate.0.id() == gate.gate_type.0.id()) + .unwrap() + .prefix; let mut prefixed_constants = Vec::new(); - prefixed_constants.extend(prefixes[&gate_inst.gate_type].iter().map(|&b| { - if b { - F::ONE - } else { - F::ZERO - } - })); - prefixed_constants.extend_from_slice(&gate_inst.constants); + prefixed_constants.extend(prefix.iter().map(|&b| if b { F::ONE } else { F::ZERO })); + prefixed_constants.extend_from_slice(&gate.constants); prefixed_constants.resize(num_constants, F::ZERO); prefixed_constants }) @@ -297,9 +295,9 @@ impl, const D: usize> CircuitBuilder { let gates = self.gates.iter().cloned().collect(); let gate_tree = Tree::from_gates(gates); - let gate_prefixes = gate_tree.into(); + let prefixed_gates = PrefixedGate::from_tree(gate_tree); - let constant_vecs = self.constant_polys(&gate_prefixes); + let constant_vecs = self.constant_polys(&prefixed_gates); let constants_commitment = ListPolynomialCommitment::new( constant_vecs.into_iter().map(|v| v.ifft()).collect(), self.config.fri_config.rate_bits, @@ -348,8 +346,7 @@ impl, const D: usize> CircuitBuilder { let common = CommonCircuitData { config: self.config, degree_bits, - gates, - gate_prefixes, + gates: prefixed_gates, num_gate_constraints, k_is, circuit_digest, diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 3b314281..e54acd4f 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -3,7 +3,7 @@ use anyhow::Result; use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::fri::FriConfig; -use crate::gates::gate::{GatePrefixes, GateRef}; +use crate::gates::gate::{GateRef, PrefixedGate}; use crate::generator::WitnessGenerator; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::proof::{Hash, HashTarget, Proof}; @@ -136,11 +136,8 @@ pub(crate) struct CommonCircuitData, const D: usize> { pub(crate) degree_bits: usize, - /// The types of gates used in this circuit. - pub(crate) gates: Vec>, - - /// The gate prefixes used to construct the selector polynomials. - pub(crate) gate_prefixes: GatePrefixes, + /// The types of gates used in this circuit, along with their prefixes. + pub(crate) gates: Vec>, /// The largest number of constraints imposed by any gate. pub(crate) num_gate_constraints: usize, @@ -169,7 +166,7 @@ impl, const D: usize> CommonCircuitData { pub fn constraint_degree(&self) -> usize { self.gates .iter() - .map(|g| g.0.degree()) + .map(|g| g.gate.0.degree()) .max() .expect("No gates?") } diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 831927e1..5882ce00 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -142,23 +142,17 @@ pub struct GateInstance, const D: usize> { /// Map each gate to a boolean prefix used to construct the gate's selector polynomial. #[derive(Debug, Clone)] -pub struct GatePrefixes, const D: usize> { - pub prefixes: HashMap, Vec>, +pub struct PrefixedGate, const D: usize> { + pub gate: GateRef, + pub prefix: Vec, } -impl, const D: usize> From>> for GatePrefixes { - fn from(tree: Tree>) -> Self { - GatePrefixes { - prefixes: HashMap::from_iter(tree.traversal()), - } - } -} - -impl, T: Borrow>, const D: usize> Index for GatePrefixes { - type Output = Vec; - - fn index(&self, index: T) -> &Self::Output { - &self.prefixes[index.borrow()] +impl, const D: usize> PrefixedGate { + pub fn from_tree(tree: Tree>) -> Vec { + tree.traversal() + .into_iter() + .map(|(gate, prefix)| PrefixedGate { gate, prefix }) + .collect() } } diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 2edd2add..617ba530 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -5,7 +5,7 @@ use crate::circuit_data::CommonCircuitData; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; use crate::field::field::Field; -use crate::gates::gate::{GatePrefixes, GateRef}; +use crate::gates::gate::{GateRef, PrefixedGate}; use crate::polynomial::commitment::SALT_SIZE; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; @@ -76,12 +76,8 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( gammas: &[F], alphas: &[F], ) -> Vec { - let constraint_terms = evaluate_gate_constraints( - &common_data.gates, - common_data.num_gate_constraints, - vars, - &common_data.gate_prefixes, - ); + let constraint_terms = + evaluate_gate_constraints(&common_data.gates, common_data.num_gate_constraints, vars); // The L_1(x) (Z(x) - 1) vanishing terms. let mut vanishing_z_1_terms = Vec::new(); @@ -129,12 +125,8 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( gammas: &[F], alphas: &[F], ) -> Vec { - let constraint_terms = evaluate_gate_constraints_base( - &common_data.gates, - common_data.num_gate_constraints, - vars, - &common_data.gate_prefixes, - ); + let constraint_terms = + evaluate_gate_constraints_base(&common_data.gates, common_data.num_gate_constraints, vars); // The L_1(x) (Z(x) - 1) vanishing terms. let mut vanishing_z_1_terms = Vec::new(); @@ -175,14 +167,13 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( /// strictly necessary, but it helps performance by ensuring that we allocate a vector with exactly /// the capacity that we need. pub fn evaluate_gate_constraints, const D: usize>( - gates: &[GateRef], + gates: &[PrefixedGate], num_gate_constraints: usize, vars: EvaluationVars, - prefixes: &GatePrefixes, ) -> Vec { let mut constraints = vec![F::Extension::ZERO; num_gate_constraints]; for gate in gates { - let gate_constraints = gate.0.eval_filtered(vars, &prefixes[gate]); + let gate_constraints = gate.gate.0.eval_filtered(vars, &gate.prefix); for (i, c) in gate_constraints.into_iter().enumerate() { debug_assert!( i < num_gate_constraints, @@ -195,14 +186,13 @@ pub fn evaluate_gate_constraints, const D: usize>( } pub fn evaluate_gate_constraints_base, const D: usize>( - gates: &[GateRef], + gates: &[PrefixedGate], num_gate_constraints: usize, vars: EvaluationVarsBase, - prefixes: &GatePrefixes, ) -> Vec { let mut constraints = vec![F::ZERO; num_gate_constraints]; for gate in gates { - let gate_constraints = gate.0.eval_filtered_base(vars, &prefixes[gate]); + let gate_constraints = gate.gate.0.eval_filtered_base(vars, &gate.prefix); for (i, c) in gate_constraints.into_iter().enumerate() { debug_assert!( i < num_gate_constraints, From c7753186b85f3e8eeaaecf6664e20ed51edc2a95 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 18:11:37 +0200 Subject: [PATCH 24/28] Clippy --- src/circuit_data.rs | 2 +- src/gates/gate.rs | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/circuit_data.rs b/src/circuit_data.rs index e54acd4f..fe4fd0e0 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -3,7 +3,7 @@ use anyhow::Result; use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::fri::FriConfig; -use crate::gates::gate::{GateRef, PrefixedGate}; +use crate::gates::gate::PrefixedGate; use crate::generator::WitnessGenerator; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::proof::{Hash, HashTarget, Proof}; diff --git a/src/gates/gate.rs b/src/gates/gate.rs index 5882ce00..4b37892c 100644 --- a/src/gates/gate.rs +++ b/src/gates/gate.rs @@ -1,9 +1,5 @@ -use std::borrow::Borrow; -use std::collections::HashMap; use std::fmt::{Debug, Error, Formatter}; use std::hash::{Hash, Hasher}; -use std::iter::FromIterator; -use std::ops::Index; use std::sync::Arc; use crate::circuit_builder::CircuitBuilder; From aa78d02c016f8c0f3708adf20a9045940e66db84 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Thu, 24 Jun 2021 10:43:44 -0700 Subject: [PATCH 25/28] Delete outdated comment --- src/bin/bench_recursion.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index 9fab2456..0682578d 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -51,12 +51,6 @@ fn bench_prove, const D: usize>() { builder.add_gate(ConstantGate::get(), vec![F::NEG_ONE]); - // for _ in 0..(40 * 5) { - // builder.add_gate( - // FriConsistencyGate::new(2, 3, 13), - // vec![F::primitive_root_of_unity(13)]); - // } - let prover = builder.build_prover(); let inputs = PartialWitness::new(); prover.prove(inputs); From e50eeb6cf49c2fa23455b3c406a5246a1d4d69c0 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Thu, 24 Jun 2021 10:44:46 -0700 Subject: [PATCH 26/28] Delete more outdated comments --- src/bin/bench_recursion.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index 0682578d..1c9104f9 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -18,12 +18,6 @@ fn main() { env_logger::Builder::from_env(Env::default().default_filter_or("debug")).init(); bench_prove::(); - - // bench_field_mul::(); - - // bench_fft(); - println!(); - // bench_gmimc::(); } fn bench_prove, const D: usize>() { From 2a38f8656ffa02c58b4665ece135ad090e2355de Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 24 Jun 2021 20:53:15 +0200 Subject: [PATCH 27/28] PR feedback --- src/circuit_builder.rs | 2 +- src/gates/gate_tree.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 221aa27b..72c27208 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -245,7 +245,7 @@ impl, const D: usize> CircuitBuilder { .find(|g| g.gate.0.id() == gate.gate_type.0.id()) .unwrap() .prefix; - let mut prefixed_constants = Vec::new(); + let mut prefixed_constants = Vec::with_capacity(num_constants); prefixed_constants.extend(prefix.iter().map(|&b| if b { F::ONE } else { F::ZERO })); prefixed_constants.extend_from_slice(&gate.constants); prefixed_constants.resize(num_constants, F::ZERO); diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs index f99ae076..1423de76 100644 --- a/src/gates/gate_tree.rs +++ b/src/gates/gate_tree.rs @@ -66,8 +66,8 @@ impl, const D: usize> Tree> { // The constraint polynomials are padded to the next power in `compute_vanishig_polys`. // So we can restrict our search space by setting `max_degree` to a power of 2. let max_degree = 1 << max_degree_bits; - for max_wires in 1..100 { - if let Some(mut tree) = Self::find_tree(&gates, max_degree, max_wires) { + for max_constants in 1..100 { + if let Some(mut tree) = Self::find_tree(&gates, max_degree, max_constants) { tree.shorten(); info!( "Found tree with max degree {} in {}s.", @@ -83,11 +83,11 @@ impl, const D: usize> Tree> { } /// Greedily add gates wherever possible. Returns `None` if this fails. - fn find_tree(gates: &[GateRef], max_degree: usize, max_wires: usize) -> Option { + fn find_tree(gates: &[GateRef], max_degree: usize, max_constants: usize) -> Option { let mut tree = Tree::default(); for g in gates { - tree.try_add_gate(g, max_degree, max_wires)?; + tree.try_add_gate(g, max_degree, max_constants)?; } Some(tree) } @@ -97,12 +97,12 @@ impl, const D: usize> Tree> { &mut self, g: &GateRef, max_degree: usize, - max_wires: usize, + max_constants: usize, ) -> Option<()> { // We want `gate.degree + depth <= max_degree` and `gate.num_constants + depth <= max_wires`. let depth = max_degree .checked_sub(g.0.degree())? - .min(max_wires.checked_sub(g.0.num_constants())?); + .min(max_constants.checked_sub(g.0.num_constants())?); self.try_add_gate_at_depth(g, depth) } @@ -249,7 +249,7 @@ mod tests { assert_ne!( &gates_with_prefix[i].1, &gates_with_prefix[j].1[0..gates_with_prefix[i].1.len()], - "Some gates share the same prefix" + "Some gates share an overlapping prefix" ); } } From 3400caa19c788a3b7f104eac0336ba88dfb8edd4 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Thu, 24 Jun 2021 12:12:57 -0700 Subject: [PATCH 28/28] Fix recursion bench (#74) Nothing was routed in before --- src/bin/bench_recursion.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index 1c9104f9..59b65e51 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -25,7 +25,7 @@ fn bench_prove, const D: usize>() { let config = CircuitConfig { num_wires: 134, - num_routed_wires: 12, + num_routed_wires: 27, security_bits: 128, rate_bits: 3, num_challenges: 3, @@ -39,11 +39,17 @@ fn bench_prove, const D: usize>() { let mut builder = CircuitBuilder::::new(config); + let zero = builder.zero(); + let zero_ext = builder.zero_extension(); + + let mut state = [zero; 12]; for _ in 0..10000 { - builder.add_gate_no_constants(gmimc_gate.clone()); + state = builder.permute(state); } - builder.add_gate(ConstantGate::get(), vec![F::NEG_ONE]); + // Random other gates. + builder.add(zero, zero); + builder.add_extension(zero_ext, zero_ext); let prover = builder.build_prover(); let inputs = PartialWitness::new();