diff --git a/src/circuit/layouter.rs b/src/circuit/layouter.rs index 563883a3..bc464fce 100644 --- a/src/circuit/layouter.rs +++ b/src/circuit/layouter.rs @@ -1,13 +1,17 @@ //! Implementations of common circuit layouters. use std::cmp; -use std::collections::{HashMap, HashSet}; +use std::collections::HashSet; use std::fmt; -use std::marker::PhantomData; -use super::{Cell, Layouter, Region, RegionIndex, RegionStart}; -use crate::arithmetic::FieldExt; -use crate::plonk::{Advice, Any, Assignment, Column, Error, Fixed, Permutation, Selector}; +use super::{Cell, RegionIndex}; +use crate::{ + arithmetic::FieldExt, + plonk::{Advice, Any, Column, Error, Fixed, Permutation, Selector}, +}; + +mod single_pass; +pub use single_pass::SingleChipLayouter; /// Helper trait for implementing a custom [`Layouter`]. /// @@ -37,6 +41,8 @@ use crate::plonk::{Advice, Any, Assignment, Column, Error, Fixed, Permutation, S /// TODO: It would be great if we could constrain the columns in these types to be /// "logical" columns that are guaranteed to correspond to the chip (and have come from /// `Chip::Config`). +/// +/// [`Layouter`]: super::Layouter pub trait RegionLayouter: fmt::Debug { /// Enables a selector at the given offset. fn enable_selector<'v>( @@ -75,97 +81,6 @@ pub trait RegionLayouter: fmt::Debug { ) -> Result<(), Error>; } -/// A [`Layouter`] for a single-chip circuit. -pub struct SingleChipLayouter<'a, F: FieldExt, CS: Assignment + 'a> { - cs: &'a mut CS, - /// Stores the starting row for each region. - regions: Vec, - /// Stores the first empty row for each column. - columns: HashMap, usize>, - _marker: PhantomData, -} - -impl<'a, F: FieldExt, CS: Assignment + 'a> fmt::Debug for SingleChipLayouter<'a, F, CS> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SingleChipLayouter") - .field("regions", &self.regions) - .field("columns", &self.columns) - .finish() - } -} - -impl<'a, F: FieldExt, CS: Assignment> SingleChipLayouter<'a, F, CS> { - /// Creates a new single-chip layouter. - pub fn new(cs: &'a mut CS) -> Result { - let ret = SingleChipLayouter { - cs, - regions: vec![], - columns: HashMap::default(), - _marker: PhantomData, - }; - Ok(ret) - } -} - -impl<'a, F: FieldExt, CS: Assignment + 'a> Layouter for SingleChipLayouter<'a, F, CS> { - type Root = Self; - - fn assign_region(&mut self, name: N, mut assignment: A) -> Result - where - A: FnMut(Region<'_, F>) -> Result, - N: Fn() -> NR, - NR: Into, - { - let region_index = self.regions.len(); - - // Get shape of the region. - let mut shape = RegionShape::new(region_index.into()); - { - let region: &mut dyn RegionLayouter = &mut shape; - assignment(region.into())?; - } - - // Lay out this region. We implement the simplest approach here: position the - // region starting at the earliest row for which none of the columns are in use. - let mut region_start = 0; - for column in &shape.columns { - region_start = cmp::max(region_start, self.columns.get(column).cloned().unwrap_or(0)); - } - self.regions.push(region_start.into()); - - // Update column usage information. - for column in shape.columns { - self.columns.insert(column, region_start + shape.row_count); - } - - self.cs.enter_region(name); - let mut region = SingleChipLayouterRegion::new(self, region_index.into()); - let result = { - let region: &mut dyn RegionLayouter = &mut region; - assignment(region.into()) - }?; - self.cs.exit_region(); - - Ok(result) - } - - fn get_root(&mut self) -> &mut Self::Root { - self - } - - fn push_namespace(&mut self, name_fn: N) - where - NR: Into, - N: FnOnce() -> NR, - { - self.cs.push_namespace(name_fn) - } - - fn pop_namespace(&mut self, gadget_name: Option) { - self.cs.pop_namespace(gadget_name) - } -} - /// The shape of a region. For a region at a certain index, we track /// the set of columns it uses as well as the number of rows it uses. #[derive(Debug)] @@ -259,104 +174,3 @@ impl RegionLayouter for RegionShape { Ok(()) } } - -struct SingleChipLayouterRegion<'r, 'a, F: FieldExt, CS: Assignment + 'a> { - layouter: &'r mut SingleChipLayouter<'a, F, CS>, - region_index: RegionIndex, -} - -impl<'r, 'a, F: FieldExt, CS: Assignment + 'a> fmt::Debug - for SingleChipLayouterRegion<'r, 'a, F, CS> -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SingleChipLayouterRegion") - .field("layouter", &self.layouter) - .field("region_index", &self.region_index) - .finish() - } -} - -impl<'r, 'a, F: FieldExt, CS: Assignment + 'a> SingleChipLayouterRegion<'r, 'a, F, CS> { - fn new(layouter: &'r mut SingleChipLayouter<'a, F, CS>, region_index: RegionIndex) -> Self { - SingleChipLayouterRegion { - layouter, - region_index, - } - } -} - -impl<'r, 'a, F: FieldExt, CS: Assignment + 'a> RegionLayouter - for SingleChipLayouterRegion<'r, 'a, F, CS> -{ - fn enable_selector<'v>( - &'v mut self, - annotation: &'v (dyn Fn() -> String + 'v), - selector: &Selector, - offset: usize, - ) -> Result<(), Error> { - self.layouter.cs.enable_selector( - annotation, - selector, - *self.layouter.regions[*self.region_index] + offset, - ) - } - - fn assign_advice<'v>( - &'v mut self, - annotation: &'v (dyn Fn() -> String + 'v), - column: Column, - offset: usize, - to: &'v mut (dyn FnMut() -> Result + 'v), - ) -> Result { - self.layouter.cs.assign_advice( - annotation, - column, - *self.layouter.regions[*self.region_index] + offset, - to, - )?; - - Ok(Cell { - region_index: self.region_index, - row_offset: offset, - column: column.into(), - }) - } - - fn assign_fixed<'v>( - &'v mut self, - annotation: &'v (dyn Fn() -> String + 'v), - column: Column, - offset: usize, - to: &'v mut (dyn FnMut() -> Result + 'v), - ) -> Result { - self.layouter.cs.assign_fixed( - annotation, - column, - *self.layouter.regions[*self.region_index] + offset, - to, - )?; - - Ok(Cell { - region_index: self.region_index, - row_offset: offset, - column: column.into(), - }) - } - - fn constrain_equal( - &mut self, - permutation: &Permutation, - left: Cell, - right: Cell, - ) -> Result<(), Error> { - self.layouter.cs.copy( - permutation, - left.column, - *self.layouter.regions[*left.region_index] + left.row_offset, - right.column, - *self.layouter.regions[*right.region_index] + right.row_offset, - )?; - - Ok(()) - } -} diff --git a/src/circuit/layouter/single_pass.rs b/src/circuit/layouter/single_pass.rs new file mode 100644 index 00000000..ac590cd5 --- /dev/null +++ b/src/circuit/layouter/single_pass.rs @@ -0,0 +1,203 @@ +use std::cmp; +use std::collections::HashMap; +use std::fmt; +use std::marker::PhantomData; + +use super::{RegionLayouter, RegionShape}; +use crate::{ + arithmetic::FieldExt, + circuit::{Cell, Layouter, Region, RegionIndex, RegionStart}, + plonk::{Advice, Any, Assignment, Column, Error, Fixed, Permutation, Selector}, +}; + +/// A [`Layouter`] for a single-chip circuit. +pub struct SingleChipLayouter<'a, F: FieldExt, CS: Assignment + 'a> { + cs: &'a mut CS, + /// Stores the starting row for each region. + regions: Vec, + /// Stores the first empty row for each column. + columns: HashMap, usize>, + _marker: PhantomData, +} + +impl<'a, F: FieldExt, CS: Assignment + 'a> fmt::Debug for SingleChipLayouter<'a, F, CS> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SingleChipLayouter") + .field("regions", &self.regions) + .field("columns", &self.columns) + .finish() + } +} + +impl<'a, F: FieldExt, CS: Assignment> SingleChipLayouter<'a, F, CS> { + /// Creates a new single-chip layouter. + pub fn new(cs: &'a mut CS) -> Result { + let ret = SingleChipLayouter { + cs, + regions: vec![], + columns: HashMap::default(), + _marker: PhantomData, + }; + Ok(ret) + } +} + +impl<'a, F: FieldExt, CS: Assignment + 'a> Layouter for SingleChipLayouter<'a, F, CS> { + type Root = Self; + + fn assign_region(&mut self, name: N, mut assignment: A) -> Result + where + A: FnMut(Region<'_, F>) -> Result, + N: Fn() -> NR, + NR: Into, + { + let region_index = self.regions.len(); + + // Get shape of the region. + let mut shape = RegionShape::new(region_index.into()); + { + let region: &mut dyn RegionLayouter = &mut shape; + assignment(region.into())?; + } + + // Lay out this region. We implement the simplest approach here: position the + // region starting at the earliest row for which none of the columns are in use. + let mut region_start = 0; + for column in &shape.columns { + region_start = cmp::max(region_start, self.columns.get(column).cloned().unwrap_or(0)); + } + self.regions.push(region_start.into()); + + // Update column usage information. + for column in shape.columns { + self.columns.insert(column, region_start + shape.row_count); + } + + self.cs.enter_region(name); + let mut region = SingleChipLayouterRegion::new(self, region_index.into()); + let result = { + let region: &mut dyn RegionLayouter = &mut region; + assignment(region.into()) + }?; + self.cs.exit_region(); + + Ok(result) + } + + fn get_root(&mut self) -> &mut Self::Root { + self + } + + fn push_namespace(&mut self, name_fn: N) + where + NR: Into, + N: FnOnce() -> NR, + { + self.cs.push_namespace(name_fn) + } + + fn pop_namespace(&mut self, gadget_name: Option) { + self.cs.pop_namespace(gadget_name) + } +} + +struct SingleChipLayouterRegion<'r, 'a, F: FieldExt, CS: Assignment + 'a> { + layouter: &'r mut SingleChipLayouter<'a, F, CS>, + region_index: RegionIndex, +} + +impl<'r, 'a, F: FieldExt, CS: Assignment + 'a> fmt::Debug + for SingleChipLayouterRegion<'r, 'a, F, CS> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SingleChipLayouterRegion") + .field("layouter", &self.layouter) + .field("region_index", &self.region_index) + .finish() + } +} + +impl<'r, 'a, F: FieldExt, CS: Assignment + 'a> SingleChipLayouterRegion<'r, 'a, F, CS> { + fn new(layouter: &'r mut SingleChipLayouter<'a, F, CS>, region_index: RegionIndex) -> Self { + SingleChipLayouterRegion { + layouter, + region_index, + } + } +} + +impl<'r, 'a, F: FieldExt, CS: Assignment + 'a> RegionLayouter + for SingleChipLayouterRegion<'r, 'a, F, CS> +{ + fn enable_selector<'v>( + &'v mut self, + annotation: &'v (dyn Fn() -> String + 'v), + selector: &Selector, + offset: usize, + ) -> Result<(), Error> { + self.layouter.cs.enable_selector( + annotation, + selector, + *self.layouter.regions[*self.region_index] + offset, + ) + } + + fn assign_advice<'v>( + &'v mut self, + annotation: &'v (dyn Fn() -> String + 'v), + column: Column, + offset: usize, + to: &'v mut (dyn FnMut() -> Result + 'v), + ) -> Result { + self.layouter.cs.assign_advice( + annotation, + column, + *self.layouter.regions[*self.region_index] + offset, + to, + )?; + + Ok(Cell { + region_index: self.region_index, + row_offset: offset, + column: column.into(), + }) + } + + fn assign_fixed<'v>( + &'v mut self, + annotation: &'v (dyn Fn() -> String + 'v), + column: Column, + offset: usize, + to: &'v mut (dyn FnMut() -> Result + 'v), + ) -> Result { + self.layouter.cs.assign_fixed( + annotation, + column, + *self.layouter.regions[*self.region_index] + offset, + to, + )?; + + Ok(Cell { + region_index: self.region_index, + row_offset: offset, + column: column.into(), + }) + } + + fn constrain_equal( + &mut self, + permutation: &Permutation, + left: Cell, + right: Cell, + ) -> Result<(), Error> { + self.layouter.cs.copy( + permutation, + left.column, + *self.layouter.regions[*left.region_index] + left.row_offset, + right.column, + *self.layouter.regions[*right.region_index] + right.row_offset, + )?; + + Ok(()) + } +}