Diagram#
- class discopy.monoidal.Diagram(inside, dom, cod, _scan=True)[source]#
Bases:
discopy.cat.Arrow,discopy.abc.MonoidalCategory,discopy.utils.RichDisplayA diagram is a tuple of composable layers
insidewith a pair of typesdomandcodas domain and codomain.- Parameters:
inside (tuple[Layer, ...]) – The layers of the diagram.
dom (C0) – The domain of the diagram, i.e. its input.
cod (C0) – The codomain of the diagram, i.e. its output.
Summary
tensor([other])Parallel composition, called using
@.The boxes in each layer of the diagram.
The offset of a box is the length of the type on its left.
draw(**params)Draws a diagram using networkx and matplotlib.
interchange(i, j[, left])Interchange a box from layer
ito layerj.normalize([left])Implements normalisation of boundary-connected diagrams, see Delpeuch and Vicary Delpeuch and Vicary [DV22].
normal_form(**params)Returns the normal form of a diagram.
- property is_generator#
Whether a Diagram is a generator, i.e. a single box.
- property generator#
The single box in a generator Diagram.
- classmethod from_callable(dom, cod)[source]#
Define a diagram using the standard syntax for Python functions.
Note that we can specify the offset as argument.
Example
>>> x = Ty('x') >>> cup, cap = Box('cup', x @ x, Ty()), Box('cap', Ty(), x @ x) >>> @Diagram.from_callable(x, x) ... def snake(left): ... middle, right = cap(offset=1) ... cup(left, middle) ... return right >>> snake.draw(doctest='docs/_static/monoidal/diagramize.svg')
- tensor(other=None, *others)[source]#
Parallel composition, called using
@.- Parameters:
- Return type:
Important
The definition of tensor is biased to the left, i.e.:
self @ other == self @ other.dom >> self.cod @ other
Example
>>> x, y, z, w = Ty('x'), Ty('y'), Ty('z'), Ty('w') >>> f0, f1 = Box('f0', x, y), Box('f1', z, w) >>> assert f0 @ f1 == f0.tensor(f1) == f0 @ Id(z) >> Id(y) @ f1
>>> (f0 @ f1).draw( ... doctest='docs/_static/monoidal/tensor-example.svg')
- property offsets: list[int]#
The offset of a box is the length of the type on its left.
- property width#
The width of a diagram, i.e. the maximum number of parallel wires.
Example
>>> x = Ty('x') >>> f = Box('f', x, x ** 4) >>> diagram = f @ x ** 2 >> x ** 2 @ f.dagger() >>> assert diagram.width == 6
- encode()[source]#
Compact encoding of a diagram as a tuple of boxes and offsets.
Example
>>> x, y, z, w = Ty('x'), Ty('y'), Ty('z'), Ty('w') >>> f0, f1, g = Box('f0', x, y), Box('f1', z, w), Box('g', y @ w, y) >>> diagram = f0 @ f1 >> g >>> dom, boxes_and_offsets = diagram.encode() >>> assert dom == x @ z >>> assert boxes_and_offsets == [(f0, 0), (f1, 1), (g, 0)] >>> assert diagram == Diagram.decode(*diagram.encode()) >>> diagram.draw(doctest='docs/_static/monoidal/arrow-example.svg')
- classmethod decode(dom, boxes_and_offsets=None, boxes=None, offsets=None, cod=None)[source]#
Turn a tuple of boxes and offsets into a diagram.
- Parameters:
- Return type:
Example
>>> x, y, z, w = map(Ty, "xyzw") >>> f, g = Box('f', x, y), Box('g', z, w) >>> assert f @ z >> y @ g == Diagram.decode( ... dom=x @ z, cod=y @ w, boxes=[f, g], offsets=[0, 1])
Note
If
boxes_and_offsets is Nonethen we set it tozip(boxes, offstes).
- to_drawing(functor_factory=None)[source]#
Called before
Diagram.draw().- Return type:
- to_staircases()[source]#
Splits layers with more than one box into staircases.
Example
>>> x, y = Ty('x'), Ty('y') >>> f0, f1 = Box('f0', x, y), Box('f1', y, x) >>> diagram = f0 @ y >> y @ f1 >>> print(diagram.foliation()) f0 @ f1 >>> print(diagram.foliation().to_staircases()) f0 @ y >> y @ f1
- to_hypergraph()[source]#
Translate a planar diagram into a hypergraph.
The offset of each state (a box with an empty domain) is recorded on the hypergraph, so
discopy.hypergraph.Hypergraph.to_diagram()can place it back without a swap. A state has no input wires for the boundary scan to follow, so without its offset that scan would default every state to the left and then need swaps to reorder them – which a category without symmetry (e.g. a formal grammar) does not have.Example
>>> x, y = Ty('x'), Ty('y') >>> f0, f1 = Box('f0', x, y), Box('f1', y, x) >>> diagram = f0 @ f1.dagger() >> f0.dagger() @ f1 >>> assert diagram.to_hypergraph().to_diagram() == diagram.foliation()
- Return type:
- foliation()[source]#
Merges layers together to reduce the length of a diagram.
Example
>>> from discopy.monoidal import * >>> x, y = Ty('x'), Ty('y') >>> f0, f1 = Box('f0', x, y), Box('f1', y, x) >>> diagram = f0 @ f1.dagger() >> f0.dagger() @ f1 >>> print(diagram) f0 @ x >> y @ f1[::-1] >> f0[::-1] @ y >> x @ f1 >>> diagram.foliation().draw( ... doctest='docs/_static/monoidal/foliation-example.svg')
Note
If one defines a foliation as a sequence of unmergeable layers, there may exist many distinct foliations for the same diagram. When the diagram lives in a symmetric category with self-dual objects and its hypergraph is monogamous, causal and boundary-connected, the foliation is read off the hypergraph in one pass over the boundary (see
discopy.hypergraph.Hypergraph.to_diagram()). The self-duality is needed because that pass reconstructs the diagram with swaps, which would not be faithful in a rigid category such as pregroup, where the left and right adjoints of an object differ. Otherwise this scans top to bottom and merges layers eagerly.
- depth()[source]#
Computes (an upper bound to) the depth of a diagram by foliating it.
Example
>>> x, y = Ty('x'), Ty('y') >>> f, g = Box('f', x, y), Box('g', y, x) >>> assert Id(x @ y).depth() == 0 >>> assert f.depth() == 1 >>> assert (f @ g).depth() == 1 >>> assert (f >> g).depth() == 2
Note
The depth of a diagram is the minimum length over all its foliations, this method just returns the length of
Diagram.foliation().
- interchange(i, j, left=False)[source]#
Interchange a box from layer
ito layerj.- Parameters:
i (int) – Index of the box to interchange.
j (int) – Index of the new position for the box.
left – Whether to apply left interchangers.
- Return type:
Note
By default, we apply right interchangers:
top >> left @ box1.dom @ mid @ box0 @ right\ >> left @ box1 @ mid @ box0.cod @ right >> bottom
gets rewritten to:
top >> left @ box1 @ mid @ box0.dom @ right\ >> left @ box1.cod @ mid @ box0 @ right >> bottom
- substitute(i, other)[source]#
Implements operadic composition of nested diagrams, replacing box
iwith diagramother. See Patterson et al Patterson et al. [PSV21].
- normalize(left=False)[source]#
Implements normalisation of boundary-connected diagrams, see Delpeuch and Vicary Delpeuch and Vicary [DV22].
- Parameters:
left – Passed to
Diagram.interchange().- Return type:
Iterator[Diagram]
Example
>>> from discopy.monoidal import * >>> s0, s1 = Box('s0', Ty(), Ty()), Box('s1', Ty(), Ty()) >>> gen = (s0 @ s1).normalize() >>> for _ in range(3): print(next(gen)) s1 >> s0 s0 >> s1 s1 >> s0
- normal_form(**params)[source]#
Returns the normal form of a diagram.
params : Passed to
Diagram.normalize().- Raises:
NotImplementedError – Whenever
normalizeyields the same rewrite steps twice, e.g. the diagram is not boundary-connected.- Return type:
- draw(**params)#
Draws a diagram using networkx and matplotlib.
- Parameters:
draw_as_nodes (bool, optional) – Whether to draw boxes as nodes, default is
False.color (string, optional) – Color of the box or node, default is white (
'#ffffff') for boxes and red ('#ff0000') for nodes.textpad (pair of floats, optional) – Padding between text and wires, default is
(0.1, 0.1).wire_labels (bool, optional) – Whether to draw type labels, default is
False.draw_box_labels (bool, optional) – Whether to draw box labels, default is
True.aspect (string, optional) – Aspect ratio, one of
['auto', 'equal'].margins (tuple, optional) – Margins, default is
(0.05, 0.05).nodesize (float, optional) – Node size for spiders and controlled gates.
fontsize (int, optional) – Font size for the boxes, default is
12.fontsize_types (int, optional) – Font size for the types, default is
12.figsize (tuple, optional) – Figure size.
path (str, optional) – Where to save the image, if None we call
plt.show().format (str, optional) – Format of the saved image, taken from the extension of
pathwhen it is a file name, required when it is an in-memory buffer.doctest (str, optional) – Path to a documentation image used as a drawing baseline: the image is created if missing and compared against otherwise, see
config.OVERRIDE_DOCTEST_IMAGES.tol (float, optional) – Comparison tolerance for raster images, default is
20.to_tikz (bool, optional) – Whether to output tikz code instead of matplotlib.
asymmetry (float, optional) – Make a box and its dagger mirror images, default is
.25 * any(box.is_dagger for box in diagram.boxes).
- to_gif(*diagrams, **params)#
Builds a gif with the normalisation steps.
- Parameters:
diagrams (
Diagram, optional) – Sequence of diagrams to draw.path (str) – Where to save the image, if
Nonea gif gets created.timestep (int, optional) – Time step in milliseconds, default is
500.loop (bool, optional) – Whether to loop, default is
Falseparams (any, optional) – Passed to
Diagram.draw().