Diagram#

class discopy.monoidal.Diagram(inside, dom, cod, _scan=True)[source]#

Bases: discopy.cat.Arrow, discopy.abc.MonoidalCategory, discopy.utils.RichDisplay

A diagram is a tuple of composable layers inside with a pair of types dom and cod as 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 @.

boxes

The boxes in each layer of the diagram.

offsets

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 i to layer j.

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.

ob#

alias of Ty

layer_factory#

alias of Layer

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')
../_images/diagramize.svg
Parameters:
  • dom (Ty) –

  • cod (Ty) –

Return type:

Callable[[Callable], Diagram]

tensor(other=None, *others)[source]#

Parallel composition, called using @.

Parameters:
  • other (Diagram) – The other diagram to tensor.

  • rest – More diagrams to tensor.

  • others (Diagram) –

Return type:

Diagram

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')
../_images/tensor-example.svg
property boxes: list[Box]#

The boxes in each layer of the diagram.

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')
../_images/arrow-example.svg
Return type:

tuple[Ty, list[tuple[Box, int]]]

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:
  • dom (Ty) – The domain of the diagram.

  • cod (Ty) – The codomain of the diagram.

  • boxes_and_offsets (list[tuple[Box, int]]) – The boxes and offsets of the diagram.

  • boxes (list[Box]) – The list of boxes.

  • offsets (list[int]) – The list of offsets.

Return type:

Diagram

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 None then we set it to zip(boxes, offstes).

to_drawing(functor_factory=None)[source]#

Called before Diagram.draw().

Return type:

Drawing

to_map()[source]#

Translate a diagram into a combinatorial map.

Return type:

CMap

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:

Hypergraph[Diagram]

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')
../_images/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 i to layer j.

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:

Diagram

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 i with diagram other. See Patterson et al Patterson et al. [PSV21].

Parameters:
  • i (int) – Index of the box to substitute.

  • other (Diagram) – The diagram to substitute with.

Return type:

Diagram

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 normalize yields the same rewrite steps twice, e.g. the diagram is not boundary-connected.

Return type:

Diagram

bubble_factory#

alias of Bubble

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 path when 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).

factory#

alias of Diagram

functor_factory#

alias of Functor

sum_factory#

alias of Sum

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 None a gif gets created.

  • timestep (int, optional) – Time step in milliseconds, default is 500.

  • loop (bool, optional) – Whether to loop, default is False

  • params (any, optional) – Passed to Diagram.draw().