halo2_axiom/circuit.rs
1//! Traits and structs for implementing circuit components.
2
3use std::{fmt, marker::PhantomData};
4
5use ff::Field;
6
7use crate::plonk::{
8 Advice, Any, Assigned, Challenge, Column, Error, Fixed, Instance, Selector, TableColumn,
9};
10
11mod value;
12pub use value::Value;
13
14pub mod floor_planner;
15pub use floor_planner::single_pass::SimpleFloorPlanner;
16
17pub mod layouter;
18mod table_layouter;
19
20pub use table_layouter::{SimpleTableLayouter, TableLayouter};
21
22/// A chip implements a set of instructions that can be used by gadgets.
23///
24/// The chip stores state that is required at circuit synthesis time in
25/// [`Chip::Config`], which can be fetched via [`Chip::config`].
26///
27/// The chip also loads any fixed configuration needed at synthesis time
28/// using its own implementation of `load`, and stores it in [`Chip::Loaded`].
29/// This can be accessed via [`Chip::loaded`].
30pub trait Chip<F: Field>: Sized {
31 /// A type that holds the configuration for this chip, and any other state it may need
32 /// during circuit synthesis, that can be derived during [`Circuit::configure`].
33 ///
34 /// [`Circuit::configure`]: crate::plonk::Circuit::configure
35 type Config: fmt::Debug + Clone;
36
37 /// A type that holds any general chip state that needs to be loaded at the start of
38 /// [`Circuit::synthesize`]. This might simply be `()` for some chips.
39 ///
40 /// [`Circuit::synthesize`]: crate::plonk::Circuit::synthesize
41 type Loaded: fmt::Debug + Clone;
42
43 /// The chip holds its own configuration.
44 fn config(&self) -> &Self::Config;
45
46 /// Provides access to general chip state loaded at the beginning of circuit
47 /// synthesis.
48 ///
49 /// Panics if called before `Chip::load`.
50 fn loaded(&self) -> &Self::Loaded;
51}
52
53/// Index of a region in a layouter
54#[derive(Clone, Copy, Debug)]
55pub struct RegionIndex(usize);
56
57impl From<usize> for RegionIndex {
58 fn from(idx: usize) -> RegionIndex {
59 RegionIndex(idx)
60 }
61}
62
63impl std::ops::Deref for RegionIndex {
64 type Target = usize;
65
66 fn deref(&self) -> &Self::Target {
67 &self.0
68 }
69}
70
71/// Starting row of a region in a layouter
72#[derive(Clone, Copy, Debug, PartialEq, Eq)]
73pub struct RegionStart(usize);
74
75impl From<usize> for RegionStart {
76 fn from(idx: usize) -> RegionStart {
77 RegionStart(idx)
78 }
79}
80
81impl std::ops::Deref for RegionStart {
82 type Target = usize;
83
84 fn deref(&self) -> &Self::Target {
85 &self.0
86 }
87}
88
89/// A pointer to a cell within a circuit.
90#[derive(Clone, Copy, Debug)]
91pub struct Cell {
92 /// Identifies the region in which this cell resides.
93 // region_index: RegionIndex,
94 /// The relative offset of this cell within its region.
95 pub row_offset: usize,
96 /// The column of this cell.
97 pub column: Column<Any>,
98}
99
100/// An assigned cell.
101#[derive(Clone, Debug)]
102pub struct AssignedCell<V, F: Field> {
103 value: Value<V>,
104 cell: Cell,
105 _marker: PhantomData<F>,
106}
107
108impl<V, F: Field> AssignedCell<V, F> {
109 /// Returns the value of the [`AssignedCell`].
110 pub fn value(&self) -> Value<&V> {
111 self.value.as_ref()
112 }
113
114 /// Returns the cell.
115 pub fn cell(&self) -> Cell {
116 self.cell
117 }
118
119 pub fn row_offset(&self) -> usize {
120 self.cell.row_offset
121 }
122
123 pub fn column(&self) -> &Column<Any> {
124 &self.cell.column
125 }
126}
127
128impl<V, F: Field> AssignedCell<V, F>
129where
130 for<'v> Assigned<F>: From<&'v V>,
131{
132 /// Returns the field element value of the [`AssignedCell`].
133 pub fn value_field(&self) -> Value<Assigned<F>> {
134 self.value.to_field()
135 }
136}
137
138impl<F: Field> AssignedCell<Assigned<F>, F> {
139 /// Evaluates this assigned cell's value directly, performing an unbatched inversion
140 /// if necessary.
141 ///
142 /// If the denominator is zero, the returned cell's value is zero.
143 pub fn evaluate(self) -> AssignedCell<F, F> {
144 AssignedCell {
145 value: self.value.evaluate(),
146 cell: self.cell,
147 _marker: Default::default(),
148 }
149 }
150}
151
152impl<'v, F: Field> AssignedCell<&'v Assigned<F>, F> {
153 /// Copies the value to a given advice cell and constrains them to be equal.
154 ///
155 /// Returns an error if either this cell or the given cell are in columns
156 /// where equality has not been enabled.
157 pub fn copy_advice(
158 &self,
159 region: &mut Region<'_, F>,
160 column: Column<Advice>,
161 offset: usize,
162 ) -> AssignedCell<&'_ Assigned<F>, F> {
163 let assigned_cell = region.assign_advice(column, offset, self.value.map(|v| *v));
164 region.constrain_equal(assigned_cell.cell, self.cell);
165 assigned_cell
166 }
167}
168
169/// A region of the circuit in which a [`Chip`] can assign cells.
170///
171/// Inside a region, the chip may freely use relative offsets; the [`Layouter`] will
172/// treat these assignments as a single "region" within the circuit.
173///
174/// The [`Layouter`] is allowed to optimise between regions as it sees fit. Chips must use
175/// [`Region::constrain_equal`] to copy in variables assigned in other regions.
176///
177/// TODO: It would be great if we could constrain the columns in these types to be
178/// "logical" columns that are guaranteed to correspond to the chip (and have come from
179/// `Chip::Config`).
180#[derive(Debug)]
181pub struct Region<'r, F: Field> {
182 region: &'r mut dyn layouter::RegionLayouter<F>,
183}
184
185impl<'r, F: Field> From<&'r mut dyn layouter::RegionLayouter<F>> for Region<'r, F> {
186 fn from(region: &'r mut dyn layouter::RegionLayouter<F>) -> Self {
187 Region { region }
188 }
189}
190
191impl<'r, F: Field> Region<'r, F> {
192 /// Enables a selector at the given offset.
193 pub(crate) fn enable_selector<A, AR>(
194 &mut self,
195 annotation: A,
196 selector: &Selector,
197 offset: usize,
198 ) -> Result<(), Error>
199 where
200 A: Fn() -> AR,
201 AR: Into<String>,
202 {
203 self.region
204 .enable_selector(&|| annotation().into(), selector, offset)
205 }
206
207 /// Allows the circuit implementor to name/annotate a Column within a Region context.
208 ///
209 /// This is useful in order to improve the amount of information that `prover.verify()`
210 /// and `prover.assert_satisfied()` can provide.
211 pub fn name_column<A, AR, T>(&mut self, annotation: A, column: T)
212 where
213 A: Fn() -> AR,
214 AR: Into<String>,
215 T: Into<Column<Any>>,
216 {
217 self.region
218 .name_column(&|| annotation().into(), column.into());
219 }
220
221 /// Assign an advice column value (witness).
222 ///
223 /// Even though `to` has `FnMut` bounds, it is guaranteed to be called at most once.
224 // The returned &'v Assigned<F> lives longer than the mutable borrow of &mut self
225 pub fn assign_advice<'v>(
226 //, V, VR, A, AR>(
227 &mut self,
228 //annotation: A,
229 column: Column<Advice>,
230 offset: usize,
231 to: Value<impl Into<Assigned<F>>>, // For now only accept Value<F>, later might change to Value<Assigned<F>> for batch inversion
232 ) -> AssignedCell<&'v Assigned<F>, F> {
233 //let mut value = Value::unknown();
234 self.region.assign_advice(
235 //&|| annotation().into(),
236 column,
237 offset,
238 to.map(|v| v.into()),
239 )
240
241 /*
242 Ok(AssignedCell {
243 value,
244 cell,
245 _marker: PhantomData,
246 })
247 */
248 }
249
250 /// Assigns a constant value to the column `advice` at `offset` within this region.
251 ///
252 /// The constant value will be assigned to a cell within one of the fixed columns
253 /// configured via `ConstraintSystem::enable_constant`.
254 ///
255 /// Returns the advice cell.
256 pub fn assign_advice_from_constant<VR, A, AR>(
257 &mut self,
258 annotation: A,
259 column: Column<Advice>,
260 offset: usize,
261 constant: VR,
262 ) -> Result<AssignedCell<VR, F>, Error>
263 where
264 for<'vr> Assigned<F>: From<&'vr VR>,
265 A: Fn() -> AR,
266 AR: Into<String>,
267 {
268 let cell = self.region.assign_advice_from_constant(
269 &|| annotation().into(),
270 column,
271 offset,
272 (&constant).into(),
273 )?;
274
275 Ok(AssignedCell {
276 value: Value::known(constant),
277 cell,
278 _marker: PhantomData,
279 })
280 }
281
282 /// Assign the value of the instance column's cell at absolute location
283 /// `row` to the column `advice` at `offset` within this region.
284 ///
285 /// Returns the advice cell, and its value if known.
286 pub fn assign_advice_from_instance<A, AR>(
287 &mut self,
288 annotation: A,
289 instance: Column<Instance>,
290 row: usize,
291 advice: Column<Advice>,
292 offset: usize,
293 ) -> Result<AssignedCell<F, F>, Error>
294 where
295 A: Fn() -> AR,
296 AR: Into<String>,
297 {
298 let (cell, value) = self.region.assign_advice_from_instance(
299 &|| annotation().into(),
300 instance,
301 row,
302 advice,
303 offset,
304 )?;
305
306 Ok(AssignedCell {
307 value,
308 cell,
309 _marker: PhantomData,
310 })
311 }
312
313 /// Returns the value of the instance column's cell at absolute location `row`.
314 ///
315 /// This method is only provided for convenience; it does not create any constraints.
316 /// Callers still need to use [`Self::assign_advice_from_instance`] to constrain the
317 /// instance values in their circuit.
318 pub fn instance_value(
319 &mut self,
320 instance: Column<Instance>,
321 row: usize,
322 ) -> Result<Value<F>, Error> {
323 self.region.instance_value(instance, row)
324 }
325
326 /// Assign a fixed value.
327 ///
328 /// Even though `to` has `FnMut` bounds, it is guaranteed to be called at most once.
329 pub fn assign_fixed(
330 &mut self,
331 // annotation: A,
332 column: Column<Fixed>,
333 offset: usize,
334 to: impl Into<Assigned<F>>,
335 ) -> Cell {
336 self.region.assign_fixed(column, offset, to.into())
337 /*
338 Ok(AssignedCell {
339 value,
340 cell,
341 _marker: PhantomData,
342 })
343 */
344 }
345
346 /// Constrains a cell to have a constant value.
347 ///
348 /// Returns an error if the cell is in a column where equality has not been enabled.
349 pub fn constrain_constant<VR>(&mut self, cell: Cell, constant: VR) -> Result<(), Error>
350 where
351 VR: Into<Assigned<F>>,
352 {
353 self.region.constrain_constant(cell, constant.into())
354 }
355
356 /// Constrains two cells to have the same value.
357 ///
358 /// Returns an error if either of the cells are in columns where equality
359 /// has not been enabled.
360 pub fn constrain_equal(&mut self, left: Cell, right: Cell) {
361 self.region.constrain_equal(left, right);
362 }
363
364 /// Queries the value of the given challenge.
365 ///
366 /// Returns `Value::unknown()` if the current synthesis phase is before the challenge can be queried.
367 pub fn get_challenge(&self, challenge: Challenge) -> Value<F> {
368 self.region.get_challenge(challenge)
369 }
370
371 /// Commit advice columns in current phase and squeeze challenges.
372 /// This can be called DURING synthesize.
373 pub fn next_phase(&mut self) {
374 self.region.next_phase();
375 }
376}
377
378/// A lookup table in the circuit.
379#[derive(Debug)]
380pub struct Table<'r, F: Field> {
381 table: &'r mut dyn TableLayouter<F>,
382}
383
384impl<'r, F: Field> From<&'r mut dyn TableLayouter<F>> for Table<'r, F> {
385 fn from(table: &'r mut dyn TableLayouter<F>) -> Self {
386 Table { table }
387 }
388}
389
390impl<'r, F: Field> Table<'r, F> {
391 /// Assigns a fixed value to a table cell.
392 ///
393 /// Returns an error if the table cell has already been assigned to.
394 ///
395 /// Even though `to` has `FnMut` bounds, it is guaranteed to be called at most once.
396 pub fn assign_cell<'v, V, VR, A, AR>(
397 &'v mut self,
398 annotation: A,
399 column: TableColumn,
400 offset: usize,
401 mut to: V,
402 ) -> Result<(), Error>
403 where
404 V: FnMut() -> Value<VR> + 'v,
405 VR: Into<Assigned<F>>,
406 A: Fn() -> AR,
407 AR: Into<String>,
408 {
409 self.table
410 .assign_cell(&|| annotation().into(), column, offset, &mut || {
411 to().into_field()
412 })
413 }
414}
415
416/// A layout strategy within a circuit. The layouter is chip-agnostic and applies its
417/// strategy to the context and config it is given.
418///
419/// This abstracts over the circuit assignments, handling row indices etc.
420///
421pub trait Layouter<F: Field> {
422 /// Represents the type of the "root" of this layouter, so that nested namespaces
423 /// can minimize indirection.
424 type Root: Layouter<F>;
425
426 /// Assign a region of gates to an absolute row number.
427 ///
428 /// Inside the closure, the chip may freely use relative offsets; the `Layouter` will
429 /// treat these assignments as a single "region" within the circuit. Outside this
430 /// closure, the `Layouter` is allowed to optimise as it sees fit.
431 ///
432 /// ```ignore
433 /// fn assign_region(&mut self, || "region name", |region| {
434 /// let config = chip.config();
435 /// region.assign_advice(config.a, offset, || { Some(value)});
436 /// });
437 /// ```
438 fn assign_region<A, AR, N, NR>(&mut self, name: N, assignment: A) -> Result<AR, Error>
439 where
440 A: FnOnce(Region<'_, F>) -> Result<AR, Error>,
441 N: Fn() -> NR,
442 NR: Into<String>;
443
444 /// Assign a table region to an absolute row number.
445 ///
446 /// ```ignore
447 /// fn assign_table(&mut self, || "table name", |table| {
448 /// let config = chip.config();
449 /// table.assign_fixed(config.a, offset, || { Some(value)});
450 /// });
451 /// ```
452 fn assign_table<A, N, NR>(&mut self, name: N, assignment: A) -> Result<(), Error>
453 where
454 A: FnMut(Table<'_, F>) -> Result<(), Error>,
455 N: Fn() -> NR,
456 NR: Into<String>;
457
458 /// Constrains a [`Cell`] to equal an instance column's row value at an
459 /// absolute position.
460 fn constrain_instance(&mut self, cell: Cell, column: Column<Instance>, row: usize);
461
462 /// Commit advice columns in current phase and squeeze challenges.
463 /// This can be called DURING synthesize.
464 fn next_phase(&mut self);
465
466 /// Queries the value of the given challenge.
467 ///
468 /// Returns `Value::unknown()` if the current synthesis phase is before the challenge can be queried.
469 fn get_challenge(&self, challenge: Challenge) -> Value<F>;
470
471 /// Gets the "root" of this assignment, bypassing the namespacing.
472 ///
473 /// Not intended for downstream consumption; use [`Layouter::namespace`] instead.
474 fn get_root(&mut self) -> &mut Self::Root;
475
476 /// Creates a new (sub)namespace and enters into it.
477 ///
478 /// Not intended for downstream consumption; use [`Layouter::namespace`] instead.
479 fn push_namespace<NR, N>(&mut self, name_fn: N)
480 where
481 NR: Into<String>,
482 N: FnOnce() -> NR;
483
484 /// Exits out of the existing namespace.
485 ///
486 /// Not intended for downstream consumption; use [`Layouter::namespace`] instead.
487 fn pop_namespace(&mut self, gadget_name: Option<String>);
488
489 /// Enters into a namespace.
490 fn namespace<NR, N>(&mut self, name_fn: N) -> NamespacedLayouter<'_, F, Self::Root>
491 where
492 NR: Into<String>,
493 N: FnOnce() -> NR,
494 {
495 self.get_root().push_namespace(name_fn);
496
497 NamespacedLayouter(self.get_root(), PhantomData)
498 }
499}
500
501/// This is a "namespaced" layouter which borrows a `Layouter` (pushing a namespace
502/// context) and, when dropped, pops out of the namespace context.
503#[derive(Debug)]
504pub struct NamespacedLayouter<'a, F: Field, L: Layouter<F> + 'a>(&'a mut L, PhantomData<F>);
505
506impl<'a, F: Field, L: Layouter<F> + 'a> Layouter<F> for NamespacedLayouter<'a, F, L> {
507 type Root = L::Root;
508
509 fn assign_region<A, AR, N, NR>(&mut self, name: N, assignment: A) -> Result<AR, Error>
510 where
511 A: FnOnce(Region<'_, F>) -> Result<AR, Error>,
512 N: Fn() -> NR,
513 NR: Into<String>,
514 {
515 self.0.assign_region(name, assignment)
516 }
517
518 fn assign_table<A, N, NR>(&mut self, name: N, assignment: A) -> Result<(), Error>
519 where
520 A: FnMut(Table<'_, F>) -> Result<(), Error>,
521 N: Fn() -> NR,
522 NR: Into<String>,
523 {
524 self.0.assign_table(name, assignment)
525 }
526
527 fn constrain_instance(&mut self, cell: Cell, column: Column<Instance>, row: usize) {
528 self.0.constrain_instance(cell, column, row);
529 }
530
531 fn get_challenge(&self, challenge: Challenge) -> Value<F> {
532 self.0.get_challenge(challenge)
533 }
534
535 fn next_phase(&mut self) {
536 self.0.next_phase();
537 }
538
539 fn get_root(&mut self) -> &mut Self::Root {
540 self.0.get_root()
541 }
542
543 fn push_namespace<NR, N>(&mut self, _name_fn: N)
544 where
545 NR: Into<String>,
546 N: FnOnce() -> NR,
547 {
548 panic!("Only the root's push_namespace should be called");
549 }
550
551 fn pop_namespace(&mut self, _gadget_name: Option<String>) {
552 panic!("Only the root's pop_namespace should be called");
553 }
554}
555
556impl<'a, F: Field, L: Layouter<F> + 'a> Drop for NamespacedLayouter<'a, F, L> {
557 fn drop(&mut self) {
558 let gadget_name = {
559 #[cfg(feature = "gadget-traces")]
560 {
561 let mut gadget_name = None;
562 let mut is_second_frame = false;
563 backtrace::trace(|frame| {
564 if is_second_frame {
565 // Resolve this instruction pointer to a symbol name.
566 backtrace::resolve_frame(frame, |symbol| {
567 gadget_name = symbol.name().map(|name| format!("{:#}", name));
568 });
569
570 // We are done!
571 false
572 } else {
573 // We want the next frame.
574 is_second_frame = true;
575 true
576 }
577 });
578 gadget_name
579 }
580
581 #[cfg(not(feature = "gadget-traces"))]
582 None
583 };
584
585 self.get_root().pop_namespace(gadget_name);
586 }
587}