halo2_axiom/circuit/floor_planner/
single_pass.rs

1use std::fmt;
2use std::marker::PhantomData;
3
4use ff::Field;
5use rustc_hash::FxHashMap;
6
7use crate::circuit::AssignedCell;
8use crate::{
9    circuit::{
10        layouter::{RegionColumn, RegionLayouter, SyncDeps, TableLayouter},
11        table_layouter::{compute_table_lengths, SimpleTableLayouter},
12        Cell, Layouter, Region, RegionIndex, Table, Value,
13    },
14    plonk::{
15        Advice, Any, Assigned, Assignment, Challenge, Circuit, Column, Error, Fixed, FloorPlanner,
16        Instance, Selector, TableColumn,
17    },
18};
19
20/// A simple [`FloorPlanner`] that performs minimal optimizations.
21///
22/// This floor planner is suitable for debugging circuits. It aims to reflect the circuit
23/// "business logic" in the circuit layout as closely as possible. It uses a single-pass
24/// layouter that does not reorder regions for optimal packing.
25#[derive(Debug)]
26pub struct SimpleFloorPlanner;
27
28impl FloorPlanner for SimpleFloorPlanner {
29    fn synthesize<F: Field, CS: Assignment<F> + SyncDeps, C: Circuit<F>>(
30        cs: &mut CS,
31        circuit: &C,
32        config: C::Config,
33        constants: Vec<Column<Fixed>>,
34    ) -> Result<(), Error> {
35        let layouter = SingleChipLayouter::new(cs, constants)?;
36        circuit.synthesize(config, layouter)
37    }
38}
39
40/// A [`Layouter`] for a single-chip circuit.
41pub struct SingleChipLayouter<'a, F: Field, CS: Assignment<F> + 'a> {
42    cs: &'a mut CS,
43    constants: Vec<Column<Fixed>>,
44    // Stores the starting row for each region.
45    // Edit: modify to just one region with RegionStart(0)
46    // regions: Vec<RegionStart>,
47    /// Stores the first empty row for each column.
48    columns: FxHashMap<RegionColumn, usize>,
49    /// Stores the table fixed columns.
50    table_columns: Vec<TableColumn>,
51    _marker: PhantomData<F>,
52}
53
54impl<'a, F: Field, CS: Assignment<F> + 'a> fmt::Debug for SingleChipLayouter<'a, F, CS> {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        f.debug_struct("SingleChipLayouter")
57            //.field("regions", &self.regions)
58            .field("columns", &self.columns)
59            .finish()
60    }
61}
62
63impl<'a, F: Field, CS: Assignment<F>> SingleChipLayouter<'a, F, CS> {
64    /// Creates a new single-chip layouter.
65    pub fn new(cs: &'a mut CS, constants: Vec<Column<Fixed>>) -> Result<Self, Error> {
66        let ret = SingleChipLayouter {
67            cs,
68            constants,
69            // regions: vec![],
70            columns: FxHashMap::default(),
71            table_columns: vec![],
72            _marker: PhantomData,
73        };
74        Ok(ret)
75    }
76}
77
78impl<'a, F: Field, CS: Assignment<F> + 'a + SyncDeps> Layouter<F>
79    for SingleChipLayouter<'a, F, CS>
80{
81    type Root = Self;
82
83    fn assign_region<A, AR, N, NR>(&mut self, name: N, assignment: A) -> Result<AR, Error>
84    where
85        A: FnOnce(Region<'_, F>) -> Result<AR, Error>,
86        N: Fn() -> NR,
87        NR: Into<String>,
88    {
89        /*
90        let region_index = self.regions.len();
91
92        // Get shape of the region.
93        let mut shape = RegionShape::new(region_index.into());
94        {
95            let region: &mut dyn RegionLayouter<F> = &mut shape;
96            assignment(region.into())?;
97        }
98
99        // Lay out this region. We implement the simplest approach here: position the
100        // region starting at the earliest row for which none of the columns are in use.
101        let region_start = 0;
102        for column in &shape.columns {
103            region_start = cmp::max(region_start, self.columns.get(column).cloned().unwrap_or(0));
104        }
105        // self.regions.push(region_start.into());
106
107        // Update column usage information.
108        for column in shape.columns {
109            self.columns.insert(column, region_start + shape.row_count);
110        }*/
111
112        // Assign region cells.
113        self.cs.enter_region(name);
114        let mut region = SingleChipLayouterRegion::new(self, 0.into()); //region_index.into());
115        let result = {
116            let region: &mut dyn RegionLayouter<F> = &mut region;
117            assignment(region.into())
118        }?;
119        let constants_to_assign = region.constants;
120        self.cs.exit_region();
121
122        // Assign constants. For the simple floor planner, we assign constants in order in
123        // the first `constants` column.
124        if self.constants.is_empty() {
125            if !constants_to_assign.is_empty() {
126                return Err(Error::NotEnoughColumnsForConstants);
127            }
128        } else {
129            let constants_column = self.constants[0];
130            let next_constant_row = self
131                .columns
132                .entry(Column::<Any>::from(constants_column).into())
133                .or_default();
134            for (constant, advice) in constants_to_assign {
135                self.cs.assign_fixed(
136                    //|| format!("Constant({:?})", constant.evaluate()),
137                    constants_column,
138                    *next_constant_row,
139                    constant,
140                );
141                self.cs.copy(
142                    constants_column.into(),
143                    *next_constant_row,
144                    advice.column,
145                    advice.row_offset, // *self.regions[*advice.region_index] + advice.row_offset,
146                );
147                *next_constant_row += 1;
148            }
149        }
150
151        Ok(result)
152    }
153
154    fn assign_table<A, N, NR>(&mut self, name: N, mut assignment: A) -> Result<(), Error>
155    where
156        A: FnMut(Table<'_, F>) -> Result<(), Error>,
157        N: Fn() -> NR,
158        NR: Into<String>,
159    {
160        // Maintenance hazard: there is near-duplicate code in `v1::AssignmentPass::assign_table`.
161        // Assign table cells.
162        self.cs.enter_region(name);
163        let mut table = SimpleTableLayouter::new(self.cs, &self.table_columns);
164        {
165            let table: &mut dyn TableLayouter<F> = &mut table;
166            assignment(table.into())
167        }?;
168        let default_and_assigned = table.default_and_assigned;
169        self.cs.exit_region();
170
171        // Check that all table columns have the same length `first_unused`,
172        // and all cells up to that length are assigned.
173        let first_unused = compute_table_lengths(&default_and_assigned)?;
174
175        // Record these columns so that we can prevent them from being used again.
176        for column in default_and_assigned.keys() {
177            self.table_columns.push(*column);
178        }
179
180        for (col, (default_val, _)) in default_and_assigned {
181            // default_val must be Some because we must have assigned
182            // at least one cell in each column, and in that case we checked
183            // that all cells up to first_unused were assigned.
184            self.cs
185                .fill_from_row(col.inner(), first_unused, default_val.unwrap())?;
186        }
187
188        Ok(())
189    }
190
191    fn constrain_instance(&mut self, cell: Cell, instance: Column<Instance>, row: usize) {
192        self.cs.copy(
193            cell.column,
194            cell.row_offset, // *self.regions[*cell.region_index] + cell.row_offset,
195            instance.into(),
196            row,
197        );
198    }
199
200    fn get_challenge(&self, challenge: Challenge) -> Value<F> {
201        self.cs.get_challenge(challenge)
202    }
203
204    fn next_phase(&mut self) {
205        self.cs.next_phase();
206    }
207
208    fn get_root(&mut self) -> &mut Self::Root {
209        self
210    }
211
212    fn push_namespace<NR, N>(&mut self, name_fn: N)
213    where
214        NR: Into<String>,
215        N: FnOnce() -> NR,
216    {
217        self.cs.push_namespace(name_fn)
218    }
219
220    fn pop_namespace(&mut self, gadget_name: Option<String>) {
221        self.cs.pop_namespace(gadget_name)
222    }
223}
224
225struct SingleChipLayouterRegion<'r, 'a, F: Field, CS: Assignment<F> + 'a> {
226    layouter: &'r mut SingleChipLayouter<'a, F, CS>,
227    region_index: RegionIndex,
228    /// Stores the constants to be assigned, and the cells to which they are copied.
229    constants: Vec<(Assigned<F>, Cell)>,
230}
231
232impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> fmt::Debug
233    for SingleChipLayouterRegion<'r, 'a, F, CS>
234{
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        f.debug_struct("SingleChipLayouterRegion")
237            .field("layouter", &self.layouter)
238            .field("region_index", &self.region_index)
239            .finish()
240    }
241}
242
243impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> SingleChipLayouterRegion<'r, 'a, F, CS> {
244    fn new(layouter: &'r mut SingleChipLayouter<'a, F, CS>, region_index: RegionIndex) -> Self {
245        SingleChipLayouterRegion {
246            layouter,
247            region_index,
248            constants: vec![],
249        }
250    }
251}
252
253impl<'r, 'a, F: Field, CS: Assignment<F> + 'a + SyncDeps> RegionLayouter<F>
254    for SingleChipLayouterRegion<'r, 'a, F, CS>
255{
256    fn enable_selector<'v>(
257        &'v mut self,
258        annotation: &'v (dyn Fn() -> String + 'v),
259        selector: &Selector,
260        offset: usize,
261    ) -> Result<(), Error> {
262        self.layouter.cs.enable_selector(
263            annotation, selector,
264            offset, // *self.layouter.regions[*self.region_index] + offset,
265        )
266    }
267
268    fn name_column<'v>(
269        &'v mut self,
270        annotation: &'v (dyn Fn() -> String + 'v),
271        column: Column<Any>,
272    ) {
273        self.layouter.cs.annotate_column(annotation, column);
274    }
275
276    fn assign_advice<'v>(
277        &mut self,
278        // annotation: &'v (dyn Fn() -> String + 'v),
279        column: Column<Advice>,
280        offset: usize,
281        to: Value<Assigned<F>>, // &'v mut (dyn FnMut() -> Value<Assigned<F>> + 'v),
282    ) -> AssignedCell<&'v Assigned<F>, F> {
283        let value = self.layouter.cs.assign_advice(
284            column, offset, //*self.layouter.regions[*self.region_index] + offset,
285            to,
286        );
287
288        AssignedCell {
289            value,
290            cell: Cell {
291                // region_index: self.region_index,
292                row_offset: offset,
293                column: column.into(),
294            },
295            _marker: PhantomData,
296        }
297    }
298
299    fn assign_advice_from_constant<'v>(
300        &'v mut self,
301        _annotation: &'v (dyn Fn() -> String + 'v),
302        column: Column<Advice>,
303        offset: usize,
304        constant: Assigned<F>,
305    ) -> Result<Cell, Error> {
306        let advice = self
307            .assign_advice(column, offset, Value::known(constant))
308            .cell;
309        self.constrain_constant(advice, constant)?;
310
311        Ok(advice)
312    }
313
314    fn assign_advice_from_instance<'v>(
315        &mut self,
316        _annotation: &'v (dyn Fn() -> String + 'v),
317        instance: Column<Instance>,
318        row: usize,
319        advice: Column<Advice>,
320        offset: usize,
321    ) -> Result<(Cell, Value<F>), Error> {
322        let value = self.layouter.cs.query_instance(instance, row)?;
323
324        let cell = self
325            .assign_advice(advice, offset, value.map(|v| Assigned::Trivial(v)))
326            .cell;
327
328        self.layouter.cs.copy(
329            cell.column,
330            cell.row_offset, // *self.layouter.regions[*cell.region_index] + cell.row_offset,
331            instance.into(),
332            row,
333        );
334
335        Ok((cell, value))
336    }
337
338    fn instance_value(
339        &mut self,
340        instance: Column<Instance>,
341        row: usize,
342    ) -> Result<Value<F>, Error> {
343        self.layouter.cs.query_instance(instance, row)
344    }
345
346    fn assign_fixed(
347        &mut self,
348        // annotation: &'v (dyn Fn() -> String + 'v),
349        column: Column<Fixed>,
350        offset: usize,
351        to: Assigned<F>,
352    ) -> Cell {
353        self.layouter.cs.assign_fixed(
354            column, offset, // *self.layouter.regions[*self.region_index] + offset,
355            to,
356        );
357
358        Cell {
359            // region_index: self.region_index,
360            row_offset: offset,
361            column: column.into(),
362        }
363    }
364
365    fn constrain_constant(&mut self, cell: Cell, constant: Assigned<F>) -> Result<(), Error> {
366        self.constants.push((constant, cell));
367        Ok(())
368    }
369
370    fn constrain_equal(&mut self, left: Cell, right: Cell) {
371        self.layouter.cs.copy(
372            left.column,
373            left.row_offset, // *self.layouter.regions[*left.region_index] + left.row_offset,
374            right.column,
375            right.row_offset, // *self.layouter.regions[*right.region_index] + right.row_offset,
376        );
377    }
378
379    fn get_challenge(&self, challenge: Challenge) -> Value<F> {
380        self.layouter.cs.get_challenge(challenge)
381    }
382
383    fn next_phase(&mut self) {
384        self.layouter.cs.next_phase();
385    }
386}
387
388#[cfg(test)]
389mod tests {
390    use halo2curves::pasta::vesta;
391
392    use super::SimpleFloorPlanner;
393    use crate::{
394        dev::MockProver,
395        plonk::{Advice, Circuit, Column, Error},
396    };
397
398    #[test]
399    fn not_enough_columns_for_constants() {
400        struct MyCircuit {}
401
402        impl Circuit<vesta::Scalar> for MyCircuit {
403            type Config = Column<Advice>;
404            type FloorPlanner = SimpleFloorPlanner;
405            type Params = ();
406
407            fn params(&self) -> Self::Params {}
408            fn without_witnesses(&self) -> Self {
409                MyCircuit {}
410            }
411
412            fn configure(meta: &mut crate::plonk::ConstraintSystem<vesta::Scalar>) -> Self::Config {
413                meta.advice_column()
414            }
415
416            fn synthesize(
417                &self,
418                config: Self::Config,
419                mut layouter: impl crate::circuit::Layouter<vesta::Scalar>,
420            ) -> Result<(), crate::plonk::Error> {
421                layouter.assign_region(
422                    || "assign constant",
423                    |mut region| {
424                        region.assign_advice_from_constant(
425                            || "one",
426                            config,
427                            0,
428                            vesta::Scalar::one(),
429                        )
430                    },
431                )?;
432
433                Ok(())
434            }
435        }
436
437        let circuit = MyCircuit {};
438        assert!(matches!(
439            MockProver::run(3, &circuit, vec![]).unwrap_err(),
440            Error::NotEnoughColumnsForConstants,
441        ));
442    }
443}