Example #1
0
    def cups(left, right):
        """
        >>> bell_state = Circuit.cups(PRO(1), PRO(1))
        >>> print(bell_state)
        CX >> H @ Id(1) >> Id(1) @ sqrt(2) @ Id(1) >> Bra(0, 0)
        >>> assert np.allclose(bell_state.eval().array, [[1, 0], [0, 1]])

        >>> double_bell_state = Circuit.cups(PRO(2), PRO(2))
        >>> print('\\n>> '.join(str(layer) for layer in double_bell_state))
        Id(1) @ CX @ Id(1)
        >> Id(1) @ H @ Id(2)
        >> Id(2) @ sqrt(2) @ Id(2)
        >> Id(1) @ Bra(0, 0) @ Id(1)
        >> CX
        >> H @ Id(1)
        >> Id(1) @ sqrt(2) @ Id(1)
        >> Bra(0, 0)
        """
        if not isinstance(left, PRO):
            raise TypeError(messages.type_err(PRO, left))
        if not isinstance(right, PRO):
            raise TypeError(messages.type_err(PRO, right))
        result = Id(left @ right)
        cup = CX >> H @ sqrt(2) @ Id(1) >> Bra(0, 0)
        for i in range(1, len(left) + 1):
            result = result >> Id(len(left) - i) @ cup @ Id(len(left) - i)
        return result
Example #2
0
 def __init__(self, name, dom, cod, **params):
     if not isinstance(dom, PRO):
         raise TypeError(messages.type_err(PRO, dom))
     if not isinstance(cod, PRO):
         raise TypeError(messages.type_err(PRO, cod))
     rigid.Box.__init__(self, name, dom, cod, **params)
     Diagram.__init__(self, dom, cod, [self], [0])
Example #3
0
 def cups(left, right):
     if not isinstance(left, Dim):
         raise TypeError(messages.type_err(Dim, left))
     if not isinstance(right, Dim):
         raise TypeError(messages.type_err(Dim, right))
     if left.r != right:
         raise AxiomError(messages.are_not_adjoints(left, right))
     return Tensor(left @ right, Dim(1), Id(left).array)
Example #4
0
 def __init__(self, name, cod, dom=Ty(), data=None, _dagger=False):
     if not isinstance(name, str):
         raise TypeError(messages.type_err(str, name))
     if not isinstance(dom, Ty):
         raise TypeError(messages.type_err(Ty, dom))
     if not isinstance(cod, Ty):
         raise TypeError(messages.type_err(Ty, cod))
     super().__init__(name, dom, cod, data, _dagger)
Example #5
0
 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))
     name = "FC({}, {})".format(left, right)
     dom, cod = left @ right, left.left << right.right
     super().__init__(name, dom, cod)
Example #6
0
 def __init__(self, name, cod, dom=None, data=None, _dagger=False, _z=0):
     if not isinstance(name, str):
         raise TypeError(messages.type_err(str, name))
     if not isinstance(cod, Ty):
         raise TypeError(messages.type_err(Ty, cod))
     dom = dom or cod[0:0]
     if not isinstance(dom, Ty):
         raise TypeError(messages.type_err(Ty, dom))
     super().__init__(name, dom, cod, data=data, _dagger=_dagger, _z=_z)
Example #7
0
 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)
Example #8
0
 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)
Example #9
0
 def __init__(self, left, right):
     if not isinstance(left, Ty):
         raise TypeError(messages.type_err(Ty, left))
     if not isinstance(right, Ty):
         raise TypeError(messages.type_err(Ty, right))
     if len(left) != 1 or len(right) != 1:
         raise ValueError(messages.cap_vs_caps(left, right))
     if left != right.r and left.r != right:
         raise AxiomError(messages.are_not_adjoints(left, right))
     self.left, self.right = left, right
     super().__init__("Cap({}, {})".format(left, right), Ty(), left @ right)
     self.draw_as_wires = True
Example #10
0
 def __init__(self, x, y):
     if not isinstance(x, Ty):
         raise TypeError(messages.type_err(Ty, x))
     if not isinstance(y, Ty):
         raise TypeError(messages.type_err(Ty, y))
     if x != y.r and x.r != y:
         raise AxiomError(messages.are_not_adjoints(x, y))
     if len(x) != 1 or len(y) != 1:
         raise ValueError(messages.cap_vs_caps(x, y))
     if x.r == y:
         raise NotImplementedError(messages.pivotal_not_implemented())
     super().__init__('CAP', Ty(), x @ y)
