Compare commits

...

5 Commits

Author SHA1 Message Date
str4d 32a5c90bae
Merge 2d4d95e739 into 7df93fd855 2024-02-27 23:55:49 +08:00
Daira-Emma Hopwood 7df93fd855
Merge pull request #814 from adria0/fix/mdbook
Fix MD book generation
2024-02-26 23:50:17 +00:00
adria0 daaa638966 fix(mdbook): fix generation 2024-02-22 22:28:36 +01:00
Jack Grigg 2d4d95e739 dev: Add halo2::dev::render_to_json API
This exposes the internally-collected data used by CircuitLayout, to
enable alternative external visualisations.
2023-04-01 06:13:15 +00:00
Jack Grigg 42ea7116a7 dev: Introduce Cell helper struct to CircuitLayout internals 2023-04-01 02:00:58 +00:00
8 changed files with 111 additions and 26 deletions

View File

@ -12,7 +12,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: nightly toolchain: '1.76.0'
override: true override: true
# - name: Setup mdBook # - name: Setup mdBook
@ -26,7 +26,7 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: install command: install
args: mdbook --git https://github.com/HollowMan6/mdBook.git --rev 62e01b34c23b957579c04ee1b24b57814ed8a4d5 args: mdbook --git https://github.com/HollowMan6/mdBook.git --rev 5830c9555a4dc051675d17f1fcb04dd0920543e8
- name: Install mdbook-katex and mdbook-pdf - name: Install mdbook-katex and mdbook-pdf
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
@ -40,6 +40,11 @@ jobs:
- name: Build halo2 book - name: Build halo2 book
run: mdbook build book/ run: mdbook build book/
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly-2023-10-05
override: true
- name: Build latest rustdocs - name: Build latest rustdocs
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:

View File

@ -14,8 +14,6 @@ title = "The halo2 Book"
macros = "macros.txt" macros = "macros.txt"
renderers = ["html"] renderers = ["html"]
[output.katex]
[output.html] [output.html]
[output.html.print] [output.html.print]

View File

