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 ifNone
.
- 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')
- 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
withLayer
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')
- static swap(left, right, ar_factory=None, swap_factory=None)[source]#
Returns a diagram that swaps the left with the right wires.
- Parameters:
left (monoidal.Ty) – left hand-side of the domain.
right (monoidal.Ty) – right hand-side of the domain.
- Returns:
diagram – with
diagram.dom == left @ right
- Return type:
- 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 toperm[i]
dom (monoidal.Ty, optional) – of the same length as
perm
, default isPRO(len(perm))
.inverse (bool) – whether to return the inverse permutation.
- Returns:
diagram
- Return type:
- permute(*perm, inverse=False)[source]#
Returns
self >> self.permutation(perm, self.dom)
.- Parameters:
perm (list of int) – such that
i
goes toperm[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
- 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)
andright = 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 isDiagram.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')
>>> drawing.equation( ... *slices, symbol=', ', figsize=(4, 2), ... path='docs/_static/imgs/monoidal/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')
- 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