Пример #1
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)
Пример #2
0
    def decorator(func):
        from discopy import messages
        from discopy.cat import AxiomError
        from discopy.cartesian import tuplify, untuplify
        graph, box_nodes = nx.DiGraph(), []

        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)

        for box in boxes:
            box._apply = apply
        inputs = []
        for i, obj in enumerate(dom):
            node = Node("input", obj=obj, i=i)
            inputs.append(node)
            graph.add_node(node)
        outputs = tuplify(func(*inputs))
        for i, obj in enumerate(cod):
            if outputs[i].obj != obj:
                raise AxiomError(
                    "Expected {} as output, got {} instead.".format(
                        obj, outputs[i].obj))
            node = Node("output", obj=obj, i=i)
            graph.add_edge(outputs[i], node)
        for box in boxes:
            del box._apply
        result = nx2diagram(graph, ob_factory=type(dom), id_factory=id_factory)
        if result.cod != cod:
            raise AxiomError(
                "Expected diagram.cod == {}, got {} instead.".format(
                    cod, result.cod))  # pragma: no cover
        return result
Пример #3
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))
     if left.r == right:
         raise AxiomError(messages.wrong_adjunction(left, right, cup=False))
     self.left, self.right, self.draw_as_wire = left, right, True
     super().__init__("Cap({}, {})".format(left, right), Ty(), left @ right)
Пример #4
0
 def __init__(self, name, dim=2, z=0):
     super().__init__(name)
     if z != 0:
         raise AxiomError("circuit.Ob are self-dual.")
     if not isinstance(dim, int) or dim < 2:
         raise ValueError("Dimension should be an int greater than 1.")
     self._dim = dim
Пример #5
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)
Пример #6
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)
Пример #7
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)
Пример #8
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)
Пример #9
0
 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)
Пример #10
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))
     self.left, self.right = left, right
     super().__init__("Cup({}, {})".format(left, right), left @ right, Ty())
     self.draw_as_wires = True
Пример #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.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
Пример #12
0
def cups(left, right, ar_factory=Diagram, cup_factory=Cup, reverse=False):
    """ Constructs a diagram of nested cups. """
    for typ in left, right:
        if not isinstance(typ, Ty):
            raise TypeError(messages.type_err(Ty, typ))
    if left.r != right and right.r != left:
        raise AxiomError(messages.are_not_adjoints(left, right))
    result = ar_factory.id(left @ right)
    for i in range(len(left)):
        j = len(left) - i - 1
        cup = cup_factory(left[j:j + 1], right[i:i + 1])
        layer = ar_factory.id(left[:j]) @ cup @ ar_factory.id(right[i + 1:])
        result = result << layer if reverse else result >> layer
    return result
Пример #13
0
    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))))
Пример #14
0
 def apply(state):
     dom, cod = Dim(*(len(self.dom) * [2])), Dim(*(len(self.cod) * [2]))
     if (state.dom, state.cod) != (Dim(1), dom):
         raise AxiomError("Non-linear gates can only be applied "
                          "to states, not processes.")
     return Tensor(Dim(1), cod, self.data(state.array))
Пример #15
0
 def ba(left, right):
     """ Backward application. """
     if right.left != left:
         raise AxiomError(messages.are_not_adjoints(left, right))
     return BA(right)
Пример #16
0
 def fa(left, right):
     """ Forward application. """
     if left.right != right:
         raise AxiomError(messages.are_not_adjoints(left, right))
     return FA(left)
Пример #17
0
 def __add__(self, other):
     if other == 0:
         return self
     if (self.dom, self.cod) != (other.dom, other.cod):
         raise AxiomError(messages.cannot_add(self, other))
     return CQMap(self.dom, self.cod, self.array + other.array)