Example #11
0
 def __init__(self, left, right):
     if not isinstance(left, Ty):
         raise TypeError(messages.type_err(Ty, left))
     if not isinstance(right, Ty):
         raise TypeError(messages.type_err(Ty, right))
     if len(left) != 1 or len(right) != 1:
         raise ValueError(messages.cup_vs_cups(left, right))
     if left.r != right and left != right.r:
         raise AxiomError(messages.are_not_adjoints(left, right))
     if left == right.r:
         raise AxiomError(messages.wrong_adjunction(left, right, cup=True))
     self.left, self.right, self.draw_as_wire = left, right, True
     super().__init__("Cup({}, {})".format(left, right), left @ right, Ty())
Example #12
0
 def __init__(self, left, right):
     if not isinstance(left, Ty):
         raise TypeError(messages.type_err(Ty, left))
     if not isinstance(right, Ty):
         raise TypeError(messages.type_err(Ty, right))
     if len(left) != 1 or len(right) != 1:
         raise ValueError(messages.cap_vs_caps(left, right))
     if left != right.r and left.r != right:
         raise AxiomError(messages.are_not_adjoints(left, right))
     monoidal.BinaryBoxConstructor.__init__(self, left, right)
     Box.__init__(
         self, "Cap({}, {})".format(left, right), Ty(), left @ right)
     self.draw_as_wires = True
Example #13
0
    def tensor(self, *others):
        """
        Returns the tensor of types, i.e. the concatenation of their lists
        of objects. This is called with the binary operator `@`.

        >>> Ty('x') @ Ty('y', 'z')
        Ty('x', 'y', 'z')

        Parameters
        ----------
        other : monoidal.Ty

        Returns
        -------
        t : monoidal.Ty
            such that :code:`t.objects == self.objects + other.objects`.

        Note
        ----
        We can take the sum of a list of type, specifying the unit `Ty()`.

        >>> types = Ty('x'), Ty('y'), Ty('z')
        >>> Ty().tensor(*types)
        Ty('x', 'y', 'z')

        We can take the exponent of a type by any natural number.

        >>> Ty('x') ** 3
        Ty('x', 'x', 'x')

        """
        for other in others:
            if not isinstance(other, Ty):
                raise TypeError(messages.type_err(Ty, other))
        return Ty(*sum([t.objects for t in [self] + list(others)], []))
Example #14
0
    def __call__(self, diagram):
        if isinstance(diagram, monoidal.Ty):

            def adjoint(obj):
                if not hasattr(obj, "z") or not obj.z:
                    return self.ob[type(diagram)(obj)]
                result = self.ob[type(diagram)(type(obj)(obj.name, z=0))]
                if obj.z < 0:
                    for _ in range(-obj.z):
                        result = result.l
                elif obj.z > 0:
                    for _ in range(obj.z):
                        result = result.r
                return result

            return self.ob_factory().tensor(*map(adjoint, diagram.objects))
        if isinstance(diagram, Cup):
            return self.ar_factory.cups(self(diagram.dom[:1]),
                                        self(diagram.dom[1:]))
        if isinstance(diagram, Cap):
            return self.ar_factory.caps(self(diagram.cod[:1]),
                                        self(diagram.cod[1:]))
        if isinstance(diagram, monoidal.Diagram):
            return super().__call__(diagram)
        raise TypeError(messages.type_err(Diagram, diagram))
Example #15
0
 def __init__(self, *dims):
     for dim in dims:
         if not isinstance(dim, int):
             raise TypeError(messages.type_err(int, dim))
         if dim < 1:
             raise ValueError
     super().__init__(*[Ob(dim) for dim in dims if dim > 1])
Example #16
0
 def __init__(self, name, dom, cod, is_mixed=True, _dagger=False):
     if dom and not isinstance(dom, BitsAndQubits):
         raise TypeError(messages.type_err(BitsAndQubits, dom))
     if cod and not isinstance(cod, BitsAndQubits):
         raise TypeError(messages.type_err(BitsAndQubits, cod))
     rigid.Box.__init__(self, name, dom, cod, _dagger=_dagger)
     Circuit.__init__(self, dom, cod, [self], [0])
     if not is_mixed:
         if all(x.name == "bit" for x in dom @ cod):
             self.classical = True
         elif all(x.name == "qubit" for x in dom @ cod):
             self.classical = False
         else:
             raise ValueError(
                 "dom and cod should be bits only or qubits only.")
     self._mixed = is_mixed
