Source code for discopy.biclosed

# -*- coding: utf-8 -*-

"""
Implements the free biclosed monoidal category.
"""

from discopy import messages, monoidal, rigid
from discopy.cat import AxiomError
from discopy.monoidal import BinaryBoxConstructor
from discopy.utils import factory_name, from_tree


[docs]class Ty(monoidal.Ty): """ Objects in a free biclosed monoidal category. Generated by the following grammar: ty ::= Ty(name) | ty @ ty | ty >> ty | ty << ty Examples -------- >>> x, y = Ty('x'), Ty('y') >>> print(y << x >> y) ((y << x) >> y) >>> print((y << x >> y) @ x) ((y << x) >> y) @ x """ @staticmethod def upgrade(old): if len(old) == 1 and isinstance(old[0], (Over, Under)): return old[0] return Ty(*old.objects) def __init__(self, *objects, left=None, right=None): self.left, self.right = left, right super().__init__(*objects) def __lshift__(self, other): return Over(self, other) def __rshift__(self, other): return Under(self, other)
class BinaryTyConstructor: """ Ty constructor with left and right as input. """ def __init__(self, left, right): self.left, self.right = left, right def to_tree(self): return { 'factory': factory_name(self), 'left': self.left.to_tree(), 'right': self.right.to_tree()} @classmethod def from_tree(cls, tree): return cls(*map(from_tree, (tree['left'], tree['right'])))
[docs]class Over(BinaryTyConstructor, Ty): """ Forward slash types. """ def __init__(self, left=None, right=None): Ty.__init__(self, self) BinaryTyConstructor.__init__(self, left, right) def __repr__(self): return "Over({}, {})".format(repr(self.left), repr(self.right)) def __str__(self): return "({} << {})".format(self.left, self.right) def __eq__(self, other): if not isinstance(other, Over): return False return self.left == other.left and self.right == other.right def __hash__(self): return hash(repr(self))
[docs]class Under(BinaryTyConstructor, Ty): """ Backward slash types. """ def __init__(self, left=None, right=None): Ty.__init__(self, self) BinaryTyConstructor.__init__(self, left, right) def __repr__(self): return "Under({}, {})".format(repr(self.left), repr(self.right)) def __str__(self): return "({} >> {})".format(self.left, self.right) def __eq__(self, other): if not isinstance(other, Under): return False return self.left == other.left and self.right == other.right def __hash__(self): return hash(repr(self))
[docs]@monoidal.Diagram.subclass class Diagram(monoidal.Diagram): """ Diagrams in a biclosed monoidal category. """
[docs] @staticmethod def fa(left, right): """ Forward application. """ return FA(left << right)
[docs] @staticmethod def ba(left, right): """ Backward application. """ return BA(left >> right)
[docs] @staticmethod def fc(left, middle, right): """ Forward composition. """ return FC(left << middle, middle << right)
[docs] @staticmethod def bc(left, middle, right): """ Backward composition. """ return BC(left >> middle, middle >> right)
[docs] @staticmethod def fx(left, middle, right): """ Forward crossed composition. """ return FX(left << middle, right >> middle)
[docs] @staticmethod def bx(left, middle, right): """ Backward crossed composition. """ return BX(middle << left, middle >> right)
[docs] @staticmethod def curry(diagram, n_wires=1, left=False): """ Diagram currying. """ return Curry(diagram, n_wires, left)
[docs]class Id(monoidal.Id, Diagram): """ Identity diagram in a biclosed monoidal category. """
Diagram.id = Id
[docs]class Box(monoidal.Box, Diagram): """ Boxes in a biclosed monoidal category. """
[docs]class Curry(Box): """ Curried diagram. Parameters ---------- diagram : :class:`Diagram` to curry. n_wires : int, optional Number :code:`<= len(diagram.dom)` of wires to curry, default is :code:`1`. left : bool, optional Whether to curry to the left, default is :code:`False`. """ def __init__(self, diagram, n_wires=1, left=False): if left: dom = diagram.dom[n_wires:] cod = diagram.dom[:n_wires] >> diagram.cod else: dom = diagram.dom[:-n_wires] cod = diagram.cod << diagram.dom[-n_wires or len(diagram.dom):] name = "Curry({}{}{})".format( diagram, ", n_wires={}".format(n_wires) if n_wires != 1 else "", ", left=True" if left else "") self.diagram, self.n_wires, self.left = diagram, n_wires, left super().__init__(name, dom, cod)
def unaryBoxConstructor(attr): class Constructor: @classmethod def from_tree(cls, tree): return cls(from_tree(tree[attr])) def to_tree(self): return { 'factory': factory_name(self), attr: getattr(self, attr).to_tree()} return Constructor
[docs]class FA(unaryBoxConstructor("over"), Box): """ Forward application box. """ def __init__(self, over): if not isinstance(over, Over): raise TypeError(messages.type_err(Over, over)) self.over = over dom, cod = over @ over.right, over.left super().__init__("FA{}".format(over), dom, cod) def __repr__(self): return "FA({})".format(repr(self.dom[:1]))
[docs]class BA(unaryBoxConstructor("under"), Box): """ Backward application box. """ def __init__(self, under): if not isinstance(under, Under): raise TypeError(Under, under) self.under = under dom, cod = under.left @ under, under.right super().__init__("BA{}".format(under), dom, cod) def __repr__(self): return "BA({})".format(repr(self.dom[1:]))
[docs]class FC(BinaryBoxConstructor, Box): """ Forward composition box. """ def __init__(self, left, right): if not isinstance(left, Over): raise TypeError(messages.type_err(Over, left)) if not isinstance(right, Over): raise TypeError(messages.type_err(Over, right)) if left.right != right.left: raise TypeError(messages.types_do_not_compose(left, right)) name = "FC({}, {})".format(left, right) dom, cod = left @ right, left.left << right.right Box.__init__(self, name, dom, cod) BinaryBoxConstructor.__init__(self, left, right)
class BC(BinaryBoxConstructor, Box): """ Backward composition box. """ def __init__(self, left, right): if not isinstance(left, Under): raise TypeError(messages.type_err(Under, left)) if not isinstance(right, Under): raise TypeError(messages.type_err(Under, right)) if left.right != right.left: raise TypeError(messages.types_do_not_compose(left, right)) name = "BC({}, {})".format(left, right) dom, cod = left @ right, left.left >> right.right Box.__init__(self, name, dom, cod) BinaryBoxConstructor.__init__(self, left, right) class FX(BinaryBoxConstructor, Box): """ Forward crossed composition box. """ def __init__(self, left, right): if not isinstance(left, Over): raise TypeError(messages.type_err(Over, left)) if not isinstance(right, Under): raise TypeError(messages.type_err(Over, right)) if left.right != right.right: raise TypeError(messages.types_do_not_compose(left, right)) name = "FX({}, {})".format(left, right) dom, cod = left @ right, right.left >> left.left Box.__init__(self, name, dom, cod) BinaryBoxConstructor.__init__(self, left, right) class BX(BinaryBoxConstructor, Box): """ Backward crossed composition box. """ def __init__(self, left, right): if not isinstance(left, Over): raise TypeError(messages.type_err(Under, left)) if not isinstance(right, Under): raise TypeError(messages.type_err(Under, right)) if left.left != right.left: raise TypeError(messages.types_do_not_compose(left, right)) name = "BX({}, {})".format(left, right) dom, cod = left @ right, right.right << left.right Box.__init__(self, name, dom, cod) BinaryBoxConstructor.__init__(self, left, right)
[docs]class Functor(monoidal.Functor): """ Functors into biclosed monoidal categories. Examples -------- >>> from discopy import rigid >>> x, y = Ty('x'), Ty('y') >>> F = Functor( ... ob={x: x, y: y}, ar={}, ... ob_factory=rigid.Ty, ... ar_factory=rigid.Diagram) >>> print(F(y >> x << y)) y.r @ x @ y.l >>> assert F((y >> x) << y) == F(y >> (x << y)) """ def __init__(self, ob, ar, ob_factory=Ty, ar_factory=Diagram): super().__init__(ob, ar, ob_factory, ar_factory) def __call__(self, diagram): if isinstance(diagram, Over): return self(diagram.left) << self(diagram.right) if isinstance(diagram, Under): return self(diagram.left) >> self(diagram.right) if isinstance(diagram, Ty) and len(diagram) > 1: return self.ob_factory.tensor(*[ self(diagram[i: i + 1]) for i in range(len(diagram))]) if isinstance(diagram, Curry): n_wires = len(self(getattr( diagram.cod, 'left' if diagram.left else 'right'))) return self.ar_factory.curry( self(diagram.diagram), n_wires, diagram.left) if isinstance(diagram, FA): left, right = diagram.over.left, diagram.over.right return self.ar_factory.fa(self(left), self(right)) if isinstance(diagram, BA): left, right = diagram.under.left, diagram.under.right return self.ar_factory.ba(self(left), self(right)) for cls, method in [(FC, 'fc'), (BC, 'bc')]: if isinstance(diagram, cls): left, right = diagram.dom[:1].left, diagram.dom[1:].right middle = diagram.dom[:1].right return getattr(self.ar_factory, method)( self(left), self(middle), self(right)) if isinstance(diagram, FX): left, right = diagram.dom[:1].left, diagram.dom[1:].left middle = diagram.dom[:1].right return getattr(self.ar_factory, 'fx')( self(left), self(middle), self(right)) if isinstance(diagram, BX): left, right = diagram.dom[:1].right, diagram.dom[1:].right middle = diagram.dom[:1].left return getattr(self.ar_factory, 'bx')( self(left), self(middle), self(right)) return super().__call__(diagram)
biclosed2rigid_ob = Functor( ob=lambda x: rigid.Ty(x[0].name), ar={}, ob_factory=rigid.Ty) biclosed2rigid = Functor( ob=biclosed2rigid_ob, ar=lambda f: rigid.Box( f.name, biclosed2rigid_ob(f.dom), biclosed2rigid_ob(f.cod)), ob_factory=rigid.Ty, ar_factory=rigid.Diagram)