plonky2/src/context_tree.rs
Daniel Lubarov 8438d23937
Tree of scopes (#106)
* Tree of scopes

This is an extension of the context concept.

Earlier I was planning to store a simple stack of contexts, but I ended up storing the whole history, in a tree structure. This gives us more control over the output, i.e. we can print the gate count of a parent scope before those of its child scopes, which seems more user-friendly.

Sample gate count output:

    [2021-07-19T18:09:24Z INFO  plonky2::circuit_builder] 27829 gates to root
    [2021-07-19T18:09:24Z INFO  plonky2::circuit_builder] | 2373 gates to evaluate the vanishing polynomial at our challenge point, zeta.
    [2021-07-19T18:09:24Z INFO  plonky2::circuit_builder] | | 1284 gates to evaluate gate constraints
    [2021-07-19T18:09:24Z INFO  plonky2::circuit_builder] | 25312 gates to verify FRI proof
    [2021-07-19T18:09:24Z INFO  plonky2::circuit_builder] | | 650 gates to verify 0'th FRI query
    [2021-07-19T18:09:24Z INFO  plonky2::circuit_builder] | | | 96 gates to check FRI initial proof
    [2021-07-19T18:09:24Z INFO  plonky2::circuit_builder] | | | 65 gates to compute x from its index
    [2021-07-19T18:09:24Z INFO  plonky2::circuit_builder] | | | 233 gates to combine initial oracles
    ...

Sample copy constraint failure:

    Error: Copy constraint 'root > verify FRI proof > verify 0'th FRI query > check FRI initial proof > verify 0'th initial Merkle proof > check Merkle root: 0-th hash element' between wire 12 of gate #2550 [...] and wire 0 of gate #0 [...] is not satisfied. Got values of 6861386743364621393 and 0 respectively.

* No min

* info -> debug

* Move to its own file
2021-07-19 12:22:18 -07:00

125 lines
3.6 KiB
Rust

use log::debug;
/// The hierarchy of contexts, and the gate count contributed by each one. Useful for debugging.
pub(crate) struct ContextTree {
/// The name of this scope.
name: String,
/// The gate count when this scope was created.
enter_gate_count: usize,
/// The gate count when this scope was destroyed, or None if it has not yet been destroyed.
exit_gate_count: Option<usize>,
/// Any child contexts.
children: Vec<ContextTree>,
}
impl ContextTree {
pub fn new() -> Self {
Self {
name: "root".to_string(),
enter_gate_count: 0,
exit_gate_count: None,
children: vec![],
}
}
/// Whether this context is still in scope.
fn is_open(&self) -> bool {
self.exit_gate_count.is_none()
}
/// A description of the stack of currently-open scopes.
pub fn open_stack(&self) -> String {
let mut stack = Vec::new();
self.open_stack_helper(&mut stack);
stack.join(" > ")
}
fn open_stack_helper(&self, stack: &mut Vec<String>) {
if self.is_open() {
stack.push(self.name.clone());
if let Some(last_child) = self.children.last() {
last_child.open_stack_helper(stack);
}
}
}
pub fn push(&mut self, ctx: &str, current_gate_count: usize) {
assert!(self.is_open());
if let Some(last_child) = self.children.last_mut() {
if last_child.is_open() {
last_child.push(ctx, current_gate_count);
return;
}
}
self.children.push(ContextTree {
name: ctx.to_string(),
enter_gate_count: current_gate_count,
exit_gate_count: None,
children: vec![],
})
}
/// Close the deepest open context from this tree.
pub fn pop(&mut self, current_gate_count: usize) {
assert!(self.is_open());
if let Some(last_child) = self.children.last_mut() {
if last_child.is_open() {
last_child.pop(current_gate_count);
return;
}
}
self.exit_gate_count = Some(current_gate_count);
}
fn gate_count_delta(&self, current_gate_count: usize) -> usize {
self.exit_gate_count.unwrap_or(current_gate_count) - self.enter_gate_count
}
/// Filter out children with a low gate count.
pub fn filter(&self, current_gate_count: usize, min_delta: usize) -> Self {
Self {
name: self.name.clone(),
enter_gate_count: self.enter_gate_count,
exit_gate_count: self.exit_gate_count,
children: self
.children
.iter()
.filter(|c| c.gate_count_delta(current_gate_count) >= min_delta)
.map(|c| c.filter(current_gate_count, min_delta))
.collect(),
}
}
pub fn print(&self, current_gate_count: usize) {
self.print_helper(current_gate_count, 0);
}
fn print_helper(&self, current_gate_count: usize, depth: usize) {
let prefix = "| ".repeat(depth);
debug!(
"{}{} gates to {}",
prefix,
self.gate_count_delta(current_gate_count),
self.name
);
for child in &self.children {
child.print_helper(current_gate_count, depth + 1);
}
}
}
/// Creates a named scope; useful for debugging.
#[macro_export]
macro_rules! context {
($builder:expr, $ctx:expr, $exp:expr) => {{
$builder.push_context($ctx);
let res = $exp;
$builder.pop_context();
res
}};
}