Example #17
0
    def tensor(self, *others):
        """
        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

        Parameters
        ----------
        other : :class:`Diagram`

        Returns
        -------
        diagram : :class:`Diagram`
            the tensor of 'self' and 'other'.
        """
        if not others:
            return self
        if len(others) > 1:
            return self.tensor(others[0]).tensor(*others[1:])
        other = others[0]
        if not isinstance(other, Diagram):
            raise TypeError(messages.type_err(Diagram, other))
        dom, cod = self.dom @ other.dom, self.cod @ other.cod
        boxes = self.boxes + other.boxes
        offsets = self.offsets + [n + len(self.cod) for n in other.offsets]
        layers = cat.Id(dom)
        for left, box, right in self.layers:
            layers = layers >> Layer(left, box, right @ other.dom)
        for left, box, right in other.layers:
            layers = layers >> Layer(self.cod @ left, box, right)
        return Diagram(dom, cod, boxes, offsets, layers=layers)
Example #18
0
 def __call__(self, diagram):
     """
     >>> x, y, z = Ty('x'), Ty('y'), Ty('z')
     >>> f, g = Box('f', x, y), Box('g', y, z)
     >>> F = Functor({x: y, y: z}, {f: g})
     >>> assert F(f.transpose_l()) == F(f).transpose_l()
     >>> assert F(f.transpose_r()) == F(f).transpose_r()
     """
     if isinstance(diagram, Ty):
         return sum([self(b) for b in diagram.objects], self.ob_factory())
     if isinstance(diagram, Ob) and not diagram.z:
         return self.ob[Ty(diagram.name)]
     if isinstance(diagram, Ob):
         result = self(Ob(diagram.name, z=0))
         if diagram.z < 0:
             for _ in range(-diagram.z):
                 result = result.l
         elif diagram.z > 0:
             for _ in range(diagram.z):
                 result = result.r
         return result
     if isinstance(diagram, Cup):
         return self.ar_factory.cups(
             self(diagram.dom[0]), self(diagram.dom[1]))
     if isinstance(diagram, Cap):
         return self.ar_factory.caps(
             self(diagram.cod[0]), self(diagram.cod[1]))
     if isinstance(diagram, Diagram):
         return super().__call__(diagram)
     raise TypeError(messages.type_err(Diagram, diagram))
Example #19
0
 def apply(box, *inputs, offset=None):
     for node in inputs:
         if not isinstance(node, Node):
             raise TypeError(messages.type_err(Node, node))
     if len(inputs) != len(box.dom):
         raise AxiomError("Expected {} inputs, got {} instead.".format(
             len(box.dom), len(inputs)))
     depth = len(box_nodes)
     box_node = Node("box", box=box, depth=depth, offset=offset)
     box_nodes.append(box_node)
     graph.add_node(box_node)
     for i, obj in enumerate(box.dom):
         if inputs[i].obj != obj:
             raise AxiomError(
                 "Expected {} as input, got {} instead.".format(
                     obj, inputs[i].obj))
         dom_node = Node("dom", obj=obj, i=i, depth=depth)
         graph.add_edge(inputs[i], dom_node)
         graph.add_edge(dom_node, box_node)
     outputs = []
     for i, obj in enumerate(box.cod):
         cod_node = Node("cod", obj=obj, i=i, depth=depth)
         graph.add_edge(box_node, cod_node)
         outputs.append(cod_node)
     return untuplify(*outputs)
Example #20
0
 def __pow__(self, n_times):
     if not isinstance(n_times, int):
         raise TypeError(messages.type_err(int, n_times))
     result = type(self)()
     for _ in range(n_times):
         result = result @ self
     return result
