Example #1
0
 def tensor(self, *others):
     if len(others) != 1:
         return monoidal.Diagram.tensor(self, *others)
     other, = others
     f = rigid.Box('f', Ty('c00', 'q00', 'q00'), Ty('c10', 'q10', 'q10'))
     g = rigid.Box('g', Ty('c01', 'q01', 'q01'), Ty('c11', 'q11', 'q11'))
     above = Diagram.id(f.dom[:1] @ g.dom[:1] @ f.dom[1:2])\
         @ Diagram.swap(g.dom[1:2], f.dom[2:]) @ Diagram.id(g.dom[2:])\
         >> Diagram.id(f.dom[:1]) @ Diagram.swap(g.dom[:1], f.dom[1:])\
         @ Diagram.id(g.dom[1:])
     below =\
         Diagram.id(f.cod[:1]) @ Diagram.swap(f.cod[1:], g.cod[:1])\
         @ Diagram.id(g.cod[1:])\
         >> Diagram.id(f.cod[:1] @ g.cod[:1] @ f.cod[1:2])\
         @ Diagram.swap(f.cod[2:], g.cod[1:2]) @ Diagram.id(g.cod[2:])
     diagram2tensor = tensor.Functor(ob={
         Ty("{}{}{}".format(a, b, c)):
         z.__getattribute__(y).__getattribute__(x)
         for a, x in zip(['c', 'q'], ['classical', 'quantum'])
         for b, y in zip([0, 1], ['dom', 'cod'])
         for c, z in zip([0, 1], [self, other])
     },
                                     ar={
                                         f: self.utensor.array,
                                         g: other.utensor.array
                                     })
     return CQMap(self.dom @ other.dom,
                  self.cod @ other.cod,
                  utensor=diagram2tensor(above >> f @ g >> below))
Example #2
0
    def eval(self, *others, backend=None, mixed=False, **params):
        """
        Evaluate a circuit on a backend, or simulate it with numpy.

        Parameters
        ----------
        others : :class:`discopy.quantum.circuit.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 discopy.quantum.tk import mockBackend
        >>> backend = mockBackend({(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]
            functor = cqmap.Functor() if mixed or self.is_mixed\
                else tensor.Functor(lambda x: x[0].dim, lambda f: f.array)
            return functor(self)
        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()
            if circuit.post_processing:
                result = result >> circuit.post_processing.eval()
            results.append(result)
        return results if len(results) > 1 else results[0]