Diagram#

class discopy.monoidal.Diagram(dom, cod, boxes, offsets, layers=None)[source]#

Bases: Arrow

Defines a diagram given dom, cod, a list of boxes and offsets.

Parameters:
  • dom (monoidal.Ty) – Domain of the diagram.

  • cod (monoidal.Ty) – Codomain of the diagram.

  • boxes (list of Diagram) – Boxes of the diagram.

  • offsets (list of int) – Offsets of each box in the diagram.

  • layers (list of Layer, optional) – Layers of the diagram, computed from boxes and offsets if None.

Raises:

AxiomError – Whenever the boxes do not compose.

Examples

>>> 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)
>>> d = Diagram(x @ z, y, [f0, f1, g], [0, 1, 0])
>>> assert d == f0 @ f1 >> g
>>> d.draw(figsize=(2, 2),
...        path='docs/_static/imgs/monoidal/arrow-example.png')
../_images/arrow-example.png
downgrade()[source]#

Downcasting to discopy.monoidal.Diagram.

property offsets#

The offset of a box is the number of wires to its left.

property layers#

A discopy.cat.Arrow with Layer boxes such that:

diagram == Id(diagram.dom).then(*[
    Id(left) @ box @ Id(right)
    for left, box, right in diagram.layers])

This is accessed using python slices:

diagram[i:j] == Diagram(
    dom=diagram.layers[i].dom,
    cod=diagram.layers[j - 1].cod,
    boxes=diagram.boxes[i:j],
    offsets=diagram.offsets[i:j],
    layers=diagram.layers[i:j])
tensor(other=None, *rest)[source]#

Returns the horizontal composition of ‘self’ with a diagram ‘other’.

This method is called using the binary operator @:

>>> 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(
...     figsize=(2, 2),
...     path='docs/_static/imgs/monoidal/tensor-example.png')
../_images/tensor-example.png
Parameters:

other (Diagram) –

Returns:

diagram – the tensor of ‘self’ and ‘other’.

Return type:

Diagram

static swap(left, right, ar_factory=None, swap_factory=None)[source]#

Returns a diagram that swaps the left with the right wires.

Parameters:
Returns:

diagram – with diagram.dom == left @ right

Return type:

monoidal.Diagram

static permutation(perm, dom=None, ar_factory=None, inverse=False)[source]#

Returns the diagram that encodes a permutation of wires.

Warning

This method used to return the inverse permutation up to and including discopy v0.4.2.

Parameters:
  • perm (list of int) – such that i goes to perm[i]

  • dom (monoidal.Ty, optional) – of the same length as perm, default is PRO(len(perm)).

  • inverse (bool) – whether to return the inverse permutation.

Returns:

diagram

Return type:

monoidal.Diagram

permute(*perm, inverse=False)[source]#

Returns self >> self.permutation(perm, self.dom).

Parameters:
  • perm (list of int) – such that i goes to perm[i]

  • inverse (bool) – whether to return the inverse permutation.

Examples

>>> x, y, z = Ty('x'), Ty('y'), Ty('z')
>>> assert Id(x @ y @ z).permute(2, 1, 0).cod == z @ y @ x
>>> assert Id(x @ y @ z).permute(2, 0).cod == z @ y @ x
static subclass(ar_factory)[source]#

Decorator for subclasses of Diagram.

open_bubbles()[source]#

Called when drawing bubbles. Replace each bubble by:

open_bubble\
    >> Id(left) @ open_bubbles(bubble.inside) @ Id(right)\
    >> close_bubble

for left = Ty(bubble.drawing_name) and right = Ty(""). Diagram.downgrade() gets called in the process.

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

  • draw_type_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().

  • 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 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().

interchange(i, j, left=False)#

Returns a new diagram with boxes i and j interchanged.

Gets called recursively whenever i < j + 1 or j < i - 1.

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

  • j (int) – Index of the new position for the box.

  • left (bool, optional) – Whether to apply left interchangers.

Notes

By default, we apply only right exchange moves:

top >> Id(left @ box1.dom @ mid) @ box0 @ Id(right)
    >> Id(left) @ box1 @ Id(mid @ box0.cod @ right) >> bottom

gets rewritten to:

top >> Id(left) @ box1 @ Id(mid @ box0.dom @ right)
    >> Id(left @ box1.cod @ mid) @ box0 @ Id(right) >> bottom
normalize(left=False)#

