Ejemplo n.º 1
0
def _apply_controlled_z(wires, control_wire, work_wires):
    r"""Provides the circuit to apply a controlled version of the :math:`Z` gate defined in
    `this <https://arxiv.org/abs/1805.00109>`__ paper.

    The multi-qubit gate :math:`Z = I - 2|0\rangle \langle 0|` can be performed using the
    conventional multi-controlled-Z gate with an additional bit flip on each qubit before and after.

    This function performs the multi-controlled-Z gate via a multi-controlled-X gate by picking an
    arbitrary target wire to perform the X and adding a Hadamard on that wire either side of the
    transformation.

    Additional control from ``control_wire`` is then included within the multi-controlled-X gate.

    Args:
        wires (Wires): the wires on which the Z gate is applied
        control_wire (Wires): the control wire from the register of phase estimation qubits
        work_wires (Wires): the work wires used in the decomposition
    """
    target_wire = wires[0]
    PauliX(target_wire)
    Hadamard(target_wire)

    control_values = "0" * (len(wires) - 1) + "1"
    control_wires = wires[1:] + control_wire
    MultiControlledX(
        control_wires=control_wires,
        wires=target_wire,
        control_values=control_values,
        work_wires=work_wires,
    )

    Hadamard(target_wire)
    PauliX(target_wire)
Ejemplo n.º 2
0
    def test_is_pauli_word(self):
        """Test for determining whether input ``Observable`` instance is a Pauli word."""

        observable_1 = PauliX(0)
        observable_2 = PauliZ(1) @ PauliX(2) @ PauliZ(4)
        observable_3 = PauliX(1) @ Hadamard(4)
        observable_4 = Hadamard(0)

        assert is_pauli_word(observable_1)
        assert is_pauli_word(observable_2)
        assert not is_pauli_word(observable_3)
        assert not is_pauli_word(observable_4)
Ejemplo n.º 3
0
    def wrapper(*args, **kwargs):
        fn(*args, **kwargs)
        for i, control_wire in enumerate(estimation_wires):
            Hadamard(control_wire)

            # Find wires eligible to be used as helper wires
            work_wires = estimation_wires.toset() - {control_wire}
            n_reps = 2**(len(estimation_wires) - (i + 1))

            q = apply_controlled_Q(
                fn,
                wires=wires,
                target_wire=target_wire,
                control_wire=control_wire,
                work_wires=work_wires,
            )

            for _ in range(n_reps):
                q(*args, **kwargs)

        QFT(wires=estimation_wires).inv()
Ejemplo n.º 4
0
import numpy as np
from pennylane import Identity, PauliX, PauliY, PauliZ, Hadamard, Hermitian, U3
from pennylane.operation import Tensor
from pennylane.wires import Wires
from pennylane.grouping.utils import (
    is_pauli_word,
    are_identical_pauli_words,
    pauli_to_binary,
    binary_to_pauli,
    is_qwc,
    observables_to_binary_matrix,
    qwc_complement_adj_matrix,
)

non_pauli_words = [
    PauliX(0) @ Hadamard(1) @ Identity(2),
    Hadamard("a"),
    U3(0.1, 1, 1, wires="a"),
    Hermitian(np.array([[3.2, 1.1 + 0.6j], [1.1 - 0.6j, 3.2]]), wires="a")
    @ PauliX("b"),
]


