From f513b7f7bb16efb25b5db8c3e096788ecf1ed426 Mon Sep 17 00:00:00 2001 From: Guillermo Alonso-Linaje <65235481+KetpuntoG@users.noreply.github.com> Date: Thu, 25 Apr 2024 17:30:45 -0400 Subject: [PATCH] `qml.Qubitization` template (#5500) Adding `qml.Qubitization` operator. --------- Co-authored-by: soranjh <40344468+soranjh@users.noreply.github.com> Co-authored-by: Jay Soni Co-authored-by: Diego <67476785+DSGuala@users.noreply.github.com> Co-authored-by: Astral Cai --- .../qubitization/thumbnail_qubitization.png | Bin 0 -> 60240 bytes doc/introduction/templates.rst | 4 + doc/releases/changelog-dev.md | 32 +- pennylane/templates/subroutines/__init__.py | 1 + .../templates/subroutines/qubitization.py | 177 +++++++++ .../test_amplitude_amplification.py | 1 - .../test_subroutines/test_qubitization.py | 335 ++++++++++++++++++ 7 files changed, 548 insertions(+), 2 deletions(-) create mode 100644 doc/_static/templates/qubitization/thumbnail_qubitization.png create mode 100644 pennylane/templates/subroutines/qubitization.py create mode 100644 tests/templates/test_subroutines/test_qubitization.py diff --git a/doc/_static/templates/qubitization/thumbnail_qubitization.png b/doc/_static/templates/qubitization/thumbnail_qubitization.png new file mode 100644 index 0000000000000000000000000000000000000000..a6cf6b3c045ae081b7e88fac0bf30a0682ac63cb GIT binary patch literal 60240 zcmeFZc{r5q8$T>dXrU;QL@7(QLc~}qMUkack}WZoFk~D1q?Afo9$VH)9wb}Y8H`rT zFi1>ej8T@sm>CSl7&G&`r|0`D-{<*0$NT>C9`ErUZ;oS{yL;~Iy6@|}&g(qS&*wa^ z`;m>6+4e1axA5@rY(IbQ%q1Qk{!tztzAZwVfiuqA?8|w0ww?4gF|j#sVj^b~=$;okICr_HsSnfy>J83SYww!oF{?Mk=kB;rQJU{c|u8-u_ zul-kc3aH41en7wY%Dj2%l)dEE&?(C;GtE^|{%!u04LV$lqZdT!X@&CipLm;kF7Toy z&(a5h&yC4E(=%z86-u{0KPlP2Y4?exHHKK|g$w+5z|UEc;ZQ!grPWu3jwCpXQ|Bcd z$B5!d-t%x!fU#llP?YD6-O}ODygUyB$8HA3o=J2S(7&`t?!(r}6Sj|3rcO9LqAU4| zeLu0If#kSR&N{4k0+!k0h`l4~bFI|=bN>lOuIm_N>cX~6 zw<8}6zc0V^dRTXWEhnZlf524PzPkTRk>RZmeZ=`zyk%N9z44<9nQ5c{>-CT*9%F`1hs8@X!%>Af#ss9Jrw^YpTWViExubHRy2Crgud zdz`yBae1{UoWV!?r~`h~c=1R}m7L0f%UQDJ_xo%=h!XEQNj%0r;?sY(F|d;_Xr~b0 zy|kc?=D6=4!qU-#=TDu?4OF#Cnf&x}{&tWn7|3Ar6dW48?lAKmHBchSMxM6|_rm7i~a^4$w^{E0_AZk(xp zVYaPm@ww23aLSHJ(ZsDb%Uezzm_&+HcbCa`+AK>PF`c|qvV#~Kki3-ðx!DgX21 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 literal 0 HcmV?d00001 diff --git a/doc/introduction/templates.rst b/doc/introduction/templates.rst index ae57b9d228f..1cda874fcbc 100644 --- a/doc/introduction/templates.rst +++ b/doc/introduction/templates.rst @@ -299,6 +299,10 @@ Other useful templates which do not belong to the previous categories can be fou :description: :doc:`FABLE <../code/api/pennylane.FABLE>` :figure: _static/templates/subroutines/fable.png +.. gallery-item:: + :description: :doc:`Qubitization <../code/api/pennylane.Qubitization>` + :figure: _static/templates/qubitization/thumbnail_qubitization.png + .. raw:: html
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index ce323abc789..c4362e58abf 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -114,7 +114,37 @@ * The `qml.qchem.hf_state` function is upgraded to be compatible with the parity and Bravyi-Kitaev bases. [(#5472)](https://github.com/PennyLaneAI/pennylane/pull/5472) -

Calculate dynamical Lie algebras 👾

+ +* Added `qml.Qubitization` operator. This operator encodes a Hamiltonian into a suitable unitary operator. + When applied in conjunction with QPE, allows computing the eigenvalue of an eigenvector of the Hamiltonian. + [(#5500)](https://github.com/PennyLaneAI/pennylane/pull/5500) + + ```python + H = qml.dot([0.1, 0.3, -0.3], [qml.Z(0), qml.Z(1), qml.Z(0) @ qml.Z(2)]) + + @qml.qnode(qml.device("default.qubit")) + def circuit(): + + # initialize the eigenvector + qml.PauliX(2) + + # apply QPE + measurements = qml.iterative_qpe( + qml.Qubitization(H, control = [3,4]), ancilla = 5, iters = 3 + ) + return qml.probs(op = measurements) + + output = circuit() + + # post-processing + lamb = sum([abs(c) for c in H.terms()[0]]) + ``` + + ```pycon + >>> print("eigenvalue: ", lamb * np.cos(2 * np.pi * (np.argmax(output)) / 8)) + eigenvalue: 0.7 + ``` + * A new `qml.lie_closure` function to compute the Lie closure of a list of operators. [(#5161)](https://github.com/PennyLaneAI/pennylane/pull/5161) diff --git a/pennylane/templates/subroutines/__init__.py b/pennylane/templates/subroutines/__init__.py index 6946de03494..0e5a8b2caac 100644 --- a/pennylane/templates/subroutines/__init__.py +++ b/pennylane/templates/subroutines/__init__.py @@ -42,3 +42,4 @@ from .fable import FABLE from .reflection import Reflection from .amplitude_amplification import AmplitudeAmplification +from .qubitization import Qubitization diff --git a/pennylane/templates/subroutines/qubitization.py b/pennylane/templates/subroutines/qubitization.py new file mode 100644 index 00000000000..86036178868 --- /dev/null +++ b/pennylane/templates/subroutines/qubitization.py @@ -0,0 +1,177 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This submodule contains the template for Qubitization. +""" + +import copy +import pennylane as qml +from pennylane import numpy as np +from pennylane.operation import Operation + + +def _positive_coeffs_hamiltonian(hamiltonian): + """Transforms a Hamiltonian to ensure that the coefficients are positive. + + Args: + hamiltonian (Union[.Hamiltonian, .Sum, .Prod, .SProd, .LinearCombination]): The Hamiltonian written as a linear combination of unitaries. + + Returns: + list(float), list(.Operation): The coefficients and unitaries of the transformed Hamiltonian. + """ + + new_unitaries = [] + + coeffs, ops = hamiltonian.terms() + + for op, coeff in zip(ops, coeffs): + angle = np.pi * (0.5 * (1 - qml.math.sign(coeff))) + new_unitaries.append(op @ qml.GlobalPhase(angle, wires=op.wires)) + + return qml.math.abs(coeffs), new_unitaries + + +class Qubitization(Operation): + r"""Applies the `Qubitization `__ operator. + + This operator encodes a Hamiltonian, written as a linear combination of unitaries, into a unitary operator. + It is implemented with a quantum walk operator that takes a Hamiltonian as input and generates: + + .. math:: + Q = (2|0\rangle\langle 0| - I) \text{Prep}_{\mathcal{H}}^{\dagger} \text{Sel}_{\mathcal{H}} \text{Prep}_{\mathcal{H}}. + + + + .. seealso:: :class:`~.AmplitudeEmbedding` and :class:`~.Select`. + + Args: + hamiltonian (Union[.Hamiltonian, .Sum, .Prod, .SProd, .LinearCombination]): The Hamiltonian written as a linear combination of unitaries. + control (Iterable[Any], Wires): The control qubits for the Qubitization operator. + + **Example** + + This operator, when applied in conjunction with QPE, allows computing the eigenvalue of an eigenvector of the Hamiltonian. + + .. code-block:: + + H = qml.dot([0.1, 0.3, -0.3], [qml.Z(0), qml.Z(1), qml.Z(0) @ qml.Z(2)]) + + @qml.qnode(qml.device("default.qubit")) + def circuit(): + + # initiate the eigenvector + qml.PauliX(2) + + # apply QPE + measurements = qml.iterative_qpe( + qml.Qubitization(H, control = [3,4]), ancilla = 5, iters = 3 + ) + return qml.probs(op = measurements) + + output = circuit() + + # post-processing + lamb = sum([abs(c) for c in H.terms()[0]]) + + .. code-block:: pycon + + >>> print("eigenvalue: ", lamb * np.cos(2 * np.pi * (np.argmax(output)) / 8)) + eigenvalue: 0.7 + """ + + def __init__(self, hamiltonian, control, id=None): + wires = hamiltonian.wires + qml.wires.Wires(control) + + self._hyperparameters = { + "hamiltonian": hamiltonian, + "control": qml.wires.Wires(control), + } + + super().__init__(wires=wires, id=id) + + def _flatten(self): + data = (self.hyperparameters["hamiltonian"],) + metadata = tuple( + (key, value) for key, value in self.hyperparameters.items() if key != "hamiltonian" + ) + return data, metadata + + @classmethod + def _unflatten(cls, data, metadata): + hamiltonian = data[0] + hyperparams_dict = dict(metadata) + return cls(hamiltonian, **hyperparams_dict) + + def __copy__(self): + + clone = Qubitization.__new__(Qubitization) + + # Ensure the operators in the hyper-parameters are copied instead of aliased. + clone._hyperparameters = { + "hamiltonian": copy.copy(self._hyperparameters["hamiltonian"]), + "control": copy.copy(self._hyperparameters["control"]), + } + + for attr, value in vars(self).items(): + if attr != "_hyperparameters": + setattr(clone, attr, value) + + return clone + + @staticmethod + def compute_decomposition(*_, **kwargs): # pylint: disable=arguments-differ + r"""Representation of the operator as a product of other operators (static method). + + .. math:: O = O_1 O_2 \dots O_n. + + .. seealso:: :meth:`~.Qubitization.decomposition`. + + Args: + *params (list): trainable parameters of the operator, as stored in the ``parameters`` attribute + wires (Iterable[Any], Wires): wires that the operator acts on + **hyperparams (dict): non-trainable hyperparameters of the operator, as stored in the ``hyperparameters`` attribute + + Returns: + list[Operator]: decomposition of the operator + + **Example:** + + >>> print(qml.Qubitization.compute_decomposition(hamiltonian = 0.1 * qml.Z(0), control = 1)) + [AmplitudeEmbedding(array([1., 0.]), wires=[1]), Select(ops=(Z(0),), control=), Adjoint(AmplitudeEmbedding(array([1., 0.]), wires=[1])), Reflection(, wires=[0])] + + """ + + hamiltonian = kwargs["hamiltonian"] + control = kwargs["control"] + + coeffs, unitaries = _positive_coeffs_hamiltonian(hamiltonian) + + decomp_ops = [] + + decomp_ops.append( + qml.AmplitudeEmbedding(qml.math.sqrt(coeffs), normalize=True, pad_with=0, wires=control) + ) + + decomp_ops.append(qml.Select(unitaries, control=control)) + decomp_ops.append( + qml.adjoint( + qml.AmplitudeEmbedding( + qml.math.sqrt(coeffs), normalize=True, pad_with=0, wires=control + ) + ) + ) + + decomp_ops.append(qml.Reflection(qml.Identity(control))) + + return decomp_ops diff --git a/tests/templates/test_subroutines/test_amplitude_amplification.py b/tests/templates/test_subroutines/test_amplitude_amplification.py index aa46bd8b4be..1ecfa3cc282 100644 --- a/tests/templates/test_subroutines/test_amplitude_amplification.py +++ b/tests/templates/test_subroutines/test_amplitude_amplification.py @@ -156,7 +156,6 @@ def test_qnode_autograd(self): params = qml.numpy.array(self.params, requires_grad=True) res = qml.grad(qnode)(params) - print(res) assert qml.math.shape(res) == (2,) assert np.allclose(res, self.exp_grad, atol=1e-5) diff --git a/tests/templates/test_subroutines/test_qubitization.py b/tests/templates/test_subroutines/test_qubitization.py new file mode 100644 index 00000000000..a8eeb82ae34 --- /dev/null +++ b/tests/templates/test_subroutines/test_qubitization.py @@ -0,0 +1,335 @@ +# Copyright 2018-2024 Xanadu Quantum Technologies Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Tests for the Qubitization template. +""" + +import copy +import pytest +import pennylane as qml +from pennylane import numpy as np + +from pennylane.templates.subroutines.qubitization import _positive_coeffs_hamiltonian + + +@pytest.mark.parametrize( + "hamiltonian, expected_unitaries", + ( + ( + qml.ops.LinearCombination( + np.array([1, -1, 2]), [qml.PauliX(0), qml.PauliY(0), qml.PauliZ(0)] + ), + [ + qml.PauliX(0) @ qml.GlobalPhase(np.array([0.0]), wires=0), + qml.PauliY(0) @ qml.GlobalPhase(np.array(np.pi), wires=0), + qml.PauliZ(0) @ qml.GlobalPhase(np.array([0.0]), wires=0), + ], + ), + ( + qml.ops.LinearCombination( + np.array([1.0, 1.0, 2.0]), [qml.PauliX(0), qml.PauliY(0), qml.PauliZ(0)] + ), + [ + qml.PauliX(0) @ qml.GlobalPhase(np.array([0.0]), wires=0), + qml.PauliY(0) @ qml.GlobalPhase(np.array([0.0]), wires=0), + qml.PauliZ(0) @ qml.GlobalPhase(np.array([0.0]), wires=0), + ], + ), + ( + qml.ops.LinearCombination( + np.array([-0.2, -0.6, 2.1]), [qml.PauliX(0), qml.PauliY(0), qml.PauliZ(0)] + ), + [ + qml.PauliX(0) @ qml.GlobalPhase(np.array(np.pi), wires=0), + qml.PauliY(0) @ qml.GlobalPhase(np.array(np.pi), wires=0), + qml.PauliZ(0) @ qml.GlobalPhase(np.array(0), wires=0), + ], + ), + ), +) +def test_positive_coeffs_hamiltonian(hamiltonian, expected_unitaries): + """Tests that the function _positive_coeffs_hamiltonian correctly transforms the Hamiltonian""" + + new_coeffs, new_unitaries = _positive_coeffs_hamiltonian(hamiltonian) + + assert np.allclose(new_coeffs, np.abs(hamiltonian.terms()[0])) + + for i, unitary in enumerate(new_unitaries): + assert qml.equal(expected_unitaries[i], unitary) + + +@pytest.mark.parametrize( + "hamiltonian", + [ + qml.dot([0.2, -0.5, 0.3], [qml.Y(0) @ qml.X(1), qml.Z(1), qml.X(0) @ qml.Z(2)]), + qml.dot([0.3, -0.5, 0.3], [qml.Z(0) @ qml.X(1), qml.X(1), qml.X(0) @ qml.Y(2)]), + qml.dot([0.4, -0.5, -0.3], [qml.Z(0) @ qml.X(2), qml.Y(0), qml.X(1) @ qml.Z(2)]), + ], +) +def test_operator_definition_qpe(hamiltonian): + """Tests that Qubitization can be used in QPE to obtain the eigenvalues of a Hamiltonian.""" + + from scipy.signal import find_peaks + + @qml.qnode(qml.device("default.qubit")) + def circuit(theta): + + # initial state + qml.RX(theta[2], wires=0) + qml.CRY(theta[3], wires=[0, 2]) + qml.RY(theta[0], wires=2) + qml.CRY(theta[4], wires=[1, 2]) + qml.RX(theta[1], wires=1) + qml.CRX(theta[2], wires=[2, 0]) + + # apply QPE (used iterative qpe here) + measurements = qml.iterative_qpe( + qml.Qubitization(hamiltonian, control=[3, 4]), ancilla=5, iters=8 + ) + + return qml.probs(op=measurements) + + theta = np.array([0.1, 0.2, 0.3, 0.4, 0.5]) * 100 + + peaks, _ = find_peaks(circuit(theta)) + + # Calculates the eigenvalues from the obtained output + lamb = sum([abs(c) for c in hamiltonian.terms()[0]]) + estimated_eigenvalues = lamb * np.cos(2 * np.pi * peaks / 2**8) + + assert np.allclose(np.sort(estimated_eigenvalues), qml.eigvals(hamiltonian), atol=0.1) + + +def test_standard_validity(): + """Check the operation using the assert_valid function.""" + H = qml.dot([0.1, -0.3, -0.3], [qml.X(0), qml.Z(1), qml.Y(0) @ qml.Z(2)]) + op = qml.Qubitization(H, control=[3, 4]) + qml.ops.functions.assert_valid(op) + + +@pytest.mark.usefixtures("use_legacy_and_new_opmath") +def test_legacy_new_opmath(): + coeffs, ops = [0.1, -0.3, -0.3], [qml.X(0), qml.Z(1), qml.Y(0) @ qml.Z(2)] + + H1 = qml.dot(coeffs, ops) + matrix_H1 = qml.matrix(qml.Qubitization(H1, control=[3, 4]), wire_order=[3, 4, 0, 1, 2]) + + H2 = qml.Hamiltonian(coeffs, ops) + matrix_H2 = qml.matrix(qml.Qubitization(H2, control=[3, 4]), wire_order=[3, 4, 0, 1, 2]) + + assert np.allclose(matrix_H1, matrix_H2) + + +@pytest.mark.parametrize( + "hamiltonian, expected_decomposition", + ( + ( + qml.ops.LinearCombination(np.array([1.0, 1.0]), [qml.PauliX(0), qml.PauliZ(0)]), + [ + qml.AmplitudeEmbedding(np.array([1.0, 1.0]) / np.sqrt(2), wires=[1]), + qml.Select( + ops=( + qml.PauliX(0) @ qml.GlobalPhase(np.array(0.0), wires=0), + qml.PauliZ(0) @ qml.GlobalPhase(np.array(0.0), wires=0), + ), + control=[1], + ), + qml.adjoint(qml.AmplitudeEmbedding(np.array([1.0, 1.0]) / np.sqrt(2), wires=[1])), + qml.Reflection(qml.Identity(wires=[1])), + ], + ), + ( + qml.ops.LinearCombination(np.array([-1.0, 1.0]), [qml.PauliX(0), qml.PauliZ(0)]), + [ + qml.AmplitudeEmbedding(np.array([1.0, 1.0]) / np.sqrt(2), wires=[1]), + qml.Select( + ops=( + qml.PauliX(0) @ qml.GlobalPhase(np.array(np.pi), wires=0), + qml.PauliZ(0) @ qml.GlobalPhase(np.array(0.0), wires=0), + ), + control=[1], + ), + qml.adjoint(qml.AmplitudeEmbedding(np.array([1.0, 1.0]) / np.sqrt(2), wires=[1])), + qml.Reflection(qml.Identity(wires=[1])), + ], + ), + ), +) +def test_decomposition(hamiltonian, expected_decomposition): + """Tests that the Qubitization template is correctly decomposed.""" + + decomposition = qml.Qubitization.compute_decomposition(hamiltonian=hamiltonian, control=[1]) + + for i, op in enumerate(decomposition): + assert qml.equal(op, expected_decomposition[i]) + + +def test_lightning_qubit(): + H = qml.ops.LinearCombination([0.1, 0.3, -0.3], [qml.Z(0), qml.Z(1), qml.Z(0) @ qml.Z(2)]) + + @qml.qnode(qml.device("lightning.qubit", wires=5)) + def circuit_lightning(): + qml.Hadamard(wires=0) + qml.Qubitization(H, control=[3, 4]) + return qml.expval(qml.PauliZ(0) @ qml.PauliZ(4)) + + @qml.qnode(qml.device("default.qubit", wires=5)) + def circuit_default(): + qml.Hadamard(wires=0) + qml.Qubitization(H, control=[3, 4]) + return qml.expval(qml.PauliZ(0) @ qml.PauliZ(4)) + + assert np.allclose(circuit_lightning(), circuit_default()) + + +class TestDifferentiability: + """Test that Qubitization is differentiable""" + + @staticmethod + def circuit(coeffs): + H = qml.ops.LinearCombination( + coeffs, [qml.Y(0), qml.Y(1) @ qml.Y(2), qml.X(0), qml.X(1) @ qml.X(2)] + ) + qml.Qubitization(H, control=(3, 4)) + return qml.expval(qml.PauliZ(3) @ qml.PauliZ(4)) + + # calculated numerically with finite diff method (h = 1e-4) + exp_grad = np.array([0.41177729, -0.21262358, 1.64370464, -0.74256522]) + + params = np.array([0.4, 0.5, 0.1, 0.3]) + + @pytest.mark.autograd + def test_qnode_autograd(self): + """Test that the QNode executes with Autograd.""" + + dev = qml.device("default.qubit") + qnode = qml.QNode(self.circuit, dev, interface="autograd") + + params = qml.numpy.array(self.params, requires_grad=True) + res = qml.grad(qnode)(params) + assert qml.math.shape(res) == (4,) + assert np.allclose(res, self.exp_grad, atol=1e-5) + + @pytest.mark.jax + @pytest.mark.parametrize( + "use_jit , shots", + ((False, None), (True, None), (False, 50000)), + ) # TODO: (True, 50000) fails because jax.jit on jax.grad does not work with AmplitudeEmbedding + def test_qnode_jax(self, shots, use_jit): + """ "Test that the QNode executes and is differentiable with JAX. The shots + argument controls whether autodiff or parameter-shift gradients are used.""" + import jax + + jax.config.update("jax_enable_x64", True) + + dev = qml.device("default.qubit", shots=shots, seed=10) + diff_method = "backprop" if shots is None else "parameter-shift" + qnode = qml.QNode(self.circuit, dev, interface="jax", diff_method=diff_method) + if use_jit: + qnode = jax.jit(qnode) + + params = jax.numpy.array(self.params) + + jac_fn = jax.jacobian(qnode) + if use_jit: + jac_fn = jax.jit(jac_fn) + + jac = jac_fn(params) + assert jac.shape == (4,) + assert np.allclose(jac, self.exp_grad, atol=0.01) + + @pytest.mark.torch + @pytest.mark.parametrize( + "shots", [None] + ) # TODO: finite shots fails because Prod is not currently differentiable. + def test_qnode_torch(self, shots): + """ "Test that the QNode executes and is differentiable with Torch. The shots + argument controls whether autodiff or parameter-shift gradients are used.""" + import torch + + dev = qml.device("default.qubit", shots=shots, seed=10) + diff_method = "backprop" if shots is None else "parameter-shift" + qnode = qml.QNode(self.circuit, dev, interface="torch", diff_method=diff_method) + + params = torch.tensor(self.params, requires_grad=True) + jac = torch.autograd.functional.jacobian(qnode, params) + assert qml.math.shape(jac) == (4,) + assert qml.math.allclose(jac, self.exp_grad, atol=0.01) + + @pytest.mark.tf + @pytest.mark.parametrize("shots", [None, 50000]) + @pytest.mark.xfail(reason="tf gradient doesn't seem to be working, returns ()") + def test_qnode_tf(self, shots): + """ "Test that the QNode executes and is differentiable with TensorFlow. The shots + argument controls whether autodiff or parameter-shift gradients are used.""" + import tensorflow as tf + + dev = qml.device("default.qubit", shots=shots, seed=10) + diff_method = "backprop" if shots is None else "parameter-shift" + qnode = qml.QNode(self.circuit, dev, interface="tf", diff_method=diff_method) + + params = tf.Variable(self.params) + with tf.GradientTape() as tape: + res = qnode(params) + + jac = tape.gradient(res, params) + assert qml.math.shape(jac) == (4,) + assert qml.math.allclose(res, self.exp_grad, atol=0.001) + + @pytest.mark.xfail(reason="see https://github.com/PennyLaneAI/pennylane/issues/5507") + @pytest.mark.usefixtures("use_legacy_and_new_opmath") + def test_legacy_new_opmath_diff(self): + coeffs, ops = np.array([0.1, -0.3, -0.3]), [qml.X(0), qml.Z(1), qml.Y(0) @ qml.Z(2)] + + dev = qml.device("default.qubit") + + @qml.qnode(dev) + def circuit_dot(coeffs): + H = qml.dot(coeffs, ops) + qml.Qubitization(H, control=[3, 4]) + return qml.expval(qml.PauliZ(0)) + + @qml.qnode(dev) + def circuit_Hamiltonian(coeffs): + H = qml.Hamiltonian(coeffs, ops) + qml.Qubitization(H, control=[3, 4]) + return qml.expval(qml.PauliZ(0)) + + assert np.allclose(qml.grad(circuit_dot)(coeffs), qml.grad(circuit_Hamiltonian)(coeffs)) + + +def test_copy(): + """Test that a Qubitization operator can be copied.""" + + H = qml.dot([1.0, 2.0], [qml.PauliX(0), qml.PauliZ(1)]) + + orig_op = qml.Qubitization(H, control=[2, 3]) + copy_op = copy.copy(orig_op) + assert qml.equal(orig_op, copy_op) + + # Ensure the (nested) operations are copied instead of aliased. + assert orig_op is not copy_op + assert orig_op.hyperparameters["hamiltonian"] is not copy_op.hyperparameters["hamiltonian"] + assert orig_op.hyperparameters["control"] is not copy_op.hyperparameters["control"] + + +def test_map_wires(): + """Test that a Qubitization operator can be mapped to a different wire mapping.""" + + H = qml.dot([1.0, 2.0], [qml.PauliX(0), qml.PauliZ(1)]) + + op = qml.Qubitization(H, control=[2, 3]) + op2 = op.map_wires({0: 5, 1: 6, 2: 7, 3: 8}) + + assert op2.wires == qml.wires.Wires([5, 6, 7, 8])