Пример #18
0
    def eval(self, *others, backend=None, mixed=False, **params):
        """
        Evaluate a circuit on a backend, or simulate it with numpy.

        Parameters
        ----------
        others : Circuit
            Other circuits to process in batch.
        backend : pytket.Backend, optional
            Backend on which to run the circuit, if none then we apply
            :class:`discopy.tensor.Functor` or :class:`CQMapFunctor` instead.
        mixed : bool, optional
            Whether to apply :class:`discopy.tensor.Functor`
            or :class:`CQMapFunctor`.
        params : kwargs, optional
            Get passed to Circuit.get_counts.

        Returns
        -------
        tensor : :class:`discopy.tensor.Tensor`
            If :code:`backend is not None` or :code:`mixed=False`.
        cqmap : :class:`CQMap`
            Otherwise.

        Examples
        --------
        We can evaluate a pure circuit (i.e. with :code:`not circuit.is_mixed`)
        as a unitary :class:`discopy.tensor.Tensor` or as a :class:`CQMap`:

        >>> from discopy.quantum import *

        >>> H.eval().round(2)
        Tensor(dom=Dim(2), cod=Dim(2), array=[0.71, 0.71, 0.71, -0.71])
        >>> H.eval(mixed=True).round(1)  # doctest: +ELLIPSIS
        CQMap(dom=Q(Dim(2)), cod=Q(Dim(2)), array=[0.5, ..., 0.5])

        We can evaluate a mixed circuit as a :class:`CQMap`:

        >>> assert Measure().eval()\\
        ...     == CQMap(dom=Q(Dim(2)), cod=C(Dim(2)),
        ...              array=[1, 0, 0, 0, 0, 0, 0, 1])
        >>> circuit = Bits(1, 0) @ Ket(0) >> Discard(bit ** 2 @ qubit)
        >>> assert circuit.eval() == CQMap(dom=CQ(), cod=CQ(), array=[1])

        We can execute any circuit on a `pytket.Backend`:

        >>> circuit = Ket(0, 0) >> sqrt(2) @ H @ X >> CX >> Measure() @ Bra(0)
        >>> from unittest.mock import Mock
        >>> backend = Mock()
        >>> backend.get_counts.return_value = {(0, 1): 512, (1, 0): 512}
        >>> assert circuit.eval(backend, n_shots=2**10).round()\\
        ...     == Tensor(dom=Dim(1), cod=Dim(2), array=[0., 1.])
        """
        from discopy import cqmap
        from discopy.quantum.gates import Bits, scalar, ClassicalGate
        if len(others) == 1 and not isinstance(others[0], Circuit):
            # This allows the syntax :code:`circuit.eval(backend)`
            return self.eval(backend=others[0], mixed=mixed, **params)
        if backend is None:
            if others:
                return [
                    circuit.eval(mixed=mixed, **params)
                    for circuit in (self, ) + others
                ]
            post_processes = Id(self.cod)
            for left, box, right in self.layers:
                if isinstance(box, ClassicalGate) and not box.is_linear:
                    if left.count(bit) or right.count(bit):
                        raise AxiomError("You can't tensor non-linear gates.")
                    post_processes = (post_processes or Id(box.dom)) >> box
                elif post_processes and not isinstance(box, ClassicalGate):
                    raise AxiomError("You can't do anything quantum "
                                     "after non-linear gates.")
            circuit = self[:-len(post_processes) or len(self)]
            functor = cqmap.Functor() if mixed or self.is_mixed\
                else tensor.Functor(lambda x: 2, lambda f: f.array)
            result = functor(circuit)
            for process in post_processes.boxes:
                result = process(result)
            return result
        circuits = [circuit.to_tk() for circuit in (self, ) + others]
        results, counts = [], circuits[0].get_counts(*circuits[1:],
                                                     backend=backend,
                                                     **params)
        for i, circuit in enumerate(circuits):
            n_bits = len(circuit.post_processing.dom)
            result = Tensor.zeros(Dim(1), Dim(*(n_bits * (2, ))))
            for bitstring, count in counts[i].items():
                result += (scalar(count) @ Bits(*bitstring)).eval()
            for process in circuit.post_processing.boxes:
                result = process(result)
            results.append(result)
        return results if len(results) > 1 else results[0]