Beispiel #1
0
    def measure(self):
        """
        Measures a circuit on the computational basis.

        Returns
        -------
        array : np.ndarray
            with real entries and the same shape as :code:`self.eval().array`.

        Examples
        --------
        >>> m = X.measure()
        >>> list(np.round(m.flatten()))
        [0.0, 1.0, 1.0, 0.0]
        >>> assert (Ket(0) >> X >> Bra(1)).measure() == m[0, 1]
        """
        def bitstring(i, length):
            return map(int, '{{:0{}b}}'.format(length).format(i))

        process = self.eval()
        states, effects = [], []
        states = [
            Ket(*bitstring(i, len(self.dom))).eval()
            for i in range(2**len(self.dom))
        ]
        effects = [
            Bra(*bitstring(j, len(self.cod))).eval()
            for j in range(2**len(self.cod))
        ]
        array = np.zeros(len(self.dom + self.cod) * (2, ))
        for state in states if self.dom else [Tensor.id(1)]:
            for effect in effects if self.cod else [Tensor.id(1)]:
                scalar = np.absolute((state >> process >> effect).array)**2
                array += scalar * (state.dagger() >> effect.dagger()).array
        return array
Beispiel #2
0
    def measure(self, mixed=False):
        """
        Measures a circuit on the computational basis using :code:`numpy`.

        Parameters
        ----------
        mixed : bool, optional
            Whether to apply :class:`tensor.Functor` or :class:`CQMapFunctor`.

        Returns
        -------
        array : numpy.ndarray
        """
        from discopy.quantum.gates import Bra, Ket
        if mixed or self.is_mixed:
            return self.init_and_discard().eval(mixed=True).array.real
        state = (Ket(*(len(self.dom) * [0])) >> self).eval()
        effects = [
            Bra(*index2bitstring(j, len(self.cod))).eval()
            for j in range(2**len(self.cod))
        ]
        array = np.zeros(len(self.cod) * (2, ) or (1, ))
        for effect in effects:
            array += effect.array * np.absolute((state >> effect).array)**2
        return array
Beispiel #3
0
def tensor_from_counts(counts, post_selection=None, scalar=1, normalize=True):
    """
    Parameters
    ----------
    counts : dict
        From bitstrings to counts.
    post_selection : dict, optional
        From qubit indices to bits.
    scalar : complex, optional
        Scale the output using the Born rule.
    normalize : bool, optional
        Whether to normalize the counts.

    Returns
    -------
    tensor : discopy.tensor.Tensor
        Of dimension :code:`n_qubits * (2, )` for :code:`n_qubits` the number
        of post-selected qubits.
    """
    if normalize:
        counts = probs_from_counts(counts)
    n_qubits = len(list(counts.keys()).pop())
    if post_selection:
        post_selected = dict()
        for bitstring, count in counts.items():
            if all(bitstring[qubit] == bit
                   for qubit, bit in post_selection.items()):
                post_selected.update({
                    tuple(bit for qubit, bit in enumerate(bitstring) if qubit not in post_selection):
                    count
                })
        n_qubits -= len(post_selection.keys())
        counts = post_selected
    array = np.zeros(n_qubits * (2, ))
    for bitstring, count in counts.items():
        array += count * Ket(*bitstring).array
    array = abs(scalar)**2 * array
    return Tensor(Dim(1), Dim(*(n_qubits * (2, ))), array)
Beispiel #4
0
    def measure(self, mixed=False):
        """
        Measures a circuit on the computational basis.

        Parameters
        ----------
        mixed : bool, optional
            Whether to apply :class:`TensorFunctor` or :class:`CQMapFunctor`.

        Returns
        -------
        array : numpy.ndarray
        """
        circuit = self.init_and_discard()
        if mixed or circuit.is_mixed:
            return circuit.eval(mixed=True).array.real
        state = circuit.eval()
        effects = [Bra(*index2bitstring(j, len(circuit.cod))).eval()
                   for j in range(2 ** len(circuit.cod))]
        array = np.zeros(len(circuit.cod) * (2, ) or (1, ))
        for effect in effects:
            array += effect.array * np.absolute((state >> effect).array) ** 2
        return array
Beispiel #5
0
    def eval(self, backend=None, mixed=False, **params):
        """
        Parameters
        ----------
        backend : pytket.Backend, optional
            Backend on which to run the circuit, if none then we apply
            :class:`TensorFunctor` or :class:`CQMapFunctor` instead.
        mixed : bool, optional
            Whether to apply :class:`TensorFunctor` 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`:

        >>> 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`:

        >>> 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.0])

        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.])
        """
        if backend is None and (mixed or self.is_mixed):
            ob = {Ty('bit'): C(Dim(2)), Ty('qubit'): Q(Dim(2))}
            F_ob = CQMapFunctor(ob, {})
            def ar(box):
                if isinstance(box, Swap):
                    return CQMap.swap(F_ob(box.dom[:1]), F_ob(box.dom[1:]))
                if isinstance(box, Discard):
                    return CQMap.discard(F_ob(box.dom))
                if isinstance(box, Measure):
                    measure = CQMap.measure(
                        F_ob(box.dom).quantum, destructive=box.destructive)
                    measure = measure @ CQMap.discard(F_ob(box.dom).classical)\
                        if box.override_bits else measure
                    return measure
                if isinstance(box, (MixedState, Encode)):
                    return ar(box.dagger()).dagger()
                if not box.is_mixed and box.classical:
                    return CQMap(F_ob(box.dom), F_ob(box.cod), box.array)
                if not box.is_mixed:
                    dom, cod = F_ob(box.dom).quantum, F_ob(box.cod).quantum
                    return CQMap.pure(Tensor(dom, cod, box.array))
                raise TypeError(messages.type_err(QuantumGate, box))
            return CQMapFunctor(ob, ar)(self)
        if backend is None:
            return TensorFunctor(lambda x: 2, lambda f: f.array)(self)
        counts = self.get_counts(backend, **params)
        n_bits = len(list(counts.keys()).pop())
        array = np.zeros(n_bits * (2, ) or (1, ))
        for bitstring, count in counts.items():
            array += count * Ket(*bitstring).array
        return Tensor(Dim(1), Dim(*(n_bits * (2, ))), array)