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)
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)
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()
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)),
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)