@ -55,6 +55,8 @@ maybe-rayon = {version = "0.1.0", default-features = false}
# Developer tooling dependencies # Developer tooling dependencies
plotters = { version = "0.3.0", default-features = false, optional = true } 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 } tabbycat = { version = "0.1", features = ["attributes"], optional = true }
# Legacy circuit compatibility # Legacy circuit compatibility
@ -78,7 +80,7 @@ getrandom = { version = "0.2", features = ["js"] }
[features] [features]
default = ["batch", "multicore"] default = ["batch", "multicore"]
multicore = ["maybe-rayon/threads"] multicore = ["maybe-rayon/threads"]
dev-graph = ["plotters", "tabbycat"] dev-graph = ["plotters", "serde", "serde_json", "tabbycat"]
test-dev-graph = [ test-dev-graph = [
"dev-graph", "dev-graph",
"plotters/bitmap_backend", "plotters/bitmap_backend",

View File

@ -122,6 +122,7 @@ pub struct RegionShape {
/// The virtual column involved in a region. This includes concrete columns, /// The virtual column involved in a region. This includes concrete columns,
/// as well as selectors that are not concrete columns at this stage. /// as well as selectors that are not concrete columns at this stage.
#[derive(Eq, PartialEq, Copy, Clone, Debug, Hash)] #[derive(Eq, PartialEq, Copy, Clone, Debug, Hash)]
#[cfg_attr(feature = "dev-graph", derive(serde::Serialize))]
pub enum RegionColumn { pub enum RegionColumn {
/// Concrete column /// Concrete column
Column(Column<Any>), Column(Column<Any>),

View File

@ -36,7 +36,10 @@ mod graph;
#[cfg(feature = "dev-graph")] #[cfg(feature = "dev-graph")]
#[cfg_attr(docsrs, doc(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)] #[derive(Debug)]
struct Region { struct Region {

View File

@ -11,6 +11,9 @@ use std::{
use ff::{Field, PrimeField}; use ff::{Field, PrimeField};
use group::prime::PrimeGroup; use group::prime::PrimeGroup;
#[cfg(feature = "dev-graph")]
use serde::ser::SerializeStruct;
use crate::{ use crate::{
circuit::{layouter::RegionColumn, Value}, circuit::{layouter::RegionColumn, Value},
plonk::{ plonk::{
@ -54,9 +57,37 @@ pub struct CircuitCost<G: PrimeGroup, ConcreteCircuit: Circuit<G::Scalar>> {
_marker: PhantomData<(G, ConcreteCircuit)>, _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 /// Region implementation used by Layout
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "dev-graph", derive(serde::Serialize))]
pub(crate) struct LayoutRegion { pub(crate) struct LayoutRegion {
/// The name of the region. Not required to be unique. /// The name of the region. Not required to be unique.
pub(crate) name: String, pub(crate) name: String,
@ -66,8 +97,9 @@ pub(crate) struct LayoutRegion {
pub(crate) offset: Option<usize>, pub(crate) offset: Option<usize>,
/// The number of rows that this region takes up. /// The number of rows that this region takes up.
pub(crate) rows: usize, pub(crate) rows: usize,
/// The cells assigned in this region. /// The cells assigned in this region. We store this as a `Vec` to track multiple
pub(crate) cells: Vec<(RegionColumn, usize)>, /// assignments to a cell.
pub(crate) cells: Vec<Cell>,
} }
/// Cost and graphing layouter /// Cost and graphing layouter
@ -84,10 +116,11 @@ pub(crate) struct Layout {
pub(crate) total_advice_rows: usize, pub(crate) total_advice_rows: usize,
/// Total fixed rows /// Total fixed rows
pub(crate) total_fixed_rows: usize, pub(crate) total_fixed_rows: usize,
/// Any cells assigned outside of a region. /// Any cells assigned outside of a region. We store this as a `Vec` to track multiple
pub(crate) loose_cells: Vec<(RegionColumn, usize)>, /// assignments to a cell.
pub(crate) loose_cells: Vec<Cell>,
/// Pairs of cells between which we have equality constraints. /// 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 /// Selector assignments used for optimization pass
pub(crate) selectors: Vec<Vec<bool>>, pub(crate) selectors: Vec<Vec<bool>>,
} }
@ -139,9 +172,9 @@ impl Layout {
region.rows = cmp::max(region.rows, row - offset + 1); region.rows = cmp::max(region.rows, row - offset + 1);
region.offset = Some(offset); region.offset = Some(offset);
region.cells.push((column, row)); region.cells.push(Cell { column, row });
} else { } else {
self.loose_cells.push((column, row)); self.loose_cells.push(Cell { column, row });
} }
} }
} }
@ -228,7 +261,16 @@ impl<F: Field> Assignment<F> for Layout {
r_col: Column<Any>, r_col: Column<Any>,
r_row: usize, r_row: usize,
) -> Result<(), crate::plonk::Error> { ) -> 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(()) Ok(())
} }

View File

@ -3,12 +3,13 @@ use plotters::{
coord::Shift, coord::Shift,
prelude::{DrawingArea, DrawingAreaErrorKind, DrawingBackend}, prelude::{DrawingArea, DrawingAreaErrorKind, DrawingBackend},
}; };
use std::collections::HashSet; use std::collections::HashSet;
use std::ops::Range; use std::ops::Range;
use crate::{ use crate::{
circuit::layouter::RegionColumn, circuit::layouter::RegionColumn,
dev::cost::Layout, dev::cost::{Cell, Layout, LayoutRegion},
plonk::{Any, Circuit, Column, ConstraintSystem, FloorPlanner}, plonk::{Any, Circuit, Column, ConstraintSystem, FloorPlanner},
}; };
@ -242,26 +243,26 @@ impl CircuitLayout {
// Darken the cells of the region that have been assigned to. // Darken the cells of the region that have been assigned to.
for region in layout.regions { 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)?; draw_cell(&root, column_index(&cs, column), row)?;
} }
} }
// Darken any loose cells that have been assigned to. // 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)?; draw_cell(&root, column_index(&cs, column), row)?;
} }
// Mark equality-constrained cells. // Mark equality-constrained cells.
if self.mark_equality_cells { if self.mark_equality_cells {
let mut cells = HashSet::new(); let mut cells = HashSet::new();
for (l_col, l_row, r_col, r_row) in &layout.equality { for (l, r) in &layout.equality {
let l_col = column_index(&cs, (*l_col).into()); let l_col = column_index(&cs, l.column);
let r_col = column_index(&cs, (*r_col).into()); let r_col = column_index(&cs, r.column);
// Deduplicate cells. // Deduplicate cells.
cells.insert((l_col, *l_row)); cells.insert((l_col, l.row));
cells.insert((r_col, *r_row)); cells.insert((r_col, r.row));
} }
for (col, row) in cells { for (col, row) in cells {
@ -274,11 +275,11 @@ impl CircuitLayout {
// Draw lines between equality-constrained cells. // Draw lines between equality-constrained cells.
if self.show_equality_constraints { if self.show_equality_constraints {
for (l_col, l_row, r_col, r_row) in &layout.equality { for (l, r) in &layout.equality {
let l_col = column_index(&cs, (*l_col).into()); let l_col = column_index(&cs, l.column);
let r_col = column_index(&cs, (*r_col).into()); let r_col = column_index(&cs, r.column);
root.draw(&PathElement::new( root.draw(&PathElement::new(
[(l_col, *l_row), (r_col, *r_row)], [(l_col, l.row), (r_col, r.row)],
ShapeStyle::from(&RED), ShapeStyle::from(&RED),
))?; ))?;
} }
@ -318,3 +319,35 @@ impl CircuitLayout {
Ok(()) 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,
})
}

View File

@ -253,6 +253,7 @@ impl TryFrom<Column<Any>> for Column<Instance> {
/// } /// }
/// ``` /// ```
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "dev-graph", derive(serde::Serialize))]
pub struct Selector(pub(crate) usize, bool); pub struct Selector(pub(crate) usize, bool);
impl Selector { impl Selector {