From faf5da15c976d1e52812e1f7a576482f5ef8c00c Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 13 Jan 2021 22:58:12 +0800 Subject: [PATCH 1/2] Track column usage in RegionShape. Co-authored-by: Jack Grigg --- src/circuit.rs | 2 +- src/circuit/layouter.rs | 101 +++++++++++++++++++++++++++++++++++----- src/plonk/circuit.rs | 10 ++-- 3 files changed, 95 insertions(+), 18 deletions(-) diff --git a/src/circuit.rs b/src/circuit.rs index fb74fad1..29d53cd9 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -138,6 +138,6 @@ pub trait Layouter { /// ``` fn assign_region( &mut self, - assignment: impl FnOnce(Region<'_, C>) -> Result<(), Error>, + assignment: impl FnMut(Region<'_, C>) -> Result<(), Error>, ) -> Result<(), Error>; } diff --git a/src/circuit/layouter.rs b/src/circuit/layouter.rs index fa329f5b..476536c9 100644 --- a/src/circuit/layouter.rs +++ b/src/circuit/layouter.rs @@ -1,11 +1,12 @@ //! Implementations of common circuit layouters. use std::cmp; +use std::collections::{HashMap, HashSet}; use std::fmt; use std::marker::PhantomData; 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`]. /// @@ -68,7 +69,8 @@ pub struct SingleChip<'a, C: Chip, CS: Assignment + 'a> { cs: &'a mut CS, config: C::Config, regions: Vec, - current_gate: usize, + /// Stores the first empty row for each column. + columns: HashMap, usize>, _marker: PhantomData, } @@ -77,7 +79,7 @@ impl<'a, C: Chip, CS: Assignment + 'a> fmt::Debug for SingleChip<'a, C f.debug_struct("SingleChip") .field("config", &self.config) .field("regions", &self.regions) - .field("current_gate", &self.current_gate) + .field("columns", &self.columns) .finish() } } @@ -89,7 +91,7 @@ impl<'a, C: Chip, CS: Assignment> SingleChip<'a, C, CS> { cs, config, regions: vec![], - current_gate: 0, + columns: HashMap::default(), _marker: PhantomData, } } @@ -102,26 +104,104 @@ impl<'a, C: Chip, CS: Assignment + 'a> Layouter for SingleChip<'a, fn assign_region( &mut self, - assignment: impl FnOnce(Region<'_, C>) -> Result<(), Error>, + mut assignment: impl FnMut(Region<'_, C>) -> Result<(), Error>, ) -> Result<(), Error> { 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 = &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 region: &mut dyn RegionLayouter = &mut region; assignment(region.into())?; } - self.current_gate += region.row_count; Ok(()) } } +#[derive(Debug)] +struct RegionShape { + region_index: usize, + columns: HashSet>, + row_count: usize, +} + +impl RegionShape { + fn new(region_index: usize) -> Self { + RegionShape { + region_index, + columns: HashSet::default(), + row_count: 0, + } + } +} + +impl RegionLayouter for RegionShape { + fn assign_advice<'v>( + &'v mut self, + column: Column, + offset: usize, + _to: &'v mut (dyn FnMut() -> Result + 'v), + ) -> Result { + 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, + offset: usize, + _to: &'v mut (dyn FnMut() -> Result + 'v), + ) -> Result { + 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 + 'a> { layouter: &'r mut SingleChip<'a, C, CS>, region_index: usize, - row_count: usize, } impl<'r, 'a, C: Chip, CS: Assignment + 'a> fmt::Debug @@ -131,7 +211,6 @@ impl<'r, 'a, C: Chip, CS: Assignment + 'a> fmt::Debug f.debug_struct("SingleChipRegion") .field("layouter", &self.layouter) .field("region_index", &self.region_index) - .field("row_count", &self.row_count) .finish() } } @@ -141,7 +220,6 @@ impl<'r, 'a, C: Chip, CS: Assignment + 'a> SingleChipRegion<'r, 'a, C, SingleChipRegion { layouter, region_index, - row_count: 0, } } } @@ -160,7 +238,6 @@ impl<'r, 'a, C: Chip, CS: Assignment + 'a> RegionLayouter self.layouter.regions[self.region_index] + offset, to, )?; - self.row_count = cmp::max(self.row_count, offset); Ok(Cell { region_index: self.region_index, @@ -180,7 +257,7 @@ impl<'r, 'a, C: Chip, CS: Assignment + 'a> RegionLayouter self.layouter.regions[self.region_index] + offset, to, )?; - self.row_count = cmp::max(self.row_count, offset); + Ok(Cell { region_index: self.region_index, row_offset: offset, diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index b2e7ab01..33feaf72 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -10,7 +10,7 @@ use crate::poly::Rotation; pub trait ColumnType: 'static + Sized {} /// A column with an index and type -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct Column { index: usize, column_type: C, @@ -27,19 +27,19 @@ impl Column { } /// An advice column -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct Advice; /// A fixed column -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct Fixed; /// An auxiliary column -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct Aux; /// An enum over the Advice, Fixed, Aux structs -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum Any { /// An Advice variant Advice, From 2255fbec8b6ec60db8c98287a798049c6afdb56e Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Fri, 15 Jan 2021 12:07:39 +0800 Subject: [PATCH 2/2] Make RegionShape struct public --- src/circuit/layouter.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/circuit/layouter.rs b/src/circuit/layouter.rs index 476536c9..a6c282b7 100644 --- a/src/circuit/layouter.rs +++ b/src/circuit/layouter.rs @@ -138,21 +138,39 @@ impl<'a, C: Chip, CS: Assignment + 'a> Layouter for SingleChip<'a, } } +/// 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)] -struct RegionShape { +pub struct RegionShape { region_index: usize, columns: HashSet>, row_count: usize, } impl RegionShape { - fn new(region_index: usize) -> Self { + /// 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> { + &self.columns + } + + /// Get the `row_count` of a `RegionShape`. + pub fn row_count(&self) -> usize { + self.row_count + } } impl RegionLayouter for RegionShape {