-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Mermaid renderer for hugrs (#852)
Adds a `HugrView::mermaid_string` which produces things like ```mermaid graph LR subgraph 0 ["(0) DFG"] direction LR 1["(1) Input"] 1--0:0-->3 1--1:1-->3 2["(2) Output"] 3["(3) test.quantum.CX"] 3--0:1-->4 3--1:0-->4 3-.2:2.-4 4["(4) test.quantum.CX"] 4--0:0-->2 4--1:1-->2 end ``` Note that edges in mermaid are unordered, so I had to add the port indices explicitly. The new code in `src/hugr/views/render.rs` is just moved from `src/hugr/views.rs`. Closes #696 Requires CQCL/portgraph#125
- Loading branch information
Showing
12 changed files
with
282 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
//! Helper methods to compute the node/edge/port style when rendering a HUGR | ||
//! into dot or mermaid format. | ||
use portgraph::render::{EdgeStyle, NodeStyle, PortStyle}; | ||
use portgraph::{LinkView, NodeIndex, PortIndex, PortView}; | ||
|
||
use crate::ops::OpName; | ||
use crate::types::EdgeKind; | ||
use crate::HugrView; | ||
|
||
/// Configuration for rendering a HUGR graph. | ||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
#[non_exhaustive] | ||
pub struct RenderConfig { | ||
/// Show the node index in the graph nodes. | ||
pub node_indices: bool, | ||
/// Show port offsets in the graph edges. | ||
pub port_offsets_in_edges: bool, | ||
/// Show type labels on edges. | ||
pub type_labels_in_edges: bool, | ||
} | ||
|
||
impl Default for RenderConfig { | ||
fn default() -> Self { | ||
Self { | ||
node_indices: true, | ||
port_offsets_in_edges: true, | ||
type_labels_in_edges: true, | ||
} | ||
} | ||
} | ||
|
||
/// Formatter method to compute a node style. | ||
pub(super) fn node_style<H: HugrView>( | ||
h: &H, | ||
config: RenderConfig, | ||
) -> Box<dyn FnMut(NodeIndex) -> NodeStyle + '_> { | ||
if config.node_indices { | ||
Box::new(move |n| { | ||
NodeStyle::Box(format!( | ||
"({ni}) {name}", | ||
ni = n.index(), | ||
name = h.get_optype(n.into()).name() | ||
)) | ||
}) | ||
} else { | ||
Box::new(move |n| NodeStyle::Box(h.get_optype(n.into()).name().to_string())) | ||
} | ||
} | ||
|
||
/// Formatter method to compute a port style. | ||
pub(super) fn port_style<H: HugrView>( | ||
h: &H, | ||
_config: RenderConfig, | ||
) -> Box<dyn FnMut(PortIndex) -> PortStyle + '_> { | ||
let graph = h.portgraph(); | ||
Box::new(move |port| { | ||
let node = graph.port_node(port).unwrap(); | ||
let optype = h.get_optype(node.into()); | ||
let offset = graph.port_offset(port).unwrap(); | ||
match optype.port_kind(offset).unwrap() { | ||
EdgeKind::Static(ty) => PortStyle::new(html_escape::encode_text(&format!("{}", ty))), | ||
EdgeKind::Value(ty) => PortStyle::new(html_escape::encode_text(&format!("{}", ty))), | ||
EdgeKind::StateOrder => match graph.port_links(port).count() > 0 { | ||
true => PortStyle::text("", false), | ||
false => PortStyle::Hidden, | ||
}, | ||
_ => PortStyle::text("", true), | ||
} | ||
}) | ||
} | ||
|
||
/// Formatter method to compute an edge style. | ||
#[allow(clippy::type_complexity)] | ||
pub(super) fn edge_style<H: HugrView>( | ||
h: &H, | ||
config: RenderConfig, | ||
) -> Box< | ||
dyn FnMut( | ||
<H::Portgraph<'_> as LinkView>::LinkEndpoint, | ||
<H::Portgraph<'_> as LinkView>::LinkEndpoint, | ||
) -> EdgeStyle | ||
+ '_, | ||
> { | ||
let graph = h.portgraph(); | ||
Box::new(move |src, tgt| { | ||
let src_node = graph.port_node(src).unwrap(); | ||
let src_optype = h.get_optype(src_node.into()); | ||
let src_offset = graph.port_offset(src).unwrap(); | ||
let tgt_offset = graph.port_offset(tgt).unwrap(); | ||
|
||
let port_kind = src_optype.port_kind(src_offset).unwrap(); | ||
|
||
// StateOrder edges: Dotted line. | ||
// Control flow edges: Dashed line. | ||
// Static and Value edges: Solid line with label. | ||
let style = match port_kind { | ||
EdgeKind::StateOrder => EdgeStyle::Dotted, | ||
EdgeKind::ControlFlow => EdgeStyle::Dashed, | ||
EdgeKind::Static(_) | EdgeKind::Value(_) => EdgeStyle::Solid, | ||
}; | ||
|
||
// Compute the label for the edge, given the setting flags. | ||
// | ||
// Only static and value edges have types to display. | ||
let label = match ( | ||
config.port_offsets_in_edges, | ||
config.type_labels_in_edges, | ||
port_kind, | ||
) { | ||
(true, true, EdgeKind::Static(ty) | EdgeKind::Value(ty)) => { | ||
format!("{}:{}\n{ty}", src_offset.index(), tgt_offset.index()) | ||
} | ||
(true, _, _) => format!("{}:{}", src_offset.index(), tgt_offset.index()), | ||
(false, true, EdgeKind::Static(ty) | EdgeKind::Value(ty)) => format!("{}", ty), | ||
_ => return style, | ||
}; | ||
style.with_label(label) | ||
}) | ||
} |
5 changes: 5 additions & 0 deletions
5
src/hugr/views/snapshots/hugr__hugr__views__tests__dot_cfg.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
source: src/hugr/views/tests.rs | ||
expression: h.dot_string() | ||
--- | ||
"digraph {\n0 [shape=plain label=<<table border=\"1\"><tr><td align=\"text\" border=\"0\" colspan=\"1\">(0) CFG</td></tr></table>>]\n1 [shape=plain label=<<table border=\"1\"><tr><td port=\"in0\" align=\"text\" colspan=\"1\" cellpadding=\"1\" border=\"0\">0</td></tr><tr><td align=\"text\" border=\"0\" colspan=\"1\">(1) ExitBlock</td></tr></table>>]\n2 [shape=plain label=<<table border=\"1\"><tr><td port=\"in0\" align=\"text\" colspan=\"2\" cellpadding=\"1\" border=\"0\">0</td></tr><tr><td align=\"text\" border=\"0\" colspan=\"2\">(2) DataflowBlock</td></tr><tr><td port=\"out0\" align=\"text\" colspan=\"1\" cellpadding=\"1\" border=\"0\">0</td><td port=\"out1\" align=\"text\" colspan=\"1\" cellpadding=\"1\" border=\"0\">1</td></tr></table>>]\n2:out0 -> 7:in0 [style=\"dashed\"]\n2:out1 -> 1:in0 [style=\"dashed\"]\n3 [shape=plain label=<<table border=\"1\"><tr><td align=\"text\" border=\"0\" colspan=\"1\">(3) Input</td></tr><tr><td port=\"out0\" align=\"text\" colspan=\"1\" cellpadding=\"1\" >0: usize</td></tr></table>>]\n3:out0 -> 5:in0 [style=\"\"]\n4 [shape=plain label=<<table border=\"1\"><tr><td port=\"in0\" align=\"text\" colspan=\"1\" cellpadding=\"1\" >0: Sum([Tuple([usize]), Tuple([usize])])</td></tr><tr><td align=\"text\" border=\"0\" colspan=\"1\">(4) Output</td></tr></table>>]\n5 [shape=plain label=<<table border=\"1\"><tr><td port=\"in0\" align=\"text\" colspan=\"1\" cellpadding=\"1\" >0: usize</td></tr><tr><td align=\"text\" border=\"0\" colspan=\"1\">(5) MakeTuple</td></tr><tr><td port=\"out0\" align=\"text\" colspan=\"1\" cellpadding=\"1\" >0: Tuple([usize])</td></tr></table>>]\n5:out0 -> 6:in0 [style=\"\"]\n6 [shape=plain label=<<table border=\"1\"><tr><td port=\"in0\" align=\"text\" colspan=\"1\" cellpadding=\"1\" >0: Tuple([usize])</td></tr><tr><td align=\"text\" border=\"0\" colspan=\"1\">(6) Tag</td></tr><tr><td port=\"out0\" align=\"text\" colspan=\"1\" cellpadding=\"1\" >0: Sum([Tuple([usize]), Tuple([usize])])</td></tr></table>>]\n6:out0 -> 4:in0 [style=\"\"]\n7 [shape=plain label=<<table border=\"1\"><tr><td port=\"in0\" align=\"text\" colspan=\"1\" cellpadding=\"1\" border=\"0\">0</td></tr><tr><td align=\"text\" border=\"0\" colspan=\"1\">(7) DataflowBlock</td></tr><tr><td port=\"out0\" align=\"text\" colspan=\"1\" cellpadding=\"1\" border=\"0\">0</td></tr></table>>]\n7:out0 -> 1:in0 [style=\"dashed\"]\n8 [shape=plain label=<<table border=\"1\"><tr><td align=\"text\" border=\"0\" colspan=\"1\">(8) Input</td></tr><tr><td port=\"out0\" align=\"text\" colspan=\"1\" cellpadding=\"1\" >0: usize</td></tr></table>>]\n8:out0 -> 9:in1 [style=\"\"]\n9 [shape=plain label=<<table border=\"1\"><tr><td port=\"in0\" align=\"text\" colspan=\"1\" cellpadding=\"1\" >0: Sum(UnitSum(1))</td><td port=\"in1\" align=\"text\" colspan=\"1\" cellpadding=\"1\" >1: usize</td></tr><tr><td align=\"text\" border=\"0\" colspan=\"2\">(9) Output</td></tr></table>>]\n10 [shape=plain label=<<table border=\"1\"><tr><td align=\"text\" border=\"0\" colspan=\"1\">(10) const:sum:{tag:0, val:const:seq:{}}</td></tr><tr><td port=\"out0\" align=\"text\" colspan=\"1\" cellpadding=\"1\" >0: Sum(UnitSum(1))</td></tr></table>>]\n10:out0 -> 11:in0 [style=\"\"]\n11 [shape=plain label=<<table border=\"1\"><tr><td port=\"in0\" align=\"text\" colspan=\"1\" cellpadding=\"1\" >0: Sum(UnitSum(1))</td></tr><tr><td align=\"text\" border=\"0\" colspan=\"1\">(11) LoadConstant</td></tr><tr><td port=\"out0\" align=\"text\" colspan=\"1\" cellpadding=\"1\" >0: Sum(UnitSum(1))</td></tr></table>>]\n11:out0 -> 9:in0 [style=\"\"]\nhier0 [shape=plain label=\"0\"]\nhier0 -> hier2 [style = \"dashed\"] \nhier0 -> hier1 [style = \"dashed\"] \nhier0 -> hier7 [style = \"dashed\"] \nhier1 [shape=plain label=\"1\"]\nhier2 [shape=plain label=\"2\"]\nhier2 -> hier3 [style = \"dashed\"] \nhier2 -> hier4 [style = \"dashed\"] \nhier2 -> hier5 [style = \"dashed\"] \nhier2 -> hier6 [style = \"dashed\"] \nhier3 [shape=plain label=\"3\"]\nhier4 [shape=plain label=\"4\"]\nhier5 [shape=plain label=\"5\"]\nhier6 [shape=plain label=\"6\"]\nhier7 [shape=plain label=\"7\"]\nhier7 -> hier8 [style = \"dashed\"] \nhier7 -> hier9 [style = \"dashed\"] \nhier7 -> hier10 [style = \"dashed\"] \nhier7 -> hier11 [style = \"dashed\"] \nhier8 [shape=plain label=\"8\"]\nhier9 [shape=plain label=\"9\"]\nhier10 [shape=plain label=\"10\"]\nhier11 [shape=plain label=\"11\"]\n}\n" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
src/hugr/views/snapshots/hugr__hugr__views__tests__dot_empty_dfg.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
source: src/hugr/views/tests.rs | ||
expression: h.dot_string() | ||
--- | ||
"digraph {\n0 [shape=plain label=<<table border=\"1\"><tr><td align=\"text\" border=\"0\" colspan=\"1\">(0) DFG</td></tr></table>>]\n1 [shape=plain label=<<table border=\"1\"><tr><td align=\"text\" border=\"0\" colspan=\"1\">(1) Input</td></tr><tr><td port=\"out0\" align=\"text\" colspan=\"1\" cellpadding=\"1\" >0: Sum(UnitSum(2))</td></tr></table>>]\n1:out0 -> 2:in0 [style=\"\"]\n2 [shape=plain label=<<table border=\"1\"><tr><td port=\"in0\" align=\"text\" colspan=\"1\" cellpadding=\"1\" >0: Sum(UnitSum(2))</td></tr><tr><td align=\"text\" border=\"0\" colspan=\"1\">(2) Output</td></tr></table>>]\nhier0 [shape=plain label=\"0\"]\nhier0 -> hier1 [style = \"dashed\"] \nhier0 -> hier2 [style = \"dashed\"] \nhier1 [shape=plain label=\"1\"]\nhier2 [shape=plain label=\"2\"]\n}\n" |
32 changes: 32 additions & 0 deletions
32
src/hugr/views/snapshots/hugr__hugr__views__tests__mmd_cfg.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
--- | ||
source: src/hugr/views/tests.rs | ||
expression: h.mermaid_string() | ||
--- | ||
graph LR | ||
subgraph 0 ["(0) CFG"] | ||
direction LR | ||
subgraph 2 ["(2) DataflowBlock"] | ||
direction LR | ||
3["(3) Input"] | ||
3--"0:0<br>usize"-->5 | ||
4["(4) Output"] | ||
5["(5) MakeTuple"] | ||
5--"0:0<br>Tuple([usize])"-->6 | ||
6["(6) Tag"] | ||
6--"0:0<br>Sum([Tuple([usize]), Tuple([usize])])"-->4 | ||
end | ||
2-."0:0".->7 | ||
2-."1:0".->1 | ||
1["(1) ExitBlock"] | ||
subgraph 7 ["(7) DataflowBlock"] | ||
direction LR | ||
8["(8) Input"] | ||
8--"0:1<br>usize"-->9 | ||
9["(9) Output"] | ||
10["(10) const:sum:{tag:0, val:const:seq:{}}"] | ||
10--"0:0<br>Sum(UnitSum(1))"-->11 | ||
11["(11) LoadConstant"] | ||
11--"0:0<br>Sum(UnitSum(1))"-->9 | ||
end | ||
7-."0:0".->1 | ||
end |
Oops, something went wrong.