Implements normalisation of connected diagrams, see arXiv:1804.07832.

Parameters:

left (bool, optional) – Passed to Diagram.interchange().

Yields:

diagram (Diagram) – Rewrite steps.

Examples

>>> 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(normalizer=None, **params)#

Returns the normal form of a diagram.

Parameters:
  • normalizer (iterable of Diagram, optional) – Generator that yields rewrite steps, default is Diagram.normalize().

  • params (any, optional) – Passed to normalizer.

Raises:

NotImplementedError – Whenever normalizer yields the same rewrite steps twice.

foliate(yield_slices=False)#

Generator yielding the interchanger steps in the foliation of self.

Yields:

diagram (Diagram) – Rewrite steps of the foliation.

Parameters:

yield_slices (bool, optional) – Yield the list of slices of self as last output, used in Diagram.foliation().

Examples

>>> from discopy.monoidal import *
>>> x, y = Ty('x'), Ty('y')
>>> f0, f1 = Box('f0', x, y), Box('f1', y, x)
>>> d = (f0 @ Id(x) >> f0.dagger() @ f1.dagger()) @ (f0 >> f1)
>>> *_, slices = d.foliate(yield_slices=True)
>>> print(slices[0])
f0 @ Id(x @ x) >> Id(y) @ f1[::-1] @ Id(x) >> Id(y @ y) @ f0
>>> print(slices[1])
f0[::-1] @ Id(y @ y) >> Id(x @ y) @ f1
>>> d.draw(figsize=(4, 2),
...        path='docs/_static/imgs/monoidal/foliate-example-1a.png')
../_images/foliate-example-1a.png
>>> drawing.equation(
...     *slices, symbol=', ', figsize=(4, 2),
...     path='docs/_static/imgs/monoidal/foliate-example-1b.png')
../_images/foliate-example-1b.png
>>> ket = Box('ket', Ty(), x)
>>> scalar = Box('scalar', Ty(), Ty())
>>> kets = scalar @ ket @ scalar @ ket
>>> a = kets.foliate()
>>> assert next(a) == kets
>>> kets.draw(figsize=(2, 2),
...           path='docs/_static/imgs/monoidal/foliate-example-2.png')
../_images/foliate-example-2.png
flatten()#

Takes a diagram of diagrams and returns a diagram.

>>> from discopy.monoidal import *
>>> x, y = Ty('x'), Ty('y')
>>> f0, f1 = Box('f0', x, y), Box('f1', y, x)
>>> g = Box('g', x @ y, y)
>>> d = (Id(y) @ f0 @ Id(x) >> f0.dagger() @ Id(y) @ f0 >>\
...      g @ f1 >> f1 @ Id(x)).normal_form()
>>> assert d.foliation().flatten().normal_form() == d
>>> assert d.foliation().dagger().flatten()\
...     == d.foliation().flatten().dagger()
foliation()#

Returns a diagram with normal_form diagrams of depth 1 as boxes such that its flattening gives the original diagram back.

>>> from discopy.monoidal import *
>>> x, y = Ty('x'), Ty('y')
>>> f0, f1 = Box('f0', x, y), Box('f1', y, x)
>>> d = f0 @ Id(y) >> f0.dagger() @ f1
>>> assert d.foliation().boxes[0] == f0 @ f1
>>> assert d.foliation().flatten().normal_form() == d
>>> assert d.foliation().flatten()\
...     == d[::-1].foliation()[::-1].flatten()\
...     == d[::-1].foliation().flatten()[::-1]
>>> assert d.foliation().flatten().foliation() == d.foliation()
>>> g = Box('g', x @ x, x @ y)
>>> diagram = (d >> g >> d) @ (d >> g >> d)
>>> slices = diagram.foliation()
>>> assert slices.boxes[0] == f0 @ f1 @ f0 @ f1
>>> *_, last_diagram = diagram.foliate()
>>> assert last_diagram == slices.flatten()
depth()#

Computes the depth of a diagram by foliating it.

>>> from discopy.monoidal import *
>>> 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
width()#

Computes the width of a diagram, i.e. the maximum number of parallel wires.

>>> from discopy.monoidal import *
>>> x = Ty('x')
>>> f = Box('f', x, x ** 4)
>>> assert (f @ Id(x ** 2) >> Id(x ** 2) @ f.dagger()).width() == 6
layer_factory#

alias of Layer

bubble_factory#

alias of Bubble

id#

alias of Id

sum#

alias of Sum