def then(self, other): if not isinstance(other, Tensor): raise TypeError(messages.type_err(Tensor, other)) if self.cod != other.dom: raise AxiomError(messages.does_not_compose(self, other)) array = np.tensordot(self.array, other.array, len(self.cod))\ if self.array.shape and other.array.shape\ else self.array * other.array return Tensor(self.dom, other.cod, array)
def __init__(self, dom, cod, boxes, _scan=True): if not isinstance(dom, Ob): raise TypeError(messages.type_err(Ob, dom)) if not isinstance(cod, Ob): raise TypeError(messages.type_err(Ob, cod)) if _scan: scan = dom for depth, box in enumerate(boxes): if not isinstance(box, Arrow): raise TypeError(messages.type_err(Arrow, box)) if box.dom != scan: raise AxiomError(messages.does_not_compose( boxes[depth - 1] if depth else Id(dom), box)) scan = box.cod if scan != cod: raise AxiomError(messages.does_not_compose( boxes[-1] if boxes else Id(dom), Id(cod))) self._dom, self._cod, self._boxes = dom, cod, boxes
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.does_not_compose(left, right)) name = "BX({}, {})".format(left, right) dom, cod = left @ right, right.right << left.right super().__init__(name, dom, cod)
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.does_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)
def then(self, *others): if len(others) != 1 or any(isinstance(other, Sum) for other in others): return monoidal.Diagram.then(self, *others) other, = others if not isinstance(other, Tensor): raise TypeError(messages.type_err(Tensor, other)) if self.cod != other.dom: raise AxiomError(messages.does_not_compose(self, other)) array = Tensor.np.tensordot(self.array, other.array, len(self.cod))\ if self.array.shape and other.array.shape\ else self.array * other.array return Tensor(self.dom, other.cod, array)
def then(self, *others): """ Returns the composition of `self` with arrows `others`. This method is called using the binary operators `>>` and `<<`: >>> x, y, z = Ob('x'), Ob('y'), Ob('z') >>> f, g, h = Box('f', x, y), Box('g', y, z), Box('h', z, x) >>> assert f.then(g) == f >> g == g << f Parameters ---------- others : cat.Arrow such that `self.cod == others[0].dom` and `all(x.cod == y.dom for x, y in zip(others, others[1:])`. Returns ------- arrow : cat.Arrow such that :code:`arrow.boxes == self.boxes + sum(other.boxes for other in others, [])`. Raises ------ :class:`cat.AxiomError` whenever `self` and `others` do not compose. Notes ----- We can check the axioms of categories (i.e. composition is unital and associative): >>> assert f >> Id(y) == f == Id(x) >> f >>> assert (f >> g) >> h == f >> (g >> h) """ if not others: return self if len(others) > 1: return self.then(others[0]).then(*others[1:]) other, = others if isinstance(other, Sum): return self.sum([self]).then(other) if not isinstance(other, Arrow): raise TypeError(messages.type_err(Arrow, other)) if self.cod != other.dom: raise AxiomError(messages.does_not_compose(self, other)) return self.upgrade( Arrow(self.dom, other.cod, self.boxes + other.boxes, _scan=False))
def then(self, other): """ Implements the sequential composition of Python functions. >>> copy = Function(1, 2, lambda *x: x + x) >>> swap = Function(2, 2, lambda x, y: (y, x)) >>> assert (copy >> swap)(1) == copy(1) >>> assert (swap >> swap)(1, 2) == (1, 2) """ if not isinstance(other, Function): raise TypeError(messages.type_err(Function, other)) if len(self.cod) != len(other.dom): raise AxiomError(messages.does_not_compose(self, other)) return Function(self.dom, other.cod, lambda *vals: other(*tuplify(self(*vals))))
def then(self, other): """ Returns the composition of `self` with an arrow `other`. This method is called using the binary operators `>>` and `<<`: >>> x, y, z = Ob('x'), Ob('y'), Ob('z') >>> f, g, h = Box('f', x, y), Box('g', y, z), Box('h', z, x) >>> assert f.then(g) == f >> g == g << f Parameters ---------- other : cat.Arrow such that `self.cod == other.dom`. Returns ------- arrow : cat.Arrow such that :code:`arrow.boxes == self.boxes + other.boxes`. Raises ------ :class:`cat.AxiomError` whenever `self` and `other` do not compose. Notes ----- We can check the axioms of categories (i.e. composition is unital and associative): >>> assert f >> Id(y) == f == Id(x) >> f >>> assert (f >> g) >> h == f >> (g >> h) """ if not isinstance(other, Arrow): raise TypeError(messages.type_err(Arrow, other)) if self.cod != other.dom: raise AxiomError(messages.does_not_compose(self, other)) boxes = self.boxes + other.boxes return Arrow(self.dom, other.cod, boxes, _scan=False)