Merge pull request #305 from zcash/integrate-layouter-with-circuit

Introduce a FloorPlanner trait and integrate it into the Circuit trait
This commit is contained in:
str4d 2021-06-22 23:53:24 +01:00 committed by GitHub
commit b88435712f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 227 additions and 117 deletions

View File

@ -3,8 +3,7 @@ extern crate criterion;
extern crate halo2;
use halo2::arithmetic::FieldExt;
use halo2::circuit::layouter::SingleChipLayouter;
use halo2::circuit::{Cell, Layouter};
use halo2::circuit::{Cell, Layouter, SimpleFloorPlanner};
use halo2::pasta::{EqAffine, Fp};
use halo2::plonk::*;
use halo2::poly::{commitment::Params, Rotation};
@ -174,6 +173,11 @@ fn bench_with_k(name: &str, k: u32, c: &mut Criterion) {
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
type Config = PLONKConfig;
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
Self { a: None, k: self.k }
}
fn configure(meta: &mut ConstraintSystem<F>) -> PLONKConfig {
let a = meta.advice_column();
@ -214,10 +218,9 @@ fn bench_with_k(name: &str, k: u32, c: &mut Criterion) {
fn synthesize(
&self,
cs: &mut impl Assignment<F>,
config: PLONKConfig,
mut layouter: impl Layouter<F>,
) -> Result<(), Error> {
let mut layouter = SingleChipLayouter::new(cs)?;
let cs = StandardPLONK::new(config);
for _ in 0..(1 << (self.k - 1)) {

View File

@ -1,9 +1,9 @@
use halo2::{
arithmetic::FieldExt,
circuit::{layouter::SingleChipLayouter, Cell, Layouter, Region},
circuit::{Cell, Layouter, Region, SimpleFloorPlanner},
dev::CircuitLayout,
pasta::Fp,
plonk::{Advice, Assignment, Circuit, Column, ConstraintSystem, Error, Fixed, Permutation},
plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Fixed, Permutation},
poly::Rotation,
};
use plotters::prelude::*;
@ -222,6 +222,14 @@ fn main() {
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
type Config = PLONKConfig;
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
Self {
a: None,
lookup_tables: self.lookup_tables.clone(),
}
}
fn configure(meta: &mut ConstraintSystem<F>) -> PLONKConfig {
let e = meta.advice_column();
@ -321,10 +329,9 @@ fn main() {
fn synthesize(
&self,
cs: &mut impl Assignment<F>,
config: PLONKConfig,
mut layouter: impl Layouter<F>,
) -> Result<(), Error> {
let mut layouter = SingleChipLayouter::new(cs)?;
let cs = StandardPLONK::new(config);
let _ = cs.public_input(&mut layouter.namespace(|| "input"), || {

View File

@ -4,12 +4,9 @@ use std::marker::PhantomData;
use halo2::{
arithmetic::FieldExt,
circuit::{layouter::SingleChipLayouter, Cell, Chip, Layouter, Region},
circuit::{Cell, Chip, Layouter, Region, SimpleFloorPlanner},
dev::VerifyFailure,
plonk::{
Advice, Assignment, Circuit, Column, ConstraintSystem, Error, Instance, Permutation,
Selector,
},
plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Instance, Permutation, Selector},
poly::Rotation,
};
@ -282,6 +279,7 @@ impl<F: FieldExt> NumericInstructions<F> for FieldChip<F> {
/// In this struct we store the private input variables. We use `Option<F>` because
/// they won't have any value during key generation. During proving, if any of these
/// were `None` we would get an error.
#[derive(Default)]
struct MyCircuit<F: FieldExt> {
a: Option<F>,
b: Option<F>,
@ -290,6 +288,11 @@ struct MyCircuit<F: FieldExt> {
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
// Since we are using a single chip for everything, we can just reuse its config.
type Config = FieldConfig;
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
Self::default()
}
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
// We create the two advice columns that FieldChip uses for I/O.
@ -301,8 +304,11 @@ impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
FieldChip::configure(meta, advice, instance)
}
fn synthesize(&self, cs: &mut impl Assignment<F>, config: Self::Config) -> Result<(), Error> {
let mut layouter = SingleChipLayouter::new(cs)?;
fn synthesize(
&self,
config: Self::Config,
mut layouter: impl Layouter<F>,
) -> Result<(), Error> {
let field_chip = FieldChip::<F>::construct(config);
// Load our private values into the circuit.

View File

@ -4,12 +4,9 @@ use std::marker::PhantomData;
use halo2::{
arithmetic::FieldExt,
circuit::{layouter::SingleChipLayouter, Cell, Chip, Layouter, Region},
circuit::{Cell, Chip, Layouter, Region, SimpleFloorPlanner},
dev::VerifyFailure,
plonk::{
Advice, Assignment, Circuit, Column, ConstraintSystem, Error, Instance, Permutation,
Selector,
},
plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Instance, Permutation, Selector},
poly::Rotation,
};
@ -553,6 +550,7 @@ impl<F: FieldExt> FieldInstructions<F> for FieldChip<F> {
/// In this struct we store the private input variables. We use `Option<F>` because
/// they won't have any value during key generation. During proving, if any of these
/// were `None` we would get an error.
#[derive(Default)]
struct MyCircuit<F: FieldExt> {
a: Option<F>,
b: Option<F>,
@ -562,6 +560,11 @@ struct MyCircuit<F: FieldExt> {
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
// Since we are using a single chip for everything, we can just reuse its config.
type Config = FieldConfig;
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
Self::default()
}
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
// We create the two advice columns that FieldChip uses for I/O.
@ -573,8 +576,11 @@ impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
FieldChip::configure(meta, advice, instance)
}
fn synthesize(&self, cs: &mut impl Assignment<F>, config: Self::Config) -> Result<(), Error> {
let mut layouter = SingleChipLayouter::new(cs)?;
fn synthesize(
&self,
config: Self::Config,
mut layouter: impl Layouter<F>,
) -> Result<(), Error> {
let field_chip = FieldChip::<F>::construct(config, ());
// Load our private values into the circuit.

View File

@ -9,6 +9,9 @@ use crate::{
plonk::{Advice, Any, Assigned, Column, Error, Fixed, Permutation, Selector},
};
pub mod floor_planner;
pub use floor_planner::single_pass::SimpleFloorPlanner;
pub mod layouter;
/// A chip implements a set of instructions that can be used by gadgets.

View File

@ -0,0 +1,6 @@
//! Implementations of common circuit floor planners.
pub(super) mod single_pass;
mod v1;
pub use v1::{V1Pass, V1};

View File

@ -5,13 +5,36 @@ use std::marker::PhantomData;
use ff::Field;
use super::{RegionLayouter, RegionShape};
use crate::plonk::Assigned;
use crate::{
circuit::{Cell, Layouter, Region, RegionIndex, RegionStart},
plonk::{Advice, Any, Assignment, Column, Error, Fixed, Permutation, Selector},
circuit::{
layouter::{RegionLayouter, RegionShape},
Cell, Layouter, Region, RegionIndex, RegionStart,
},
plonk::{
Advice, Any, Assigned, Assignment, Circuit, Column, Error, Fixed, FloorPlanner,
Permutation, Selector,
},
};
/// A simple [`FloorPlanner`] that performs minimal optimizations.
///
/// This floor planner is suitable for debugging circuits. It aims to reflect the circuit
/// "business logic" in the circuit layout as closely as possible. It uses a single-pass
/// layouter that does not reorder regions for optimal packing.
#[derive(Debug)]
pub struct SimpleFloorPlanner;
impl FloorPlanner for SimpleFloorPlanner {
fn synthesize<F: Field, CS: Assignment<F>, C: Circuit<F>>(
cs: &mut CS,
circuit: &C,
config: C::Config,
) -> Result<(), Error> {
let layouter = SingleChipLayouter::new(cs)?;
circuit.synthesize(config, layouter)
}
}
/// A [`Layouter`] for a single-chip circuit.
pub struct SingleChipLayouter<'a, F: Field, CS: Assignment<F> + 'a> {
cs: &'a mut CS,

View File

@ -3,65 +3,79 @@ use std::marker::PhantomData;
use ff::Field;
use super::{RegionLayouter, RegionShape};
use crate::plonk::Assigned;
use crate::{
circuit::{Cell, Layouter, Region, RegionIndex, RegionStart},
plonk::{Advice, Assignment, Column, Error, Fixed, Permutation, Selector},
circuit::{
layouter::{RegionLayouter, RegionShape},
Cell, Layouter, Region, RegionIndex, RegionStart,
},
plonk::{
Advice, Assigned, Assignment, Circuit, Column, Error, Fixed, FloorPlanner, Permutation,
Selector,
},
};
mod strategy;
/// The version 1 [`Layouter`] provided by `halo2`.
/// The version 1 [`FloorPlanner`] provided by `halo2`.
///
/// It is a dual-pass layouter, that has visibility into the entire `Circuit::synthesize`
/// step.
pub struct V1<'a, F: Field, CS: Assignment<F> + 'a> {
/// - No column optimizations are performed. Circuit configuration is left entirely to the
/// circuit designer.
/// - A dual-pass layouter is used to measures regions prior to assignment.
/// - Regions are measured as rectangles, bounded on the cells they assign.
/// - Regions are layed out using a greedy first-fit strategy, after sorting regions by
/// their "advice area" (number of advice columns * rows).
#[derive(Debug)]
pub struct V1;
struct V1Plan<'a, F: Field, CS: Assignment<F> + 'a> {
cs: &'a mut CS,
/// Stores the starting row for each region.
regions: Vec<RegionStart>,
_marker: PhantomData<F>,
}
impl<'a, F: Field, CS: Assignment<F> + 'a> fmt::Debug for V1<'a, F, CS> {
impl<'a, F: Field, CS: Assignment<F> + 'a> fmt::Debug for V1Plan<'a, F, CS> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("layouter::V1").finish()
f.debug_struct("floor_planner::V1Plan").finish()
}
}
impl<'a, F: Field, CS: Assignment<F>> V1<'a, F, CS> {
impl<'a, F: Field, CS: Assignment<F>> V1Plan<'a, F, CS> {
/// Creates a new v1 layouter.
pub fn new(cs: &'a mut CS) -> Result<Self, Error> {
let ret = V1 {
let ret = V1Plan {
cs,
regions: vec![],
_marker: PhantomData,
};
Ok(ret)
}
}
impl FloorPlanner for V1 {
fn synthesize<F: Field, CS: Assignment<F>, C: Circuit<F>>(
cs: &mut CS,
circuit: &C,
config: C::Config,
) -> Result<(), Error> {
let mut plan = V1Plan::new(cs)?;
/// Runs the layouter to synthesize the circuit.
///
/// Even though `synthesis` has `FnMut` bounds, any value-assignment closures
/// contained within it are guaranteed to be called at most once.
pub fn run<S>(&mut self, mut synthesis: S) -> Result<(), Error>
where
S: FnMut(V1Pass<F, CS>) -> Result<(), Error>,
{
// First pass: measure the regions within the circuit.
let mut measure = MeasurementPass::new();
{
let pass = &mut measure;
synthesis(V1Pass::measure(pass))?;
circuit
.without_witnesses()
.synthesize(config.clone(), V1Pass::<_, CS>::measure(pass))?;
}
self.regions = strategy::slot_in_biggest_advice_first(measure.regions);
plan.regions = strategy::slot_in_biggest_advice_first(measure.regions);
// Second pass: assign the regions.
let mut assign = AssignmentPass::new(self);
let mut assign = AssignmentPass::new(&mut plan);
{
let pass = &mut assign;
synthesis(V1Pass::assign(pass))?;
circuit.synthesize(config, V1Pass::assign(pass))?;
}
Ok(())
@ -113,13 +127,13 @@ impl<'p, 'a, F: Field, CS: Assignment<F> + 'a> Layouter<F> for V1Pass<'p, 'a, F,
N: FnOnce() -> NR,
{
if let Pass::Assignment(pass) = &mut self.0 {
pass.layouter.cs.push_namespace(name_fn);
pass.plan.cs.push_namespace(name_fn);
}
}
fn pop_namespace(&mut self, gadget_name: Option<String>) {
if let Pass::Assignment(pass) = &mut self.0 {
pass.layouter.cs.pop_namespace(gadget_name);
pass.plan.cs.pop_namespace(gadget_name);
}
}
}
@ -156,15 +170,15 @@ impl MeasurementPass {
/// Assigns the circuit.
#[derive(Debug)]
pub struct AssignmentPass<'p, 'a, F: Field, CS: Assignment<F> + 'a> {
layouter: &'p mut V1<'a, F, CS>,
plan: &'p mut V1Plan<'a, F, CS>,
/// Counter tracking which region we need to assign next.
region_index: usize,
}
impl<'p, 'a, F: Field, CS: Assignment<F> + 'a> AssignmentPass<'p, 'a, F, CS> {
fn new(layouter: &'p mut V1<'a, F, CS>) -> Self {
fn new(plan: &'p mut V1Plan<'a, F, CS>) -> Self {
AssignmentPass {
layouter,
plan,
region_index: 0,
}
}
@ -179,38 +193,35 @@ impl<'p, 'a, F: Field, CS: Assignment<F> + 'a> AssignmentPass<'p, 'a, F, CS> {
let region_index = self.region_index;
self.region_index += 1;
self.layouter.cs.enter_region(name);
let mut region = V1Region::new(self.layouter, region_index.into());
self.plan.cs.enter_region(name);
let mut region = V1Region::new(self.plan, region_index.into());
let result = {
let region: &mut dyn RegionLayouter<F> = &mut region;
assignment(region.into())
}?;
self.layouter.cs.exit_region();
self.plan.cs.exit_region();
Ok(result)
}
}
struct V1Region<'r, 'a, F: Field, CS: Assignment<F> + 'a> {
layouter: &'r mut V1<'a, F, CS>,
plan: &'r mut V1Plan<'a, F, CS>,
region_index: RegionIndex,
}
impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> fmt::Debug for V1Region<'r, 'a, F, CS> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("V1Region")
.field("layouter", &self.layouter)
.field("plan", &self.plan)
.field("region_index", &self.region_index)
.finish()
}
}
impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> V1Region<'r, 'a, F, CS> {
fn new(layouter: &'r mut V1<'a, F, CS>, region_index: RegionIndex) -> Self {
V1Region {
layouter,
region_index,
}
fn new(plan: &'r mut V1Plan<'a, F, CS>, region_index: RegionIndex) -> Self {
V1Region { plan, region_index }
}
}
@ -221,10 +232,10 @@ impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> RegionLayouter<F> for V1Region<'r
selector: &Selector,
offset: usize,
) -> Result<(), Error> {
self.layouter.cs.enable_selector(
self.plan.cs.enable_selector(
annotation,
selector,
*self.layouter.regions[*self.region_index] + offset,
*self.plan.regions[*self.region_index] + offset,
)
}
@ -235,10 +246,10 @@ impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> RegionLayouter<F> for V1Region<'r
offset: usize,
to: &'v mut (dyn FnMut() -> Result<Assigned<F>, Error> + 'v),
) -> Result<Cell, Error> {
self.layouter.cs.assign_advice(
self.plan.cs.assign_advice(
annotation,
column,
*self.layouter.regions[*self.region_index] + offset,
*self.plan.regions[*self.region_index] + offset,
to,
)?;
@ -256,10 +267,10 @@ impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> RegionLayouter<F> for V1Region<'r
offset: usize,
to: &'v mut (dyn FnMut() -> Result<Assigned<F>, Error> + 'v),
) -> Result<Cell, Error> {
self.layouter.cs.assign_fixed(
self.plan.cs.assign_fixed(
annotation,
column,
*self.layouter.regions[*self.region_index] + offset,
*self.plan.regions[*self.region_index] + offset,
to,
)?;
@ -276,12 +287,12 @@ impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> RegionLayouter<F> for V1Region<'r
left: Cell,
right: Cell,
) -> Result<(), Error> {
self.layouter.cs.copy(
self.plan.cs.copy(
permutation,
left.column,
*self.layouter.regions[*left.region_index] + left.row_offset,
*self.plan.regions[*left.region_index] + left.row_offset,
right.column,
*self.layouter.regions[*right.region_index] + right.row_offset,
*self.plan.regions[*right.region_index] + right.row_offset,
)?;
Ok(())

View File

@ -10,12 +10,6 @@ use super::{Cell, RegionIndex};
use crate::plonk::Assigned;
use crate::plonk::{Advice, Any, Column, Error, Fixed, Permutation, Selector};
mod single_pass;
pub use single_pass::SingleChipLayouter;
mod v1;
pub use v1::{V1Pass, V1};
/// Helper trait for implementing a custom [`Layouter`].
///
/// This trait is used for implementing region assignments:
@ -88,9 +82,9 @@ pub trait RegionLayouter<F: Field>: fmt::Debug {
/// the set of columns it uses as well as the number of rows it uses.
#[derive(Clone, Debug)]
pub struct RegionShape {
region_index: RegionIndex,
columns: HashSet<Column<Any>>,
row_count: usize,
pub(super) region_index: RegionIndex,
pub(super) columns: HashSet<Column<Any>>,
pub(super) row_count: usize,
}
impl RegionShape {

View File

@ -11,7 +11,7 @@ use crate::{
arithmetic::{FieldExt, Group},
plonk::{
permutation, Advice, Any, Assignment, Circuit, Column, ColumnType, ConstraintSystem, Error,
Expression, Fixed, Permutation, Selector,
Expression, Fixed, FloorPlanner, Permutation, Selector,
},
poly::Rotation,
};
@ -192,9 +192,10 @@ impl Region {
/// ```
/// use halo2::{
/// arithmetic::FieldExt,
/// circuit::{Layouter, SimpleFloorPlanner},
/// dev::{MockProver, VerifyFailure},
/// pasta::Fp,
/// plonk::{Advice, Assignment, Circuit, Column, ConstraintSystem, Error},
/// plonk::{Advice, Circuit, Column, ConstraintSystem, Error},
/// poly::Rotation,
/// };
/// const K: u32 = 5;
@ -206,7 +207,7 @@ impl Region {
/// c: Column<Advice>,
/// }
///
/// #[derive(Clone)]
/// #[derive(Clone, Default)]
/// struct MyCircuit {
/// a: Option<u64>,
/// b: Option<u64>,
@ -214,6 +215,11 @@ impl Region {
///
/// impl<F: FieldExt> Circuit<F> for MyCircuit {
/// type Config = MyConfig;
/// type FloorPlanner = SimpleFloorPlanner;
///
/// fn without_witnesses(&self) -> Self {
/// Self::default()
/// }
///
/// fn configure(meta: &mut ConstraintSystem<F>) -> MyConfig {
/// let a = meta.advice_column();
@ -232,17 +238,20 @@ impl Region {
/// MyConfig { a, b, c }
/// }
///
/// fn synthesize(&self, cs: &mut impl Assignment<F>, config: MyConfig) -> Result<(), Error> {
/// cs.assign_advice(|| "a", config.a, 0, || {
/// self.a.map(|v| F::from_u64(v)).ok_or(Error::SynthesisError)
/// })?;
/// cs.assign_advice(|| "b", config.b, 0, || {
/// self.b.map(|v| F::from_u64(v)).ok_or(Error::SynthesisError)
/// })?;
/// cs.assign_advice(|| "c", config.c, 0, || {
/// self.a
/// .and_then(|a| self.b.map(|b| F::from_u64(a * b)))
/// .ok_or(Error::SynthesisError)
/// fn synthesize(&self, config: MyConfig, mut layouter: impl Layouter<F>) -> Result<(), Error> {
/// layouter.assign_region(|| "Example region", |mut region| {
/// region.assign_advice(|| "a", config.a, 0, || {
/// self.a.map(|v| F::from_u64(v)).ok_or(Error::SynthesisError)
/// })?;
/// region.assign_advice(|| "b", config.b, 0, || {
/// self.b.map(|v| F::from_u64(v)).ok_or(Error::SynthesisError)
/// })?;
/// region.assign_advice(|| "c", config.c, 0, || {
/// self.a
/// .and_then(|a| self.b.map(|b| F::from_u64(a * b)))
/// .ok_or(Error::SynthesisError)
/// })?;
/// Ok(())
/// })
/// }
/// }
@ -463,7 +472,7 @@ impl<F: FieldExt> MockProver<F> {
permutations,
};
circuit.synthesize(&mut prover, config)?;
ConcreteCircuit::FloorPlanner::synthesize(&mut prover, circuit, config)?;
Ok(prover)
}
@ -709,8 +718,8 @@ mod tests {
use super::{MockProver, VerifyFailure};
use crate::{
circuit::{layouter::SingleChipLayouter, Layouter},
plonk::{Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Error, Selector},
circuit::{Layouter, SimpleFloorPlanner},
plonk::{Advice, Any, Circuit, Column, ConstraintSystem, Error, Selector},
poly::Rotation,
};
@ -728,6 +737,7 @@ mod tests {
impl Circuit<Fp> for FaultyCircuit {
type Config = FaultyCircuitConfig;
type FloorPlanner = SimpleFloorPlanner;
fn configure(meta: &mut ConstraintSystem<Fp>) -> Self::Config {
let a = meta.advice_column();
@ -746,12 +756,15 @@ mod tests {
FaultyCircuitConfig { a, q }
}
fn without_witnesses(&self) -> Self {
Self {}
}
fn synthesize(
&self,
cs: &mut impl Assignment<Fp>,
config: Self::Config,
mut layouter: impl Layouter<Fp>,
) -> Result<(), Error> {
let mut layouter = SingleChipLayouter::new(cs)?;
layouter.assign_region(
|| "Faulty synthesis",
|mut region| {

View File

@ -3,7 +3,7 @@ use tabbycat::{AttrList, Edge, GraphBuilder, GraphType, Identity, StmtList};
use crate::plonk::{
Advice, Any, Assigned, Assignment, Circuit, Column, ConstraintSystem, Error, Fixed,
Permutation, Selector,
FloorPlanner, Permutation, Selector,
};
pub mod layout;
@ -21,7 +21,7 @@ pub fn circuit_dot_graph<F: Field, ConcreteCircuit: Circuit<F>>(
let mut cs = ConstraintSystem::default();
let config = ConcreteCircuit::configure(&mut cs);
let mut graph = Graph::default();
circuit.synthesize(&mut graph, config).unwrap();
ConcreteCircuit::FloorPlanner::synthesize(&mut graph, circuit, config).unwrap();
// Construct the node labels. We need to store these, because tabbycat operates on
// string references, and we need those references to live long enough.

View File

@ -9,7 +9,7 @@ use std::ops::Range;
use crate::plonk::{
Advice, Any, Assigned, Assignment, Circuit, Column, ConstraintSystem, Error, Fixed,
Permutation, Selector,
FloorPlanner, Permutation, Selector,
};
/// Graphical renderer for circuit layouts.
@ -75,7 +75,7 @@ impl CircuitLayout {
let mut cs = ConstraintSystem::default();
let config = ConcreteCircuit::configure(&mut cs);
let mut layout = Layout::default();
circuit.synthesize(&mut layout, config).unwrap();
ConcreteCircuit::FloorPlanner::synthesize(&mut layout, circuit, config).unwrap();
// Figure out what order to render the columns in.
// TODO: For now, just render them in the order they were configured.

View File

@ -7,6 +7,7 @@ use std::{
};
use super::{lookup, permutation, Error};
use crate::circuit::Layouter;
use crate::{arithmetic::FieldExt, circuit::Region, poly::Rotation};
/// A column type
@ -509,12 +510,38 @@ pub trait Assignment<F: Field> {
fn pop_namespace(&mut self, gadget_name: Option<String>);
}
/// A floor planning strategy for a circuit.
///
/// The floor planner is chip-agnostic and applies its strategy to the circuit it is used
/// within.
pub trait FloorPlanner {
/// Given the provided `cs`, synthesize the given circuit.
///
/// Internally, a floor planner will perform the following operations:
/// - Instantiate a [`Layouter`] for this floor planner.
/// - Perform any necessary setup or measurement tasks, which may involve one or more
/// calls to `Circuit::default().synthesize(config, &mut layouter)`.
/// - Call `circuit.synthesize(config, &mut layouter)` exactly once.
fn synthesize<F: Field, CS: Assignment<F>, C: Circuit<F>>(
cs: &mut CS,
circuit: &C,
config: C::Config,
) -> Result<(), Error>;
}
/// This is a trait that circuits provide implementations for so that the
/// backend prover can ask the circuit to synthesize using some given
/// [`ConstraintSystem`] implementation.
pub trait Circuit<F: Field> {
/// This is a configuration object that stores things like columns.
type Config: Clone;
/// The floor planner used for this circuit. This is an associated type of the
/// `Circuit` trait because its behaviour is circuit-critical.
type FloorPlanner: FloorPlanner;
/// Returns a copy of this circuit with no witness values (i.e. all witnesses set to
/// `None`). For most circuits, this will be equal to `Self::default()`.
fn without_witnesses(&self) -> Self;
/// The circuit is given an opportunity to describe the exact gate
/// arrangement, column arrangement, etc.
@ -523,7 +550,7 @@ pub trait Circuit<F: Field> {
/// Given the provided `cs`, synthesize the circuit. The concrete type of
/// the caller will be different depending on the context, and they may or
/// may not expect to have a witness present.
fn synthesize(&self, cs: &mut impl Assignment<F>, config: Self::Config) -> Result<(), Error>;
fn synthesize(&self, config: Self::Config, layouter: impl Layouter<F>) -> Result<(), Error>;
}
/// Low-degree expression representing an identity that must hold over the committed columns.

View File

@ -2,7 +2,9 @@ use ff::Field;
use group::Curve;
use super::{
circuit::{Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Fixed, Selector},
circuit::{
Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Fixed, FloorPlanner, Selector,
},
permutation, Assigned, Error, LagrangeCoeff, Permutation, Polynomial, ProvingKey, VerifyingKey,
};
use crate::poly::{
@ -177,7 +179,7 @@ where
};
// Synthesize the circuit to obtain URS
circuit.synthesize(&mut assembly, config)?;
ConcreteCircuit::FloorPlanner::synthesize(&mut assembly, circuit, config)?;
let fixed = batch_invert_assigned(&assembly.fixed);
@ -228,7 +230,7 @@ where
};
// Synthesize the circuit to obtain URS
circuit.synthesize(&mut assembly, config)?;
ConcreteCircuit::FloorPlanner::synthesize(&mut assembly, circuit, config)?;
let fixed = batch_invert_assigned(&assembly.fixed);

View File

@ -3,7 +3,9 @@ use group::Curve;
use std::iter;
use super::{
circuit::{Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Fixed, Selector},
circuit::{
Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Fixed, FloorPlanner, Selector,
},
lookup, permutation, vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX,
ChallengeY, Error, Permutation, ProvingKey,
};
@ -216,7 +218,7 @@ pub fn create_proof<
};
// Synthesize the circuit to obtain the witness and other information.
circuit.synthesize(&mut witness, config.clone())?;
ConcreteCircuit::FloorPlanner::synthesize(&mut witness, circuit, config.clone())?;
let advice = batch_invert_assigned(&witness.advice);

View File

@ -3,12 +3,12 @@
use group::Curve;
use halo2::arithmetic::FieldExt;
use halo2::circuit::{layouter::SingleChipLayouter, Cell, Layouter};
use halo2::circuit::{Cell, Layouter, SimpleFloorPlanner};
use halo2::dev::MockProver;
use halo2::pasta::{EqAffine, Fp};
use halo2::plonk::{
create_proof, keygen_pk, keygen_vk, verify_proof, Advice, Assignment, Circuit, Column,
ConstraintSystem, Error, Fixed, Permutation, VerifyingKey,
create_proof, keygen_pk, keygen_vk, verify_proof, Advice, Circuit, Column, ConstraintSystem,
Error, Fixed, Permutation, VerifyingKey,
};
use halo2::poly::{
commitment::{Blind, Params},
@ -264,6 +264,14 @@ fn plonk_api() {
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
type Config = PLONKConfig;
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
Self {
a: None,
lookup_tables: self.lookup_tables.clone(),
}
}
fn configure(meta: &mut ConstraintSystem<F>) -> PLONKConfig {
let e = meta.advice_column();
@ -363,10 +371,9 @@ fn plonk_api() {
fn synthesize(
&self,
cs: &mut impl Assignment<F>,
config: PLONKConfig,
mut layouter: impl Layouter<F>,
) -> Result<(), Error> {
let mut layouter = SingleChipLayouter::new(cs)?;
let cs = StandardPLONK::new(config);
let _ = cs.public_input(&mut layouter, || Ok(F::one() + F::one()))?;