class TestGroupingUtils:
    """Basic usage and edge-case tests for the measurement optimization utility functions."""

    ops_to_vecs_explicit_wires = [
        (PauliX(0) @ PauliY(1) @ PauliZ(2), np.array([1, 1, 0, 0, 1, 1])),
        (PauliZ(0) @ PauliY(2), np.array([0, 1, 1, 1])),
        (PauliY(1) @ PauliX(2), np.array([1, 1, 1, 0])),
        (Identity(0), np.zeros(2)),
Ejemplo n.º 5
0
class TestMeasurementTransformations:
    """Tests for the functions involved in obtaining post-rotations necessary in the measurement
    optimization schemes implemented in :mod:`grouping`."""
    def are_identical_rotation_gates(self, gate_1, gate_2, param_tol=1e-6):
        """Checks whether the two input gates are identical up to a certain threshold in their
        parameters.

        Arguments:
            gate_1 (Union[RX, RY, RZ]): the first single-qubit rotation gate
            gate_2 (Union[RX, RY, RZ]): the second single-qubit rotation gate

        Keyword arguments:
            param_tol (float): the absolute tolerance for considering whether two gates parameter
                values are the same

        Returns:
            bool: whether the input rotation gates are identical up to the parameter tolerance

        """

        return (gate_1.wires == gate_2.wires and np.allclose(
            gate_1.parameters, gate_2.parameters, atol=param_tol, rtol=0)
                and gate_1.name == gate_2.name)

    qwc_rotation_io = [
        ([PauliX(0), PauliZ(1), PauliZ(3)], [RY(-np.pi / 2, wires=[0])]),
        ([Identity(0), PauliZ(1)], []),
        (
            [PauliX(2), PauliY(0), Identity(1)],
            [RY(-np.pi / 2, wires=[2]),
             RX(np.pi / 2, wires=[0])],
        ),
        (
            [PauliZ("a"), PauliX("b"), PauliY(0)],
            [RY(-np.pi / 2, wires=["b"]),
             RX(np.pi / 2, wires=[0])],
        ),
    ]

    @pytest.mark.parametrize("pauli_ops,qwc_rot_sol", qwc_rotation_io)
    def test_qwc_rotation(self, pauli_ops, qwc_rot_sol):
        """Tests that the correct single-qubit post-rotation gates are obtained for the input list
        of Pauli operators."""

        qwc_rot = qwc_rotation(pauli_ops)

        assert all([
            self.are_identical_rotation_gates(qwc_rot[i], qwc_rot_sol[i])
            for i in range(len(qwc_rot))
        ])

    invalid_qwc_rotation_inputs = [
        [PauliX(0), PauliY(1), Hadamard(2)],
        [PauliX(0) @ PauliY(1), PauliZ(1),
         Identity(2)],
        [RX(1, wires="a"), PauliX("b")],
    ]

    @pytest.mark.parametrize("bad_input", invalid_qwc_rotation_inputs)
    def test_invalid_qwc_rotation_input_catch(self, bad_input):
        """Verifies that a TypeError is raised when the input to qwc_rotations is not a list of
        single Pauli operators."""

        assert pytest.raises(TypeError, qwc_rotation, bad_input)

    diagonalized_paulis = [
        (Identity("a"), Identity("a")),
        (PauliX(1) @ Identity(2) @ PauliZ("b"), PauliZ(1) @ PauliZ("b")),
        (PauliZ(1) @ PauliZ(2), PauliZ(1) @ PauliZ(2)),
        (PauliX("a") @ PauliY("b") @ PauliY("c"),
         PauliZ("a") @ PauliZ("b") @ PauliZ("c")),
    ]

    @pytest.mark.parametrize("pauli_word, diag_pauli_word",
                             diagonalized_paulis)
    def test_diagonalize_pauli_word(self, pauli_word, diag_pauli_word):
        """Tests `diagonalize_pauli_word` returns the correct diagonal Pauli word in computational
        basis for a given Pauli word."""

        assert are_identical_pauli_words(diagonalize_pauli_word(pauli_word),
                                         diag_pauli_word)

    non_pauli_words = [
        PauliX(0) @ Hadamard(1) @ Identity(2),
        Hadamard("a"),
        U3(0.1, 1, 1, wires="a"),
        Hermitian(np.array([[3.2, 1.1 + 0.6j], [1.1 - 0.6j, 3.2]]), wires="a")
        @ PauliX("b"),
    ]

    @pytest.mark.parametrize("non_pauli_word", non_pauli_words)
    def test_diagonalize_pauli_word_catch_non_pauli_word(self, non_pauli_word):
        assert pytest.raises(TypeError, diagonalize_pauli_word, non_pauli_word)

    qwc_diagonalization_io = [
        (
            [PauliX(0) @ PauliY(1),
             PauliX(0) @ PauliZ(2)],
            (
                [RY(-np.pi / 2, wires=[0]),
                 RX(np.pi / 2, wires=[1])],
                [
                    PauliZ(wires=[0]) @ PauliZ(wires=[1]),
                    PauliZ(wires=[0]) @ PauliZ(wires=[2])
                ],
            ),
        ),
        (
            [
                PauliX(2) @ Identity(0),
                PauliY(1),
                PauliZ(0) @ PauliY(1),
                PauliX(2) @ PauliY(1)
            ],
            (
                [RY(-np.pi / 2, wires=[2]),
                 RX(np.pi / 2, wires=[1])],
                [
                    PauliZ(wires=[2]),
                    PauliZ(wires=[1]),
                    PauliZ(wires=[0]) @ PauliZ(wires=[1]),
                    PauliZ(wires=[2]) @ PauliZ(wires=[1]),
                ],
            ),
        ),
        (
            [
                PauliZ("a") @ PauliY("b") @ PauliZ("c"),
                PauliY("b") @ PauliZ("d")
            ],
            (
                [RX(np.pi / 2, wires=["b"])],
                [
                    PauliZ(wires=["a"]) @ PauliZ(wires=["b"])
                    @ PauliZ(wires=["c"]),
                    PauliZ(wires=["b"]) @ PauliZ(wires=["d"]),
                ],
            ),
        ),
        ([PauliX("a")], ([RY(-np.pi / 2,
                             wires=["a"])], [PauliZ(wires=["a"])])),
    ]

    @pytest.mark.parametrize("qwc_grouping,qwc_sol_tuple",
                             qwc_diagonalization_io)
    def test_diagonalize_qwc_pauli_words(self, qwc_grouping, qwc_sol_tuple):
        """Tests for validating diagonalize_qwc_pauli_words solutions."""

        qwc_rot, diag_qwc_grouping = diagonalize_qwc_pauli_words(qwc_grouping)
        qwc_rot_sol, diag_qwc_grouping_sol = qwc_sol_tuple

        assert all([
            self.are_identical_rotation_gates(qwc_rot[i], qwc_rot_sol[i])
            for i in range(len(qwc_rot))
        ])
        assert all([
            are_identical_pauli_words(diag_qwc_grouping[i],
                                      diag_qwc_grouping_sol[i])
            for i in range(len(diag_qwc_grouping))
        ])

    not_qwc_groupings = [
        [PauliX("a"), PauliY("a")],
        [
            PauliZ(0) @ Identity(1),
            PauliZ(0) @ PauliZ(1),
            PauliX(0) @ Identity(1)
        ],
        [PauliX("a") @ PauliX(0),
         PauliZ(0) @ PauliZ("a")],
    ]

    @pytest.mark.parametrize("not_qwc_grouping", not_qwc_groupings)
    def test_diagonalize_qwc_pauli_words_catch_when_not_qwc(
            self, not_qwc_grouping):
        """Test for ValueError raise when diagonalize_qwc_pauli_words is not given a list of
        qubit-wise commuting Pauli words."""

        assert pytest.raises(ValueError, diagonalize_qwc_pauli_words,
                             not_qwc_grouping)