1use std::cmp;
2use std::collections::HashMap;
3use std::fmt;
4use std::marker::PhantomData;
5
6use ff::Field;
7
8use crate::{
9 circuit::{
10 layouter::{RegionColumn, RegionLayouter, RegionShape, TableLayouter},
11 Cell, Layouter, Region, RegionIndex, RegionStart, Table,
12 },
13 plonk::{
14 Advice, Any, Assigned, Assignment, Circuit, Column, Error, Fixed, FloorPlanner, Instance,
15 Selector, TableColumn,
16 },
17};
18
19#[derive(Debug)]
25pub struct SimpleFloorPlanner;
26
27impl FloorPlanner for SimpleFloorPlanner {
28 fn synthesize<F: Field, CS: Assignment<F>, C: Circuit<F>>(
29 cs: &mut CS,
30 circuit: &C,
31 config: C::Config,
32 constants: Vec<Column<Fixed>>,
33 ) -> Result<(), Error> {
34 let layouter = SingleChipLayouter::new(cs, constants)?;
35 circuit.synthesize(config, layouter)
36 }
37}
38
39pub struct SingleChipLayouter<'a, F: Field, CS: Assignment<F> + 'a> {
41 cs: &'a mut CS,
42 constants: Vec<Column<Fixed>>,
43 regions: Vec<RegionStart>,
45 columns: HashMap<RegionColumn, usize>,
47 table_columns: Vec<TableColumn>,
49 _marker: PhantomData<F>,
50}
51
52impl<'a, F: Field, CS: Assignment<F> + 'a> fmt::Debug for SingleChipLayouter<'a, F, CS> {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 f.debug_struct("SingleChipLayouter")
55 .field("regions", &self.regions)
56 .field("columns", &self.columns)
57 .finish()
58 }
59}
60
61impl<'a, F: Field, CS: Assignment<F>> SingleChipLayouter<'a, F, CS> {
62 pub fn new(cs: &'a mut CS, constants: Vec<Column<Fixed>>) -> Result<Self, Error> {
64 let ret = SingleChipLayouter {
65 cs,
66 constants,
67 regions: vec![],
68 columns: HashMap::default(),
69 table_columns: vec![],
70 _marker: PhantomData,
71 };
72 Ok(ret)
73 }
74}
75
76impl<'a, F: Field, CS: Assignment<F> + 'a> Layouter<F> for SingleChipLayouter<'a, F, CS> {
77 type Root = Self;
78
79 fn assign_region<A, AR, N, NR>(&mut self, name: N, mut assignment: A) -> Result<AR, Error>
80 where
81 A: FnMut(Region<'_, F>) -> Result<AR, Error>,
82 N: Fn() -> NR,
83 NR: Into<String>,
84 {
85 let region_index = self.regions.len();
86
87 let mut shape = RegionShape::new(region_index.into());
89 {
90 let region: &mut dyn RegionLayouter<F> = &mut shape;
91 assignment(region.into())?;
92 }
93
94 let mut region_start = 0;
97 for column in &shape.columns {
98 region_start = cmp::max(region_start, self.columns.get(column).cloned().unwrap_or(0));
99 }
100 self.regions.push(region_start.into());
101
102 for column in shape.columns {
104 self.columns.insert(column, region_start + shape.row_count);
105 }
106
107 self.cs.enter_region(name);
109 let mut region = SingleChipLayouterRegion::new(self, region_index.into());
110 let result = {
111 let region: &mut dyn RegionLayouter<F> = &mut region;
112 assignment(region.into())
113 }?;
114 let constants_to_assign = region.constants;
115 self.cs.exit_region();
116
117 if self.constants.is_empty() {
120 if !constants_to_assign.is_empty() {
121 return Err(Error::NotEnoughColumnsForConstants);
122 }
123 } else {
124 let constants_column = self.constants[0];
125 let next_constant_row = self
126 .columns
127 .entry(Column::<Any>::from(constants_column).into())
128 .or_default();
129 for (constant, advice) in constants_to_assign {
130 self.cs.assign_fixed(
131 || format!("Constant({:?})", constant.evaluate()),
132 constants_column,
133 *next_constant_row,
134 || Ok(constant),
135 )?;
136 self.cs.copy(
137 constants_column.into(),
138 *next_constant_row,
139 advice.column,
140 *self.regions[*advice.region_index] + advice.row_offset,
141 )?;
142 *next_constant_row += 1;
143 }
144 }
145
146 Ok(result)
147 }
148
149 fn assign_table<A, N, NR>(&mut self, name: N, mut assignment: A) -> Result<(), Error>
150 where
151 A: FnMut(Table<'_, F>) -> Result<(), Error>,
152 N: Fn() -> NR,
153 NR: Into<String>,
154 {
155 self.cs.enter_region(name);
158 let mut table = SimpleTableLayouter::new(self.cs, &self.table_columns);
159 {
160 let table: &mut dyn TableLayouter<F> = &mut table;
161 assignment(table.into())
162 }?;
163 let default_and_assigned = table.default_and_assigned;
164 self.cs.exit_region();
165
166 let first_unused = {
169 match default_and_assigned
170 .values()
171 .map(|(_, assigned)| {
172 if assigned.iter().all(|b| *b) {
173 Some(assigned.len())
174 } else {
175 None
176 }
177 })
178 .reduce(|acc, item| match (acc, item) {
179 (Some(a), Some(b)) if a == b => Some(a),
180 _ => None,
181 }) {
182 Some(Some(len)) => len,
183 _ => return Err(Error::Synthesis), }
185 };
186
187 for column in default_and_assigned.keys() {
189 self.table_columns.push(*column);
190 }
191
192 for (col, (default_val, _)) in default_and_assigned {
193 self.cs
197 .fill_from_row(col.inner(), first_unused, default_val.unwrap())?;
198 }
199
200 Ok(())
201 }
202
203 fn constrain_instance(
204 &mut self,
205 cell: Cell,
206 instance: Column<Instance>,
207 row: usize,
208 ) -> Result<(), Error> {
209 self.cs.copy(
210 cell.column,
211 *self.regions[*cell.region_index] + cell.row_offset,
212 instance.into(),
213 row,
214 )
215 }
216
217 fn get_root(&mut self) -> &mut Self::Root {
218 self
219 }
220
221 fn push_namespace<NR, N>(&mut self, name_fn: N)
222 where
223 NR: Into<String>,
224 N: FnOnce() -> NR,
225 {
226 self.cs.push_namespace(name_fn)
227 }
228
229 fn pop_namespace(&mut self, gadget_name: Option<String>) {
230 self.cs.pop_namespace(gadget_name)
231 }
232}
233
234struct SingleChipLayouterRegion<'r, 'a, F: Field, CS: Assignment<F> + 'a> {
235 layouter: &'r mut SingleChipLayouter<'a, F, CS>,
236 region_index: RegionIndex,
237 constants: Vec<(Assigned<F>, Cell)>,
239}
240
241impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> fmt::Debug
242 for SingleChipLayouterRegion<'r, 'a, F, CS>
243{
244 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245 f.debug_struct("SingleChipLayouterRegion")
246 .field("layouter", &self.layouter)
247 .field("region_index", &self.region_index)
248 .finish()
249 }
250}
251
252impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> SingleChipLayouterRegion<'r, 'a, F, CS> {
253 fn new(layouter: &'r mut SingleChipLayouter<'a, F, CS>, region_index: RegionIndex) -> Self {
254 SingleChipLayouterRegion {
255 layouter,
256 region_index,
257 constants: vec![],
258 }
259 }
260}
261
262impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> RegionLayouter<F>
263 for SingleChipLayouterRegion<'r, 'a, F, CS>
264{
265 fn enable_selector<'v>(
266 &'v mut self,
267 annotation: &'v (dyn Fn() -> String + 'v),
268 selector: &Selector,
269 offset: usize,
270 ) -> Result<(), Error> {
271 self.layouter.cs.enable_selector(
272 annotation,
273 selector,
274 *self.layouter.regions[*self.region_index] + offset,
275 )
276 }
277
278 fn assign_advice<'v>(
279 &'v mut self,
280 annotation: &'v (dyn Fn() -> String + 'v),
281 column: Column<Advice>,
282 offset: usize,
283 to: &'v mut (dyn FnMut() -> Result<Assigned<F>, Error> + 'v),
284 ) -> Result<Cell, Error> {
285 self.layouter.cs.assign_advice(
286 annotation,
287 column,
288 *self.layouter.regions[*self.region_index] + offset,
289 to,
290 )?;
291
292 Ok(Cell {
293 region_index: self.region_index,
294 row_offset: offset,
295 column: column.into(),
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.assign_advice(annotation, column, offset, &mut || Ok(constant))?;
307 self.constrain_constant(advice, constant)?;
308
309 Ok(advice)
310 }
311
312 fn assign_advice_from_instance<'v>(
313 &mut self,
314 annotation: &'v (dyn Fn() -> String + 'v),
315 instance: Column<Instance>,
316 row: usize,
317 advice: Column<Advice>,
318 offset: usize,
319 ) -> Result<(Cell, Option<F>), Error> {
320 let value = self.layouter.cs.query_instance(instance, row)?;
321
322 let cell = self.assign_advice(annotation, advice, offset, &mut || {
323 value.ok_or(Error::Synthesis).map(|v| v.into())
324 })?;
325
326 self.layouter.cs.copy(
327 cell.column,
328 *self.layouter.regions[*cell.region_index] + cell.row_offset,
329 instance.into(),
330 row,
331 )?;
332
333 Ok((cell, value))
334 }
335
336 fn assign_fixed<'v>(
337 &'v mut self,
338 annotation: &'v (dyn Fn() -> String + 'v),
339 column: Column<Fixed>,
340 offset: usize,
341 to: &'v mut (dyn FnMut() -> Result<Assigned<F>, Error> + 'v),
342 ) -> Result<Cell, Error> {
343 self.layouter.cs.assign_fixed(
344 annotation,
345 column,
346 *self.layouter.regions[*self.region_index] + offset,
347 to,
348 )?;
349
350 Ok(Cell {
351 region_index: self.region_index,
352 row_offset: offset,
353 column: column.into(),
354 })
355 }
356
357 fn constrain_constant(&mut self, cell: Cell, constant: Assigned<F>) -> Result<(), Error> {
358 self.constants.push((constant, cell));
359 Ok(())
360 }
361
362 fn constrain_equal(&mut self, left: Cell, right: Cell) -> Result<(), Error> {
363 self.layouter.cs.copy(
364 left.column,
365 *self.layouter.regions[*left.region_index] + left.row_offset,
366 right.column,
367 *self.layouter.regions[*right.region_index] + right.row_offset,
368 )?;
369
370 Ok(())
371 }
372}
373
374type DefaultTableValue<F> = Option<Option<Assigned<F>>>;
382
383pub(crate) struct SimpleTableLayouter<'r, 'a, F: Field, CS: Assignment<F> + 'a> {
384 cs: &'a mut CS,
385 used_columns: &'r [TableColumn],
386 pub(crate) default_and_assigned: HashMap<TableColumn, (DefaultTableValue<F>, Vec<bool>)>,
388}
389
390impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> fmt::Debug for SimpleTableLayouter<'r, 'a, F, CS> {
391 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
392 f.debug_struct("SimpleTableLayouter")
393 .field("used_columns", &self.used_columns)
394 .field("default_and_assigned", &self.default_and_assigned)
395 .finish()
396 }
397}
398
399impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> SimpleTableLayouter<'r, 'a, F, CS> {
400 pub(crate) fn new(cs: &'a mut CS, used_columns: &'r [TableColumn]) -> Self {
401 SimpleTableLayouter {
402 cs,
403 used_columns,
404 default_and_assigned: HashMap::default(),
405 }
406 }
407}
408
409impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> TableLayouter<F>
410 for SimpleTableLayouter<'r, 'a, F, CS>
411{
412 fn assign_cell<'v>(
413 &'v mut self,
414 annotation: &'v (dyn Fn() -> String + 'v),
415 column: TableColumn,
416 offset: usize,
417 to: &'v mut (dyn FnMut() -> Result<Assigned<F>, Error> + 'v),
418 ) -> Result<(), Error> {
419 if self.used_columns.contains(&column) {
420 return Err(Error::Synthesis); }
422
423 let entry = self.default_and_assigned.entry(column).or_default();
424
425 let mut value = None;
426 self.cs.assign_fixed(
427 annotation,
428 column.inner(),
429 offset, || {
431 let res = to();
432 value = res.as_ref().ok().cloned();
433 res
434 },
435 )?;
436
437 match (entry.0.is_none(), offset) {
438 (true, 0) => entry.0 = Some(value),
440 (false, 0) => return Err(Error::Synthesis), _ => (),
444 }
445 if entry.1.len() <= offset {
446 entry.1.resize(offset + 1, false);
447 }
448 entry.1[offset] = true;
449
450 Ok(())
451 }
452}
453
454#[cfg(test)]
455mod tests {
456 use pasta_curves::vesta;
457
458 use super::SimpleFloorPlanner;
459 use crate::{
460 dev::MockProver,
461 plonk::{Advice, Circuit, Column, Error},
462 };
463
464 #[test]
465 fn not_enough_columns_for_constants() {
466 struct MyCircuit {}
467
468 impl Circuit<vesta::Scalar> for MyCircuit {
469 type Config = Column<Advice>;
470 type FloorPlanner = SimpleFloorPlanner;
471
472 fn without_witnesses(&self) -> Self {
473 MyCircuit {}
474 }
475
476 fn configure(meta: &mut crate::plonk::ConstraintSystem<vesta::Scalar>) -> Self::Config {
477 meta.advice_column()
478 }
479
480 fn synthesize(
481 &self,
482 config: Self::Config,
483 mut layouter: impl crate::circuit::Layouter<vesta::Scalar>,
484 ) -> Result<(), crate::plonk::Error> {
485 layouter.assign_region(
486 || "assign constant",
487 |mut region| {
488 region.assign_advice_from_constant(
489 || "one",
490 config,
491 0,
492 vesta::Scalar::one(),
493 )
494 },
495 )?;
496
497 Ok(())
498 }
499 }
500
501 let circuit = MyCircuit {};
502 assert!(matches!(
503 MockProver::run(3, &circuit, vec![]).unwrap_err(),
504 Error::NotEnoughColumnsForConstants,
505 ));
506 }
507}