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}