Example #21
0
File: zx.py Project: Doomsk/discopy
    def to_pyzx(self):
        """
        Returns a :class:`pyzx.Graph`.

        >>> bialgebra = Z(1, 2, .25) @ Z(1, 2, .75)\\
        ...     >> Id(1) @ SWAP @ Id(1) >> X(2, 1, .5) @ X(2, 1, .5)
        >>> graph = bialgebra.to_pyzx()
        >>> assert len(graph.vertices()) == 8
        >>> assert (graph.inputs, graph.outputs) == ([0, 1], [6, 7])
        >>> from pyzx import VertexType
        >>> assert graph.type(2) == graph.type(3) == VertexType.Z
        >>> assert graph.phase(2) == 2 * .25 and graph.phase(3) == 2 * .75
        >>> assert graph.type(4) == graph.type(5) == VertexType.X
        >>> assert graph.phase(4) == graph.phase(5) == 2 * .5
        >>> assert graph.graph == {
        ...     0: {2: 1},
        ...     1: {3: 1},
        ...     2: {0: 1, 4: 1, 5: 1},
        ...     3: {1: 1, 4: 1, 5: 1},
        ...     4: {2: 1, 3: 1, 6: 1},
        ...     5: {2: 1, 3: 1, 7: 1},
        ...     6: {4: 1},
        ...     7: {5: 1}}
        """
        from pyzx import Graph, VertexType, EdgeType
        graph, scan = Graph(), []
        for i, _ in enumerate(self.dom):
            node, hadamard = graph.add_vertex(VertexType.BOUNDARY), False
            scan.append((node, hadamard))
            graph.inputs.append(node)
            graph.set_position(node, i, 0)
        for row, (box, offset) in enumerate(zip(self.boxes, self.offsets)):
            if isinstance(box, Spider):
                node = graph.add_vertex(
                    VertexType.Z if isinstance(box, Z) else VertexType.X,
                    phase=box.phase * 2 if box.phase else None)
                graph.set_position(node, offset, row + 1)
                for i, _ in enumerate(box.dom):
                    input, hadamard = scan[offset + i]
                    etype = EdgeType.HADAMARD if hadamard else EdgeType.SIMPLE
                    graph.add_edge((input, node), etype)
                scan = scan[:offset] + len(box.cod) * [(node, False)]\
                    + scan[offset + len(box.dom):]
            elif isinstance(box, Swap):
                scan = scan[:offset] + [scan[offset + 1], scan[offset]]\
                    + scan[offset + 2:]
            elif box == H:
                node, hadamard = scan[offset]
                scan[offset] = (node, not hadamard)
            else:
                raise TypeError(messages.type_err(Box, box))
        for i, _ in enumerate(self.cod):
            node = graph.add_vertex(VertexType.BOUNDARY)
            input, hadamard = scan[i]
            etype = EdgeType.HADAMARD if hadamard else EdgeType.SIMPLE
            graph.add_edge((input, node), etype)
            graph.set_position(node, i, len(self) + 1)
            graph.outputs.append(node)
        return graph
Example #22
0
 def tensor(self, other):
     if not isinstance(other, Tensor):
         raise TypeError(messages.type_err(Tensor, other))
     dom, cod = self.dom + other.dom, self.cod + other.cod
     array = np.tensordot(self.array, other.array, 0)\
         if self.array.shape and other.array.shape\
         else self.array * other.array
     return Tensor(dom, cod, array)
Example #23
0
 def __add__(self, other):
     if other == 0:
         return self
     if not isinstance(other, Tensor):
         raise TypeError(messages.type_err(Tensor, other))
     if (self.dom, self.cod) != (other.dom, other.cod):
         raise AxiomError(messages.cannot_add(self, other))
     return Tensor(self.dom, self.cod, self.array + other.array)
Example #24
0
 def __init__(self, name, dom, cod,
              is_mixed=True, data=None, _dagger=False):
     if dom and not isinstance(dom, Ty):
         raise TypeError(messages.type_err(Ty, dom))
     if cod and not isinstance(cod, Ty):
         raise TypeError(messages.type_err(Ty, cod))
     rigid.Box.__init__(self, name, dom, cod, data=data, _dagger=_dagger)
     Circuit.__init__(self, dom, cod, [self], [0])
     if not is_mixed:
         if all(isinstance(x, Digit) for x in dom @ cod):
             self.classical = True
         elif all(isinstance(x, Qudit) for x in dom @ cod):
             self.classical = False
         else:
             raise ValueError(
                 "dom and cod should be Digits only or Qudits only.")
     self._mixed = is_mixed
