Wiring diagrams in Graphviz
Catlab can draw wiring diagrams using the dot
program in Graphviz. This feature requires that Graphviz be installed, but does not require any additional Julia packages.
using Catlab.WiringDiagrams, Catlab.Graphics
Examples
Symmetric monoidal category
using Catlab.Doctrines
A, B = Ob(FreeSymmetricMonoidalCategory, :A, :B)
f = Hom(:f, A, B)
g = Hom(:g, B, A)
h = Hom(:h, otimes(A,B), otimes(A,B));
To start, here are a few very simple examples.
to_graphviz(f)
to_graphviz(compose(f,g))
to_graphviz(otimes(f,g))
In the next example, notice how Graphviz automatically "untwists" the double braiding to minimize edge crossings.
to_graphviz(compose(braid(A,A), otimes(f,f), braid(B,B)))
Here is a larger composite morphism.
composite = compose(otimes(g,f), h, otimes(f,g))
to_graphviz(composite)
By default, the wiring diagram is laid out from top to bottom. Other layout orientations can be requested, such as left-to-right or bottom-to-top:
to_graphviz(composite, orientation=LeftToRight)
to_graphviz(composite, orientation=BottomToTop)
When working with very large diagrams (larger than the ones shown here), it is sometimes convenient to omit the ports of the outer box and any wires attached to them.
to_graphviz(composite, outer_ports=false)
Biproduct category
A, B = Ob(FreeBiproductCategory, :A, :B)
f = Hom(:f, A, B)
g = Hom(:g, B, A);
By default, copies and merges are drawn the way they are represented internally, as multiple wires.
f1 = compose(mcopy(A), otimes(f,f))
to_graphviz(f1)
f2 = compose(mcopy(A), otimes(f,f), mmerge(B))
to_graphviz(f2)
To draw nodes for copies and merges, we need to add junctions to the wiring diagram.
to_graphviz(add_junctions!(to_wiring_diagram(f1)))
to_graphviz(add_junctions!(to_wiring_diagram(f2)))
Custom styles
The visual appearance of wiring diagrams can be customized by setting Graphviz attributes at the graph, node, edge, and cell levels. Graph, node, and edge attributes are described in the Graphviz documentation. Cell attributes are passed to the primary cell of the HTML-like label used for the boxes.
to_graphviz(compose(f,g),
labels = true, label_attr=:headlabel,
node_attrs = Dict(
:fontname => "Courier",
),
edge_attrs = Dict(
:fontname => "Courier",
:labelangle => "25",
:labeldistance => "2",
),
cell_attrs = Dict(
:bgcolor => "lavender",
)
)
Output formats
The function to_graphviz
returns an object of a type Graphviz.Graph
, representing a Graphviz graph as an abstract syntax tree. When displayed interactively, this object is automatically run through Graphviz and rendered as an SVG image. Sometimes it is convenient to perform this process manually, to change the output format or further customize the generated dot file.
To generate a dot file, use the builtin pretty-printer. This feature does not require Graphviz to be installed.
using Catlab.Graphics: Graphviz
graph = to_graphviz(compose(f,g))
Graphviz.pprint(graph)
digraph G {
graph [fontname="Serif",rankdir="TB"];
node [fontname="Serif",shape="none",width="0",height="0",margin="0"];
edge [arrowsize="0.5",fontname="Serif"];
{
graph [rank="source",rankdir="LR"];
node [style="invis",shape="none",label="",width="0",height="0.333"];
edge [style="invis"];
n1p1 [id="in1"];
n1p1;
}
{
graph [rank="sink",rankdir="LR"];
node [style="invis",shape="none",label="",width="0",height="0.333"];
edge [style="invis"];
n2p1 [id="out1"];
n2p1;
}
n3 [comment="f",id="n3",label=<<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0">
<TR><TD><TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0"><TR><TD HEIGHT="0" WIDTH="24" PORT="in1"></TD></TR></TABLE></TD></TR>
<TR><TD BORDER="1" CELLPADDING="4">f</TD></TR>
<TR><TD><TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0"><TR><TD HEIGHT="0" WIDTH="24" PORT="out1"></TD></TR></TABLE></TD></TR>
</TABLE>>];
n4 [comment="g",id="n4",label=<<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0">
<TR><TD><TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0"><TR><TD HEIGHT="0" WIDTH="24" PORT="in1"></TD></TR></TABLE></TD></TR>
<TR><TD BORDER="1" CELLPADDING="4">g</TD></TR>
<TR><TD><TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0"><TR><TD HEIGHT="0" WIDTH="24" PORT="out1"></TD></TR></TABLE></TD></TR>
</TABLE>>];
n1p1:s -> n3:in1:n [comment="A",id="e1"];
n3:out1:s -> n4:in1:n [comment="B",id="e2"];
n4:out1:s -> n2p1:n [comment="A",id="e3"];
}
Catlab provides a simple wrapper around the Graphviz command-line programs. For example, here the JSON output for the graph.
import JSON
JSON.parse(Graphviz.run_graphviz(graph, format="json0"))
Dict{String,Any} with 9 entries:
"name" => "G"
"strict" => false
"bb" => "0,0,24,206"
"objects" => Any[Dict{String,Any}("rank"=>"source","name"=>"%3","nodes"…
"fontname" => "Serif"
"rankdir" => "TB"
"directed" => true
"edges" => Any[Dict{String,Any}("headport"=>"in1:n","tail"=>2,"head"=…
"_subgraph_cnt" => 2