def test_expected_tape(self): """Tests if QuantumPhaseEstimation populates the tape as expected for a fixed example""" m = qml.RX(0.3, wires=0).matrix op = qml.QuantumPhaseEstimation(m, target_wires=[0], estimation_wires=[1, 2]) tape = op.expand() with qml.tape.QuantumTape() as tape2: qml.Hadamard(1), qml.ControlledQubitUnitary(m @ m, control_wires=[1], wires=[0]), qml.Hadamard(2), qml.ControlledQubitUnitary(m, control_wires=[2], wires=[0]), qml.QFT(wires=[1, 2]).inv() assert len(tape2.queue) == len(tape.queue) assert all([ op1.name == op2.name for op1, op2 in zip(tape.queue, tape2.queue) ]) assert all([ op1.wires == op2.wires for op1, op2 in zip(tape.queue, tape2.queue) ]) assert np.allclose(tape.queue[1].matrix, tape2.queue[1].matrix) assert np.allclose(tape.queue[3].matrix, tape2.queue[3].matrix)
def test_qubit_unitary(): """Test ctrl on QubitUnitary and ControlledQubitUnitary""" with QuantumTape() as tape: ctrl(qml.QubitUnitary, 1)(np.array([[1.0, 1.0], [1.0, -1.0]]) / np.sqrt(2.0), wires=0) tape = expand_tape( tape, 3, stop_at=lambda op: not isinstance(op, ControlledOperation)) assert_equal_operations(tape.operations, [ qml.ControlledQubitUnitary( np.array([[1.0, 1.0], [1.0, -1.0]]) / np.sqrt(2.0), control_wires=1, wires=0) ]) with QuantumTape() as tape: ctrl(qml.ControlledQubitUnitary, 1)(np.array([[1.0, 1.0], [1.0, -1.0]]) / np.sqrt(2.0), control_wires=2, wires=0) tape = expand_tape( tape, 3, stop_at=lambda op: not isinstance(op, ControlledOperation)) assert_equal_operations(tape.operations, [ qml.ControlledQubitUnitary( np.array([[1.0, 1.0], [1.0, -1.0]]) / np.sqrt(2.0), control_wires=[1, 2], wires=0) ])
def test_matrix(self): """Test if ControlledQubitUnitary returns the correct matrix for a control-control-X (Toffoli) gate""" mat = qml.ControlledQubitUnitary(X, control_wires=[0, 1], wires=2).matrix mat2 = qml.Toffoli(wires=[0, 1, 2]).matrix assert np.allclose(mat, mat2)
def qfunc(a, b, c, angles): qml.RX(a, wires=0) qml.RX(b, wires=1) qml.PauliZ(1) qml.CNOT(wires=[0, 1]).inv() qml.CRY(b, wires=[3, 1]) qml.RX(angles[0], wires=0) qml.RX(4 * angles[1], wires=1) qml.PhaseShift(17 / 9 * c, wires=2) qml.RZ(b, wires=3) qml.RX(angles[2], wires=2).inv() qml.CRY(0.3589, wires=[3, 1]).inv() qml.CSWAP(wires=[4, 2, 1]).inv() qml.QubitUnitary(np.eye(2), wires=[2]) qml.ControlledQubitUnitary(np.eye(2), control_wires=[0, 1], wires=[2]) qml.MultiControlledX(control_wires=[0, 1, 2], wires=[3]) qml.Toffoli(wires=[0, 2, 1]) qml.CNOT(wires=[0, 2]) qml.PauliZ(wires=[1]) qml.PauliZ(wires=[1]).inv() qml.CZ(wires=[0, 1]) qml.CZ(wires=[0, 2]).inv() qml.CNOT(wires=[2, 1]) qml.CNOT(wires=[0, 2]) qml.SWAP(wires=[0, 2]).inv() qml.CNOT(wires=[1, 3]) qml.RZ(b, wires=3) qml.CSWAP(wires=[4, 0, 1]) return [ qml.expval(qml.PauliY(0)), qml.var(qml.Hadamard(wires=1)), qml.sample(qml.PauliX(2)), qml.expval(qml.Hermitian(np.eye(4), wires=[3, 4])), ]
def test_apply(self, n_wires): """Test if the apply_controlled_Q performs the correct transformation by reconstructing the unitary and comparing against the one provided in make_Q. Random unitaries are chosen for a_mat and r_mat.""" n_all_wires = n_wires + 1 wires = range(n_wires) target_wire = n_wires - 1 control_wire = n_wires a_mat = unitary_group.rvs(2**(n_wires - 1), random_state=1967) r_mat = unitary_group.rvs(2**n_wires, random_state=1967) q_mat = make_Q(a_mat, r_mat) def fn(): qml.QubitUnitary(a_mat, wires=wires[:-1]) qml.QubitUnitary(r_mat, wires=wires) circ = apply_controlled_Q(fn, wires=wires, target_wire=target_wire, control_wire=control_wire, work_wires=None) u = get_unitary(circ, n_all_wires) circ = lambda: qml.ControlledQubitUnitary( q_mat, wires=wires, control_wires=control_wire) u_ideal = get_unitary(circ, n_all_wires) assert np.allclose(u_ideal, u)
def f1(): qml.QubitUnitary(U1, wires=range(4)) qml.ControlledQubitUnitary(U, control_wires=control_wires, wires=target_wires) qml.QubitUnitary(U2, wires=range(4)) return qml.state()
def circuit_mixed_polarity(): qml.templates.ArbitraryStatePreparation(control_state_weights, wires=control_wires) qml.templates.ArbitraryStatePreparation(target_state_weights, wires=target_wires) qml.ControlledQubitUnitary( U, control_wires=control_wires, wires=target_wires, control_values=control_values ) return qml.state()
def test_wrong_shape(self): """Test if ControlledQubitUnitary raises a ValueError if a unitary of shape inconsistent with wires is provided""" with pytest.raises(ValueError, match=r"Input unitary must be of shape \(2, 2\)"): qml.ControlledQubitUnitary(np.eye(4), control_wires=[0, 1], wires=2)
def test_invalid_mixed_polarity_controls( self, control_wires, wires, control_values, expected_error_message ): """Test if ControlledQubitUnitary properly handles invalid mixed-polarity control values.""" target_wires = Wires(wires) with pytest.raises(ValueError, match=expected_error_message): qml.ControlledQubitUnitary( X, control_wires=control_wires, wires=target_wires, control_values=control_values )
def op(op_name): ops_list = { "RX": qml.RX(0.123, wires=0), "RY": qml.RY(1.434, wires=0), "RZ": qml.RZ(2.774, wires=0), "S": qml.S(wires=0), "SX": qml.SX(wires=0), "T": qml.T(wires=0), "CNOT": qml.CNOT(wires=[0, 1]), "CZ": qml.CZ(wires=[0, 1]), "CY": qml.CY(wires=[0, 1]), "SWAP": qml.SWAP(wires=[0, 1]), "ISWAP": qml.ISWAP(wires=[0, 1]), "SISWAP": qml.SISWAP(wires=[0, 1]), "SQISW": qml.SQISW(wires=[0, 1]), "CSWAP": qml.CSWAP(wires=[0, 1, 2]), "PauliRot": qml.PauliRot(0.123, "Y", wires=0), "IsingXX": qml.IsingXX(0.123, wires=[0, 1]), "IsingXY": qml.IsingXY(0.123, wires=[0, 1]), "IsingYY": qml.IsingYY(0.123, wires=[0, 1]), "IsingZZ": qml.IsingZZ(0.123, wires=[0, 1]), "Identity": qml.Identity(wires=0), "Rot": qml.Rot(0.123, 0.456, 0.789, wires=0), "Toffoli": qml.Toffoli(wires=[0, 1, 2]), "PhaseShift": qml.PhaseShift(2.133, wires=0), "ControlledPhaseShift": qml.ControlledPhaseShift(1.777, wires=[0, 2]), "CPhase": qml.CPhase(1.777, wires=[0, 2]), "MultiRZ": qml.MultiRZ(0.112, wires=[1, 2, 3]), "CRX": qml.CRX(0.836, wires=[2, 3]), "CRY": qml.CRY(0.721, wires=[2, 3]), "CRZ": qml.CRZ(0.554, wires=[2, 3]), "Hadamard": qml.Hadamard(wires=0), "PauliX": qml.PauliX(wires=0), "PauliY": qml.PauliY(wires=0), "PauliZ": qml.PauliZ(wires=0), "CRot": qml.CRot(0.123, 0.456, 0.789, wires=[0, 1]), "DiagonalQubitUnitary": qml.DiagonalQubitUnitary(np.array([1.0, 1.0j]), wires=1), "ControlledQubitUnitary": qml.ControlledQubitUnitary( np.eye(2) * 1j, wires=[0], control_wires=[2] ), "MultiControlledX": qml.MultiControlledX(wires=(0, 1, 2), control_values="01"), "SingleExcitation": qml.SingleExcitation(0.123, wires=[0, 3]), "SingleExcitationPlus": qml.SingleExcitationPlus(0.123, wires=[0, 3]), "SingleExcitationMinus": qml.SingleExcitationMinus(0.123, wires=[0, 3]), "DoubleExcitation": qml.DoubleExcitation(0.123, wires=[0, 1, 2, 3]), "DoubleExcitationPlus": qml.DoubleExcitationPlus(0.123, wires=[0, 1, 2, 3]), "DoubleExcitationMinus": qml.DoubleExcitationMinus(0.123, wires=[0, 1, 2, 3]), "QFT": qml.QFT(wires=0), "QubitSum": qml.QubitSum(wires=[0, 1, 2]), "QubitCarry": qml.QubitCarry(wires=[0, 1, 2, 3]), "QubitUnitary": qml.QubitUnitary(np.eye(2) * 1j, wires=0), } return ops_list.get(op_name)
def test_controlled_qubit_unitary_init(self): """Test for the init of ControlledQubitUnitary""" control_wires = [3, 2] target_wires = [1, 0] U = qml.CRX._matrix(0.4) op = qml.ControlledQubitUnitary(U, control_wires=control_wires, wires=target_wires) target_data = [np.block([[np.eye(12), np.zeros((12, 4))], [np.zeros((4, 12)), U]])] assert op.name == qml.ControlledQubitUnitary.__name__ assert np.allclose(target_data, op.data) assert op._wires == Wires(control_wires) + Wires(target_wires)
def circuit_pauli_x(): qml.templates.ArbitraryStatePreparation(control_state_weights, wires=control_wires) qml.templates.ArbitraryStatePreparation(target_state_weights, wires=target_wires) for wire in x_locations: qml.PauliX(wires=control_wires[wire]) qml.ControlledQubitUnitary(U, control_wires=control_wires, wires=wires) for wire in x_locations: qml.PauliX(wires=control_wires[wire]) return qml.state()
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: qml.Hadamard(wire) qml.ControlledQubitUnitary(unitary_powers.pop(), control_wires=wire, wires=self.target_wires) qml.QFT(wires=self.estimation_wires).inv() return tape
def parameterized_qubit_tape(): """A parametrized qubit ciruit.""" a, b, c = 0.1, 0.2, 0.3 angles = np.array([0.4, 0.5, 0.6]) with qml.tape.QuantumTape() as tape: qml.RX(a, wires=0) qml.RX(b, wires=1) qml.PauliZ(1) qml.CNOT(wires=[0, 1]).inv() qml.CRY(b, wires=[3, 1]) qml.RX(angles[0], wires=0) qml.RX(4 * angles[1], wires=1) qml.PhaseShift(17 / 9 * c, wires=2) qml.RZ(b, wires=3) qml.RX(angles[2], wires=2).inv() qml.CRY(0.3589, wires=[3, 1]).inv() qml.CSWAP(wires=[4, 2, 1]).inv() qml.QubitUnitary(np.eye(2), wires=[2]) qml.ControlledQubitUnitary(np.eye(2), control_wires=[0, 1], wires=[2]) qml.MultiControlledX(control_wires=[0, 1, 2], wires=[3]) qml.Toffoli(wires=[0, 2, 1]) qml.CNOT(wires=[0, 2]) qml.PauliZ(wires=[1]) qml.PauliZ(wires=[1]).inv() qml.CZ(wires=[0, 1]) qml.CZ(wires=[0, 2]).inv() qml.CY(wires=[1, 2]) qml.CY(wires=[2, 0]).inv() qml.CNOT(wires=[2, 1]) qml.CNOT(wires=[0, 2]) qml.SWAP(wires=[0, 2]).inv() qml.CNOT(wires=[1, 3]) qml.RZ(b, wires=3) qml.CSWAP(wires=[4, 0, 1]) qml.expval(qml.PauliY(0)), qml.var(qml.Hadamard(wires=1)), qml.sample(qml.PauliX(2)), qml.expval(qml.Hermitian(np.eye(4), wires=[3, 4])), return tape
def _get_gen_op(op, allow_nonunitary, aux_wire): r"""Get the controlled-generator operation for a given operation. Args: op (pennylane.operation.Operation): Operation from which to extract the generator allow_nonunitary (bool): Whether non-unitary gates are allowed in the circuit aux_wire (int or pennylane.wires.Wires): Auxiliary wire on which to control the operation Returns qml.Operation: Controlled-generator operation of the generator of ``op``, controlled on wire ``aux_wire``. Raises ValueError: If the generator of ``op`` is not known or it is non-unitary while ``allow_nonunitary=False``. If ``allow_nonunitary=True``, a general :class:`~.pennylane.ControlledQubitUnitary` is returned, otherwise only controlled Pauli operations are used. If the operation has a non-unitary generator but ``allow_nonunitary=False``, the operation ``op`` should have been decomposed before, leading to a ``ValueError``. """ gen, _ = op.generator try: if isinstance(gen, np.ndarray): cgen = _OP_TO_CGEN[op.__class__] else: cgen = _GEN_TO_CGEN.get(gen, None) if cgen is None: cgen = _OP_TO_CGEN[op.__class__] return cgen(wires=[aux_wire, *op.wires]) except KeyError as e: if allow_nonunitary: if (not isinstance(gen, np.ndarray)) and issubclass( gen, qml.operation.Observable): gen = gen.matrix return qml.ControlledQubitUnitary(gen, control_wires=aux_wire, wires=op.wires) raise ValueError( f"Generator for operation {op} not known and non-unitary operations " "deactivated via allow_nonunitary=False.") from e
def test_apply_controlled_v(n_wires): """Test if the _apply_controlled_v performs the correct transformation by reconstructing the unitary and comparing against the one provided in _make_V.""" n_all_wires = n_wires + 1 wires = Wires(range(n_wires)) control_wire = Wires(n_wires) circ = lambda: _apply_controlled_v(target_wire=Wires([n_wires - 1]), control_wire=control_wire) u = get_unitary(circ, n_all_wires) # Note the sign flip in the following. The sign does not matter when performing the Q unitary # because two Vs are used. v_ideal = -_make_V(2**n_wires) circ = lambda: qml.ControlledQubitUnitary( v_ideal, wires=wires, control_wires=control_wire) u_ideal = get_unitary(circ, n_all_wires) assert np.allclose(u, u_ideal)
def expand_with_control(tape, control_wire): """Expand a tape to include a control wire on all queued operations. Args: tape (.QuantumTape): quantum tape to be controlled control_wire (int): a single wire to use as the control wire Returns: .QuantumTape: A new QuantumTape with the controlled operations. """ with QuantumTape(do_queue=False) as new_tape: for op in tape.operations: if hasattr(op, "_controlled"): # Execute the controlled version of the operation # and add that the to the tape context. # pylint: disable=protected-access op._controlled(control_wire) else: # Attempt to decompose the operation and apply # controls to each gate in the decomposition. with new_tape.stop_recording(): try: tmp_tape = op.expand() except NotImplementedError: # No decomposition is defined. Create a # ControlledQubitUnitary gate using the operation # matrix representation. with QuantumTape() as tmp_tape: qml.ControlledQubitUnitary( op.matrix, control_wires=control_wire, wires=op.wires) tmp_tape = expand_with_control(tmp_tape, control_wire) requeue_ops_in_tape(tmp_tape) return new_tape
"CRZ": qml.CRZ(0, wires=[0, 1]), "CRot": qml.CRot(0, 0, 0, wires=[0, 1]), "CSWAP": qml.CSWAP(wires=[0, 1, 2]), "CZ": qml.CZ(wires=[0, 1]), "CY": qml.CY(wires=[0, 1]), "DiagonalQubitUnitary": qml.DiagonalQubitUnitary(np.array([1, 1]), wires=[0]), "Hadamard": qml.Hadamard(wires=[0]), "MultiRZ": qml.MultiRZ(0, wires=[0]), "PauliX": qml.PauliX(wires=[0]), "PauliY": qml.PauliY(wires=[0]), "PauliZ": qml.PauliZ(wires=[0]), "PhaseShift": qml.PhaseShift(0, wires=[0]), "ControlledPhaseShift": qml.ControlledPhaseShift(0, wires=[0, 1]), "QubitStateVector": qml.QubitStateVector(np.array([1.0, 0.0]), wires=[0]), "QubitUnitary": qml.QubitUnitary(np.eye(2), wires=[0]), "ControlledQubitUnitary": qml.ControlledQubitUnitary(np.eye(2), control_wires=[1], wires=[0]), "MultiControlledX": qml.MultiControlledX(control_wires=[1, 2], wires=[0]), "RX": qml.RX(0, wires=[0]), "RY": qml.RY(0, wires=[0]), "RZ": qml.RZ(0, wires=[0]), "Rot": qml.Rot(0, 0, 0, wires=[0]), "S": qml.S(wires=[0]), "SWAP": qml.SWAP(wires=[0, 1]), "T": qml.T(wires=[0]), "SX": qml.SX(wires=[0]), "Toffoli": qml.Toffoli(wires=[0, 1, 2]), "QFT": qml.QFT(wires=[0, 1, 2]), "SingleExcitation": qml.SingleExcitation(0, wires=[0, 1]), "SingleExcitationPlus": qml.SingleExcitationPlus(0, wires=[0, 1]), "SingleExcitationMinus": qml.SingleExcitationMinus(0, wires=[0, 1]), "DoubleExcitation": qml.DoubleExcitation(0, wires=[0, 1, 2, 3]),
def test_shared_control(self): """Test if ControlledQubitUnitary raises an error if control wires are shared with wires""" with pytest.raises( ValueError, match="The control wires must be different from the wires"): qml.ControlledQubitUnitary(X, control_wires=[0, 2], wires=2)
def test_no_control(self): """Test if ControlledQubitUnitary raises an error if control wires are not specified""" with pytest.raises(ValueError, match="Must specify control wires"): qml.ControlledQubitUnitary(X, wires=2)
qml.PauliX(wires=control_wires[wire]) qml.ControlledQubitUnitary(U, control_wires=control_wires, wires=wires) for wire in x_locations: qml.PauliX(wires=control_wires[wire]) return qml.state() mixed_polarity_state = circuit_mixed_polarity() pauli_x_state = circuit_pauli_x() assert np.allclose(mixed_polarity_state, pauli_x_state) label_data = [ (qml.QubitUnitary(X, wires=0), "U"), (qml.DiagonalQubitUnitary([1, 1], wires=1), "U"), (qml.ControlledQubitUnitary(X, control_wires=0, wires=1), "U"), ] @pytest.mark.parametrize("op, label", label_data) def test_label(op, label): assert op.label() == label assert op.label(decimals=5) == label op.inv() assert op.label() == label + "⁻¹"
def QuantumPhaseEstimation(unitary, target_wires, estimation_wires): r"""Performs the `quantum phase estimation <https://en.wikipedia.org/wiki/Quantum_phase_estimation_algorithm>`__ circuit. Given a unitary matrix :math:`U`, this template applies the circuit for quantum phase estimation. The unitary is applied to the qubits specified by ``target_wires`` and :math:`n` qubits are used for phase estimation as specified by ``estimation_wires``. .. figure:: ../../_static/templates/subroutines/qpe.svg :align: center :width: 60% :target: javascript:void(0); This circuit can be used to perform the standard quantum phase estimation algorithm, consisting of the following steps: #. Prepare ``target_wires`` in a given state. If ``target_wires`` are prepared in an eigenstate of :math:`U` that has corresponding eigenvalue :math:`e^{2 \pi i \theta}` with phase :math:`\theta \in [0, 1)`, this algorithm will measure :math:`\theta`. Other input states can be prepared more generally. #. Apply the ``QuantumPhaseEstimation`` circuit. #. Measure ``estimation_wires`` using :func:`~.probs`, giving a probability distribution over measurement outcomes in the computational basis. #. Find the index of the largest value in the probability distribution and divide that number by :math:`2^{n}`. This number will be an estimate of :math:`\theta` with an error that decreases exponentially with the number of qubits :math:`n`. Note that if :math:`\theta \in (-1, 0]`, we can estimate the phase by again finding the index :math:`i` found in step 4 and calculating :math:`\theta \approx \frac{1 - i}{2^{n}}`. The usage details below give an example of this case. Args: unitary (array): the phase estimation unitary, specified as a matrix target_wires (Union[Wires, Sequence[int], or int]): the target wires to apply the unitary estimation_wires (Union[Wires, Sequence[int], or int]): the wires to be used for phase estimation Raises: QuantumFunctionError: if the ``target_wires`` and ``estimation_wires`` share a common element .. UsageDetails:: Consider the matrix corresponding to a rotation from an :class:`~.RX` gate: .. code-block:: python import pennylane as qml from pennylane.templates import QuantumPhaseEstimation from pennylane import numpy as np phase = 5 target_wires = [0] unitary = qml.RX(phase, wires=0).matrix The ``phase`` parameter can be estimated using ``QuantumPhaseEstimation``. An example is shown below using a register of five phase-estimation qubits: .. code-block:: python n_estimation_wires = 5 estimation_wires = range(1, n_estimation_wires + 1) dev = qml.device("default.qubit", wires=n_estimation_wires + 1) @qml.qnode(dev) def circuit(): # Start in the |+> eigenstate of the unitary qml.Hadamard(wires=target_wires) QuantumPhaseEstimation( unitary, target_wires=target_wires, estimation_wires=estimation_wires, ) return qml.probs(estimation_wires) phase_estimated = np.argmax(circuit()) / 2 ** n_estimation_wires # Need to rescale phase due to convention of RX gate phase_estimated = 4 * np.pi * (1 - phase_estimated) """ target_wires = Wires(target_wires) estimation_wires = Wires(estimation_wires) if len(Wires.shared_wires([target_wires, estimation_wires])) != 0: raise qml.QuantumFunctionError( "The target wires and estimation wires must be different") unitary_powers = [unitary] for _ in range(len(estimation_wires) - 1): new_power = unitary_powers[-1] @ unitary_powers[-1] unitary_powers.append(new_power) for wire in estimation_wires: qml.Hadamard(wire) qml.ControlledQubitUnitary(unitary_powers.pop(), control_wires=wire, wires=target_wires) qml.QFT(wires=estimation_wires).inv()
def qfunc(): qml.PauliX(wires=2) qml.ControlledQubitUnitary(np.array([[0, 1], [1, 0]]), control_wires=0, wires=2) qml.PauliX(wires=2)