Layouts versus drawings of wiring diagrams

In Catlab, layout and drawing (rendering) of wiring diagrams are mostly decoupled. This notebook shows how to lay out diagrams using Graphviz's rank-based layout or Catlab's series-parallel layout and then render them using Compose.jl or TikZ.

The morphism we will visualize is:

using Catlab.Theories

X = Ob(FreeSymmetricMonoidalCategory, :X)
f, g, h = (Hom(sym, X, X) for sym in (:f, :g, :h))

expr = otimes(f, compose(f,g), compose(f,g,h))

\[f \otimes \left(f \cdot g\right) \otimes \left(f \cdot g \cdot h\right) : X \otimes X \otimes X \to X \otimes X \otimes X\]

Let's convert this expression into a wiring diagram. This yields a purely combinatorial object, as evidenced by its underlying graph.

using Catlab.WiringDiagrams, Catlab.Graphics

diagram = to_wiring_diagram(expr)
Catlab.WiringDiagrams.DirectedWiringDiagrams.WiringDiagramGraphACSet{Int64} with elements V = 1:8, E = 1:9
V box
1 1
2 2
3 3
4 4
5 5
6 6
7 -2
8 -1
E src tgt wire
1 2 3 1
2 5 6 2
3 4 5 3
4 7 1 1
5 7 2 2
6 7 4 3
7 1 8 1
8 3 8 2
9 6 8 3

Graphviz layout

Calling to_graphviz both lays out and draws the diagram, entirely within Graphviz.

to_graphviz(diagram, orientation=LeftToRight)
G n1 f n0in1:e->n1:w n2 f n0in2:e->n2:w n4 f n0in3:e->n4:w n1:e->n0out1:w n3 g n2:e->n3:w n3:e->n0out2:w n5 g n4:e->n5:w n6 h n5:e->n6:w n6:e->n0out3:w

To get just the layout from Graphviz, we call graphviz_layout instead. We can then render this layout using Compose.jl. Note that the Graphviz layout has units in points.

import Compose

layout = graphviz_layout(diagram, orientation=LeftToRight)
h g f g f f

The same layout can be rendered in TikZ:

import TikzPictures

layout_to_tikz(layout, base_unit="1pt")

Series-parallel layout

Catlab has its own layout system based on series-parallel decomposition. In this case, the layout exactly recovers the structure of the morphism expression created at the beginning.

layout = layout_diagram(FreeSymmetricMonoidalCategory, diagram,
h g f g f f