Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dev: Add halo2_proofs::dev::render_to_json API #760

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion halo2_proofs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ maybe-rayon = {version = "0.1.0", default-features = false}

# Developer tooling dependencies
plotters = { version = "0.3.0", default-features = false, optional = true }
serde = { version = "1", features = ["derive"], optional = true }
serde_json = { version = "1", optional = true }
tabbycat = { version = "0.1", features = ["attributes"], optional = true }

# Legacy circuit compatibility
Expand All @@ -73,7 +75,7 @@ getrandom = { version = "0.2", features = ["js"] }
[features]
default = ["batch", "multicore"]
multicore = ["maybe-rayon/threads"]
dev-graph = ["plotters", "tabbycat"]
dev-graph = ["plotters", "serde", "serde_json", "tabbycat"]
test-dev-graph = [
"dev-graph",
"plotters/bitmap_backend",
Expand Down
1 change: 1 addition & 0 deletions halo2_proofs/src/circuit/layouter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ pub struct RegionShape {
/// The virtual column involved in a region. This includes concrete columns,
/// as well as selectors that are not concrete columns at this stage.
#[derive(Eq, PartialEq, Copy, Clone, Debug, Hash)]
#[cfg_attr(feature = "dev-graph", derive(serde::Serialize))]
pub enum RegionColumn {
/// Concrete column
Column(Column<Any>),
Expand Down
5 changes: 4 additions & 1 deletion halo2_proofs/src/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ mod graph;

#[cfg(feature = "dev-graph")]
#[cfg_attr(docsrs, doc(cfg(feature = "dev-graph")))]
pub use graph::{circuit_dot_graph, layout::CircuitLayout};
pub use graph::{
circuit_dot_graph,
layout::{render_to_json, CircuitLayout},
};

#[derive(Debug)]
struct Region {
Expand Down
58 changes: 50 additions & 8 deletions halo2_proofs/src/dev/cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ use std::{
use ff::{Field, PrimeField};
use group::prime::PrimeGroup;

#[cfg(feature = "dev-graph")]
use serde::ser::SerializeStruct;

use crate::{
circuit::{layouter::RegionColumn, Value},
plonk::{
Expand Down Expand Up @@ -54,9 +57,37 @@ pub struct CircuitCost<G: PrimeGroup, ConcreteCircuit: Circuit<G::Scalar>> {
_marker: PhantomData<(G, ConcreteCircuit)>,
}

#[cfg(feature = "dev-graph")]
impl serde::Serialize for Column<Any> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut state = serializer.serialize_struct("Column", 2)?;
state.serialize_field(
"kind",
match self.column_type() {
Any::Advice => "advice",
Any::Fixed => "fixed",
Any::Instance => "instance",
},
)?;
state.serialize_field("index", &self.index())?;
state.end()
}
}

#[derive(Debug)]
#[cfg_attr(feature = "dev-graph", derive(serde::Serialize))]
pub(crate) struct Cell {
pub(crate) column: RegionColumn,
pub(crate) row: usize,
}

/// Region implementation used by Layout
#[allow(dead_code)]
#[derive(Debug)]
#[cfg_attr(feature = "dev-graph", derive(serde::Serialize))]
pub(crate) struct LayoutRegion {
/// The name of the region. Not required to be unique.
pub(crate) name: String,
Expand All @@ -66,8 +97,9 @@ pub(crate) struct LayoutRegion {
pub(crate) offset: Option<usize>,
/// The number of rows that this region takes up.
pub(crate) rows: usize,
/// The cells assigned in this region.
pub(crate) cells: Vec<(RegionColumn, usize)>,
/// The cells assigned in this region. We store this as a `Vec` to track multiple
/// assignments to a cell.
pub(crate) cells: Vec<Cell>,
}

/// Cost and graphing layouter
Expand All @@ -84,10 +116,11 @@ pub(crate) struct Layout {
pub(crate) total_advice_rows: usize,
/// Total fixed rows
pub(crate) total_fixed_rows: usize,
/// Any cells assigned outside of a region.
pub(crate) loose_cells: Vec<(RegionColumn, usize)>,
/// Any cells assigned outside of a region. We store this as a `Vec` to track multiple
/// assignments to a cell.
pub(crate) loose_cells: Vec<Cell>,
/// Pairs of cells between which we have equality constraints.
pub(crate) equality: Vec<(Column<Any>, usize, Column<Any>, usize)>,
pub(crate) equality: Vec<(Cell, Cell)>,
/// Selector assignments used for optimization pass
pub(crate) selectors: Vec<Vec<bool>>,
}
Expand Down Expand Up @@ -139,9 +172,9 @@ impl Layout {
region.rows = cmp::max(region.rows, row - offset + 1);
region.offset = Some(offset);

region.cells.push((column, row));
region.cells.push(Cell { column, row });
} else {
self.loose_cells.push((column, row));
self.loose_cells.push(Cell { column, row });
}
}
}
Expand Down Expand Up @@ -228,7 +261,16 @@ impl<F: Field> Assignment<F> for Layout {
r_col: Column<Any>,
r_row: usize,
) -> Result<(), crate::plonk::Error> {
self.equality.push((l_col, l_row, r_col, r_row));
self.equality.push((
Cell {
column: l_col.into(),
row: l_row,
},
Cell {
column: r_col.into(),
row: r_row,
},
));
Ok(())
}

Expand Down
57 changes: 45 additions & 12 deletions halo2_proofs/src/dev/graph/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ use plotters::{
coord::Shift,
prelude::{DrawingArea, DrawingAreaErrorKind, DrawingBackend},
};

use std::collections::HashSet;
use std::ops::Range;

use crate::{
circuit::layouter::RegionColumn,
dev::cost::Layout,
dev::cost::{Cell, Layout, LayoutRegion},
plonk::{Any, Circuit, Column, ConstraintSystem, FloorPlanner},
};

Expand Down Expand Up @@ -242,26 +243,26 @@ impl CircuitLayout {

// Darken the cells of the region that have been assigned to.
for region in layout.regions {
for (column, row) in region.cells {
for Cell { column, row } in region.cells {
draw_cell(&root, column_index(&cs, column), row)?;
}
}

// Darken any loose cells that have been assigned to.
for (column, row) in layout.loose_cells {
for Cell { column, row } in layout.loose_cells {
draw_cell(&root, column_index(&cs, column), row)?;
}

// Mark equality-constrained cells.
if self.mark_equality_cells {
let mut cells = HashSet::new();
for (l_col, l_row, r_col, r_row) in &layout.equality {
let l_col = column_index(&cs, (*l_col).into());
let r_col = column_index(&cs, (*r_col).into());
for (l, r) in &layout.equality {
let l_col = column_index(&cs, l.column);
let r_col = column_index(&cs, r.column);

// Deduplicate cells.
cells.insert((l_col, *l_row));
cells.insert((r_col, *r_row));
cells.insert((l_col, l.row));
cells.insert((r_col, r.row));
}

for (col, row) in cells {
Expand All @@ -274,11 +275,11 @@ impl CircuitLayout {

// Draw lines between equality-constrained cells.
if self.show_equality_constraints {
for (l_col, l_row, r_col, r_row) in &layout.equality {
let l_col = column_index(&cs, (*l_col).into());
let r_col = column_index(&cs, (*r_col).into());
for (l, r) in &layout.equality {
let l_col = column_index(&cs, l.column);
let r_col = column_index(&cs, r.column);
root.draw(&PathElement::new(
[(l_col, *l_row), (r_col, *r_row)],
[(l_col, l.row), (r_col, r.row)],
ShapeStyle::from(&RED),
))?;
}
Expand Down Expand Up @@ -318,3 +319,35 @@ impl CircuitLayout {
Ok(())
}
}

/// Renders the given circuit layout to a JSON string.
pub fn render_to_json<F: Field, ConcreteCircuit: Circuit<F>>(
circuit: &ConcreteCircuit,
) -> Result<String, serde_json::Error> {
// Collect the layout details.
let mut cs = ConstraintSystem::default();
let config = ConcreteCircuit::configure(&mut cs);
let mut layout = Layout::default();
ConcreteCircuit::FloorPlanner::synthesize(&mut layout, circuit, config, cs.constants).unwrap();

// Render.
#[derive(serde::Serialize)]
struct Circuit {
num_instance_columns: usize,
num_advice_columns: usize,
num_fixed_columns: usize,
total_rows: usize,
regions: Vec<LayoutRegion>,
loose_cells: Vec<Cell>,
selectors: Vec<Vec<bool>>,
}
serde_json::to_string(&Circuit {
num_instance_columns: cs.num_instance_columns,
num_advice_columns: cs.num_advice_columns,
num_fixed_columns: cs.num_fixed_columns,
total_rows: layout.total_rows,
regions: layout.regions,
loose_cells: layout.loose_cells,
selectors: layout.selectors,
})
}
1 change: 1 addition & 0 deletions halo2_proofs/src/plonk/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ impl TryFrom<Column<Any>> for Column<Instance> {
/// }
/// ```
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "dev-graph", derive(serde::Serialize))]
pub struct Selector(pub(crate) usize, bool);

impl Selector {
Expand Down