Exemplo n.º 1
0
    def expand(self):

        weight = self.parameters[0]

        # Interpret first and last wire as r and p
        r = self.wires[0]
        p = self.wires[-1]

        # Sequence of the wires entering the CNOTs between wires 'r' and 'p'
        set_cnot_wires = [
            self.wires[l:l + 2] for l in range(len(self.wires) - 1)
        ]

        with qml.tape.QuantumTape() as tape:

            # ------------------------------------------------------------------
            # Apply the first layer

            # U_1, U_2 acting on wires 'r' and 'p'
            RX(-np.pi / 2, wires=r)
            Hadamard(wires=p)

            # Applying CNOTs between wires 'r' and 'p'
            for cnot_wires in set_cnot_wires:
                CNOT(wires=cnot_wires)

            # Z rotation acting on wire 'p'
            RZ(weight / 2, wires=p)

            # Applying CNOTs in reverse order
            for cnot_wires in reversed(set_cnot_wires):
                CNOT(wires=cnot_wires)

            # U_1^+, U_2^+ acting on wires 'r' and 'p'
            RX(np.pi / 2, wires=r)
            Hadamard(wires=p)

            # ------------------------------------------------------------------
            # Apply the second layer

            # U_1, U_2 acting on wires 'r' and 'p'
            Hadamard(wires=r)
            RX(-np.pi / 2, wires=p)

            # Applying CNOTs between wires 'r' and 'p'
            for cnot_wires in set_cnot_wires:
                CNOT(wires=cnot_wires)

            # Z rotation acting on wire 'p'
            RZ(-weight / 2, wires=p)

            # Applying CNOTs in reverse order
            for cnot_wires in reversed(set_cnot_wires):
                CNOT(wires=cnot_wires)

            # U_1^+, U_2^+ acting on wires 'r' and 'p'
            Hadamard(wires=r)
            RX(np.pi / 2, wires=p)

        return tape
Exemplo n.º 2
0
    def test_hadamard_state(self, nr_wires, tol):
        """Tests that applying Hadamard gates on all qubits produces an equal superposition over
        all basis states"""
        dev = qml.device("default.mixed", wires=nr_wires)
        ops = [Hadamard(i) for i in range(nr_wires)]
        dev.apply(ops)

        assert np.allclose(dev.state, hadamard_state(nr_wires), atol=tol, rtol=0)
Exemplo n.º 3
0
    def test_bell_state(self, tol):
        """Tests that we correctly prepare a Bell state by applying a Hadamard then a CNOT"""
        dev = qml.device("default.mixed", wires=2)
        ops = [Hadamard(0), CNOT(wires=[0, 1])]
        dev.apply(ops)
        bell = np.zeros((4, 4))
        bell[0, 0] = bell[0, 3] = bell[3, 0] = bell[3, 3] = 1 / 2

        assert np.allclose(bell, dev.state, atol=tol, rtol=0)
def _layer2(weight, s, r, q, p, set_cnot_wires):
    r"""Implement the second layer of the circuit to exponentiate the double-excitation
    operator entering the UCCSD ansatz.

    .. math::

        \hat{U}_{pqrs}^{(2)}(\theta) = \mathrm{exp} \Big\{ \frac{i\theta}{8}
        \bigotimes_{b=s+1}^{r-1} \hat{Z}_b \bigotimes_{a=q+1}^{p-1} \hat{Z}_a
        (\hat{Y}_s \hat{X}_r \hat{Y}_q \hat{Y}_p) \Big\}

    Args:
        weight (float): angle :math:`\theta` entering the Z rotation acting on wire ``p``
        s (int): qubit index ``s``
        r (int): qubit index ``r``
        q (int): qubit index ``q``
        p (int): qubit index ``p``
        set_cnot_wires (sequence[int]): two-element sequence with the indices of the qubits
            the CNOT gates act on
    """

    # U_1, U_2, U_3, U_4 acting on wires 's', 'r', 'q' and 'p'
    RX(-np.pi / 2, wires=s)
    Hadamard(wires=r)
    RX(-np.pi / 2, wires=q)
    RX(-np.pi / 2, wires=p)

    # Applying CNOTs between wires 's' and 'p'
    for cnot_wires in set_cnot_wires:
        CNOT(wires=cnot_wires)

    # Z rotation acting on wire 'p'
    RZ(weight / 8, wires=p)

    # Applying CNOTs in reverse order
    for cnot_wires in reversed(set_cnot_wires):
        CNOT(wires=cnot_wires)

    # U_1^+, U_2^+, U_3^+, U_4^+ acting on wires 's', 'r', 'q' and 'p'
    RX(np.pi / 2, wires=s)
    Hadamard(wires=r)
    RX(np.pi / 2, wires=q)
    RX(np.pi / 2, wires=p)
