Skip to main content

binius_frontend/compiler/
circuit.rs

1// Copyright 2025 Irreducible Inc.
2use std::{error, fmt};
3
4use binius_core::{
5	constraint_system::{ConstraintSystem, ValueIndex, ValueVec},
6	word::Word,
7};
8use cranelift_entity::SecondaryMap;
9
10use crate::compiler::{
11	eval_form::EvalForm,
12	gate_graph::{GateGraph, Wire},
13};
14
15/// Error returned when populating wire witness fails due to assertion failures.
16#[derive(Debug)]
17pub struct PopulateError {
18	/// List of assertion failure messages (limited to MAX_ASSERTION_MESSAGES).
19	pub messages: Vec<String>,
20	/// Total count of assertion failures (may exceed messages.len()).
21	pub total_count: usize,
22}
23
24impl fmt::Display for PopulateError {
25	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26		writeln!(f, "assertions failed:")?;
27		for message in &self.messages {
28			writeln!(f, "{message}")?;
29		}
30		if self.total_count > self.messages.len() {
31			writeln!(f, "(Some assertions are omitted. Total: {})", self.total_count)?;
32		}
33		Ok(())
34	}
35}
36
37impl error::Error for PopulateError {}
38
39/// A helper struct for filling witness values in a circuit.
40pub struct WitnessFiller<'a> {
41	pub(crate) circuit: &'a Circuit,
42	pub(crate) value_vec: ValueVec,
43}
44
45impl<'a> WitnessFiller<'a> {
46	/// Destruct the witness filler and extracts the underlying value vector.
47	pub fn into_value_vec(self) -> ValueVec {
48		self.value_vec
49	}
50}
51
52impl<'a> std::ops::Index<Wire> for WitnessFiller<'a> {
53	type Output = Word;
54
55	fn index(&self, wire: Wire) -> &Self::Output {
56		&self.value_vec[self.circuit.witness_index(wire)]
57	}
58}
59
60impl<'a> std::ops::IndexMut<Wire> for WitnessFiller<'a> {
61	fn index_mut(&mut self, wire: Wire) -> &mut Self::Output {
62		&mut self.value_vec[self.circuit.witness_index(wire)]
63	}
64}
65
66/// An artifact that represents a built circuit.
67///
68/// The difference from [`ConstraintSystem`] is that a circuit retains enough information to
69/// perform circuit evaluation to generate internal witness values.
70pub struct Circuit {
71	gate_graph: GateGraph,
72	constraint_system: ConstraintSystem,
73	wire_mapping: SecondaryMap<Wire, ValueIndex>,
74	eval_form: EvalForm,
75}
76
77impl Circuit {
78	/// Creates a new circuit with the given shared data and wire mapping. Only used during building
79	/// by the circuit builder.
80	pub(super) fn new(
81		gate_graph: GateGraph,
82		constraint_system: ConstraintSystem,
83		wire_mapping: SecondaryMap<Wire, ValueIndex>,
84		eval_form: EvalForm,
85	) -> Self {
86		assert!(constraint_system.value_vec_layout.validate().is_ok());
87		Self {
88			gate_graph,
89			constraint_system,
90			wire_mapping,
91			eval_form,
92		}
93	}
94
95	/// For the given wire, returns its index in the witness vector.
96	#[inline(always)]
97	pub fn witness_index(&self, wire: Wire) -> ValueIndex {
98		self.wire_mapping[wire]
99	}
100
101	/// Creates a new witness filler for this circuit.
102	pub fn new_witness_filler(&self) -> WitnessFiller<'_> {
103		WitnessFiller {
104			circuit: self,
105			value_vec: ValueVec::new(self.constraint_system.value_vec_layout.clone()),
106		}
107	}
108
109	/// Populates non-input values (wires) in the witness.
110	///
111	/// Specifically, this will evaluate the circuit gate-by-gate and save the results in the
112	/// witness vector.
113	///
114	/// This function expects that the input wires are already filled. The input wires are
115	///
116	/// - [`CircuitBuilder::add_inout`],
117	/// - [`CircuitBuilder::add_witness`] that were not created by the gates,
118	///
119	/// The wires created by [`CircuitBuilder::add_constant`] (and its convenience methods)
120	/// are automatically populated by this function as well.
121	///
122	/// # Errors
123	///
124	/// In case the circuit is not satisfiable (any assertion fails), this function will return
125	/// an error with a list of assertion failure messages.
126	///
127	///
128	/// [`CircuitBuilder::add_constant`]: super::CircuitBuilder::add_constant
129	/// [`CircuitBuilder::add_inout`]: super::CircuitBuilder::add_inout
130	/// [`CircuitBuilder::add_witness`]: super::CircuitBuilder::add_witness
131	pub fn populate_wire_witness(&self, w: &mut WitnessFiller) -> Result<(), PopulateError> {
132		// Fill the constant part from the witness.
133		for (index, constant) in self.constraint_system.constants.iter().enumerate() {
134			w.value_vec.set(index, *constant);
135		}
136
137		// Execute the evaluation form - it modifies the ValueVec in place
138		// Pass the PathSpecTree for assertion error symbolication
139		self.eval_form
140			.evaluate(&mut w.value_vec, Some(&self.gate_graph.path_spec_tree))?;
141
142		Ok(())
143	}
144
145	/// Returns the constraint system for this circuit.
146	pub fn constraint_system(&self) -> &ConstraintSystem {
147		&self.constraint_system
148	}
149
150	/// Returns the number of gates in this circuit.
151	///
152	/// Depending on what type of gates this circuit uses, the number of constraints might be
153	/// significantly larger.
154	pub fn n_gates(&self) -> usize {
155		self.gate_graph.gates.len()
156	}
157
158	/// Returns the number of evaluation instructions in this circuit.
159	pub fn n_eval_insn(&self) -> usize {
160		self.eval_form.n_eval_insn()
161	}
162
163	/// Returns a string with a JSON dump that is useful to profile the circuit.
164	pub fn simple_json_dump(&self) -> String {
165		crate::compiler::dump::dump_composition(&self.gate_graph)
166	}
167}