diff --git a/halo2_proofs/Cargo.toml b/halo2_proofs/Cargo.toml index f82e03a5db..8b3bd41d4d 100644 --- a/halo2_proofs/Cargo.toml +++ b/halo2_proofs/Cargo.toml @@ -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 @@ -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", diff --git a/halo2_proofs/src/circuit/layouter.rs b/halo2_proofs/src/circuit/layouter.rs index 695c450432..a2bdc2bb0b 100644 --- a/halo2_proofs/src/circuit/layouter.rs +++ b/halo2_proofs/src/circuit/layouter.rs @@ -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), diff --git a/halo2_proofs/src/dev.rs b/halo2_proofs/src/dev.rs index a292b0ab4e..a085731eb4 100644 --- a/halo2_proofs/src/dev.rs +++ b/halo2_proofs/src/dev.rs @@ -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 { diff --git a/halo2_proofs/src/dev/cost.rs b/halo2_proofs/src/dev/cost.rs index b0a9171eac..99b86fa2d6 100644 --- a/halo2_proofs/src/dev/cost.rs +++ b/halo2_proofs/src/dev/cost.rs @@ -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::{ @@ -54,7 +57,28 @@ pub struct CircuitCost> { _marker: PhantomData<(G, ConcreteCircuit)>, } +#[cfg(feature = "dev-graph")] +impl serde::Serialize for Column { + fn serialize(&self, serializer: S) -> Result + 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, @@ -63,6 +87,7 @@ pub(crate) struct Cell { /// 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, diff --git a/halo2_proofs/src/dev/graph/layout.rs b/halo2_proofs/src/dev/graph/layout.rs index b1060d921b..8c6ae5e640 100644 --- a/halo2_proofs/src/dev/graph/layout.rs +++ b/halo2_proofs/src/dev/graph/layout.rs @@ -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::{Cell, Layout}, + dev::cost::{Cell, Layout, LayoutRegion}, plonk::{Any, Circuit, Column, ConstraintSystem, FloorPlanner}, }; @@ -318,3 +319,35 @@ impl CircuitLayout { Ok(()) } } + +/// Renders the given circuit layout to a JSON string. +pub fn render_to_json>( + circuit: &ConcreteCircuit, +) -> Result { + // 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, + loose_cells: Vec, + selectors: Vec>, + } + 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, + }) +} diff --git a/halo2_proofs/src/plonk/circuit.rs b/halo2_proofs/src/plonk/circuit.rs index 334755dded..0bcb880cc8 100644 --- a/halo2_proofs/src/plonk/circuit.rs +++ b/halo2_proofs/src/plonk/circuit.rs @@ -253,6 +253,7 @@ impl TryFrom> for Column { /// } /// ``` #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "dev-graph", derive(serde::Serialize))] pub struct Selector(pub(crate) usize, bool); impl Selector {