Exemplo n.º 5
0
 def test_undo_rotations(self, nr_wires, tol):
     """Tests that rotations are correctly applied by adding their inverse as initial
     operations"""
     dev = qml.device("default.mixed", wires=nr_wires)
     ops = [Hadamard(i) for i in range(nr_wires)]
     rots = ops
     dev.apply(ops, rots)
     basis = np.reshape(basis_state(0, nr_wires), [2] * (2 * nr_wires))
     # dev.state = pre-rotated state, dev._state = state after rotations
     assert np.allclose(dev.state, hadamard_state(nr_wires), atol=tol, rtol=0)
     assert np.allclose(dev._state, basis, atol=tol, rtol=0)
Exemplo n.º 6
0
def _layer8(weight, s, r, q, p, set_cnot_wires):
    r"""Implement the eighth layer of the circuit to exponentiate the double-excitation
    operator entering the UCCSD ansatz.

    .. math::

        \hat{U}_{pqrs}^{(8)}(\theta) = \mathrm{exp} \Big\{ -\frac{i\theta}{8}
        \bigotimes_{b=s+1}^{r-1} \hat{Z}_b \bigotimes_{a=q+1}^{p-1} \hat{Z}_a
        (\hat{Y}_s \hat{Y}_r \hat{X}_q \hat{Y}_p) \Big\}

    Args:
        weight (float): angle :math:`\theta` entering the Z rotation acting on wire ``p``
        s (int): qubit index ``s``
        r (int): qubit index ``r``
        q (int): qubit index ``q``
        p (int): qubit index ``p``
        set_cnot_wires (list[Wires]): list of CNOT wires
    """

    # U_1, U_2, U_3, U_4 acting on wires 's', 'r', 'q' and 'p'
    RX(-np.pi / 2, wires=s)
    RX(-np.pi / 2, wires=r)
    Hadamard(wires=q)
    RX(-np.pi / 2, wires=p)

    # Applying CNOTs
    for cnot_wires in set_cnot_wires:
        CNOT(wires=cnot_wires)

    # Z rotation acting on wire 'p'
    RZ(-weight / 8, wires=p)

    # Applying CNOTs in reverse order
    for cnot_wires in reversed(set_cnot_wires):
        CNOT(wires=cnot_wires)

    # U_1^+, U_2^+, U_3^+, U_4^+ acting on wires 's', 'r', 'q' and 'p'
    RX(np.pi / 2, wires=s)
    RX(np.pi / 2, wires=r)
    Hadamard(wires=q)
    RX(np.pi / 2, wires=p)
Exemplo n.º 7
0
    def expand(self):
        ctrl_str = "0" * (len(self.wires) - 1)

        with qml.tape.QuantumTape() as tape:
            for wire in self.wires[:-1]:
                Hadamard(wire)

            PauliZ(self.wires[-1])
            MultiControlledX(
                control_values=ctrl_str,
                control_wires=self.wires[:-1],
                wires=self.wires[-1],
                work_wires=self.work_wires,
            )

            PauliZ(self.wires[-1])

            for wire in self.wires[:-1]:
                Hadamard(wire)

        return tape
