-
Notifications
You must be signed in to change notification settings - Fork 3
Add witness hints fillers #169
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
ea95dfd
e4f69d0
1bdffb8
5aae193
9f0dc4f
b17326d
21b5dfd
9b90963
bcdeb91
f9ef0e8
2f94892
7f31679
43465ba
9208435
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| use alloc::boxed::Box; | ||
| use alloc::string::ToString; | ||
| use alloc::vec; | ||
| use alloc::vec::Vec; | ||
|
|
||
|
|
@@ -8,6 +10,7 @@ use crate::Op; | |
| use crate::builder::CircuitBuilderError; | ||
| use crate::builder::compiler::get_witness_id; | ||
| use crate::expr::{Expr, ExpressionGraph}; | ||
| use crate::op::WitnessHintFiller; | ||
| use crate::types::{ExprId, WitnessAllocator, WitnessId}; | ||
|
|
||
| /// Sparse disjoint-set "find" with path compression over a HashMap (iterative). | ||
|
|
@@ -70,10 +73,15 @@ pub struct ExpressionLowerer<'a, F> { | |
| /// Number of public inputs | ||
| public_input_count: usize, | ||
|
|
||
| /// The hint witnesses with their respective filler | ||
| hints_with_fillers: &'a [HintsWithFiller<F>], | ||
|
|
||
| /// Witness allocator | ||
| witness_alloc: WitnessAllocator, | ||
| } | ||
|
|
||
| pub type HintsWithFiller<F> = (Vec<ExprId>, Box<dyn WitnessHintFiller<F>>); | ||
|
|
||
| impl<'a, F> ExpressionLowerer<'a, F> | ||
| where | ||
| F: Clone + PrimeCharacteristicRing + PartialEq + Eq + core::hash::Hash, | ||
|
|
@@ -83,12 +91,14 @@ where | |
| graph: &'a ExpressionGraph<F>, | ||
| pending_connects: &'a [(ExprId, ExprId)], | ||
| public_input_count: usize, | ||
| hints_with_fillers: &'a [HintsWithFiller<F>], | ||
| witness_alloc: WitnessAllocator, | ||
| ) -> Self { | ||
| Self { | ||
| graph, | ||
| pending_connects, | ||
| public_input_count, | ||
| hints_with_fillers, | ||
| witness_alloc, | ||
| } | ||
| } | ||
|
|
@@ -162,7 +172,7 @@ where | |
| } | ||
| }; | ||
|
|
||
| // Pass B: emit public inputs | ||
| // Pass B: emit public inputs and process witness hints | ||
4l0n50 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| for (expr_idx, expr) in self.graph.nodes().iter().enumerate() { | ||
| if let Expr::Public(pos) = expr { | ||
| let id = ExprId(expr_idx as u32); | ||
|
|
@@ -179,18 +189,57 @@ where | |
| } | ||
| } | ||
|
|
||
| // Pass C: emit arithmetic ops in creation order; tie outputs to class slot if connected | ||
| // Pass C: collect witness hints and emit `Unconstrained` operatios | ||
4l0n50 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| for (expr_idx, _) in self | ||
| .graph | ||
| .nodes() | ||
| .iter() | ||
| .enumerate() | ||
| .filter(|(_, expr)| matches!(expr, Expr::Witness)) | ||
| { | ||
| let expr_id = ExprId(expr_idx as u32); | ||
| let out_widx = alloc_witness_id_for_expr(expr_idx); | ||
| expr_to_widx.insert(expr_id, out_widx); | ||
| } | ||
| for (witness_hints, filler) in self.hints_with_fillers.iter() { | ||
| let inputs = filler | ||
| .inputs() | ||
| .iter() | ||
| .map(|expr_id| { | ||
| expr_to_widx | ||
| .get(expr_id) | ||
| .ok_or(CircuitBuilderError::MissingExprMapping { | ||
| expr_id: *expr_id, | ||
| context: "Unconstrained op".to_string(), | ||
| }) | ||
| .copied() | ||
| }) | ||
| .collect::<Result<Vec<WitnessId>, _>>()?; | ||
| let outputs = witness_hints | ||
| .iter() | ||
| .map(|expr_id| { | ||
| expr_to_widx | ||
| .get(expr_id) | ||
| .ok_or(CircuitBuilderError::MissingExprMapping { | ||
| expr_id: *expr_id, | ||
| context: "Unconstrained op".to_string(), | ||
| }) | ||
| .copied() | ||
| }) | ||
| .collect::<Result<Vec<WitnessId>, _>>()?; | ||
| primitive_ops.push(Op::Unconstrained { | ||
| inputs, | ||
| outputs, | ||
| filler: filler.clone(), | ||
| }) | ||
| } | ||
|
|
||
| // Pass D: emit arithmetic ops in creation order; tie outputs to class slot if connected | ||
| for (expr_idx, expr) in self.graph.nodes().iter().enumerate() { | ||
| let expr_id = ExprId(expr_idx as u32); | ||
| match expr { | ||
| Expr::Const(_) | Expr::Public(_) => { /* handled above */ } | ||
| Expr::Witness => { | ||
| // Allocate a fresh witness slot (non-primitive op) | ||
| // Allows non-primitive operations to set values during execution that | ||
| // are not part of the central Witness bus. | ||
| let out_widx = alloc_witness_id_for_expr(expr_idx); | ||
| expr_to_widx.insert(expr_id, out_widx); | ||
| } | ||
| Expr::Const(_) | Expr::Public(_) | Expr::Witness => { /* handled above */ } | ||
|
|
||
| Expr::Add { lhs, rhs } => { | ||
| let out_widx = alloc_witness_id_for_expr(expr_idx); | ||
| let a_widx = get_witness_id( | ||
|
|
@@ -394,9 +443,10 @@ mod tests { | |
| let quot = graph.add_expr(Expr::Div { lhs: diff, rhs: p2 }); | ||
|
|
||
| let connects = vec![]; | ||
| let hints_with_fillers = vec![]; | ||
| let alloc = WitnessAllocator::new(); | ||
|
|
||
| let lowerer = ExpressionLowerer::new(&graph, &connects, 3, alloc); | ||
| let lowerer = ExpressionLowerer::new(&graph, &connects, 3, &hints_with_fillers, alloc); | ||
| let (prims, public_rows, expr_map, public_map, witness_count) = lowerer.lower().unwrap(); | ||
|
|
||
| // Verify Primitives | ||
|
|
@@ -563,9 +613,10 @@ mod tests { | |
| // Group B: p1 ~ p2 ~ p3 (transitive) | ||
| // Group C: sum ~ p4 (operation result shared) | ||
| let connects = vec![(c_42, p0), (p1, p2), (p2, p3), (sum, p4)]; | ||
| let hints_with_fillers = vec![]; | ||
| let alloc = WitnessAllocator::new(); | ||
|
|
||
| let lowerer = ExpressionLowerer::new(&graph, &connects, 5, alloc); | ||
| let lowerer = ExpressionLowerer::new(&graph, &connects, 5, &hints_with_fillers, alloc); | ||
| let (prims, public_rows, expr_map, public_map, witness_count) = lowerer.lower().unwrap(); | ||
|
|
||
| // Verify Primitives | ||
|
|
@@ -703,8 +754,9 @@ mod tests { | |
| }); | ||
|
|
||
| let connects = vec![]; | ||
| let hints_with_fillers = vec![]; | ||
| let alloc = WitnessAllocator::new(); | ||
| let lowerer = ExpressionLowerer::new(&graph, &connects, 0, alloc); | ||
| let lowerer = ExpressionLowerer::new(&graph, &connects, 0, &hints_with_fillers, alloc); | ||
| let result = lowerer.lower(); | ||
|
|
||
| assert!(result.is_err()); | ||
|
|
@@ -724,8 +776,9 @@ mod tests { | |
| }); | ||
|
|
||
| let connects = vec![]; | ||
| let hints_with_fillers = vec![]; | ||
| let alloc = WitnessAllocator::new(); | ||
| let lowerer = ExpressionLowerer::new(&graph, &connects, 0, alloc); | ||
| let lowerer = ExpressionLowerer::new(&graph, &connects, 0, &hints_with_fillers, alloc); | ||
| let result = lowerer.lower(); | ||
|
|
||
| assert!(result.is_err()); | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -23,6 +23,10 @@ pub enum CircuitBuilderError { | |||||
| got: usize, | ||||||
| }, | ||||||
|
|
||||||
| /// Unconstrained op received an unexpected number of input expressions. | ||||||
| #[error("Expects exactly {expected} witness expressions, got {got}")] | ||||||
| UnconstrainedeOpArity { expected: String, got: usize }, | ||||||
|
||||||
| UnconstrainedeOpArity { expected: String, got: usize }, | |
| UnconstrainedOpArity { expected: String, got: usize }, |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also this is actually never used?
Uh oh!
There was an error while loading. Please reload this page.