Merge pull request #137 from zcash/layouter-strategy

Simple layouter strategy to track column usage
This commit is contained in:
str4d 2021-01-30 13:06:16 +13:00 committed by GitHub
commit f437bffc97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 113 additions and 18 deletions

View File

@ -138,6 +138,6 @@ pub trait Layouter<C: Chip> {
/// ``` /// ```
fn assign_region( fn assign_region(
&mut self, &mut self,
assignment: impl FnOnce(Region<'_, C>) -> Result<(), Error>, assignment: impl FnMut(Region<'_, C>) -> Result<(), Error>,
) -> Result<(), Error>; ) -> Result<(), Error>;
} }

View File

@ -1,11 +1,12 @@
//! Implementations of common circuit layouters. //! Implementations of common circuit layouters.
use std::cmp; use std::cmp;
use std::collections::{HashMap, HashSet};
use std::fmt; use std::fmt;
use std::marker::PhantomData; use std::marker::PhantomData;
use super::{Cell, Chip, Layouter, Permutation, Region}; use super::{Cell, Chip, Layouter, Permutation, Region};
use crate::plonk::{Advice, Assignment, Column, Error, Fixed}; use crate::plonk::{Advice, Any, Assignment, Column, Error, Fixed};
/// Helper trait for implementing a custom [`Layouter`]. /// Helper trait for implementing a custom [`Layouter`].
/// ///
@ -68,7 +69,8 @@ pub struct SingleChip<'a, C: Chip, CS: Assignment<C::Field> + 'a> {
cs: &'a mut CS, cs: &'a mut CS,
config: C::Config, config: C::Config,
regions: Vec<usize>, regions: Vec<usize>,
current_gate: usize, /// Stores the first empty row for each column.
columns: HashMap<Column<Any>, usize>,
_marker: PhantomData<C>, _marker: PhantomData<C>,
} }
@ -77,7 +79,7 @@ impl<'a, C: Chip, CS: Assignment<C::Field> + 'a> fmt::Debug for SingleChip<'a, C
f.debug_struct("SingleChip") f.debug_struct("SingleChip")
.field("config", &self.config) .field("config", &self.config)
.field("regions", &self.regions) .field("regions", &self.regions)
.field("current_gate", &self.current_gate) .field("columns", &self.columns)
.finish() .finish()
} }
} }
@ -89,7 +91,7 @@ impl<'a, C: Chip, CS: Assignment<C::Field>> SingleChip<'a, C, CS> {
cs, cs,
config, config,
regions: vec![], regions: vec![],
current_gate: 0, columns: HashMap::default(),
_marker: PhantomData, _marker: PhantomData,
} }
} }
@ -102,26 +104,122 @@ impl<'a, C: Chip, CS: Assignment<C::Field> + 'a> Layouter<C> for SingleChip<'a,
fn assign_region( fn assign_region(
&mut self, &mut self,
assignment: impl FnOnce(Region<'_, C>) -> Result<(), Error>, mut assignment: impl FnMut(Region<'_, C>) -> Result<(), Error>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let region_index = self.regions.len(); let region_index = self.regions.len();
self.regions.push(self.current_gate);
// Get shape of the region.
let mut shape = RegionShape::new(region_index);
{
let region: &mut dyn RegionLayouter<C> = &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);
// Update column usage information.
for column in shape.columns {
self.columns.insert(column, region_start + shape.row_count);
}
let mut region = SingleChipRegion::new(self, region_index); let mut region = SingleChipRegion::new(self, region_index);
{ {
let region: &mut dyn RegionLayouter<C> = &mut region; let region: &mut dyn RegionLayouter<C> = &mut region;
assignment(region.into())?; assignment(region.into())?;
} }
self.current_gate += region.row_count;
Ok(()) Ok(())
} }
} }
/// 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)]
pub struct RegionShape {
region_index: usize,
columns: HashSet<Column<Any>>,
row_count: usize,
}
impl RegionShape {
/// Create a new `RegionShape` for a region at `region_index`.
pub fn new(region_index: usize) -> Self {
RegionShape {
region_index,
columns: HashSet::default(),
row_count: 0,
}
}
/// Get the `region_index` of a `RegionShape`.
pub fn region_index(&self) -> usize {
self.region_index
}
/// Get a reference to the set of `columns` used in a `RegionShape`.
pub fn columns(&self) -> &HashSet<Column<Any>> {
&self.columns
}
/// Get the `row_count` of a `RegionShape`.
pub fn row_count(&self) -> usize {
self.row_count
}
}
impl<C: Chip> RegionLayouter<C> for RegionShape {
fn assign_advice<'v>(
&'v mut self,
column: Column<Advice>,
offset: usize,
_to: &'v mut (dyn FnMut() -> Result<C::Field, Error> + 'v),
) -> Result<Cell, Error> {
self.columns.insert(column.into());
self.row_count = cmp::max(self.row_count, offset + 1);
Ok(Cell {
region_index: self.region_index,
row_offset: offset,
column: column.into(),
})
}
fn assign_fixed<'v>(
&'v mut self,
column: Column<Fixed>,
offset: usize,
_to: &'v mut (dyn FnMut() -> Result<C::Field, Error> + 'v),
) -> Result<Cell, Error> {
self.columns.insert(column.into());
self.row_count = cmp::max(self.row_count, offset + 1);
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> {
// Equality constraints don't affect the region shape.
Ok(())
}
}
struct SingleChipRegion<'r, 'a, C: Chip, CS: Assignment<C::Field> + 'a> { struct SingleChipRegion<'r, 'a, C: Chip, CS: Assignment<C::Field> + 'a> {
layouter: &'r mut SingleChip<'a, C, CS>, layouter: &'r mut SingleChip<'a, C, CS>,
region_index: usize, region_index: usize,
row_count: usize,
} }
impl<'r, 'a, C: Chip, CS: Assignment<C::Field> + 'a> fmt::Debug impl<'r, 'a, C: Chip, CS: Assignment<C::Field> + 'a> fmt::Debug
@ -131,7 +229,6 @@ impl<'r, 'a, C: Chip, CS: Assignment<C::Field> + 'a> fmt::Debug
f.debug_struct("SingleChipRegion") f.debug_struct("SingleChipRegion")
.field("layouter", &self.layouter) .field("layouter", &self.layouter)
.field("region_index", &self.region_index) .field("region_index", &self.region_index)
.field("row_count", &self.row_count)
.finish() .finish()
} }
} }
@ -141,7 +238,6 @@ impl<'r, 'a, C: Chip, CS: Assignment<C::Field> + 'a> SingleChipRegion<'r, 'a, C,
SingleChipRegion { SingleChipRegion {
layouter, layouter,
region_index, region_index,
row_count: 0,
} }
} }
} }
@ -160,7 +256,6 @@ impl<'r, 'a, C: Chip, CS: Assignment<C::Field> + 'a> RegionLayouter<C>
self.layouter.regions[self.region_index] + offset, self.layouter.regions[self.region_index] + offset,
to, to,
)?; )?;
self.row_count = cmp::max(self.row_count, offset);
Ok(Cell { Ok(Cell {
region_index: self.region_index, region_index: self.region_index,
@ -180,7 +275,7 @@ impl<'r, 'a, C: Chip, CS: Assignment<C::Field> + 'a> RegionLayouter<C>
self.layouter.regions[self.region_index] + offset, self.layouter.regions[self.region_index] + offset,
to, to,
)?; )?;
self.row_count = cmp::max(self.row_count, offset);
Ok(Cell { Ok(Cell {
region_index: self.region_index, region_index: self.region_index,
row_offset: offset, row_offset: offset,

View File

@ -10,7 +10,7 @@ use crate::poly::Rotation;
pub trait ColumnType: 'static + Sized {} pub trait ColumnType: 'static + Sized {}
/// A column with an index and type /// A column with an index and type
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Column<C: ColumnType> { pub struct Column<C: ColumnType> {
index: usize, index: usize,
column_type: C, column_type: C,
@ -27,19 +27,19 @@ impl<C: ColumnType> Column<C> {
} }
/// An advice column /// An advice column
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Advice; pub struct Advice;
/// A fixed column /// A fixed column
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Fixed; pub struct Fixed;
/// An auxiliary column /// An auxiliary column
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Aux; pub struct Aux;
/// An enum over the Advice, Fixed, Aux structs /// An enum over the Advice, Fixed, Aux structs
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum Any { pub enum Any {
/// An Advice variant /// An Advice variant
Advice, Advice,