Example #25
0
 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)
Example #26
0
 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
Example #27
0
 def __init__(self, *dims):
     dims = map(lambda x: x if isinstance(x, monoidal.Ob) else Ob(x), dims)
     dims = list(filter(lambda x: x.name != 1, dims))  # Dim(1) == Dim()
     for dim in dims:
         if not isinstance(dim.name, int):
             raise TypeError(messages.type_err(int, dim.name))
         if dim.name < 1:
             raise ValueError
     super().__init__(*dims)
Example #28
0
    def caps(left, right):
        """ Constructs nested cups witnessing adjointness of x and y

        >>> a, b = Ty('a'), Ty('b')
        >>> assert Diagram.caps(a, a.l) == Cap(a, a.l)
        >>> assert Diagram.caps(a @ b, (a @ b).l) == (Cap(a, a.l)
        ...                 >> Id(a) @ Cap(b, b.l) @ Id(a.l))
        """
        if not isinstance(left, Ty):
            raise TypeError(messages.type_err(Ty, left))
        if not isinstance(right, Ty):
            raise TypeError(messages.type_err(Ty, right))
        caps = Id(left @ right)
        for i in range(len(left)):
            j = len(left) - i - 1
            cap = Cap(left[j:j + 1], right[i:i + 1])
            caps = caps << Id(left[:j]) @ cap @ Id(right[i + 1:])
        return caps
Example #29
0
    def cups(left, right):
        """ Constructs nested cups witnessing adjointness of x and y

        >>> a, b = Ty('a'), Ty('b')
        >>> assert Diagram.cups(a, a.r) == Cup(a, a.r)
        >>> assert Diagram.cups(a @ b, (a @ b).r) ==\\
        ...     Id(a) @ Cup(b, b.r) @ Id(a.r) >> Cup(a, a.r)
        """
        if not isinstance(left, Ty):
            raise TypeError(messages.type_err(Ty, left))
        if not isinstance(right, Ty):
            raise TypeError(messages.type_err(Ty, right))
        cups = Id(left @ right)
        for i in range(len(left)):
            j = len(left) - i - 1
            cup = Cup(left[j:j + 1], right[i:i + 1])
            cups = cups >> Id(left[:j]) @ cup @ Id(right[i + 1:])
        return cups
Example #30
0
def draw(diagram, **params):
    """
    Draws a pregroup diagram, i.e. of shape :code:`word @ ... @ word >> cups`.

    Parameters
    ----------
    width : float, optional
        Width of the word triangles, default is :code:`2.0`.
    space : float, optional
        Space between word triangles, default is :code:`0.5`.
    textpad : pair of floats, optional
        Padding between text and wires, default is :code:`(0.1, 0.2)`.
    draw_type_labels : bool, optional
        Whether to draw type labels, default is :code:`True`.
    aspect : string, optional
        Aspect ratio, one of :code:`['equal', 'auto']`.
    margins : tuple, optional
        Margins, default is :code:`(0.05, 0.05)`.
    fontsize : int, optional
        Font size for the words, default is :code:`12`.
    fontsize_types : int, optional
        Font size for the types, default is :code:`12`.
    figsize : tuple, optional
        Figure size.
    path : str, optional
        Where to save the image, if :code:`None` we call :code:`plt.show()`.
    pretty_types : bool, optional
        Whether to draw type labels with superscript, default is :code:`False`.
    triangles : bool, optional
        Whether to draw words as triangular states, default is :code:`False`.

    Raises
    ------
    ValueError
        Whenever the input is not a pregroup diagram.
    """
    from discopy.rigid import Swap
    if not isinstance(diagram, Diagram):
        raise TypeError(messages.type_err(Diagram, diagram))
    words, is_pregroup = Id(Ty()), True
    for _, box, right in diagram.layers:
        if isinstance(box, Word):
            if right:  # word boxes should be tensored left to right.
                is_pregroup = False
                break
            words = words @ box
        else:
            break
    cups = diagram[len(words):].foliation().boxes\
        if len(words) < len(diagram) else []
    is_pregroup = is_pregroup and words and all(
        isinstance(box, Cup) or isinstance(box, Swap) for s in cups
        for box in s.boxes)
    if not is_pregroup:
        raise ValueError(messages.expected_pregroup())
    drawing.pregroup_draw(words, cups, **params)