Exemplo n.º 8
0
def qaoa_feature_encoding_hamiltonian(features, n_features, wires):
    """Implements the encoding Hamiltonian of the QAOA embedding.

    Args:
        features (array): array of features to encode
        n_features (int): number of features to encode
    """
    for idx, w in enumerate(wires):
        # Either feed in feature
        if idx < n_features:
            RX(features[idx], wires=w)
        # or a Hadamard
        else:
            Hadamard(wires=w)
Exemplo n.º 9
0
    def expand(self):
        unitary = self.parameters[0]
        unitary_powers = [unitary]

        for _ in range(len(self.estimation_wires) - 1):
            new_power = unitary_powers[-1] @ unitary_powers[-1]
            unitary_powers.append(new_power)

        with qml.tape.QuantumTape() as tape:
            for wire in self.estimation_wires:
                Hadamard(wire)
                ControlledQubitUnitary(
                    unitary_powers.pop(), control_wires=wire, wires=self.target_wires
                )

            qml.templates.QFT(wires=self.estimation_wires).inv()

        return tape
Exemplo n.º 10
0
def SingleExcitationUnitary(weight, wires=None):
    r"""Circuit to exponentiate the tensor product of Pauli matrices representing the
    single-excitation operator entering the Unitary Coupled-Cluster Singles
    and Doubles (UCCSD) ansatz. UCCSD is a VQE ansatz commonly used to run quantum
    chemistry simulations.

    The CC single-excitation operator is given by

    .. math::

        \hat{U}_{pr}(\theta) = \mathrm{exp} \{ \theta_{pr} (\hat{c}_p^\dagger \hat{c}_r
        -\mathrm{H.c.}) \},

    where :math:`\hat{c}` and :math:`\hat{c}^\dagger` are the fermionic annihilation and
    creation operators and the indices :math:`r` and :math:`p` run over the occupied and
    unoccupied molecular orbitals, respectively. Using the `Jordan-Wigner transformation
    <https://arxiv.org/abs/1208.5986>`_ the fermionic operator defined above can be written
    in terms of Pauli matrices (for more details see
    `arXiv:1805.04340 <https://arxiv.org/abs/1805.04340>`_).

    .. math::

        \hat{U}_{pr}(\theta) = \mathrm{exp} \Big\{ \frac{i\theta}{2}
        \bigotimes_{a=r+1}^{p-1}\hat{Z}_a (\hat{Y}_r \hat{X}_p) \Big\}
        \mathrm{exp} \Big\{ -\frac{i\theta}{2}
        \bigotimes_{a=r+1}^{p-1} \hat{Z}_a (\hat{X}_r \hat{Y}_p) \Big\}.

    The quantum circuit to exponentiate the tensor product of Pauli matrices entering
    the latter equation is shown below (see `arXiv:1805.04340 <https://arxiv.org/abs/1805.04340>`_):

    |

    .. figure:: ../../_static/templates/subroutines/single_excitation_unitary.png
        :align: center
        :width: 60%
        :target: javascript:void(0);

    |

    As explained in `Seely et al. (2012) <https://arxiv.org/abs/1208.5986>`_,
    the exponential of a tensor product of Pauli-Z operators can be decomposed in terms of
    :math:`2(n-1)` CNOT gates and a single-qubit Z-rotation referred to as :math:`U_\theta` in
    the figure above. If there are :math:`X` or :math:`Y` Pauli matrices in the product,
    the Hadamard (:math:`H`) or :math:`R_x` gate has to be applied to change to the
    :math:`X` or :math:`Y` basis, respectively. The latter operations are denoted as
    :math:`U_1` and :math:`U_2` in the figure above. See the Usage Details section for more
    information.

    Args:
        weight (float): angle :math:`\theta` entering the Z rotation acting on wire ``p``
        wires (Iterable or Wires): Wires that the template acts on.
            The wires represent the subset of orbitals in the interval ``[r, p]``. Must be of
            minimum length 2. The first wire is interpreted as ``r`` and the last wire as ``p``.
            Wires in between are acted on with CNOT gates to compute the parity of the set
            of qubits.

    Raises:
        ValueError: if inputs do not have the correct format

    .. UsageDetails::

        Notice that:

        #. :math:`\hat{U}_{pr}(\theta)` involves two exponentiations where :math:`\hat{U}_1`,
           :math:`\hat{U}_2`, and :math:`\hat{U}_\theta` are defined as follows,

           .. math::
               [U_1, U_2, U_{\theta}] = \Bigg\{\bigg[R_x(-\pi/2), H, R_z(\theta/2)\bigg],
               \bigg[H, R_x(-\frac{\pi}{2}), R_z(-\theta/2) \bigg] \Bigg\}

        #. For a given pair ``[r, p]``, ten single-qubit and ``4*(len(wires)-1)`` CNOT
           operations are applied. Notice also that CNOT gates act only on qubits
           ``wires[1]`` to ``wires[-2]``. The operations performed across these qubits
           are shown in dashed lines in the figure above.

        An example of how to use this template is shown below:

        .. code-block:: python

            import pennylane as qml
            from pennylane.templates import SingleExcitationUnitary

            dev = qml.device('default.qubit', wires=3)

            @qml.qnode(dev)
            def circuit(weight, wires=None):
                SingleExcitationUnitary(weight, wires=wires)
                return qml.expval(qml.PauliZ(0))

            weight = 0.56
            print(circuit(weight, wires=[0, 1, 2]))

    """

    ##############
    # Input checks

    wires = Wires(wires)

    if len(wires) < 2:
        raise ValueError("expected at least two wires; got {}".format(
            len(wires)))

    expected_shape = ()
    check_shape(
        weight,
        expected_shape,
        msg="'weight' must be of shape {}; got {}".format(
            expected_shape, get_shape(weight)),
    )

    ###############

    # Interpret first and last wire as r and p
    r = wires[0]
    p = wires[-1]

    # Sequence of the wires entering the CNOTs between wires 'r' and 'p'
    set_cnot_wires = [wires.subset([l, l + 1]) for l in range(len(wires) - 1)]

    # ------------------------------------------------------------------
    # Apply the first layer

    # U_1, U_2 acting on wires 'r' and 'p'
    RX(-np.pi / 2, wires=r)
    Hadamard(wires=p)

    # Applying CNOTs between wires 'r' and 'p'
    for cnot_wires in set_cnot_wires:
        CNOT(wires=cnot_wires)

    # Z rotation acting on wire 'p'
    RZ(weight / 2, wires=p)

    # Applying CNOTs in reverse order
    for cnot_wires in reversed(set_cnot_wires):
        CNOT(wires=cnot_wires)

    # U_1^+, U_2^+ acting on wires 'r' and 'p'
    RX(np.pi / 2, wires=r)
    Hadamard(wires=p)

    # ------------------------------------------------------------------
    # Apply the second layer

    # U_1, U_2 acting on wires 'r' and 'p'
    Hadamard(wires=r)
    RX(-np.pi / 2, wires=p)

    # Applying CNOTs between wires 'r' and 'p'
    for cnot_wires in set_cnot_wires:
        CNOT(wires=cnot_wires)

    # Z rotation acting on wire 'p'
    RZ(-weight / 2, wires=p)

    # Applying CNOTs in reverse order
    for cnot_wires in reversed(set_cnot_wires):
        CNOT(wires=cnot_wires)

    # U_1^+, U_2^+ acting on wires 'r' and 'p'
    Hadamard(wires=r)
    RX(np.pi / 2, wires=p)