From cfa3d3a6609401d52d1acefd5541bceeee5dd862 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 22 Jun 2021 15:34:50 +0200 Subject: [PATCH] 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 {