plonky2/src/gates/gate_tree.rs

170 lines
6.5 KiB
Rust
Raw Normal View History

2021-06-22 14:31:46 +02:00
use crate::field::extension_field::Extendable;
use crate::gates::gate::GateRef;
2021-06-22 15:34:50 +02:00
/// A binary tree where leaves hold some type `T` and other nodes are empty.
2021-06-22 14:31:46 +02:00
#[derive(Debug, Clone)]
2021-06-22 14:50:08 +02:00
pub enum Tree<T> {
2021-06-22 15:34:50 +02:00
Leaf(T),
2021-06-22 14:50:08 +02:00
Bifurcation(Option<Box<Tree<T>>>, Option<Box<Tree<T>>>),
2021-06-22 14:31:46 +02:00
}
impl<T> Default for Tree<T> {
fn default() -> Self {
2021-06-22 14:50:08 +02:00
Self::Bifurcation(None, None)
2021-06-22 14:31:46 +02:00
}
}
impl<T: Clone> Tree<T> {
2021-06-22 15:34:50 +02:00
/// 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<bool>)> {
2021-06-22 14:31:46 +02:00
let mut res = Vec::new();
let prefix = [];
self.traverse(&prefix, &mut res);
res
}
2021-06-22 15:34:50 +02:00
/// Utility function to traverse the tree.
2021-06-22 14:31:46 +02:00
fn traverse(&self, prefix: &[bool], current: &mut Vec<(T, Vec<bool>)>) {
2021-06-22 14:50:08 +02:00
match &self {
2021-06-22 15:34:50 +02:00
// If node is a leaf, collect the data and position.
Tree::Leaf(t) => {
2021-06-22 14:50:08 +02:00
current.push((t.clone(), prefix.to_vec()));
2021-06-22 14:31:46 +02:00
}
2021-06-22 15:34:50 +02:00
// Otherwise, traverse the left subtree and then the right subtree.
2021-06-22 14:50:08 +02:00
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);
}
2021-06-22 14:31:46 +02:00
}
}
}
}
impl<F: Extendable<D>, const D: usize> Tree<GateRef<F, D>> {
2021-06-22 15:34:50 +02:00
/// 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.
2021-06-22 14:31:46 +02:00
pub fn from_gates(mut gates: Vec<GateRef<F, D>>) -> 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) {
2021-06-22 15:34:50 +02:00
tree.shorten();
2021-06-22 14:31:46 +02:00
println!(
"Found tree with max degree {} in {}s.",
max_degree,
timer.elapsed().as_secs_f32()
);
return tree;
}
}
panic!("Can't find a tree.")
}
2021-06-22 15:34:50 +02:00
/// Greedily add gates wherever possible. Returns `None` if this fails.
2021-06-22 14:31:46 +02:00
fn find_tree(gates: &[GateRef<F, D>], max_degree: usize) -> Option<Self> {
let mut tree = Tree::default();
for g in gates {
tree.try_add_gate(g, max_degree)?;
}
Some(tree)
}
2021-06-22 15:34:50 +02:00
/// Try to add a gate in the tree. Returns `None` if this fails.
2021-06-22 14:31:46 +02:00
fn try_add_gate(&mut self, g: &GateRef<F, D>, max_degree: usize) -> Option<()> {
let depth = max_degree.checked_sub(g.0.num_constants() + g.0.degree())?;
2021-06-22 14:50:08 +02:00
self.try_add_gate_at_depth(g, depth)
2021-06-22 14:31:46 +02:00
}
2021-06-22 15:34:50 +02:00
/// Try to add a gate in the tree at a specified depth. Returns `None` if this fails.
2021-06-22 14:50:08 +02:00
fn try_add_gate_at_depth(&mut self, g: &GateRef<F, D>, depth: usize) -> Option<()> {
2021-06-22 15:34:50 +02:00
// If depth is 0, we have to insert the gate here.
2021-06-22 14:31:46 +02:00
if depth == 0 {
2021-06-22 14:50:08 +02:00
return if let Tree::Bifurcation(_, _) = self {
2021-06-22 15:34:50 +02:00
// Insert the gate as a new leaf.
*self = Tree::Leaf(g.clone());
2021-06-22 14:50:08 +02:00
Some(())
2021-06-22 14:31:46 +02:00
} else {
2021-06-22 15:34:50 +02:00
// A leaf is already here.
2021-06-22 14:50:08 +02:00
None
2021-06-22 14:31:46 +02:00
};
}
2021-06-22 15:34:50 +02:00
// A leaf is already here so we cannot go deeper.
if let Tree::Leaf(_) = self {
2021-06-22 14:50:08 +02:00
return None;
2021-06-22 14:31:46 +02:00
}
2021-06-22 14:50:08 +02:00
if let Tree::Bifurcation(left, right) = self {
if let Some(left) = left {
2021-06-22 15:34:50 +02:00
// Try to add the gate to the left if there's already a left subtree.
2021-06-22 14:50:08 +02:00
if left.try_add_gate_at_depth(g, depth - 1).is_some() {
return Some(());
}
} else {
2021-06-22 15:34:50 +02:00
// Add a new left subtree and try to add the gate to it.
2021-06-22 14:50:08 +02:00
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(());
}
2021-06-22 14:31:46 +02:00
}
2021-06-22 14:50:08 +02:00
if let Some(right) = right {
2021-06-22 15:34:50 +02:00
// Try to add the gate to the right if there's already a right subtree.
2021-06-22 14:50:08 +02:00
if right.try_add_gate_at_depth(g, depth - 1).is_some() {
return Some(());
}
} else {
2021-06-22 15:34:50 +02:00
// Add a new right subtree and try to add the gate to it.
2021-06-22 14:50:08 +02:00
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(());
}
2021-06-22 14:31:46 +02:00
}
}
2021-06-22 14:50:08 +02:00
None
2021-06-22 14:31:46 +02:00
}
2021-06-22 15:34:50 +02:00
/// `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) {
2021-06-22 14:50:08 +02:00
if let Tree::Bifurcation(left, right) = self {
if let (Some(left), None) = (left, right) {
2021-06-22 15:34:50 +02:00
// If the node has a left but no right subtree, set the node to its (shortened) left subtree.
2021-06-22 14:50:08 +02:00
let mut new = *left.clone();
2021-06-22 15:34:50 +02:00
new.shorten();
2021-06-22 14:50:08 +02:00
*self = new;
}
2021-06-22 14:31:46 +02:00
}
2021-06-22 14:50:08 +02:00
if let Tree::Bifurcation(left, right) = self {
if let Some(left) = left {
2021-06-22 15:34:50 +02:00
// Shorten the left subtree if there is one.
left.shorten();
2021-06-22 14:50:08 +02:00
}
if let Some(right) = right {
2021-06-22 15:34:50 +02:00
// Shorten the right subtree if there is one.
right.shorten();
2021-06-22 14:50:08 +02:00
}
2021-06-22 14:31:46 +02:00
}
}
}