From e548bd9717c19394c6106dcec2540bb38b3283e5 Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Sat, 13 Jul 2024 01:47:01 -0400 Subject: [PATCH 01/38] added warnings for legacy devices and warning capture fixture --- pennylane/devices/default_qubit_autograd.py | 13 ++++++ pennylane/devices/default_qubit_jax.py | 12 ++++++ pennylane/devices/default_qubit_legacy.py | 12 +++++- pennylane/devices/default_qubit_tf.py | 13 +++++- pennylane/devices/default_qubit_torch.py | 13 +++++- tests/conftest.py | 11 +++++ tests/devices/test_default_qubit_legacy.py | 3 +- .../test_default_qubit_legacy_broadcasting.py | 3 +- tests/test_debugging.py | 42 ------------------- tests/test_qnode_legacy.py | 32 ++++++++------ 10 files changed, 93 insertions(+), 61 deletions(-) diff --git a/pennylane/devices/default_qubit_autograd.py b/pennylane/devices/default_qubit_autograd.py index 5ad6b629625..363beb0a5dc 100644 --- a/pennylane/devices/default_qubit_autograd.py +++ b/pennylane/devices/default_qubit_autograd.py @@ -14,6 +14,9 @@ """This module contains an autograd implementation of the :class:`~.DefaultQubitLegacy` reference plugin. """ +import warnings + +from pennylane import PennyLaneDeprecationWarning from pennylane import numpy as pnp from pennylane.devices import DefaultQubitLegacy @@ -34,6 +37,9 @@ class DefaultQubitAutograd(DefaultQubitLegacy): pip install autograd + .. warning:: + This device is deprecated. Use :class:`~.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``. + **Example** The ``default.qubit.autograd`` is designed to be used with end-to-end classical backpropagation @@ -104,6 +110,13 @@ def _const_mul(constant, array): return constant * array def __init__(self, wires, *, shots=None, analytic=None): + warnings.warn( + f"Use of '{self.short_name}' is deprecated. Instead, use 'default.qubit'. " + "If you experience issues, reach out to the PennyLane team on " + "the discussion forum: https://discuss.pennylane.ai/", + PennyLaneDeprecationWarning, + ) + r_dtype = pnp.float64 c_dtype = pnp.complex128 super().__init__(wires, shots=shots, r_dtype=r_dtype, c_dtype=c_dtype, analytic=analytic) diff --git a/pennylane/devices/default_qubit_jax.py b/pennylane/devices/default_qubit_jax.py index f349b8ff7c9..e9a1db8b113 100644 --- a/pennylane/devices/default_qubit_jax.py +++ b/pennylane/devices/default_qubit_jax.py @@ -14,6 +14,8 @@ """This module contains a jax implementation of the :class:`~.DefaultQubitLegacy` reference plugin. """ +import warnings + # pylint: disable=ungrouped-imports import numpy as np @@ -49,6 +51,9 @@ class DefaultQubitJax(DefaultQubitLegacy): pip install jax jaxlib + .. warning:: + This device is deprecated. Use :class:`~.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``. + **Example** The ``default.qubit.jax`` device is designed to be used with end-to-end classical backpropagation @@ -165,6 +170,13 @@ def circuit(): operations = DefaultQubitLegacy.operations.union({"ParametrizedEvolution"}) def __init__(self, wires, *, shots=None, prng_key=None, analytic=None): + warnings.warn( + f"Use of '{self.short_name}' is deprecated. Instead, use 'default.qubit'. " + "If you experience issues, reach out to the PennyLane team on " + "the discussion forum: https://discuss.pennylane.ai/", + qml.PennyLaneDeprecationWarning, + ) + if jax.config.read("jax_enable_x64"): c_dtype = jnp.complex128 r_dtype = jnp.float64 diff --git a/pennylane/devices/default_qubit_legacy.py b/pennylane/devices/default_qubit_legacy.py index 426a0fe31d4..8370e869526 100644 --- a/pennylane/devices/default_qubit_legacy.py +++ b/pennylane/devices/default_qubit_legacy.py @@ -20,6 +20,7 @@ """ import functools import itertools +import warnings from string import ascii_letters as ABC from typing import List @@ -82,8 +83,8 @@ class DefaultQubitLegacy(QubitDevice): .. warning:: - This is the legacy implementation of DefaultQubit. It has been replaced by - ``qml.devices.DefaultQubit``, which can be accessed with the familiar constructor, + This is the legacy implementation of DefaultQubit and is deprecated. It has been replaced by + :class:`~.devices.DefaultQubit`, which can be accessed with the familiar constructor, ``qml.device("default.qubit")``. This change will not alter device behaviour for most workflows, but may have implications for @@ -207,6 +208,13 @@ class DefaultQubitLegacy(QubitDevice): def __init__( self, wires, *, r_dtype=np.float64, c_dtype=np.complex128, shots=None, analytic=None ): + warnings.warn( + f"Use of '{self.short_name}' is deprecated. Instead, use 'default.qubit'. " + "If you experience issues, reach out to the PennyLane team on " + "the discussion forum: https://discuss.pennylane.ai/", + qml.PennyLaneDeprecationWarning, + ) + super().__init__(wires, shots, r_dtype=r_dtype, c_dtype=c_dtype, analytic=analytic) self._debugger = None diff --git a/pennylane/devices/default_qubit_tf.py b/pennylane/devices/default_qubit_tf.py index 48d7c60b788..75749798622 100644 --- a/pennylane/devices/default_qubit_tf.py +++ b/pennylane/devices/default_qubit_tf.py @@ -15,6 +15,7 @@ reference plugin. """ import itertools +import warnings import numpy as np from packaging.version import Version @@ -42,7 +43,7 @@ class DefaultQubitTF(DefaultQubitLegacy): - """Simulator plugin based on ``"default.qubit.legacy"``, written using TensorFlow. + r"""Simulator plugin based on ``"default.qubit.legacy"``, written using TensorFlow. **Short name:** ``default.qubit.tf`` @@ -57,6 +58,9 @@ class DefaultQubitTF(DefaultQubitLegacy): pip install tensorflow>=2.0 + .. warning:: + This device is deprecated. Use :class:`~.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``. + **Example** The ``default.qubit.tf`` is designed to be used with end-to-end classical backpropagation @@ -162,6 +166,13 @@ def _asarray(array, dtype=None): return res def __init__(self, wires, *, shots=None, analytic=None): + warnings.warn( + f"Use of '{self.short_name}' is deprecated. Instead, use 'default.qubit'. " + "If you experience issues, reach out to the PennyLane team on " + "the discussion forum: https://discuss.pennylane.ai/", + qml.PennyLaneDeprecationWarning, + ) + r_dtype = tf.float64 c_dtype = tf.complex128 diff --git a/pennylane/devices/default_qubit_torch.py b/pennylane/devices/default_qubit_torch.py index e0bb276e513..b8d825937a6 100644 --- a/pennylane/devices/default_qubit_torch.py +++ b/pennylane/devices/default_qubit_torch.py @@ -34,6 +34,7 @@ import numpy as np +from pennylane import PennyLaneDeprecationWarning from pennylane.ops.qubit.attributes import diagonal_in_z_basis from . import DefaultQubitLegacy @@ -43,7 +44,7 @@ class DefaultQubitTorch(DefaultQubitLegacy): - """Simulator plugin based on ``"default.qubit.legacy"``, written using PyTorch. + r"""Simulator plugin based on ``"default.qubit.legacy"``, written using PyTorch. **Short name:** ``default.qubit.torch`` @@ -58,6 +59,10 @@ class DefaultQubitTorch(DefaultQubitLegacy): pip install torch>=1.8.0 + .. warning:: + This device is deprecated. Use :class:`~.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``. + + **Example** The ``default.qubit.torch`` is designed to be used with end-to-end classical backpropagation @@ -165,6 +170,12 @@ def circuit(x): _ndim = staticmethod(lambda tensor: tensor.ndim) def __init__(self, wires, *, shots=None, analytic=None, torch_device=None): + warnings.warn( + f"Use of '{self.short_name}' is deprecated. Instead, use 'default.qubit'. " + "If you experience issues, reach out to the PennyLane team on " + "the discussion forum: https://discuss.pennylane.ai/", + PennyLaneDeprecationWarning, + ) # Store if the user specified a Torch device. Otherwise the execute # method attempts to infer the Torch device from the gate parameters. self._torch_device_specified = torch_device is not None diff --git a/tests/conftest.py b/tests/conftest.py index 4c8eba9dc6d..e058f5e90be 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -49,6 +49,17 @@ def set_numpy_seed(): yield +@pytest.fixture(scope="function", autouse=True) +def capture_legacy_device_deprecation_warnings(recwarn): + yield + + for w in recwarn: + if isinstance(w.message, qml.PennyLaneDeprecationWarning): + assert "Use of 'default.qubit." in str(w.message) + assert "is deprecated" in str(w.message) + assert "use 'default.qubit'" in str(w.message) + + @pytest.fixture(scope="session") def tol(): """Numerical tolerance for equality tests.""" diff --git a/tests/devices/test_default_qubit_legacy.py b/tests/devices/test_default_qubit_legacy.py index ec9fd6e65f5..be8e586adcc 100644 --- a/tests/devices/test_default_qubit_legacy.py +++ b/tests/devices/test_default_qubit_legacy.py @@ -2001,7 +2001,8 @@ class TestApplyOps: gates in DefaultQubitLegacy.""" state = np.arange(2**4, dtype=np.complex128).reshape((2, 2, 2, 2)) - dev = qml.device("default.qubit.legacy", wires=4) + with pytest.warns(qml.PennyLaneDeprecationWarning): + dev = qml.device("default.qubit.legacy", wires=4) single_qubit_ops = [ (qml.PauliX, dev._apply_x), diff --git a/tests/devices/test_default_qubit_legacy_broadcasting.py b/tests/devices/test_default_qubit_legacy_broadcasting.py index ebf21502971..14d741d0423 100644 --- a/tests/devices/test_default_qubit_legacy_broadcasting.py +++ b/tests/devices/test_default_qubit_legacy_broadcasting.py @@ -1619,7 +1619,8 @@ class TestApplyOpsBroadcasted: gates in DefaultQubitLegacy.""" broadcasted_state = np.arange(2**4 * 3, dtype=np.complex128).reshape((3, 2, 2, 2, 2)) - dev = qml.device("default.qubit.legacy", wires=4) + with pytest.warns(qml.PennyLaneDeprecationWarning): + dev = qml.device("default.qubit.legacy", wires=4) single_qubit_ops = [ (qml.PauliX, dev._apply_x), diff --git a/tests/test_debugging.py b/tests/test_debugging.py index 207b97bca04..f93502235fc 100644 --- a/tests/test_debugging.py +++ b/tests/test_debugging.py @@ -273,48 +273,6 @@ def circuit(): _compare_numpy_dicts(result, expected) - @pytest.mark.parametrize("diff_method", [None, "backprop", "parameter-shift", "adjoint"]) - def test_default_qubit_legacy_only_supports_state(self, diff_method): - dev = qml.device("default.qubit.legacy", wires=2) - - assert qml.debugging.snapshot._is_snapshot_compatible(dev) - - @qml.qnode(dev, diff_method=diff_method) - def circuit_faulty(): - qml.Hadamard(wires=0) - qml.Snapshot("important_expval", measurement=qml.expval(qml.PauliX(0))) - qml.CNOT(wires=[0, 1]) - qml.Snapshot() - return qml.expval(qml.PauliX(0)) - - circuit_faulty() - assert dev._debugger is None - if diff_method is not None: - assert circuit_faulty.interface == "auto" - - with pytest.raises(NotImplementedError, match="only supports `qml.state` measurements"): - qml.snapshots(circuit_faulty)() - - @qml.qnode(dev, diff_method=diff_method) - def circuit(): - qml.Hadamard(wires=0) - qml.CNOT(wires=[0, 1]) - qml.Snapshot() - return qml.expval(qml.PauliX(0)) - - expected = { - 0: np.array([1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)]), - "execution_results": np.array(0), - } - - result = qml.snapshots(circuit)() - _compare_numpy_dicts(result, expected) - - if diff_method not in ("backprop", "adjoint"): - result_shots = qml.snapshots(circuit)(shots=200) - expected["execution_results"] = np.array(-0.04) - _compare_numpy_dicts(result_shots, expected) - # pylint: disable=protected-access @pytest.mark.parametrize("method", [None, "parameter-shift"]) def test_default_mixed(self, method): diff --git a/tests/test_qnode_legacy.py b/tests/test_qnode_legacy.py index c98fc3f05fd..5627a455c86 100644 --- a/tests/test_qnode_legacy.py +++ b/tests/test_qnode_legacy.py @@ -491,13 +491,14 @@ def test_adjoint_finite_shots(self): dev = qml.device("default.qubit.legacy", wires=1, shots=1) - @qnode(dev, diff_method="adjoint") - def circ(): - return qml.expval(qml.PauliZ(0)) - with pytest.warns( UserWarning, match="Requested adjoint differentiation to be computed with finite shots." ): + + @qnode(dev, diff_method="adjoint") + def circ(): + return qml.expval(qml.PauliZ(0)) + circ() @pytest.mark.autograd @@ -621,7 +622,8 @@ def circuit(params): } def test_autograd_interface_device_switched_no_warnings(self): - """Test that checks that no warning is raised for device switch when you define an interface.""" + """Test that checks that no warning is raised for device switch when you define an interface, + except for thee deprecation warnings.""" dev = qml.device("default.qubit.legacy", wires=1) @qml.qnode(dev, interface="autograd") @@ -632,14 +634,18 @@ def circuit(params): with warnings.catch_warnings(record=True) as record: circuit(qml.numpy.array(0.1, requires_grad=True)) - assert len(record) == 0 + # Two warnings. One for the device and one for the interface + assert len(record) == 2 + assert all(isinstance(w.message, qml.PennyLaneDeprecationWarning) for w in record) def test_not_giving_mode_kwarg_does_not_raise_warning(self): - """Test that not providing a value for mode does not raise a warning.""" + """Test that not providing a value for mode does not raise a warning + except for the deprecation warning.""" with warnings.catch_warnings(record=True) as record: - _ = qml.QNode(lambda f: f, qml.device("default.qubit.legacy", wires=1)) + qml.QNode(lambda f: f, qml.device("default.qubit.legacy", wires=1)) - assert len(record) == 0 + assert len(record) == 1 + assert isinstance(record[0].message, qml.PennyLaneDeprecationWarning) class TestTapeConstruction: @@ -1089,9 +1095,7 @@ def circuit(): assert len(circuit.tape.operations) == 2 assert isinstance(circuit.tape.operations[1], qml.measurements.MidMeasureMP) - @pytest.mark.parametrize( - "dev", [qml.device("default.qubit", wires=3), qml.device("default.qubit.legacy", wires=3)] - ) + @pytest.mark.parametrize("dev_name", ["default.qubit", "default.qubit.legacy"]) @pytest.mark.parametrize("first_par", np.linspace(0.15, np.pi - 0.3, 3)) @pytest.mark.parametrize("sec_par", np.linspace(0.15, np.pi - 0.3, 3)) @pytest.mark.parametrize( @@ -1106,12 +1110,14 @@ def circuit(): ], ) def test_defer_meas_if_mcm_unsupported( - self, dev, first_par, sec_par, return_type, mv_return, mv_res, mocker + self, dev_name, first_par, sec_par, return_type, mv_return, mv_res, mocker ): # pylint: disable=too-many-arguments """Tests that the transform using the deferred measurement principle is applied if the device doesn't support mid-circuit measurements natively.""" + dev = qml.device(dev_name, wires=3) + @qml.qnode(dev) def cry_qnode(x, y): """QNode where we apply a controlled Y-rotation.""" From bd31a81357dd11150993932fafa322b9be79e16d Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Sat, 13 Jul 2024 02:06:39 -0400 Subject: [PATCH 02/38] minor fixes to failing tests --- tests/drawer/test_draw.py | 16 ++++--------- tests/drawer/test_draw_mpl.py | 24 ++++++------------- tests/interfaces/test_jacobian_products.py | 3 ++- .../test_transform_program_integration.py | 11 +++++---- tests/ops/qubit/test_observables.py | 8 +++---- tests/resource/test_specs.py | 13 ++++++---- tests/test_qnode.py | 8 +++---- 7 files changed, 35 insertions(+), 48 deletions(-) diff --git a/tests/drawer/test_draw.py b/tests/drawer/test_draw.py index 83e153b4401..c8560bc2d4b 100644 --- a/tests/drawer/test_draw.py +++ b/tests/drawer/test_draw.py @@ -878,13 +878,11 @@ def circ(): class TestLevelExpansionStrategy: - @pytest.fixture( - params=[qml.device("default.qubit.legacy", wires=3), qml.devices.DefaultQubit()], - ) - def transforms_circuit(self, request): + @pytest.fixture + def transforms_circuit(self): @qml.transforms.merge_rotations @qml.transforms.cancel_inverses - @qml.qnode(request.param, diff_method="parameter-shift") + @qml.qnode(qml.device("default.qubit"), diff_method="parameter-shift") def circ(weights, order): qml.RandomLayers(weights, wires=(0, 1)) qml.Permute(order, wires=(0, 1, 2)) @@ -1015,11 +1013,7 @@ def circ(): assert draw(circ)() == expected -@pytest.mark.parametrize( - "device", - [qml.device("default.qubit.legacy", wires=2), qml.device("default.qubit", wires=2)], -) -def test_applied_transforms(device): +def test_applied_transforms(): """Test that any transforms applied to the qnode are included in the output.""" @qml.transform @@ -1028,7 +1022,7 @@ def just_pauli_x(_): return (new_tape,), lambda res: res[0] @just_pauli_x - @qml.qnode(device) + @qml.qnode(qml.device("default.qubit", wires=2)) def my_circuit(x): qml.RX(x, wires=0) qml.SWAP(wires=(0, 1)) diff --git a/tests/drawer/test_draw_mpl.py b/tests/drawer/test_draw_mpl.py index 2a8d2c83330..c0c3a466c43 100644 --- a/tests/drawer/test_draw_mpl.py +++ b/tests/drawer/test_draw_mpl.py @@ -81,13 +81,11 @@ def test_fig_argument(): class TestLevelExpansionStrategy: - @pytest.fixture( - params=[qml.device("default.qubit.legacy", wires=3), qml.devices.DefaultQubit()], - ) - def transforms_circuit(self, request): + @pytest.fixture + def transforms_circuit(self): @qml.transforms.merge_rotations @qml.transforms.cancel_inverses - @qml.qnode(request.param, diff_method="parameter-shift") + @qml.qnode(qml.device("default.qubit"), diff_method="parameter-shift") def circ(weights, order): qml.RandomLayers(weights, wires=(0, 1)) qml.Permute(order, wires=(0, 1, 2)) @@ -125,18 +123,14 @@ def test_equivalent_levels(self, transforms_circuit, levels, expected_metadata): plt.close("all") - @pytest.mark.parametrize( - "device", - [qml.device("default.qubit.legacy", wires=3), qml.devices.DefaultQubit(wires=3)], - ) @pytest.mark.parametrize( "strategy, initial_strategy, n_lines", [("gradient", "device", 3), ("device", "gradient", 13)], ) - def test_expansion_strategy(self, device, strategy, initial_strategy, n_lines): + def test_expansion_strategy(self, strategy, initial_strategy, n_lines): """Test that the expansion strategy keyword controls what operations are drawn.""" - @qml.qnode(device, expansion_strategy=initial_strategy) + @qml.qnode(qml.device("default.qubit"), expansion_strategy=initial_strategy) def circuit(): qml.Permute([2, 0, 1], wires=(0, 1, 2)) return qml.expval(qml.PauliZ(0)) @@ -499,11 +493,7 @@ def circuit(): assert ax.texts[-1].get_text() == "X†" -@pytest.mark.parametrize( - "device", - [qml.device("default.qubit.legacy", wires=2), qml.device("default.qubit", wires=2)], -) -def test_applied_transforms(device): +def test_applied_transforms(): """Test that any transforms applied to the qnode are included in the output.""" @qml.transform @@ -512,7 +502,7 @@ def just_pauli_x(_): return (new_tape,), lambda res: res[0] @just_pauli_x - @qml.qnode(device) + @qml.qnode(qml.device("default.qubit", wires=2)) def my_circuit(): qml.SWAP(wires=(0, 1)) qml.CNOT(wires=(0, 1)) diff --git a/tests/interfaces/test_jacobian_products.py b/tests/interfaces/test_jacobian_products.py index 07f3ccb60ab..44510e71879 100644 --- a/tests/interfaces/test_jacobian_products.py +++ b/tests/interfaces/test_jacobian_products.py @@ -31,7 +31,8 @@ ) dev = qml.device("default.qubit") -dev_old = qml.device("default.qubit.legacy", wires=5) +with pytest.warns(qml.PennyLaneDeprecationWarning): + dev_old = qml.device("default.qubit.legacy", wires=5) dev_lightning = qml.device("lightning.qubit", wires=5) adjoint_config = qml.devices.ExecutionConfig(gradient_method="adjoint") dev_ps = ParamShiftDerivativesDevice() diff --git a/tests/interfaces/test_transform_program_integration.py b/tests/interfaces/test_transform_program_integration.py index 6d254f953dc..4e82f725f54 100644 --- a/tests/interfaces/test_transform_program_integration.py +++ b/tests/interfaces/test_transform_program_integration.py @@ -24,11 +24,12 @@ import pennylane as qml -device_suite = ( - qml.device("default.qubit.legacy", wires=5), - qml.devices.DefaultQubit(), - qml.device("lightning.qubit", wires=5), -) +with pytest.warns(qml.PennyLaneDeprecationWarning): + device_suite = ( + qml.device("default.qubit.legacy", wires=5), + qml.devices.DefaultQubit(), + qml.device("lightning.qubit", wires=5), + ) @pytest.mark.all_interfaces diff --git a/tests/ops/qubit/test_observables.py b/tests/ops/qubit/test_observables.py index 50aa733fa68..3e291fe74c0 100644 --- a/tests/ops/qubit/test_observables.py +++ b/tests/ops/qubit/test_observables.py @@ -676,10 +676,10 @@ def test_matrix_representation(self, basis_state, expected, n_wires, tol): assert np.allclose(res_dynamic, expected, atol=tol) assert np.allclose(res_static, expected, atol=tol) - @pytest.mark.parametrize( - "dev", (qml.device("default.qubit"), qml.device("default.qubit.legacy", wires=1)) - ) - def test_integration_batched_state(self, dev): + @pytest.mark.parametrize("dev_name", ("default.qubit", "default.qubit.legacy")) + def test_integration_batched_state(self, dev_name): + dev = qml.device(dev_name, wires=1) + @qml.qnode(dev) def circuit(x): qml.RX(x, wires=0) diff --git a/tests/resource/test_specs.py b/tests/resource/test_specs.py index 2dd69be8dd3..d66e2013e4c 100644 --- a/tests/resource/test_specs.py +++ b/tests/resource/test_specs.py @@ -21,6 +21,13 @@ import pennylane as qml from pennylane import numpy as pnp +with pytest.warns(qml.PennyLaneDeprecationWarning): + devices_list = [ + (qml.device("default.qubit"), 1), + (qml.device("default.qubit", wires=2), 2), + (qml.device("default.qubit.legacy", wires=2), 2), + ] + class TestSpecsTransform: """Tests for the transform specs using the QNode""" @@ -317,11 +324,7 @@ def circuit(): @pytest.mark.parametrize( "device,num_wires", - [ - (qml.device("default.qubit"), 1), - (qml.device("default.qubit", wires=2), 2), - (qml.device("default.qubit.legacy", wires=2), 2), - ], + devices_list, ) def test_num_wires_source_of_truth(self, device, num_wires): """Tests that num_wires behaves differently on old and new devices.""" diff --git a/tests/test_qnode.py b/tests/test_qnode.py index 966f6d31467..ae94fd8e10c 100644 --- a/tests/test_qnode.py +++ b/tests/test_qnode.py @@ -878,10 +878,7 @@ def circuit(x, y): assert np.allclose(res, expected, atol=tol, rtol=0) - @pytest.mark.parametrize( - "dev", - [qml.device("default.qubit", wires=3), qml.device("default.qubit.legacy", wires=3)], - ) + @pytest.mark.parametrize("dev_name", ["default.qubit", "default.qubit.legacy"]) @pytest.mark.parametrize("first_par", np.linspace(0.15, np.pi - 0.3, 3)) @pytest.mark.parametrize("sec_par", np.linspace(0.15, np.pi - 0.3, 3)) @pytest.mark.parametrize( @@ -896,11 +893,12 @@ def circuit(x, y): ], ) def test_defer_meas_if_mcm_unsupported( - self, dev, first_par, sec_par, return_type, mv_return, mv_res, mocker + self, dev_name, first_par, sec_par, return_type, mv_return, mv_res, mocker ): # pylint: disable=too-many-arguments """Tests that the transform using the deferred measurement principle is applied if the device doesn't support mid-circuit measurements natively.""" + dev = qml.device(dev_name, wires=3) @qml.qnode(dev) def cry_qnode(x, y): From 8302975b1a24594945804fb84e72c36d414aa911 Mon Sep 17 00:00:00 2001 From: Ahmed Darwish Date: Mon, 15 Jul 2024 11:06:35 -0400 Subject: [PATCH 03/38] Update pennylane/devices/default_qubit_autograd.py Co-authored-by: Thomas R. Bromley <49409390+trbromley@users.noreply.github.com> --- pennylane/devices/default_qubit_autograd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/devices/default_qubit_autograd.py b/pennylane/devices/default_qubit_autograd.py index 363beb0a5dc..577c5a6878e 100644 --- a/pennylane/devices/default_qubit_autograd.py +++ b/pennylane/devices/default_qubit_autograd.py @@ -38,7 +38,7 @@ class DefaultQubitAutograd(DefaultQubitLegacy): pip install autograd .. warning:: - This device is deprecated. Use :class:`~.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``. + This device is deprecated. Use :class:`~.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``, which now supports backpropagation. **Example** From dc894731c668adfbbb946d2c0ea65c74152ebc07 Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Mon, 15 Jul 2024 11:17:07 -0400 Subject: [PATCH 04/38] improved warnings messages for the deprecated devices --- pennylane/devices/default_qubit_autograd.py | 5 +++-- pennylane/devices/default_qubit_jax.py | 7 ++++--- pennylane/devices/default_qubit_legacy.py | 7 ++++--- pennylane/devices/default_qubit_tf.py | 5 +++-- pennylane/devices/default_qubit_torch.py | 5 +++-- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/pennylane/devices/default_qubit_autograd.py b/pennylane/devices/default_qubit_autograd.py index 577c5a6878e..ef228327d54 100644 --- a/pennylane/devices/default_qubit_autograd.py +++ b/pennylane/devices/default_qubit_autograd.py @@ -22,7 +22,7 @@ class DefaultQubitAutograd(DefaultQubitLegacy): - """Simulator plugin based on ``"default.qubit.legacy"``, written using Autograd. + r"""Simulator plugin based on ``"default.qubit.legacy"``, written using Autograd. **Short name:** ``default.qubit.autograd`` @@ -111,7 +111,8 @@ def _const_mul(constant, array): def __init__(self, wires, *, shots=None, analytic=None): warnings.warn( - f"Use of '{self.short_name}' is deprecated. Instead, use 'default.qubit'. " + f"Use of '{self.short_name}' is deprecated. Instead, use 'default.qubit', " + "which supports backpropagation. " "If you experience issues, reach out to the PennyLane team on " "the discussion forum: https://discuss.pennylane.ai/", PennyLaneDeprecationWarning, diff --git a/pennylane/devices/default_qubit_jax.py b/pennylane/devices/default_qubit_jax.py index e9a1db8b113..68e0da05b0a 100644 --- a/pennylane/devices/default_qubit_jax.py +++ b/pennylane/devices/default_qubit_jax.py @@ -36,7 +36,7 @@ class DefaultQubitJax(DefaultQubitLegacy): - """Simulator plugin based on ``"default.qubit.legacy"``, written using jax. + r"""Simulator plugin based on ``"default.qubit.legacy"``, written using jax. **Short name:** ``default.qubit.jax`` @@ -52,7 +52,7 @@ class DefaultQubitJax(DefaultQubitLegacy): pip install jax jaxlib .. warning:: - This device is deprecated. Use :class:`~.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``. + This device is deprecated. Use :class:`~.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``, which now supports backpropagation. **Example** @@ -171,7 +171,8 @@ def circuit(): def __init__(self, wires, *, shots=None, prng_key=None, analytic=None): warnings.warn( - f"Use of '{self.short_name}' is deprecated. Instead, use 'default.qubit'. " + f"Use of '{self.short_name}' is deprecated. Instead, use 'default.qubit', " + "which supports backpropagation. " "If you experience issues, reach out to the PennyLane team on " "the discussion forum: https://discuss.pennylane.ai/", qml.PennyLaneDeprecationWarning, diff --git a/pennylane/devices/default_qubit_legacy.py b/pennylane/devices/default_qubit_legacy.py index 8370e869526..49c7d31fbe2 100644 --- a/pennylane/devices/default_qubit_legacy.py +++ b/pennylane/devices/default_qubit_legacy.py @@ -79,13 +79,13 @@ def _get_slice(index, axis, num_axes): # pylint: disable=unused-argument class DefaultQubitLegacy(QubitDevice): - """Default qubit device for PennyLane. + r"""Default qubit device for PennyLane. .. warning:: This is the legacy implementation of DefaultQubit and is deprecated. It has been replaced by :class:`~.devices.DefaultQubit`, which can be accessed with the familiar constructor, - ``qml.device("default.qubit")``. + ``qml.device("default.qubit")``, and now supports backpropagation. This change will not alter device behaviour for most workflows, but may have implications for plugin developers and users who directly interact with device methods. Please consult @@ -209,7 +209,8 @@ def __init__( self, wires, *, r_dtype=np.float64, c_dtype=np.complex128, shots=None, analytic=None ): warnings.warn( - f"Use of '{self.short_name}' is deprecated. Instead, use 'default.qubit'. " + f"Use of '{self.short_name}' is deprecated. Instead, use 'default.qubit', " + "which supports backpropagation. " "If you experience issues, reach out to the PennyLane team on " "the discussion forum: https://discuss.pennylane.ai/", qml.PennyLaneDeprecationWarning, diff --git a/pennylane/devices/default_qubit_tf.py b/pennylane/devices/default_qubit_tf.py index 75749798622..160af64c0d9 100644 --- a/pennylane/devices/default_qubit_tf.py +++ b/pennylane/devices/default_qubit_tf.py @@ -59,7 +59,7 @@ class DefaultQubitTF(DefaultQubitLegacy): pip install tensorflow>=2.0 .. warning:: - This device is deprecated. Use :class:`~.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``. + This device is deprecated. Use :class:`~.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``, which now supports backpropagation. **Example** @@ -167,7 +167,8 @@ def _asarray(array, dtype=None): def __init__(self, wires, *, shots=None, analytic=None): warnings.warn( - f"Use of '{self.short_name}' is deprecated. Instead, use 'default.qubit'. " + f"Use of '{self.short_name}' is deprecated. Instead, use 'default.qubit', " + "which supports backpropagation. " "If you experience issues, reach out to the PennyLane team on " "the discussion forum: https://discuss.pennylane.ai/", qml.PennyLaneDeprecationWarning, diff --git a/pennylane/devices/default_qubit_torch.py b/pennylane/devices/default_qubit_torch.py index b8d825937a6..59253c2c4cc 100644 --- a/pennylane/devices/default_qubit_torch.py +++ b/pennylane/devices/default_qubit_torch.py @@ -60,7 +60,7 @@ class DefaultQubitTorch(DefaultQubitLegacy): pip install torch>=1.8.0 .. warning:: - This device is deprecated. Use :class:`~.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``. + This device is deprecated. Use :class:`~.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``, which now supports backpropagation. **Example** @@ -171,7 +171,8 @@ def circuit(x): def __init__(self, wires, *, shots=None, analytic=None, torch_device=None): warnings.warn( - f"Use of '{self.short_name}' is deprecated. Instead, use 'default.qubit'. " + f"Use of '{self.short_name}' is deprecated. Instead, use 'default.qubit', " + "which supports backpropagation. " "If you experience issues, reach out to the PennyLane team on " "the discussion forum: https://discuss.pennylane.ai/", PennyLaneDeprecationWarning, From 632c006328454f74765e39c518ad668224d329d0 Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Mon, 15 Jul 2024 11:30:32 -0400 Subject: [PATCH 05/38] more fixes to failing tests --- tests/conftest.py | 3 ++- tests/ops/op_math/test_linear_combination.py | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index e058f5e90be..3812ade33f3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -92,7 +92,8 @@ def n_subsystems_fixture(request): @pytest.fixture(scope="session") def qubit_device(n_subsystems): - return qml.device("default.qubit.legacy", wires=n_subsystems) + with pytest.warns(qml.PennyLaneDeprecationWarning, match="Use of 'default.qubit.legacy'"): + return qml.device("default.qubit.legacy", wires=n_subsystems) @pytest.fixture(scope="function", params=[(np.float32, np.complex64), (np.float64, np.complex128)]) diff --git a/tests/ops/op_math/test_linear_combination.py b/tests/ops/op_math/test_linear_combination.py index c6e1584d19c..3c9b065bc41 100644 --- a/tests/ops/op_math/test_linear_combination.py +++ b/tests/ops/op_math/test_linear_combination.py @@ -53,13 +53,15 @@ def test_isinstance_Hamiltonian(self): assert isinstance(H, qml.Hamiltonian) -@pytest.mark.filterwarnings( - "ignore:Using 'qml.ops.Hamiltonian' with new operator arithmetic is deprecated" -) def test_mixed_legacy_warning_Hamiltonian(): """Test that mixing legacy ops and LinearCombination.compare raises a warning""" op1 = qml.ops.LinearCombination([0.5, 0.5], [X(0) @ X(1), qml.Hadamard(0)]) - op2 = qml.ops.Hamiltonian([0.5, 0.5], [qml.operation.Tensor(X(0), X(1)), qml.Hadamard(0)]) + + with pytest.warns( + qml.PennyLaneDeprecationWarning, + match="Using 'qml.ops.Hamiltonian' with new operator arithmetic is deprecated", + ): + op2 = qml.ops.Hamiltonian([0.5, 0.5], [qml.operation.Tensor(X(0), X(1)), qml.Hadamard(0)]) with pytest.warns(UserWarning, match="Attempting to compare a legacy operator class instance"): res = op1.compare(op2) From e98becbea2dea58e60e0b5223df16cfc95604d2b Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Tue, 16 Jul 2024 15:10:59 -0400 Subject: [PATCH 06/38] update device to default.qubit in pulse examples --- pennylane/devices/qubit/simulate.py | 2 -- pennylane/gradients/pulse_gradient.py | 2 +- pennylane/gradients/pulse_gradient_odegen.py | 2 +- pennylane/ops/functions/evolve.py | 15 +++++---- pennylane/pulse/__init__.py | 8 +++-- pennylane/pulse/convenience_functions.py | 13 +++++--- pennylane/pulse/hardware_hamiltonian.py | 26 ++++++++-------- pennylane/pulse/parametrized_evolution.py | 32 +++++++++++--------- pennylane/pulse/rydberg.py | 14 +++++++-- pennylane/pulse/transmon.py | 6 +++- pennylane/tape/qscript.py | 2 +- pennylane/workflow/set_shots.py | 7 ++--- 12 files changed, 76 insertions(+), 53 deletions(-) diff --git a/pennylane/devices/qubit/simulate.py b/pennylane/devices/qubit/simulate.py index e55741b029d..9017e300316 100644 --- a/pennylane/devices/qubit/simulate.py +++ b/pennylane/devices/qubit/simulate.py @@ -230,7 +230,6 @@ def measure_final_state(circuit, state, is_state_batched, **execution_kwargs) -> mid_measurements = execution_kwargs.get("mid_measurements", None) # analytic case - if not circuit.shots: if mid_measurements is not None: raise TypeError("Native mid-circuit measurements are only supported with finite shots.") @@ -243,7 +242,6 @@ def measure_final_state(circuit, state, is_state_batched, **execution_kwargs) -> ) # finite-shot case - rng = default_rng(rng) results = measure_with_samples( circuit.measurements, diff --git a/pennylane/gradients/pulse_gradient.py b/pennylane/gradients/pulse_gradient.py index 2cc0852fbb7..0b8f9635f5f 100644 --- a/pennylane/gradients/pulse_gradient.py +++ b/pennylane/gradients/pulse_gradient.py @@ -392,7 +392,7 @@ def stoch_pulse_grad( jax.config.update("jax_enable_x64", True) - dev = qml.device("default.qubit.jax") + dev = qml.device("default.qubit") def sin(p, t): return jax.numpy.sin(p * t) diff --git a/pennylane/gradients/pulse_gradient_odegen.py b/pennylane/gradients/pulse_gradient_odegen.py index 4e899b33314..e84d35b0131 100644 --- a/pennylane/gradients/pulse_gradient_odegen.py +++ b/pennylane/gradients/pulse_gradient_odegen.py @@ -484,7 +484,7 @@ def pulse_odegen( .. code-block:: python - dev = qml.device("default.qubit.jax") + dev = qml.device("default.qubit") @qml.qnode(dev, interface="jax", diff_method=qml.gradients.pulse_odegen) def circuit(params): diff --git a/pennylane/ops/functions/evolve.py b/pennylane/ops/functions/evolve.py index c7d2722b9b2..96660aea77e 100644 --- a/pennylane/ops/functions/evolve.py +++ b/pennylane/ops/functions/evolve.py @@ -129,7 +129,10 @@ def evolve(*args, **kwargs): # pylint: disable=unused-argument import jax - dev = qml.device("default.qubit.jax", wires=4) + jax.config.update("jax_enable_x64", True) + + dev = qml.device("default.qubit") + @jax.jit @qml.qnode(dev, interface="jax") def circuit(params): @@ -138,13 +141,13 @@ def circuit(params): >>> params = [1., 2., 3., 4.] >>> circuit(params) - Array(0.8627419, dtype=float32) + Array(0.86231063, dtype=float64) >>> jax.grad(circuit)(params) - [Array(50.690746, dtype=float32), - Array(-6.296886e-05, dtype=float32), - Array(-6.3341584e-05, dtype=float32), - Array(-7.052516e-05, dtype=float32)] + [Array(50.391273, dtype=float64), + Array(-9.42415807e-05, dtype=float64), + Array(-0.0001049, dtype=float64), + Array(-0.00010601, dtype=float64)] .. note:: In the example above, the decorator ``@jax.jit`` is used to compile this execution just-in-time. This means diff --git a/pennylane/pulse/__init__.py b/pennylane/pulse/__init__.py index a5396032ccd..8bbafb5af6d 100644 --- a/pennylane/pulse/__init__.py +++ b/pennylane/pulse/__init__.py @@ -236,7 +236,9 @@ import jax - dev = qml.device("default.qubit.jax", wires=1) + jax.config.update("jax_enable_x64", True) + + dev = qml.device("default.qubit", wires=1) @jax.jit @qml.qnode(dev, interface="jax") @@ -246,10 +248,10 @@ def circuit(params): >>> params = [1.2] >>> circuit(params) -Array(0.96632576, dtype=float32) +Array(0.96632722, dtype=float64) >>> jax.grad(circuit)(params) -[Array(2.3569832, dtype=float32)] +[Array(2.35694829, dtype=float64)] We can use the decorator ``jax.jit`` to compile this execution just-in-time. This means the first execution will typically take a little longer with the benefit that all following executions will be significantly faster. diff --git a/pennylane/pulse/convenience_functions.py b/pennylane/pulse/convenience_functions.py index c1ef567a1c8..89f739bee90 100644 --- a/pennylane/pulse/convenience_functions.py +++ b/pennylane/pulse/convenience_functions.py @@ -25,7 +25,7 @@ # pylint: disable=unused-argument def constant(scalar, time): - """Returns the given ``scalar``, for use in defining a :class:`~.ParametrizedHamiltonian` with a + r"""Returns the given ``scalar``, for use in defining a :class:`~.ParametrizedHamiltonian` with a trainable coefficient. Args: @@ -57,7 +57,12 @@ def constant(scalar, time): .. code-block:: python - dev = qml.device("default.qubit.jax", wires=1) + import jax + + jax.config.update("jax_enable_x64", True) + + dev = qml.device("default.qubit") + @qml.qnode(dev, interface="jax") def circuit(params): qml.evolve(H)(params, t=2) @@ -66,10 +71,10 @@ def circuit(params): >>> params = jnp.array([5.0]) >>> circuit(params) - Array(0.40808904, dtype=float32) + Array(0.40808193, dtype=float64) >>> jax.grad(circuit)(params) - Array([-3.6517754], dtype=float32) + Array([-3.65178003], dtype=float64) """ return scalar diff --git a/pennylane/pulse/hardware_hamiltonian.py b/pennylane/pulse/hardware_hamiltonian.py index e54073f93de..57006e24dca 100644 --- a/pennylane/pulse/hardware_hamiltonian.py +++ b/pennylane/pulse/hardware_hamiltonian.py @@ -95,7 +95,9 @@ def drive(amplitude, phase, wires): .. code-block:: python3 - dev = qml.device("default.qubit.jax", wires=wires) + jax.config.update("jax_enable_x64", True) + + dev = qml.device("default.qubit", wires=wires) @qml.qnode(dev, interface="jax") def circuit(params): @@ -104,9 +106,9 @@ def circuit(params): >>> params = [2.4] >>> circuit(params) - Array(0.32495208, dtype=float64) + Array(-0.17375104, dtype=float64) >>> jax.grad(circuit)(params) - [Array(1.31956098, dtype=float64)] + [Array(13.66916253, dtype=float64, weak_type=True)] We can also create a Hamiltonian with multiple local drives. The following circuit corresponds to the evolution where an additional local drive that changes in time is acting on wires ``[0, 1]`` is added to the Hamiltonian: @@ -131,12 +133,12 @@ def circuit_local(params): params = (p_global, p_amp, p_phase) >>> circuit_local(params) - Array(-0.5334795, dtype=float64) + Array(0.37385014, dtype=float64) >>> jax.grad(circuit_local)(params) - (Array(0.01654573, dtype=float64), - [Array(-0.04422795, dtype=float64, weak_type=True), - Array(-0.51375441, dtype=float64, weak_type=True)], - Array(0.21901967, dtype=float64)) + (Array(-3.35835837, dtype=float64), + [Array(-3.35835837, dtype=float64, weak_type=True), + Array(-3.35835837, dtype=float64, weak_type=True)], + Array(0.1339487, dtype=float64)) .. details:: :title: Theoretical background @@ -188,7 +190,7 @@ def circuit_local(params): H_d = qml.pulse.drive(amplitude, phase, wires) # detuning term - H_z = qml.dot([-3*np.pi/4]*len(wires), [qml.Z(i) for i in wires]) + H_z = qml.dot([-3*jnp.pi/4]*len(wires), [qml.Z(i) for i in wires]) The total Hamiltonian of that evolution is given by @@ -201,7 +203,7 @@ def circuit_local(params): .. code-block:: python3 - dev = qml.device("default.qubit.jax", wires=wires) + dev = qml.device("default.qubit", wires=wires) @qml.qnode(dev, interface="jax") def circuit(params): qml.evolve(H_i + H_z + H_d)(params, t=[0, 10]) @@ -209,9 +211,9 @@ def circuit(params): >>> params = [2.4] >>> circuit(params) - Array(0.6962041, dtype=float64) + Array(0.96347734, dtype=float64) >>> jax.grad(circuit)(params) - [Array(1.75825695, dtype=float64)] + [Array(-0.4311521, dtype=float64, weak_type=True)] """ wires = Wires(wires) diff --git a/pennylane/pulse/parametrized_evolution.py b/pennylane/pulse/parametrized_evolution.py index 3a0dfa08649..65fc9748fbe 100644 --- a/pennylane/pulse/parametrized_evolution.py +++ b/pennylane/pulse/parametrized_evolution.py @@ -137,7 +137,9 @@ class ParametrizedEvolution(Operation): import jax - dev = qml.device("default.qubit.jax", wires=1) + jax.config.update("jax_enable_x64", True) + + dev = qml.device("default.qubit", wires=1) @jax.jit @qml.qnode(dev, interface="jax") def circuit(params): @@ -146,10 +148,10 @@ def circuit(params): >>> params = [1.2] >>> circuit(params) - Array(0.96632576, dtype=float32) + Array(0.96632722, dtype=float64) >>> jax.grad(circuit)(params) - [Array(2.3569832, dtype=float32)] + [Array(2.35694829, dtype=float64)] .. note:: In the example above, the decorator ``@jax.jit`` is used to compile this execution just-in-time. This means @@ -223,7 +225,7 @@ def f2(p, t): .. code-block:: python - dev = qml.device("default.qubit.jax", wires=3) + dev = qml.device("default.qubit", wires=3) @qml.qnode(dev, interface="jax") def circuit1(params): @@ -245,11 +247,11 @@ def circuit2(params): >>> params = jnp.array([1., 2., 3.]) >>> circuit1(params) - Array(-0.01543971, dtype=float32) + Array(-0.01542578, dtype=float64) >>> params = jnp.concatenate([params, params]) # H1 + H2 requires 6 parameters! >>> circuit2(params) - Array(-0.78236955, dtype=float32) + Array(-0.78235162, dtype=float64) Here, ``circuit1`` is not executing the evolution of ``H1`` and ``H2`` simultaneously, but rather executing ``H1`` in the ``[0, 10]`` time window and then executing ``H2`` with the same time window, @@ -268,10 +270,10 @@ def circuit(params): return qml.expval(qml.Z(0) @ qml.Z(1) @ qml.Z(2)) >>> circuit(params) - Array(-0.78236955, dtype=float32) + Array(-0.78235162, dtype=float64) >>> jax.grad(circuit)(params) - Array([-4.8066125 , 3.703827 , -1.3297377 , -2.406232 , 0.6811726 , - -0.52277344], dtype=float32) + Array([-4.80708632, 3.70323783, -1.32958799, -2.40642477, 0.68105214, + -0.52269657], dtype=float64) Given that we used the same time window (``[0, 10]``), the results are the same as before. @@ -311,7 +313,7 @@ def circuit(params): .. code-block:: python - dev = qml.device("default.qubit.jax", wires=1) + dev = qml.device("default.qubit", wires=1) @qml.qnode(dev, interface="jax") def circuit(param, time): @@ -320,11 +322,11 @@ def circuit(param, time): >>> circuit(param, time) Array([[1. , 0. ], - [0.9897738 , 0.01022595], - [0.9599043 , 0.04009585], - [0.9123617 , 0.08763832], - [0.84996957, 0.15003097], - [0.7761489 , 0.22385144]], dtype=float32) + [0.98977406, 0.01022594], + [0.95990416, 0.04009584], + [0.91236167, 0.08763833], + [0.84996865, 0.15003133], + [0.77614817, 0.22385181]], dtype=float64) **Computing complementary time evolution** diff --git a/pennylane/pulse/rydberg.py b/pennylane/pulse/rydberg.py index 761c61ffdc8..2059d3e9fd5 100644 --- a/pennylane/pulse/rydberg.py +++ b/pennylane/pulse/rydberg.py @@ -86,7 +86,11 @@ def rydberg_interaction( .. code-block:: python - dev = qml.device("default.qubit.jax", wires=9) + import jax + + jax.config.update("jax_enable_x64", True) + + dev = qml.device("default.qubit", wires=9) @qml.qnode(dev, interface="jax") def circuit(): @@ -94,7 +98,7 @@ def circuit(): return qml.expval(qml.Z(0)) >>> circuit() - Array(1., dtype=float32) + Array(1., dtype=float64) """ if wires is None: wires = list(range(len(register))) @@ -208,7 +212,11 @@ def rydberg_drive(amplitude, phase, detuning, wires): .. code-block:: python - dev = qml.device("default.qubit.jax", wires=wires) + import jax + + jax.config.update("jax_enable_x64", True) + + dev = qml.device("default.qubit", wires=wires) @qml.qnode(dev, interface="jax") def circuit(params): qml.evolve(H_i + H_d)(params, t=[0, 0.5]) diff --git a/pennylane/pulse/transmon.py b/pennylane/pulse/transmon.py index 15662340764..67c0e14b119 100644 --- a/pennylane/pulse/transmon.py +++ b/pennylane/pulse/transmon.py @@ -350,6 +350,10 @@ def phase(phi0, t): .. code-block:: python3 + import jax + + jax.config.update("jax_enable_x64", True) + qubit_freqs = [5.1, 5., 5.3] connections = [[0, 1], [1, 2]] # qubits 0 and 1 are coupled, as are 1 and 2 g = [0.02, 0.05] @@ -363,7 +367,7 @@ def amp(max_amp, t): return max_amp * jnp.sin(t) ** 2 for q in range(3): H += qml.pulse.transmon_drive(amp, phase, freq, q) # Parametrized drive for each qubit - dev = qml.device("default.qubit.jax", wires=range(3)) + dev = qml.device("default.qubit", wires=range(3)) @jax.jit @qml.qnode(dev, interface="jax") diff --git a/pennylane/tape/qscript.py b/pennylane/tape/qscript.py index dbd8e2aa7ff..56cb2aa32c5 100644 --- a/pennylane/tape/qscript.py +++ b/pennylane/tape/qscript.py @@ -537,7 +537,7 @@ def data(self): @property def trainable_params(self): - """Store or return a list containing the indices of parameters that support + r"""Store or return a list containing the indices of parameters that support differentiability. The indices provided match the order of appearence in the quantum circuit. diff --git a/pennylane/workflow/set_shots.py b/pennylane/workflow/set_shots.py index bb463233fed..1bc7dff7f33 100644 --- a/pennylane/workflow/set_shots.py +++ b/pennylane/workflow/set_shots.py @@ -24,15 +24,14 @@ @contextlib.contextmanager def set_shots(device, shots): - """Context manager to temporarily change the shots - of a device. + r"""Context manager to temporarily change the shots of a device. This context manager can be used in two ways. As a standard context manager: >>> dev = qml.device("default.qubit.legacy", wires=2, shots=None) - >>> with set_shots(dev, shots=100): + >>> with qml.workflow.set_shots(dev, shots=100): ... print(dev.shots) 100 >>> print(dev.shots) @@ -40,7 +39,7 @@ def set_shots(device, shots): Or as a decorator that acts on a function that uses the device: - >>> set_shots(dev, shots=100)(lambda: dev.shots)() + >>> qml.workflow.set_shots(dev, shots=100)(lambda: dev.shots)() 100 """ if isinstance(device, qml.devices.Device): From e8038473c5cb22b98499b3453921d987356cb948 Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Tue, 16 Jul 2024 15:18:09 -0400 Subject: [PATCH 07/38] update tests to use default.qubit and add missing tests to new device interface --- .../default_tensor/test_default_tensor.py | 2 +- tests/gpu/test_gpu_torch.py | 12 +- .../gradients/core/test_hadamard_gradient.py | 64 +- tests/gradients/core/test_metric_tensor.py | 212 +--- .../test_autograd.py} | 0 .../test_autograd_qnode.py} | 0 .../test_autograd_qnode_shot_vector.py | 647 +++++++++++++ .../test_execute.py} | 0 .../test_jax.py} | 0 .../default_qubit_integration/test_jax_jit.py | 913 ++++++++++++++++++ .../test_jax_jit_qnode.py} | 0 .../test_jax_qnode.py} | 0 .../test_jax_qnode_shot_vector.py | 883 +++++++++++++++++ .../test_set_shots.py | 31 + .../test_tensorflow.py} | 0 ...tensorflow_autograph_qnode_shot_vector.py} | 0 .../test_tensorflow_qnode.py} | 0 .../test_tensorflow_qnode_shot_vector.py} | 0 .../test_torch.py} | 0 .../test_torch_qnode.py} | 0 .../test_autograd_legacy.py} | 0 .../test_autograd_qnode_legacy.py} | 0 ...test_autograd_qnode_shot_vector_legacy.py} | 0 .../test_execute_legacy.py} | 0 .../test_jax_jit_legacy.py} | 0 .../test_jax_jit_qnode_legacy.py} | 0 .../test_jax_legacy.py} | 0 .../test_jax_qnode_legacy.py} | 0 .../test_jax_qnode_shot_vector_legacy.py} | 0 .../test_set_shots_legacy.py} | 11 - ...low_autograph_qnode_shot_vector_legacy.py} | 0 .../test_tensorflow_legacy.py} | 0 .../test_tensorflow_qnode_legacy.py} | 0 ...st_tensorflow_qnode_shot_vector_legacy.py} | 0 .../test_torch_legacy.py} | 0 .../test_torch_qnode_legacy.py} | 0 .../test_transform_program_integration.py | 2 +- tests/math/test_functions.py | 2 +- tests/measurements/test_state.py | 30 +- tests/ops/op_math/test_evolution.py | 2 +- tests/ops/qubit/test_parametric_ops.py | 4 +- tests/ops/qubit/test_qchem_ops.py | 100 +- tests/pulse/test_transmon.py | 2 +- tests/qnn/test_keras.py | 12 +- tests/qnn/test_qnn_torch.py | 10 +- tests/test_device.py | 6 +- tests/test_operation.py | 2 +- tests/test_qubit_device.py | 30 +- tests/transforms/test_qcut.py | 2 +- 49 files changed, 2625 insertions(+), 354 deletions(-) rename tests/interfaces/{default_qubit_2_integration/test_autograd_default_qubit_2.py => default_qubit_integration/test_autograd.py} (100%) rename tests/interfaces/{default_qubit_2_integration/test_autograd_qnode_default_qubit_2.py => default_qubit_integration/test_autograd_qnode.py} (100%) create mode 100644 tests/interfaces/default_qubit_integration/test_autograd_qnode_shot_vector.py rename tests/interfaces/{default_qubit_2_integration/test_execute_default_qubit_2.py => default_qubit_integration/test_execute.py} (100%) rename tests/interfaces/{default_qubit_2_integration/test_jax_default_qubit_2.py => default_qubit_integration/test_jax.py} (100%) create mode 100644 tests/interfaces/default_qubit_integration/test_jax_jit.py rename tests/interfaces/{default_qubit_2_integration/test_jax_jit_qnode_default_qubit_2.py => default_qubit_integration/test_jax_jit_qnode.py} (100%) rename tests/interfaces/{default_qubit_2_integration/test_jax_qnode_default_qubit_2.py => default_qubit_integration/test_jax_qnode.py} (100%) create mode 100644 tests/interfaces/default_qubit_integration/test_jax_qnode_shot_vector.py create mode 100644 tests/interfaces/default_qubit_integration/test_set_shots.py rename tests/interfaces/{default_qubit_2_integration/test_tensorflow_default_qubit_2.py => default_qubit_integration/test_tensorflow.py} (100%) rename tests/interfaces/{default_qubit_2_integration/test_tensorflow_autograph_qnode_shot_vector_default_qubit_2.py => default_qubit_integration/test_tensorflow_autograph_qnode_shot_vector.py} (100%) rename tests/interfaces/{default_qubit_2_integration/test_tensorflow_qnode_default_qubit_2.py => default_qubit_integration/test_tensorflow_qnode.py} (100%) rename tests/interfaces/{default_qubit_2_integration/test_tensorflow_qnode_shot_vector_default_qubit_2.py => default_qubit_integration/test_tensorflow_qnode_shot_vector.py} (100%) rename tests/interfaces/{default_qubit_2_integration/test_torch_default_qubit_2.py => default_qubit_integration/test_torch.py} (100%) rename tests/interfaces/{default_qubit_2_integration/test_torch_qnode_default_qubit_2.py => default_qubit_integration/test_torch_qnode.py} (100%) rename tests/interfaces/{test_autograd.py => legacy_devices_integration/test_autograd_legacy.py} (100%) rename tests/interfaces/{test_autograd_qnode.py => legacy_devices_integration/test_autograd_qnode_legacy.py} (100%) rename tests/interfaces/{test_autograd_qnode_shot_vector.py => legacy_devices_integration/test_autograd_qnode_shot_vector_legacy.py} (100%) rename tests/interfaces/{test_execute.py => legacy_devices_integration/test_execute_legacy.py} (100%) rename tests/interfaces/{test_jax_jit.py => legacy_devices_integration/test_jax_jit_legacy.py} (100%) rename tests/interfaces/{test_jax_jit_qnode.py => legacy_devices_integration/test_jax_jit_qnode_legacy.py} (100%) rename tests/interfaces/{test_jax.py => legacy_devices_integration/test_jax_legacy.py} (100%) rename tests/interfaces/{test_jax_qnode.py => legacy_devices_integration/test_jax_qnode_legacy.py} (100%) rename tests/interfaces/{test_jax_qnode_shot_vector.py => legacy_devices_integration/test_jax_qnode_shot_vector_legacy.py} (100%) rename tests/interfaces/{test_set_shots.py => legacy_devices_integration/test_set_shots_legacy.py} (83%) rename tests/interfaces/{test_tensorflow_autograph_qnode_shot_vector.py => legacy_devices_integration/test_tensorflow_autograph_qnode_shot_vector_legacy.py} (100%) rename tests/interfaces/{test_tensorflow.py => legacy_devices_integration/test_tensorflow_legacy.py} (100%) rename tests/interfaces/{test_tensorflow_qnode.py => legacy_devices_integration/test_tensorflow_qnode_legacy.py} (100%) rename tests/interfaces/{test_tensorflow_qnode_shot_vector.py => legacy_devices_integration/test_tensorflow_qnode_shot_vector_legacy.py} (100%) rename tests/interfaces/{test_torch.py => legacy_devices_integration/test_torch_legacy.py} (100%) rename tests/interfaces/{test_torch_qnode.py => legacy_devices_integration/test_torch_qnode_legacy.py} (100%) diff --git a/tests/devices/default_tensor/test_default_tensor.py b/tests/devices/default_tensor/test_default_tensor.py index 9fb622e3174..203680053d4 100644 --- a/tests/devices/default_tensor/test_default_tensor.py +++ b/tests/devices/default_tensor/test_default_tensor.py @@ -405,7 +405,7 @@ def test_jax(self, method): jax = pytest.importorskip("jax") dev = qml.device("default.tensor", wires=1, method=method) - ref_dev = qml.device("default.qubit.jax", wires=1) + ref_dev = qml.device("default.qubit", wires=1) def circuit(x): qml.RX(x[1], wires=0) diff --git a/tests/gpu/test_gpu_torch.py b/tests/gpu/test_gpu_torch.py index cd461140e17..8c76734e683 100644 --- a/tests/gpu/test_gpu_torch.py +++ b/tests/gpu/test_gpu_torch.py @@ -33,7 +33,7 @@ class TestTorchDevice: def test_device_to_cuda(self): """Checks device executes with cuda is input data is cuda""" - dev = qml.device("default.qubit.torch", wires=1) + dev = qml.device("default.qubit", wires=1) x = torch.tensor(0.1, requires_grad=True, device=torch.device("cuda")) @@ -53,7 +53,7 @@ def test_device_to_cuda(self): def test_mixed_devices(self): """Asserts works with both cuda and cpu input data""" - dev = qml.device("default.qubit.torch", wires=1) + dev = qml.device("default.qubit", wires=1) x = torch.tensor(0.1, requires_grad=True, device=torch.device("cuda")) y = torch.tensor(0.2, requires_grad=True, device=torch.device("cpu")) @@ -77,7 +77,7 @@ def test_mixed_devices(self): def test_matrix_input(self): """Test goes to GPU for matrix valued inputs.""" - dev = qml.device("default.qubit.torch", wires=1) + dev = qml.device("default.qubit", wires=1) U = torch.eye(2, requires_grad=False, device=torch.device("cuda")) @@ -93,7 +93,7 @@ def test_matrix_input(self): def test_resets(self): """Asserts reverts to cpu after execution on gpu""" - dev = qml.device("default.qubit.torch", wires=1) + dev = qml.device("default.qubit", wires=1) x = torch.tensor(0.1, requires_grad=True, device=torch.device("cuda")) y = torch.tensor(0.2, requires_grad=True, device=torch.device("cpu")) @@ -143,7 +143,7 @@ def test_different_devices_creation_and_parameters_warn(self, init_device, par_d PennyLane device creation differs from the Torch device of gate parameters. """ - dev = qml.device("default.qubit.torch", wires=1, torch_device=init_device) + dev = qml.device("default.qubit", wires=1, torch_device=init_device) p = torch.tensor(0.543, dtype=torch.float64, device=par_device) @@ -180,7 +180,7 @@ def circuit_cuda(inputs): @pytest.mark.skipif(not torch_cuda.is_available(), reason="no cuda support") -class TestqnnTorchLayer: +class TestQnnTorchLayer: def test_torch_device_cuda_if_tensors_on_cuda(self): """Test that if any tensor passed to operators is on the GPU then CUDA is set internally as a device option for 'default.qubit.torch'.""" diff --git a/tests/gradients/core/test_hadamard_gradient.py b/tests/gradients/core/test_hadamard_gradient.py index a455824e1df..682f685b99f 100644 --- a/tests/gradients/core/test_hadamard_gradient.py +++ b/tests/gradients/core/test_hadamard_gradient.py @@ -875,61 +875,21 @@ def circuit(weights): with pytest.raises(qml.QuantumFunctionError, match="No trainable parameters."): qml.gradients.hadamard_grad(circuit)(weights) - @pytest.mark.autograd - def test_no_trainable_params_qnode_autograd_legacy_opmath(self): + @pytest.mark.parametrize( + "interface", + [ + pytest.param("jax", marks=pytest.mark.jax), + pytest.param("autograd", marks=pytest.mark.autograd), + pytest.param("torch", marks=pytest.mark.torch), + pytest.param("tf", marks=pytest.mark.tf), + ], + ) + def test_no_trainable_params_qnode_legacy_opmath(self, interface): """Test that the correct ouput and warning is generated in the absence of any trainable parameters""" - dev = qml.device("default.qubit.autograd", wires=2) - - @qml.qnode(dev, interface="autograd") - def circuit(weights): - qml.RX(weights[0], wires=0) - qml.RY(weights[1], wires=0) - return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1)) - - weights = [0.1, 0.2] - with pytest.raises(qml.QuantumFunctionError, match="No trainable parameters."): - qml.gradients.hadamard_grad(circuit)(weights) + dev = qml.device(f"default.qubit.{interface}", wires=2) - @pytest.mark.torch - def test_no_trainable_params_qnode_torch_legacy_opmath(self): - """Test that the correct ouput and warning is generated in the absence of any trainable - parameters""" - dev = qml.device("default.qubit.torch", wires=2) - - @qml.qnode(dev, interface="torch") - def circuit(weights): - qml.RX(weights[0], wires=0) - qml.RY(weights[1], wires=0) - return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1)) - - weights = [0.1, 0.2] - with pytest.raises(qml.QuantumFunctionError, match="No trainable parameters."): - qml.gradients.hadamard_grad(circuit)(weights) - - @pytest.mark.tf - def test_no_trainable_params_qnode_tf_legacy_opmath(self): - """Test that the correct ouput and warning is generated in the absence of any trainable - parameters""" - dev = qml.device("default.qubit.tf", wires=2) - - @qml.qnode(dev, interface="tf") - def circuit(weights): - qml.RX(weights[0], wires=0) - qml.RY(weights[1], wires=0) - return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1)) - - weights = [0.1, 0.2] - with pytest.raises(qml.QuantumFunctionError, match="No trainable parameters."): - qml.gradients.hadamard_grad(circuit)(weights) - - @pytest.mark.jax - def test_no_trainable_params_qnode_jax_legacy_opmath(self): - """Test that the correct ouput and warning is generated in the absence of any trainable - parameters""" - dev = qml.device("default.qubit.jax", wires=2) - - @qml.qnode(dev, interface="jax") + @qml.qnode(dev, interface=interface) def circuit(weights): qml.RX(weights[0], wires=0) qml.RY(weights[1], wires=0) diff --git a/tests/gradients/core/test_metric_tensor.py b/tests/gradients/core/test_metric_tensor.py index 01b43bc8177..364516d6079 100644 --- a/tests/gradients/core/test_metric_tensor.py +++ b/tests/gradients/core/test_metric_tensor.py @@ -14,7 +14,9 @@ """ Unit tests for the metric tensor transform. """ -# pylint: disable=too-many-arguments,too-many-public-methods,too-few-public-methods,not-callable +import importlib + +# pylint: disable=too-many-arguments,too-many-public-methods,too-few-public-methods,not-callable,too-many-statements import pytest from scipy.linalg import block_diag @@ -538,189 +540,31 @@ def layer3_diag(params): G_expected = block_diag(G1, G3, G2) assert qml.math.allclose(G, G_expected, atol=tol, rtol=0) - @pytest.mark.autograd - @pytest.mark.parametrize("interface", ["autograd"]) - def test_argnum_metric_tensor_autograd(self, tol, interface): - """Test that argnum successfully reduces the number of tapes and gives - the desired outcome.""" - dev = qml.device("default.qubit.autograd", wires=3) - - def circuit(weights): - qml.RX(weights[0], wires=0) - qml.RY(weights[1], wires=0) - qml.CNOT(wires=[0, 1]) - qml.RZ(weights[2], wires=1) - qml.RZ(weights[3], wires=0) - - weights = np.array([0.1, 0.2, 0.3, 0.5], requires_grad=True) - - with qml.tape.QuantumTape() as tape: - circuit(weights) - - tapes, proc_fn = qml.metric_tensor(tape) - res = qml.execute(tapes, dev, None) - mt = proc_fn(res) - - tapes, proc_fn = qml.metric_tensor(tape, argnum=(0, 1, 3)) - res = qml.execute(tapes, dev, None, interface=interface) - mt013 = proc_fn(res) - assert isinstance(mt013, np.ndarray) - - assert len(tapes) == 6 - assert mt.shape == mt013.shape - assert qml.math.allclose(mt[:2, :2], mt013[:2, :2], atol=tol, rtol=0) - assert qml.math.allclose(mt[3, 3], mt013[3, 3], atol=tol, rtol=0) - assert qml.math.allclose(0, mt013[2, :], atol=tol, rtol=0) - assert qml.math.allclose(0, mt013[:, 2], atol=tol, rtol=0) - - tapes, proc_fn = qml.metric_tensor(tape, argnum=(2, 3)) - res = qml.execute(tapes, dev, None, interface=interface) - mt23 = proc_fn(res) - assert isinstance(mt23, np.ndarray) - - assert len(tapes) == 1 - assert mt.shape == mt23.shape - assert qml.math.allclose(mt[2:, 2:], mt23[2:, 2:], atol=tol, rtol=0) - assert qml.math.allclose(0, mt23[:2, :], atol=tol, rtol=0) - assert qml.math.allclose(0, mt23[:, :2], atol=tol, rtol=0) - - tapes, proc_fn = qml.metric_tensor(tape, argnum=0) - res = qml.execute(tapes, dev, None, interface=interface) - mt0 = proc_fn(res) - assert isinstance(mt0, np.ndarray) - - assert len(tapes) == 1 - assert mt.shape == mt0.shape - assert qml.math.allclose(mt[0, 0], mt0[0, 0], atol=tol, rtol=0) - assert qml.math.allclose(0, mt0[1:, :], atol=tol, rtol=0) - assert qml.math.allclose(0, mt0[:, 1:], atol=tol, rtol=0) - - @pytest.mark.tf - @pytest.mark.parametrize("interface", ["tf"]) - def test_argnum_metric_tensor_tf(self, tol, interface): - """Test that argnum successfully reduces the number of tapes and gives - the desired outcome.""" - import tensorflow as tf - - dev = qml.device("default.qubit.tf", wires=3) - - def circuit(weights): - qml.RX(weights[0], wires=0) - qml.RY(weights[1], wires=0) - qml.CNOT(wires=[0, 1]) - qml.RZ(weights[2], wires=1) - qml.RZ(weights[3], wires=0) - - weights = tf.Variable([0.1, 0.2, 0.3, 0.5]) - - with qml.tape.QuantumTape() as tape: - circuit(weights) - - tapes, proc_fn = qml.metric_tensor(tape) - res = qml.execute(tapes, dev, None) - mt = proc_fn(res) - - tapes, proc_fn = qml.metric_tensor(tape, argnum=(0, 1, 3)) - res = qml.execute(tapes, dev, None, interface=interface) - mt013 = proc_fn(res) - assert isinstance(mt013, tf.Tensor) - - assert len(tapes) == 6 - assert mt.shape == mt013.shape - assert qml.math.allclose(mt[:2, :2], mt013[:2, :2], atol=tol, rtol=0) - assert qml.math.allclose(mt[3, 3], mt013[3, 3], atol=tol, rtol=0) - assert qml.math.allclose(0, mt013[2, :], atol=tol, rtol=0) - assert qml.math.allclose(0, mt013[:, 2], atol=tol, rtol=0) - - tapes, proc_fn = qml.metric_tensor(tape, argnum=(2, 3)) - res = qml.execute(tapes, dev, None, interface=interface) - mt23 = proc_fn(res) - assert isinstance(mt23, tf.Tensor) - - assert len(tapes) == 1 - assert mt.shape == mt23.shape - assert qml.math.allclose(mt[2:, 2:], mt23[2:, 2:], atol=tol, rtol=0) - assert qml.math.allclose(0, mt23[:2, :], atol=tol, rtol=0) - assert qml.math.allclose(0, mt23[:, :2], atol=tol, rtol=0) - - tapes, proc_fn = qml.metric_tensor(tape, argnum=0) - res = qml.execute(tapes, dev, None, interface=interface) - mt0 = proc_fn(res) - assert isinstance(mt0, tf.Tensor) - - assert len(tapes) == 1 - assert mt.shape == mt0.shape - assert qml.math.allclose(mt[0, 0], mt0[0, 0], atol=tol, rtol=0) - assert qml.math.allclose(0, mt0[1:, :], atol=tol, rtol=0) - assert qml.math.allclose(0, mt0[:, 1:], atol=tol, rtol=0) - - @pytest.mark.torch - @pytest.mark.parametrize("interface", ["torch"]) - def test_argnum_metric_tensor_torch(self, tol, interface): + @pytest.mark.parametrize( + "interface,array_cls", + [ + pytest.param("jax", "array", marks=pytest.mark.jax), + pytest.param("autograd", "array", marks=pytest.mark.autograd), + pytest.param("tf", "Variable", marks=pytest.mark.tf), + pytest.param("torch", "Tensor", marks=pytest.mark.torch), + ], + ) + def test_argnum_metric_tensor_interfaces(self, tol, interface, array_cls): """Test that argnum successfully reduces the number of tapes and gives the desired outcome.""" - import torch - - dev = qml.device("default.qubit.torch", wires=3) - - def circuit(weights): - qml.RX(weights[0], wires=0) - qml.RY(weights[1], wires=0) - qml.CNOT(wires=[0, 1]) - qml.RZ(weights[2], wires=1) - qml.RZ(weights[3], wires=0) - - weights = torch.tensor([0.1, 0.2, 0.3, 0.5], requires_grad=True) - - with qml.tape.QuantumTape() as tape: - circuit(weights) - - tapes, proc_fn = qml.metric_tensor(tape) - res = qml.execute(tapes, dev, None) - mt = proc_fn(res) - - tapes, proc_fn = qml.metric_tensor(tape, argnum=(0, 1, 3)) - res = qml.execute(tapes, dev, None, interface=interface) - mt013 = proc_fn(res) - assert isinstance(mt013, torch.Tensor) - - assert len(tapes) == 6 - assert mt.shape == mt013.shape - assert qml.math.allclose(mt[:2, :2], mt013[:2, :2], atol=tol, rtol=0) - assert qml.math.allclose(mt[3, 3], mt013[3, 3], atol=tol, rtol=0) - assert qml.math.allclose(0, mt013[2, :], atol=tol, rtol=0) - assert qml.math.allclose(0, mt013[:, 2], atol=tol, rtol=0) - - tapes, proc_fn = qml.metric_tensor(tape, argnum=(2, 3)) - res = qml.execute(tapes, dev, None, interface=interface) - mt23 = proc_fn(res) - assert isinstance(mt23, torch.Tensor) - - assert len(tapes) == 1 - assert mt.shape == mt23.shape - assert qml.math.allclose(mt[2:, 2:], mt23[2:, 2:], atol=tol, rtol=0) - assert qml.math.allclose(0, mt23[:2, :], atol=tol, rtol=0) - assert qml.math.allclose(0, mt23[:, :2], atol=tol, rtol=0) - - tapes, proc_fn = qml.metric_tensor(tape, argnum=0) - res = qml.execute(tapes, dev, None, interface=interface) - mt0 = proc_fn(res) - assert isinstance(mt0, torch.Tensor) - - assert len(tapes) == 1 - assert mt.shape == mt0.shape - assert qml.math.allclose(mt[0, 0], mt0[0, 0], atol=tol, rtol=0) - assert qml.math.allclose(0, mt0[1:, :], atol=tol, rtol=0) - assert qml.math.allclose(0, mt0[:, 1:], atol=tol, rtol=0) + if interface == "tf": + interface_name = "tensorflow" + elif interface == "jax": + interface_name = "jax.numpy" + elif interface == "autograd": + interface_name = "numpy" + else: + interface_name = interface - @pytest.mark.jax - @pytest.mark.parametrize("interface", ["jax"]) - def test_argnum_metric_tensor_jax(self, tol, interface): - """Test that argnum successfully reduces the number of tapes and gives - the desired outcome.""" - import jax + mod = importlib.import_module(interface_name) + type_ = type(getattr(mod, array_cls)([])) if interface != "tf" else getattr(mod, "Tensor") - dev = qml.device("default.qubit.jax", wires=3) + dev = qml.device("default.qubit", wires=3) def circuit(weights): qml.RX(weights[0], wires=0) @@ -729,7 +573,7 @@ def circuit(weights): qml.RZ(weights[2], wires=1) qml.RZ(weights[3], wires=0) - weights = jax.numpy.array([0.1, 0.2, 0.3, 0.5]) + weights = getattr(mod, array_cls)([0.1, 0.2, 0.3, 0.5]) with qml.tape.QuantumTape() as tape: circuit(weights) @@ -741,7 +585,7 @@ def circuit(weights): tapes, proc_fn = qml.metric_tensor(tape, argnum=(0, 1, 3)) res = qml.execute(tapes, dev, None, interface=interface) mt013 = proc_fn(res) - assert isinstance(mt013, jax.numpy.ndarray) + assert isinstance(mt013, type_) assert len(tapes) == 6 assert mt.shape == mt013.shape @@ -753,7 +597,7 @@ def circuit(weights): tapes, proc_fn = qml.metric_tensor(tape, argnum=(2, 3)) res = qml.execute(tapes, dev, None, interface=interface) mt23 = proc_fn(res) - assert isinstance(mt23, jax.numpy.ndarray) + assert isinstance(mt23, type_) assert len(tapes) == 1 assert mt.shape == mt23.shape @@ -764,7 +608,7 @@ def circuit(weights): tapes, proc_fn = qml.metric_tensor(tape, argnum=0) res = qml.execute(tapes, dev, None, interface=interface) mt0 = proc_fn(res) - assert isinstance(mt0, jax.numpy.ndarray) + assert isinstance(mt0, type_) assert len(tapes) == 1 assert mt.shape == mt0.shape diff --git a/tests/interfaces/default_qubit_2_integration/test_autograd_default_qubit_2.py b/tests/interfaces/default_qubit_integration/test_autograd.py similarity index 100% rename from tests/interfaces/default_qubit_2_integration/test_autograd_default_qubit_2.py rename to tests/interfaces/default_qubit_integration/test_autograd.py diff --git a/tests/interfaces/default_qubit_2_integration/test_autograd_qnode_default_qubit_2.py b/tests/interfaces/default_qubit_integration/test_autograd_qnode.py similarity index 100% rename from tests/interfaces/default_qubit_2_integration/test_autograd_qnode_default_qubit_2.py rename to tests/interfaces/default_qubit_integration/test_autograd_qnode.py diff --git a/tests/interfaces/default_qubit_integration/test_autograd_qnode_shot_vector.py b/tests/interfaces/default_qubit_integration/test_autograd_qnode_shot_vector.py new file mode 100644 index 00000000000..87e534fdb52 --- /dev/null +++ b/tests/interfaces/default_qubit_integration/test_autograd_qnode_shot_vector.py @@ -0,0 +1,647 @@ +# Copyright 2022 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. +"""Integration tests for using the Autograd interface with shot vectors and with a QNode""" +# pylint: disable=too-many-arguments + +import pytest + +import pennylane as qml +from pennylane import numpy as np +from pennylane import qnode + +pytestmark = pytest.mark.autograd + +shots_and_num_copies = [(((5, 2), 1, 10), 4), ((1, 10, (5, 2)), 4)] +shots_and_num_copies_hess = [(((5, 1), 10), 2), ((10, (5, 1)), 2)] + +SEED_FOR_SPSA = 42 +spsa_kwargs = {"h": 0.05, "num_directions": 20, "sampler_rng": np.random.default_rng(SEED_FOR_SPSA)} + +qubit_device_and_diff_method = [ + ["default.qubit", "finite-diff", {"h": 0.05}], + ["default.qubit", "parameter-shift", {}], + ["default.qubit", "spsa", spsa_kwargs], +] + +TOLS = { + "finite-diff": 0.3, + "parameter-shift": 1e-2, + "spsa": 0.3, +} + + +@pytest.mark.parametrize("shots,num_copies", shots_and_num_copies) +@pytest.mark.parametrize("dev_name,diff_method,gradient_kwargs", qubit_device_and_diff_method) +class TestReturnWithShotVectors: + """Class to test the shape of the Jacobian/Hessian with different return types and shot vectors.""" + + def test_jac_single_measurement_param( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """For one measurement and one param, the gradient is a float.""" + dev = qml.device(dev_name, wires=1, shots=shots) + + @qnode(dev, diff_method=diff_method, **gradient_kwargs) + def circuit(a): + qml.RY(a, wires=0) + qml.RX(0.2, wires=0) + return qml.expval(qml.PauliZ(0)) + + a = np.array(0.1) + + def cost(a): + return qml.math.stack(circuit(a)) + + jac = qml.jacobian(cost)(a) + + assert isinstance(jac, np.ndarray) + assert jac.shape == (num_copies,) + + def test_jac_single_measurement_multiple_param( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """For one measurement and multiple param, the gradient is a tuple of arrays.""" + dev = qml.device(dev_name, wires=1, shots=shots) + + @qnode(dev, diff_method=diff_method, **gradient_kwargs) + def circuit(a, b): + qml.RY(a, wires=0) + qml.RX(b, wires=0) + return qml.expval(qml.PauliZ(0)) + + a = np.array(0.1) + b = np.array(0.2) + + def cost(a, b): + return qml.math.stack(circuit(a, b)) + + jac = qml.jacobian(cost, argnum=[0, 1])(a, b) + + assert isinstance(jac, tuple) + assert len(jac) == 2 + for j in jac: + assert isinstance(j, np.ndarray) + assert j.shape == (num_copies,) + + def test_jacobian_single_measurement_multiple_param_array( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """For one measurement and multiple param as a single array params, the gradient is an array.""" + dev = qml.device(dev_name, wires=1, shots=shots) + + @qnode(dev, diff_method=diff_method, **gradient_kwargs) + def circuit(a): + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + return qml.expval(qml.PauliZ(0)) + + a = np.array([0.1, 0.2]) + + def cost(a): + return qml.math.stack(circuit(a)) + + jac = qml.jacobian(cost)(a) + + assert isinstance(jac, np.ndarray) + assert jac.shape == (num_copies, 2) + + def test_jacobian_single_measurement_param_probs( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """For a multi dimensional measurement (probs), check that a single array is returned with the correct + dimension""" + dev = qml.device(dev_name, wires=2, shots=shots) + + @qnode(dev, diff_method=diff_method, **gradient_kwargs) + def circuit(a): + qml.RY(a, wires=0) + qml.RX(0.2, wires=0) + return qml.probs(wires=[0, 1]) + + a = np.array(0.1) + + def cost(a): + return qml.math.stack(circuit(a)) + + jac = qml.jacobian(cost)(a) + + assert isinstance(jac, np.ndarray) + assert jac.shape == (num_copies, 4) + + def test_jacobian_single_measurement_probs_multiple_param( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """For a multi dimensional measurement (probs), check that a single tuple is returned containing arrays with + the correct dimension""" + dev = qml.device(dev_name, wires=2, shots=shots) + + @qnode(dev, diff_method=diff_method, **gradient_kwargs) + def circuit(a, b): + qml.RY(a, wires=0) + qml.RX(b, wires=0) + return qml.probs(wires=[0, 1]) + + a = np.array(0.1) + b = np.array(0.2) + + def cost(a, b): + return qml.math.stack(circuit(a, b)) + + jac = qml.jacobian(cost, argnum=[0, 1])(a, b) + + assert isinstance(jac, tuple) + assert len(jac) == 2 + for j in jac: + assert isinstance(j, np.ndarray) + assert j.shape == (num_copies, 4) + + def test_jacobian_single_measurement_probs_multiple_param_single_array( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """For a multi dimensional measurement (probs), check that a single tuple is returned containing arrays with + the correct dimension""" + dev = qml.device(dev_name, wires=2, shots=shots) + + @qnode(dev, diff_method=diff_method, **gradient_kwargs) + def circuit(a): + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + return qml.probs(wires=[0, 1]) + + a = np.array([0.1, 0.2]) + + def cost(a): + return qml.math.stack(circuit(a)) + + jac = qml.jacobian(cost)(a) + + assert isinstance(jac, np.ndarray) + assert jac.shape == (num_copies, 4, 2) + + def test_jacobian_expval_expval_multiple_params( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """The gradient of multiple measurements with multiple params return a tuple of arrays.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + par_0 = np.array(0.1) + par_1 = np.array(0.2) + + @qnode(dev, diff_method=diff_method, max_diff=1, **gradient_kwargs) + def circuit(x, y): + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)), qml.expval(qml.PauliZ(0)) + + def cost(x, y): + res = circuit(x, y) + return qml.math.stack([qml.math.stack(r) for r in res]) + + jac = qml.jacobian(cost, argnum=[0, 1])(par_0, par_1) + + assert isinstance(jac, tuple) + assert len(jac) == 2 + for j in jac: + assert isinstance(j, np.ndarray) + assert j.shape == (num_copies, 2) + + def test_jacobian_expval_expval_multiple_params_array( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """The jacobian of multiple measurements with a multiple params array return a single array.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + @qnode(dev, diff_method=diff_method, **gradient_kwargs) + def circuit(a): + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.RY(a[2], wires=0) + return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)), qml.expval(qml.PauliZ(0)) + + a = np.array([0.1, 0.2, 0.3]) + + def cost(a): + res = circuit(a) + return qml.math.stack([qml.math.stack(r) for r in res]) + + jac = qml.jacobian(cost)(a) + + assert isinstance(jac, np.ndarray) + assert jac.shape == (num_copies, 2, 3) + + def test_jacobian_var_var_multiple_params( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """The jacobian of multiple measurements with multiple params return a tuple of arrays.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + par_0 = np.array(0.1) + par_1 = np.array(0.2) + + @qnode(dev, diff_method=diff_method, **gradient_kwargs) + def circuit(x, y): + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.var(qml.PauliZ(0) @ qml.PauliX(1)), qml.var(qml.PauliZ(0)) + + def cost(x, y): + res = circuit(x, y) + return qml.math.stack([qml.math.stack(r) for r in res]) + + jac = qml.jacobian(cost, argnum=[0, 1])(par_0, par_1) + + assert isinstance(jac, tuple) + assert len(jac) == 2 + for j in jac: + assert isinstance(j, np.ndarray) + assert j.shape == (num_copies, 2) + + def test_jacobian_var_var_multiple_params_array( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """The jacobian of multiple measurements with a multiple params array return a single array.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + @qnode(dev, diff_method=diff_method, **gradient_kwargs) + def circuit(a): + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.RY(a[2], wires=0) + return qml.var(qml.PauliZ(0) @ qml.PauliX(1)), qml.var(qml.PauliZ(0)) + + a = np.array([0.1, 0.2, 0.3]) + + def cost(a): + res = circuit(a) + return qml.math.stack([qml.math.stack(r) for r in res]) + + jac = qml.jacobian(cost)(a) + + assert isinstance(jac, np.ndarray) + assert jac.shape == (num_copies, 2, 3) + + def test_jacobian_multiple_measurement_single_param( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """The jacobian of multiple measurements with a single params return an array.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + @qnode(dev, diff_method=diff_method, **gradient_kwargs) + def circuit(a): + qml.RY(a, wires=0) + qml.RX(0.2, wires=0) + return qml.expval(qml.PauliZ(0)), qml.probs(wires=[0, 1]) + + a = np.array(0.1) + + def cost(a): + res = circuit(a) + return qml.math.stack([qml.math.hstack(r) for r in res]) + + jac = qml.jacobian(cost)(a) + + assert isinstance(jac, np.ndarray) + assert jac.shape == (num_copies, 5) + + def test_jacobian_multiple_measurement_multiple_param( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """The jacobian of multiple measurements with a multiple params return a tuple of arrays.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + @qnode(dev, diff_method=diff_method, **gradient_kwargs) + def circuit(a, b): + qml.RY(a, wires=0) + qml.RX(b, wires=0) + return qml.expval(qml.PauliZ(0)), qml.probs(wires=[0, 1]) + + a = np.array(0.1, requires_grad=True) + b = np.array(0.2, requires_grad=True) + + def cost(a, b): + res = circuit(a, b) + return qml.math.stack([qml.math.hstack(r) for r in res]) + + jac = qml.jacobian(cost, argnum=[0, 1])(a, b) + + assert isinstance(jac, tuple) + assert len(jac) == 2 + for j in jac: + assert isinstance(j, np.ndarray) + assert j.shape == (num_copies, 5) + + def test_jacobian_multiple_measurement_multiple_param_array( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """The jacobian of multiple measurements with a multiple params array return a single array.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + @qnode(dev, diff_method=diff_method, **gradient_kwargs) + def circuit(a): + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + return qml.expval(qml.PauliZ(0)), qml.probs(wires=[0, 1]) + + a = np.array([0.1, 0.2]) + + def cost(a): + res = circuit(a) + return qml.math.stack([qml.math.hstack(r) for r in res]) + + jac = qml.jacobian(cost)(a) + + assert isinstance(jac, np.ndarray) + assert jac.shape == (num_copies, 5, 2) + + +@pytest.mark.slow +@pytest.mark.parametrize("shots,num_copies", shots_and_num_copies_hess) +@pytest.mark.parametrize("dev_name,diff_method,gradient_kwargs", qubit_device_and_diff_method) +class TestReturnShotVectorHessian: + """Class to test the shape of the Hessian with different return types and shot vectors.""" + + def test_hessian_expval_multiple_params( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """The hessian of a single measurement with multiple params return a tuple of arrays.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + par_0 = np.array(0.1) + par_1 = np.array(0.2) + + @qnode(dev, diff_method=diff_method, max_diff=2, **gradient_kwargs) + def circuit(x, y): + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) + + def cost(x, y): + def cost2(x, y): + res = circuit(x, y) + return qml.math.stack(res) + + return qml.math.stack(qml.jacobian(cost2, argnum=[0, 1])(x, y)) + + hess = qml.jacobian(cost, argnum=[0, 1])(par_0, par_1) + + assert isinstance(hess, tuple) + assert len(hess) == 2 + for h in hess: + assert isinstance(h, np.ndarray) + assert h.shape == (2, num_copies) + + def test_hessian_expval_multiple_param_array( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """The hessian of single measurement with a multiple params array return a single array.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + params = np.array([0.1, 0.2]) + + @qnode(dev, diff_method=diff_method, max_diff=2, **gradient_kwargs) + def circuit(x): + qml.RX(x[0], wires=[0]) + qml.RY(x[1], wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) + + def cost(x): + def cost2(x): + res = circuit(x) + return qml.math.stack(res) + + return qml.jacobian(cost2)(x) + + hess = qml.jacobian(cost)(params) + + assert isinstance(hess, np.ndarray) + assert hess.shape == (num_copies, 2, 2) + + def test_hessian_var_multiple_params( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """The hessian of a single measurement with multiple params return a tuple of arrays.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + par_0 = np.array(0.1) + par_1 = np.array(0.2) + + @qnode(dev, diff_method=diff_method, max_diff=2, **gradient_kwargs) + def circuit(x, y): + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.var(qml.PauliZ(0) @ qml.PauliX(1)) + + def cost(x, y): + def cost2(x, y): + res = circuit(x, y) + return qml.math.stack(res) + + return qml.math.stack(qml.jacobian(cost2, argnum=[0, 1])(x, y)) + + hess = qml.jacobian(cost, argnum=[0, 1])(par_0, par_1) + + assert isinstance(hess, tuple) + assert len(hess) == 2 + for h in hess: + assert isinstance(h, np.ndarray) + assert h.shape == (2, num_copies) + + def test_hessian_var_multiple_param_array( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """The hessian of single measurement with a multiple params array return a single array.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + params = np.array([0.1, 0.2]) + + @qnode(dev, diff_method=diff_method, max_diff=2, **gradient_kwargs) + def circuit(x): + qml.RX(x[0], wires=[0]) + qml.RY(x[1], wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.var(qml.PauliZ(0) @ qml.PauliX(1)) + + def cost(x): + def cost2(x): + res = circuit(x) + return qml.math.stack(res) + + return qml.jacobian(cost2)(x) + + hess = qml.jacobian(cost)(params) + + assert isinstance(hess, np.ndarray) + assert hess.shape == (num_copies, 2, 2) + + def test_hessian_probs_expval_multiple_params( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """The hessian of multiple measurements with multiple params return a tuple of arrays.""" + if diff_method == "spsa": + pytest.skip("SPSA does not support iterated differentiation in Autograd.") + dev = qml.device(dev_name, wires=2, shots=shots) + + par_0 = np.array(0.1) + par_1 = np.array(0.2) + + @qnode(dev, diff_method=diff_method, max_diff=2, **gradient_kwargs) + def circuit(x, y): + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)), qml.probs(wires=[0]) + + def cost(x, y): + def cost2(x, y): + res = circuit(x, y) + return qml.math.stack([qml.math.hstack(r) for r in res]) + + return qml.math.stack(qml.jacobian(cost2, argnum=[0, 1])(x, y)) + + hess = qml.jacobian(cost, argnum=[0, 1])(par_0, par_1) + + assert isinstance(hess, tuple) + assert len(hess) == 2 + for h in hess: + assert isinstance(h, np.ndarray) + assert h.shape == (2, num_copies, 3) + + def test_hessian_expval_probs_multiple_param_array( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """The hessian of multiple measurements with a multiple param array return a single array.""" + if diff_method == "spsa": + pytest.skip("SPSA does not support iterated differentiation in Autograd.") + + dev = qml.device(dev_name, wires=2, shots=shots) + + params = np.array([0.1, 0.2]) + + @qnode(dev, diff_method=diff_method, max_diff=2, **gradient_kwargs) + def circuit(x): + qml.RX(x[0], wires=[0]) + qml.RY(x[1], wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)), qml.probs(wires=[0]) + + def cost(x): + def cost2(x): + res = circuit(x) + return qml.math.stack([qml.math.hstack(r) for r in res]) + + return qml.jacobian(cost2)(x) + + hess = qml.jacobian(cost)(params) + + assert isinstance(hess, np.ndarray) + assert hess.shape == (num_copies, 3, 2, 2) + + +shots_and_num_copies = [((1000000, 900000, 800000), 3), ((1000000, (900000, 2)), 3)] + + +@pytest.mark.skip("failing in CI for inscrutable reasons, passes locally") +@pytest.mark.parametrize("shots,num_copies", shots_and_num_copies) +@pytest.mark.parametrize("dev_name,diff_method,gradient_kwargs", qubit_device_and_diff_method) +class TestReturnShotVectorIntegration: + """Tests for the integration of shots with the autograd interface.""" + + def test_single_expectation_value( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """Tests correct output shape and evaluation for a tape + with a single expval output""" + dev = qml.device(dev_name, wires=2, shots=shots) + x = np.array(0.543) + y = np.array(-0.654) + + @qnode(dev, diff_method=diff_method, **gradient_kwargs) + def circuit(x, y): + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) + + def cost(x, y): + res = circuit(x, y) + return qml.math.stack(res) + + all_res = qml.jacobian(cost, argnum=[0, 1])(x, y) + + assert isinstance(all_res, tuple) + assert len(all_res) == 2 + + expected = np.array([-np.sin(y) * np.sin(x), np.cos(y) * np.cos(x)]) + tol = TOLS[diff_method] + + for res, exp in zip(all_res, expected): + assert isinstance(res, np.ndarray) + assert res.shape == (num_copies,) + assert np.allclose(res, exp, atol=tol, rtol=0) + + def test_prob_expectation_values( + self, dev_name, diff_method, gradient_kwargs, shots, num_copies + ): + """Tests correct output shape and evaluation for a tape + with prob and expval outputs""" + dev = qml.device(dev_name, wires=2, shots=shots) + x = np.array(0.543) + y = np.array(-0.654) + + @qnode(dev, diff_method=diff_method, **gradient_kwargs) + def circuit(x, y): + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0)), qml.probs(wires=[0, 1]) + + def cost(x, y): + res = circuit(x, y) + return qml.math.stack([qml.math.hstack(r) for r in res]) + + all_res = qml.jacobian(cost, argnum=[0, 1])(x, y) + + assert isinstance(all_res, tuple) + assert len(all_res) == 2 + + expected = np.array( + [ + [ + -np.sin(x), + -(np.cos(y / 2) ** 2 * np.sin(x)) / 2, + -(np.sin(x) * np.sin(y / 2) ** 2) / 2, + (np.sin(x) * np.sin(y / 2) ** 2) / 2, + (np.cos(y / 2) ** 2 * np.sin(x)) / 2, + ], + [ + 0, + -(np.cos(x / 2) ** 2 * np.sin(y)) / 2, + (np.cos(x / 2) ** 2 * np.sin(y)) / 2, + (np.sin(x / 2) ** 2 * np.sin(y)) / 2, + -(np.sin(x / 2) ** 2 * np.sin(y)) / 2, + ], + ] + ) + + tol = TOLS[diff_method] + + for res, exp in zip(all_res, expected): + assert isinstance(res, np.ndarray) + assert res.shape == (num_copies, 5) + assert np.allclose(res, exp, atol=tol, rtol=0) diff --git a/tests/interfaces/default_qubit_2_integration/test_execute_default_qubit_2.py b/tests/interfaces/default_qubit_integration/test_execute.py similarity index 100% rename from tests/interfaces/default_qubit_2_integration/test_execute_default_qubit_2.py rename to tests/interfaces/default_qubit_integration/test_execute.py diff --git a/tests/interfaces/default_qubit_2_integration/test_jax_default_qubit_2.py b/tests/interfaces/default_qubit_integration/test_jax.py similarity index 100% rename from tests/interfaces/default_qubit_2_integration/test_jax_default_qubit_2.py rename to tests/interfaces/default_qubit_integration/test_jax.py diff --git a/tests/interfaces/default_qubit_integration/test_jax_jit.py b/tests/interfaces/default_qubit_integration/test_jax_jit.py new file mode 100644 index 00000000000..4fe3d4946d2 --- /dev/null +++ b/tests/interfaces/default_qubit_integration/test_jax_jit.py @@ -0,0 +1,913 @@ +# Copyright 2018-2021 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. +"""Unit tests for the JAX-JIT interface""" +import numpy as np + +# pylint: disable=protected-access,too-few-public-methods,unnecessary-lambda +import pytest + +import pennylane as qml +from pennylane import execute +from pennylane.gradients import param_shift +from pennylane.typing import TensorLike + +pytestmark = pytest.mark.jax + +jax = pytest.importorskip("jax") +jax.config.update("jax_enable_x64", True) + + +class TestJaxExecuteUnitTests: + """Unit tests for jax execution""" + + def test_jacobian_options(self, mocker): + """Test setting jacobian options""" + spy = mocker.spy(qml.gradients, "param_shift") + + a = jax.numpy.array([0.1, 0.2]) + + dev = qml.device("default.qubit", wires=1) + + def cost(a, device): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.expval(qml.PauliZ(0)) + + tape = qml.tape.QuantumScript.from_queue(q) + + return execute( + [tape], + device, + gradient_fn=param_shift, + gradient_kwargs={"shifts": [(np.pi / 4,)] * 2}, + )[0] + + jax.grad(cost)(a, device=dev) + + for args in spy.call_args_list: + assert args[1]["shifts"] == [(np.pi / 4,)] * 2 + + def test_incorrect_gradients_on_execution(self): + """Test that an error is raised if an gradient transform + is used with grad_on_execution=True""" + a = jax.numpy.array([0.1, 0.2]) + + dev = qml.device("default.qubit", wires=1) + + def cost(a, device): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.expval(qml.PauliZ(0)) + + tape = qml.tape.QuantumScript.from_queue(q) + + return execute( + [tape], + device, + gradient_fn=param_shift, + grad_on_execution=True, + )[0] + + with pytest.raises( + ValueError, match="Gradient transforms cannot be used with grad_on_execution=True" + ): + jax.grad(cost)(a, device=dev) + + def test_unknown_interface(self): + """Test that an error is raised if the interface is unknown""" + a = jax.numpy.array([0.1, 0.2]) + + dev = qml.device("default.qubit", wires=1) + + def cost(a, device): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.expval(qml.PauliZ(0)) + + tape = qml.tape.QuantumScript.from_queue(q) + + return execute( + [tape], + device, + gradient_fn=param_shift, + interface="None", + )[0] + + with pytest.raises(ValueError, match="Unknown interface"): + cost(a, device=dev) + + def test_grad_on_execution(self, mocker): + """Test that grad on execution uses the `device.execute_and_gradients` pathway""" + dev = qml.device("default.qubit", wires=1) + spy = mocker.spy(dev, "execute_and_compute_derivatives") + + def cost(a): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.expval(qml.PauliZ(0)) + + tape = qml.tape.QuantumScript.from_queue(q) + + return execute( + [tape], + dev, + gradient_fn="device", + gradient_kwargs={ + "method": "adjoint_jacobian", + "use_device_state": True, + }, + )[0] + + a = jax.numpy.array([0.1, 0.2]) + + with qml.Tracker(dev) as tracker: + jax.jit(cost)(a) + + # adjoint method only performs a single device execution + # gradients are not requested when we only want the results + spy.assert_not_called() + assert tracker.totals["executions"] == 1 + + # when the jacobian is requested, we always calculate it at the same time as the results + jax.grad(jax.jit(cost))(a) + spy.assert_called() + + def test_no_gradients_on_execution(self): + """Test that no grad on execution uses the `device.execute` and `device.compute_derivatives` pathway""" + dev = qml.device("default.qubit", wires=1) + + def cost(a): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.expval(qml.PauliZ(0)) + + tape = qml.tape.QuantumScript.from_queue(q) + + return execute( + [tape], + dev, + gradient_fn="device", + grad_on_execution=False, + gradient_kwargs={"method": "adjoint_jacobian"}, + )[0] + + a = jax.numpy.array([0.1, 0.2]) + + with qml.Tracker(dev) as tracker: + jax.jit(cost)(a) + + assert tracker.totals["executions"] == 1 + assert "derivatives" not in tracker.totals + + with qml.Tracker(dev) as tracker: + jax.grad(jax.jit(cost))(a) + assert tracker.totals["derivatives"] == 1 + + +class TestCaching: + """Test for caching behaviour""" + + def test_cache_maxsize(self, mocker): + """Test the cachesize property of the cache""" + dev = qml.device("default.qubit", wires=1) + spy = mocker.spy(qml.workflow.execution._cache_transform, "_transform") + + def cost(a, cachesize): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.expval(qml.PauliZ(0)) + + tape = qml.tape.QuantumScript.from_queue(q) + + return execute( + [tape], + dev, + gradient_fn=param_shift, + cachesize=cachesize, + )[0] + + params = jax.numpy.array([0.1, 0.2]) + jax.jit(jax.grad(cost), static_argnums=1)(params, cachesize=2) + cache = spy.call_args.kwargs["cache"] + + assert cache.maxsize == 2 + assert cache.currsize == 2 + assert len(cache) == 2 + + def test_custom_cache(self, mocker): + """Test the use of a custom cache object""" + dev = qml.device("default.qubit", wires=1) + spy = mocker.spy(qml.workflow.execution._cache_transform, "_transform") + + def cost(a, cache): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.expval(qml.PauliZ(0)) + + tape = qml.tape.QuantumScript.from_queue(q) + + return execute( + [tape], + dev, + gradient_fn=param_shift, + cache=cache, + )[0] + + custom_cache = {} + params = jax.numpy.array([0.1, 0.2]) + jax.grad(cost)(params, cache=custom_cache) + + cache = spy.call_args.kwargs["cache"] + assert cache is custom_cache + + def test_custom_cache_multiple(self, mocker): + """Test the use of a custom cache object with multiple tapes""" + dev = qml.device("default.qubit", wires=1) + spy = mocker.spy(qml.workflow.execution._cache_transform, "_transform") + + a = jax.numpy.array(0.1) + b = jax.numpy.array(0.2) + + def cost(a, b, cache): + with qml.queuing.AnnotatedQueue() as q1: + qml.RY(a, wires=0) + qml.RX(b, wires=0) + qml.expval(qml.PauliZ(0)) + + tape1 = qml.tape.QuantumScript.from_queue(q1) + + with qml.queuing.AnnotatedQueue() as q2: + qml.RY(a, wires=0) + qml.RX(b, wires=0) + qml.expval(qml.PauliZ(0)) + + tape2 = qml.tape.QuantumScript.from_queue(q2) + + res = execute( + [tape1, tape2], + dev, + gradient_fn=param_shift, + cache=cache, + ) + return res[0] + + custom_cache = {} + jax.grad(cost)(a, b, cache=custom_cache) + + cache = spy.call_args.kwargs["cache"] + assert cache is custom_cache + + def test_caching_param_shift(self, tol): + """Test that, when using parameter-shift transform, + caching produces the optimum number of evaluations.""" + dev = qml.device("default.qubit", wires=1) + + def cost(a, cache): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.expval(qml.PauliZ(0)) + + tape = qml.tape.QuantumScript.from_queue(q) + + return execute( + [tape], + dev, + gradient_fn=param_shift, + cache=cache, + )[0] + + # Without caching, 5 evaluations are required to compute + # the Jacobian: 1 (forward pass) + 2 (backward pass) * (2 shifts * 2 params) + params = jax.numpy.array([0.1, 0.2]) + with qml.Tracker(dev) as tracker: + jax.grad(cost)(params, cache=None) + + assert tracker.totals["executions"] == 5 + + # With caching, 5 evaluations are required to compute + # the Jacobian: 1 (forward pass) + (2 shifts * 2 params) + with qml.Tracker(dev) as tracker: + jac_fn = jax.grad(cost) + grad1 = jac_fn(params, cache=True) + assert tracker.totals["executions"] == 5 + + # Check that calling the cost function again + # continues to evaluate the device (that is, the cache + # is emptied between calls) + with qml.Tracker(dev) as tracker: + grad2 = jac_fn(params, cache=True) + assert tracker.totals["executions"] == 5 + assert np.allclose(grad1, grad2, atol=tol, rtol=0) + + # Check that calling the cost function again + # with different parameters produces a different Jacobian + with qml.Tracker(dev) as tracker: + grad2 = jac_fn(2 * params, cache=True) + assert tracker.totals["executions"] == 5 + assert not np.allclose(grad1, grad2, atol=tol, rtol=0) + + def test_caching_adjoint_backward(self): + """Test that caching produces the optimum number of adjoint evaluations + when mode=backward""" + dev = qml.device("default.qubit", wires=2) + params = jax.numpy.array([0.1, 0.2, 0.3]) + + def cost(a, cache): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.RY(a[2], wires=0) + qml.expval(qml.PauliZ(0)) + + tape = qml.tape.QuantumScript.from_queue(q) + + return execute( + [tape], + dev, + gradient_fn="device", + cache=cache, + grad_on_execution=False, + gradient_kwargs={"method": "adjoint_jacobian"}, + )[0] + + # Without caching, 2 evaluations are required. + # 1 for the forward pass, and one per output dimension + # on the backward pass. + with qml.Tracker(dev) as tracker: + jax.grad(cost)(params, cache=None) + assert tracker.totals["executions"] == 1 + assert tracker.totals["derivatives"] == 1 + + # With caching, also 2 evaluations are required. One + # for the forward pass, and one for the backward pass. + with qml.Tracker(dev) as tracker: + jac_fn = jax.grad(cost) + jac_fn(params, cache=True) + assert tracker.totals["executions"] == 1 + assert tracker.totals["derivatives"] == 1 + + +execute_kwargs_integration = [ + {"gradient_fn": param_shift}, + { + "gradient_fn": "device", + "grad_on_execution": True, + "gradient_kwargs": {"method": "adjoint_jacobian", "use_device_state": True}, + }, + { + "gradient_fn": "device", + "grad_on_execution": False, + "gradient_kwargs": {"method": "adjoint_jacobian"}, + }, +] + + +@pytest.mark.parametrize("execute_kwargs", execute_kwargs_integration) +class TestJaxExecuteIntegration: + """Test the jax interface execute function + integrates well for both forward and backward execution""" + + def test_execution(self, execute_kwargs): + """Test execution""" + dev = qml.device("default.qubit", wires=1) + + def cost(a, b): + with qml.queuing.AnnotatedQueue() as q1: + qml.RY(a, wires=0) + qml.RX(b, wires=0) + qml.expval(qml.PauliZ(0)) + + tape1 = qml.tape.QuantumScript.from_queue(q1) + + with qml.queuing.AnnotatedQueue() as q2: + qml.RY(a, wires=0) + qml.RX(b, wires=0) + qml.expval(qml.PauliZ(0)) + + tape2 = qml.tape.QuantumScript.from_queue(q2) + + return execute([tape1, tape2], dev, **execute_kwargs) + + a = jax.numpy.array(0.1) + b = jax.numpy.array(0.2) + res = cost(a, b) + + assert len(res) == 2 + assert res[0].shape == () + assert res[1].shape == () + + def test_scalar_jacobian(self, execute_kwargs, tol): + """Test scalar jacobian calculation""" + a = jax.numpy.array(0.1) + dev = qml.device("default.qubit", wires=2) + + def cost(a): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a, wires=0) + qml.expval(qml.PauliZ(0)) + + tape = qml.tape.QuantumScript.from_queue(q) + + return execute([tape], dev, **execute_kwargs)[0] + + res = jax.jit(jax.grad(cost))(a) + assert res.shape == () + + # compare to standard tape jacobian + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a, wires=0) + qml.expval(qml.PauliZ(0)) + + tape = qml.tape.QuantumScript.from_queue(q) + + tape.trainable_params = [0] + tapes, fn = param_shift(tape) + expected = fn(dev.execute(tapes)) + + assert expected.shape == () + assert np.allclose(res, expected, atol=tol, rtol=0) + + def test_reusing_quantum_tape(self, execute_kwargs, tol): + """Test re-using a quantum tape by passing new parameters""" + a = jax.numpy.array(0.1) + b = jax.numpy.array(0.2) + + dev = qml.device("default.qubit", wires=2) + + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a, wires=0) + qml.RX(b, wires=1) + qml.CNOT(wires=[0, 1]) + qml.expval(qml.PauliZ(0)) + + tape = qml.tape.QuantumScript.from_queue(q) + + assert tape.trainable_params == [0, 1] + + def cost(a, b): + # An explicit call to _update() is required here to update the + # trainable parameters in between tape executions. + # This is different from how the autograd interface works. + # Unless the update is issued, the validation check related to the + # number of provided parameters fails in the tape: (len(params) != + # required_length) and the tape produces incorrect results. + tape._update() + new_tape = tape.bind_new_parameters([a, b], [0, 1]) + return execute([new_tape], dev, **execute_kwargs)[0] + + jac_fn = jax.jit(jax.grad(cost)) + jac = jac_fn(a, b) + + a = jax.numpy.array(0.54) + b = jax.numpy.array(0.8) + + # check that the cost function continues to depend on the + # values of the parameters for subsequent calls + res2 = cost(2 * a, b) + expected = [np.cos(2 * a)] + assert np.allclose(res2, expected, atol=tol, rtol=0) + + jac_fn = jax.jit(jax.grad(lambda a, b: cost(2 * a, b))) + jac = jac_fn(a, b) + expected = -2 * np.sin(2 * a) + assert np.allclose(jac, expected, atol=tol, rtol=0) + + def test_grad_with_backward_mode(self, execute_kwargs): + """Test jax grad for adjoint diff method in backward mode""" + dev = qml.device("default.qubit", wires=2) + params = jax.numpy.array([0.1, 0.2, 0.3]) + expected_results = jax.numpy.array([-0.3875172, -0.18884787, -0.38355705]) + + def cost(a, cache): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.RY(a[2], wires=0) + qml.expval(qml.PauliZ(0)) + + tape = qml.tape.QuantumScript.from_queue(q) + + res = qml.execute([tape], dev, cache=cache, **execute_kwargs)[0] + return res + + cost = jax.jit(cost) + + results = jax.grad(cost)(params, cache=None) + for r, e in zip(results, expected_results): + assert jax.numpy.allclose(r, e, atol=1e-7) + + def test_classical_processing_single_tape(self, execute_kwargs): + """Test classical processing within the quantum tape for a single tape""" + a = jax.numpy.array(0.1) + b = jax.numpy.array(0.2) + c = jax.numpy.array(0.3) + + def cost(a, b, c, device): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a * c, wires=0) + qml.RZ(b, wires=0) + qml.RX(c + c**2 + jax.numpy.sin(a), wires=0) + qml.expval(qml.PauliZ(0)) + + tape = qml.tape.QuantumScript.from_queue(q) + + return execute([tape], device, **execute_kwargs)[0] + + dev = qml.device("default.qubit", wires=2) + res = jax.jit(jax.grad(cost, argnums=(0, 1, 2)), static_argnums=3)(a, b, c, device=dev) + assert len(res) == 3 + + def test_classical_processing_multiple_tapes(self, execute_kwargs): + """Test classical processing within the quantum tape for multiple + tapes""" + dev = qml.device("default.qubit", wires=2) + params = jax.numpy.array([0.3, 0.2]) + + def cost_fn(x): + with qml.queuing.AnnotatedQueue() as q1: + qml.Hadamard(0) + qml.RY(x[0], wires=[0]) + qml.CNOT(wires=[0, 1]) + qml.expval(qml.PauliZ(0)) + + tape1 = qml.tape.QuantumScript.from_queue(q1) + + with qml.queuing.AnnotatedQueue() as q2: + qml.Hadamard(0) + qml.CRX(2 * x[0] * x[1], wires=[0, 1]) + qml.RX(2 * x[1], wires=[1]) + qml.expval(qml.PauliZ(0)) + + tape2 = qml.tape.QuantumScript.from_queue(q2) + + result = execute(tapes=[tape1, tape2], device=dev, **execute_kwargs) + return result[0] + result[1] - 7 * result[1] + + res = jax.jit(jax.grad(cost_fn))(params) + assert res.shape == (2,) + + def test_multiple_tapes_output(self, execute_kwargs): + """Test the output types for the execution of multiple quantum tapes""" + dev = qml.device("default.qubit", wires=2) + params = jax.numpy.array([0.3, 0.2]) + + def cost_fn(x): + with qml.queuing.AnnotatedQueue() as q1: + qml.Hadamard(0) + qml.RY(x[0], wires=[0]) + qml.CNOT(wires=[0, 1]) + qml.expval(qml.PauliZ(0)) + + tape1 = qml.tape.QuantumScript.from_queue(q1) + + with qml.queuing.AnnotatedQueue() as q2: + qml.Hadamard(0) + qml.CRX(2 * x[0] * x[1], wires=[0, 1]) + qml.RX(2 * x[1], wires=[1]) + qml.expval(qml.PauliZ(0)) + + tape2 = qml.tape.QuantumScript.from_queue(q2) + + return execute(tapes=[tape1, tape2], device=dev, **execute_kwargs) + + res = jax.jit(cost_fn)(params) + assert isinstance(res, TensorLike) + assert all(isinstance(r, jax.numpy.ndarray) for r in res) + assert all(r.shape == () for r in res) + + def test_matrix_parameter(self, execute_kwargs, tol): + """Test that the jax interface works correctly + with a matrix parameter""" + a = jax.numpy.array(0.1) + U = jax.numpy.array([[0, 1], [1, 0]]) + + def cost(a, U, device): + with qml.queuing.AnnotatedQueue() as q: + qml.QubitUnitary(U, wires=0) + qml.RY(a, wires=0) + qml.expval(qml.PauliZ(0)) + + tape = qml.tape.QuantumScript.from_queue(q) + + tape.trainable_params = [0] + return execute([tape], device, **execute_kwargs)[0] + + dev = qml.device("default.qubit", wires=2) + res = jax.jit(cost, static_argnums=2)(a, U, device=dev) + assert np.allclose(res, -np.cos(a), atol=tol, rtol=0) + + jac_fn = jax.grad(cost, argnums=0) + res = jac_fn(a, U, device=dev) + assert np.allclose(res, np.sin(a), atol=tol, rtol=0) + + def test_differentiable_expand(self, execute_kwargs, tol): + """Test that operation and nested tapes expansion + is differentiable""" + + class U3(qml.U3): + def expand(self): + theta, phi, lam = self.data + wires = self.wires + return [ + qml.Rot(lam, theta, -lam, wires=wires), + qml.PhaseShift(phi + lam, wires=wires), + ] + + def cost_fn(a, p, device): + qscript = qml.tape.QuantumScript( + [qml.RX(a, wires=0), U3(*p, wires=0)], [qml.expval(qml.PauliX(0))] + ) + qscript = qscript.expand( + stop_at=lambda obj: qml.devices.default_qubit.stopping_condition(obj) + ) + return execute([qscript], device, **execute_kwargs)[0] + + a = jax.numpy.array(0.1) + p = jax.numpy.array([0.1, 0.2, 0.3]) + + dev = qml.device("default.qubit", wires=1) + res = jax.jit(cost_fn, static_argnums=2)(a, p, device=dev) + expected = np.cos(a) * np.cos(p[1]) * np.sin(p[0]) + np.sin(a) * ( + np.cos(p[2]) * np.sin(p[1]) + np.cos(p[0]) * np.cos(p[1]) * np.sin(p[2]) + ) + assert np.allclose(res, expected, atol=tol, rtol=0) + + jac_fn = jax.jit(jax.grad(cost_fn, argnums=1), static_argnums=2) + res = jac_fn(a, p, device=dev) + expected = jax.numpy.array( + [ + np.cos(p[1]) * (np.cos(a) * np.cos(p[0]) - np.sin(a) * np.sin(p[0]) * np.sin(p[2])), + np.cos(p[1]) * np.cos(p[2]) * np.sin(a) + - np.sin(p[1]) + * (np.cos(a) * np.sin(p[0]) + np.cos(p[0]) * np.sin(a) * np.sin(p[2])), + np.sin(a) + * (np.cos(p[0]) * np.cos(p[1]) * np.cos(p[2]) - np.sin(p[1]) * np.sin(p[2])), + ] + ) + assert np.allclose(res, expected, atol=tol, rtol=0) + + def test_independent_expval(self, execute_kwargs): + """Tests computing an expectation value that is independent of trainable + parameters.""" + dev = qml.device("default.qubit", wires=2) + params = jax.numpy.array([0.1, 0.2, 0.3]) + + def cost(a, cache): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.RY(a[2], wires=0) + qml.expval(qml.PauliZ(1)) + + tape = qml.tape.QuantumScript.from_queue(q) + + res = execute([tape], dev, cache=cache, **execute_kwargs) + return res[0] + + res = jax.jit(jax.grad(cost), static_argnums=1)(params, cache=None) + assert res.shape == (3,) + + +@pytest.mark.parametrize("execute_kwargs", execute_kwargs_integration) +class TestVectorValuedJIT: + """Test vector-valued returns for the JAX-JIT interface.""" + + @pytest.mark.parametrize( + "ret_type, shape, expected_type", + [ + ([qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))], (), tuple), + ([qml.probs(wires=[0, 1])], (4,), jax.numpy.ndarray), + ([qml.probs()], (4,), jax.numpy.ndarray), + ], + ) + def test_shapes(self, execute_kwargs, ret_type, shape, expected_type): + """Test the shape of the result of vector-valued QNodes.""" + adjoint = execute_kwargs.get("gradient_kwargs", {}).get("method", "") == "adjoint_jacobian" + if adjoint: + pytest.skip("The adjoint diff method doesn't support probabilities.") + + dev = qml.device("default.qubit", wires=2) + params = jax.numpy.array([0.1, 0.2, 0.3]) + + def cost(a, cache): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.RY(a[2], wires=0) + for r in ret_type: + qml.apply(r) + + tape = qml.tape.QuantumScript.from_queue(q) + + res = qml.execute([tape], dev, cache=cache, **execute_kwargs) + return res[0] + + res = jax.jit(cost)(params, cache=None) + assert isinstance(res, expected_type) + + if expected_type is tuple: + for r in res: + assert r.shape == shape + else: + assert res.shape == shape + + def test_independent_expval(self, execute_kwargs): + """Tests computing an expectation value that is independent of trainable + parameters.""" + dev = qml.device("default.qubit", wires=2) + params = jax.numpy.array([0.1, 0.2, 0.3]) + + def cost(a, cache): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.RY(a[2], wires=0) + qml.expval(qml.PauliZ(1)) + + tape = qml.tape.QuantumScript.from_queue(q) + + res = qml.execute([tape], dev, cache=cache, **execute_kwargs) + return res[0] + + res = jax.jit(jax.grad(cost), static_argnums=1)(params, cache=None) + assert res.shape == (3,) + + ret_and_output_dim = [ + ([qml.probs(wires=0)], (2,), jax.numpy.ndarray), + ([qml.state()], (4,), jax.numpy.ndarray), + ([qml.density_matrix(wires=0)], (2, 2), jax.numpy.ndarray), + # Multi measurements + ([qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))], (), tuple), + ([qml.var(qml.PauliZ(0)), qml.var(qml.PauliZ(1))], (), tuple), + ([qml.probs(wires=0), qml.probs(wires=1)], (2,), tuple), + ] + + @pytest.mark.parametrize("ret, out_dim, expected_type", ret_and_output_dim) + def test_vector_valued_qnode(self, execute_kwargs, ret, out_dim, expected_type): + """Tests the shape of vector-valued QNode results.""" + + dev = qml.device("default.qubit", wires=2) + params = jax.numpy.array([0.1, 0.2, 0.3]) + grad_meth = ( + execute_kwargs["gradient_kwargs"]["method"] + if "gradient_kwargs" in execute_kwargs + else "" + ) + if "adjoint" in grad_meth and any( + r.return_type + in (qml.measurements.Probability, qml.measurements.State, qml.measurements.Variance) + for r in ret + ): + pytest.skip("Adjoint does not support probs") + + def cost(a, cache): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.RY(a[2], wires=0) + + for r in ret: + qml.apply(r) + + tape = qml.tape.QuantumScript.from_queue(q) + + res = qml.execute([tape], dev, cache=cache, **execute_kwargs)[0] + return res + + res = jax.jit(cost, static_argnums=1)(params, cache=None) + + assert isinstance(res, expected_type) + if expected_type is tuple: + for r in res: + assert r.shape == out_dim + else: + assert res.shape == out_dim + + def test_qnode_sample(self, execute_kwargs): + """Tests computing multiple expectation values in a tape.""" + dev = qml.device("default.qubit", wires=2, shots=10) + params = jax.numpy.array([0.1, 0.2, 0.3]) + + grad_meth = ( + execute_kwargs["gradient_kwargs"]["method"] + if "gradient_kwargs" in execute_kwargs + else "" + ) + if "adjoint" in grad_meth or "backprop" in grad_meth: + pytest.skip("Adjoint does not support probs") + + def cost(a, cache): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.RY(a[2], wires=0) + qml.sample(qml.PauliZ(0)) + + tape = qml.tape.QuantumScript.from_queue(q, shots=dev.shots) + + res = qml.execute([tape], dev, cache=cache, **execute_kwargs)[0] + return res + + res = jax.jit(cost, static_argnums=1)(params, cache=None) + + assert res.shape == (dev.shots.total_shots,) + + def test_multiple_expvals_grad(self, execute_kwargs): + """Tests computing multiple expectation values in a tape.""" + dev = qml.device("default.qubit", wires=2) + params = jax.numpy.array([0.1, 0.2, 0.3]) + + def cost(a, cache): + with qml.queuing.AnnotatedQueue() as q: + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + qml.RY(a[2], wires=0) + qml.expval(qml.PauliZ(0)) + qml.expval(qml.PauliZ(1)) + + tape = qml.tape.QuantumScript.from_queue(q) + + res = qml.execute([tape], dev, cache=cache, **execute_kwargs)[0] + return res[0] + res[1] + + res = jax.jit(jax.grad(cost), static_argnums=1)(params, cache=None) + assert res.shape == (3,) + + def test_multi_tape_jacobian_probs_expvals(self, execute_kwargs): + """Test the jacobian computation with multiple tapes with probability + and expectation value computations.""" + adjoint = execute_kwargs.get("gradient_kwargs", {}).get("method", "") == "adjoint_jacobian" + if adjoint: + pytest.skip("The adjoint diff method doesn't support probabilities.") + + def cost(x, y, device, interface, ek): + with qml.queuing.AnnotatedQueue() as q1: + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + qml.expval(qml.PauliZ(0)) + qml.expval(qml.PauliZ(1)) + + tape1 = qml.tape.QuantumScript.from_queue(q1) + + with qml.queuing.AnnotatedQueue() as q2: + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + qml.probs(wires=[0]) + qml.probs(wires=[1]) + + tape2 = qml.tape.QuantumScript.from_queue(q2) + + return qml.execute([tape1, tape2], device, **ek, interface=interface)[0] + + dev = qml.device("default.qubit", wires=2) + x = jax.numpy.array(0.543) + y = jax.numpy.array(-0.654) + + x_ = np.array(0.543) + y_ = np.array(-0.654) + + res = cost(x, y, dev, interface="jax-jit", ek=execute_kwargs) + + exp = cost(x_, y_, dev, interface="autograd", ek=execute_kwargs) + + for r, e in zip(res, exp): + assert jax.numpy.allclose(r, e, atol=1e-7) + + +def test_diff_method_None_jit(): + """Test that jitted execution works when `gradient_fn=None`.""" + + dev = qml.device("default.qubit.jax", wires=1, shots=10) + + @jax.jit + def wrapper(x): + with qml.queuing.AnnotatedQueue() as q: + qml.RX(x, wires=0) + qml.expval(qml.PauliZ(0)) + + tape = qml.tape.QuantumScript.from_queue(q) + + return qml.execute([tape], dev, gradient_fn=None) + + assert jax.numpy.allclose(wrapper(jax.numpy.array(0.0))[0], 1.0) diff --git a/tests/interfaces/default_qubit_2_integration/test_jax_jit_qnode_default_qubit_2.py b/tests/interfaces/default_qubit_integration/test_jax_jit_qnode.py similarity index 100% rename from tests/interfaces/default_qubit_2_integration/test_jax_jit_qnode_default_qubit_2.py rename to tests/interfaces/default_qubit_integration/test_jax_jit_qnode.py diff --git a/tests/interfaces/default_qubit_2_integration/test_jax_qnode_default_qubit_2.py b/tests/interfaces/default_qubit_integration/test_jax_qnode.py similarity index 100% rename from tests/interfaces/default_qubit_2_integration/test_jax_qnode_default_qubit_2.py rename to tests/interfaces/default_qubit_integration/test_jax_qnode.py diff --git a/tests/interfaces/default_qubit_integration/test_jax_qnode_shot_vector.py b/tests/interfaces/default_qubit_integration/test_jax_qnode_shot_vector.py new file mode 100644 index 00000000000..697a1e90223 --- /dev/null +++ b/tests/interfaces/default_qubit_integration/test_jax_qnode_shot_vector.py @@ -0,0 +1,883 @@ +# Copyright 2022 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. +"""Integration tests for using the jax interface with shot vectors and with a QNode""" +# pylint: disable=too-many-arguments,too-many-public-methods +import pytest +from flaky import flaky + +import pennylane as qml +from pennylane import numpy as np +from pennylane import qnode + +pytestmark = pytest.mark.jax + +jax = pytest.importorskip("jax") +jax.config.update("jax_enable_x64", True) + +all_shots = [(1, 20, 100), (1, (20, 1), 100), (1, (5, 4), 100)] + +qubit_device_and_diff_method = [ + ["default.qubit", "finite-diff", {"h": 10e-2}], + ["default.qubit", "parameter-shift", {}], + ["default.qubit", "spsa", {"h": 10e-2, "num_directions": 20}], +] + +interface_and_qubit_device_and_diff_method = [ + ["jax"] + inner_list for inner_list in qubit_device_and_diff_method +] + +TOLS = { + "finite-diff": 0.3, + "parameter-shift": 1e-2, + "spsa": 0.32, +} + +jacobian_fn = [jax.jacobian, jax.jacrev, jax.jacfwd] + + +@pytest.mark.parametrize("shots", all_shots) +@pytest.mark.parametrize( + "interface,dev_name,diff_method,gradient_kwargs", interface_and_qubit_device_and_diff_method +) +class TestReturnWithShotVectors: + """Class to test the shape of the Grad/Jacobian/Hessian with different return types and shot vectors.""" + + @pytest.mark.parametrize("jacobian", jacobian_fn) + def test_jac_single_measurement_param( + self, dev_name, diff_method, gradient_kwargs, shots, jacobian, interface + ): + """For one measurement and one param, the gradient is a float.""" + dev = qml.device(dev_name, wires=1, shots=shots) + + @qnode(dev, interface=interface, diff_method=diff_method, **gradient_kwargs) + def circuit(a): + qml.RY(a, wires=0) + qml.RX(0.2, wires=0) + return qml.expval(qml.PauliZ(0)) + + a = jax.numpy.array(0.1) + + jac = jacobian(circuit)(a) + + assert isinstance(jac, tuple) + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(jac) == num_copies + for j in jac: + assert isinstance(j, jax.numpy.ndarray) + assert j.shape == () + + @pytest.mark.parametrize("jacobian", jacobian_fn) + def test_jac_single_measurement_multiple_param( + self, dev_name, diff_method, gradient_kwargs, shots, jacobian, interface + ): + """For one measurement and multiple param, the gradient is a tuple of arrays.""" + dev = qml.device(dev_name, wires=1, shots=shots) + + @qnode(dev, interface=interface, diff_method=diff_method, **gradient_kwargs) + def circuit(a, b): + qml.RY(a, wires=0) + qml.RX(b, wires=0) + return qml.expval(qml.PauliZ(0)) + + a = jax.numpy.array(0.1) + b = jax.numpy.array(0.2) + + jac = jacobian(circuit, argnums=[0, 1])(a, b) + + assert isinstance(jac, tuple) + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(jac) == num_copies + for j in jac: + assert isinstance(j, tuple) + assert len(j) == 2 + assert j[0].shape == () + assert j[1].shape == () + + @pytest.mark.parametrize("jacobian", jacobian_fn) + def test_jacobian_single_measurement_multiple_param_array( + self, dev_name, diff_method, gradient_kwargs, shots, jacobian, interface + ): + """For one measurement and multiple param as a single array params, the gradient is an array.""" + dev = qml.device(dev_name, wires=1, shots=shots) + + @qnode(dev, interface=interface, diff_method=diff_method, **gradient_kwargs) + def circuit(a): + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + return qml.expval(qml.PauliZ(0)) + + a = jax.numpy.array([0.1, 0.2]) + + jac = jacobian(circuit)(a) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(jac) == num_copies + for j in jac: + assert isinstance(j, jax.numpy.ndarray) + assert j.shape == (2,) + + @pytest.mark.parametrize("jacobian", jacobian_fn) + def test_jacobian_single_measurement_param_probs( + self, dev_name, diff_method, gradient_kwargs, shots, jacobian, interface + ): + """For a multi dimensional measurement (probs), check that a single array is returned with the correct + dimension""" + dev = qml.device(dev_name, wires=2, shots=shots) + + @qnode(dev, interface=interface, diff_method=diff_method, **gradient_kwargs) + def circuit(a): + qml.RY(a, wires=0) + qml.RX(0.2, wires=0) + return qml.probs(wires=[0, 1]) + + a = jax.numpy.array(0.1) + + jac = jacobian(circuit)(a) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(jac) == num_copies + for j in jac: + assert isinstance(j, jax.numpy.ndarray) + assert j.shape == (4,) + + @pytest.mark.parametrize("jacobian", jacobian_fn) + def test_jacobian_single_measurement_probs_multiple_param( + self, dev_name, diff_method, gradient_kwargs, shots, jacobian, interface + ): + """For a multi dimensional measurement (probs), check that a single tuple is returned containing arrays with + the correct dimension""" + dev = qml.device(dev_name, wires=2, shots=shots) + + @qnode(dev, interface=interface, diff_method=diff_method, **gradient_kwargs) + def circuit(a, b): + qml.RY(a, wires=0) + qml.RX(b, wires=0) + return qml.probs(wires=[0, 1]) + + a = jax.numpy.array(0.1) + b = jax.numpy.array(0.2) + + jac = jacobian(circuit, argnums=[0, 1])(a, b) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(jac) == num_copies + for j in jac: + assert isinstance(j, tuple) + + assert isinstance(j[0], jax.numpy.ndarray) + assert j[0].shape == (4,) + + assert isinstance(j[1], jax.numpy.ndarray) + assert j[1].shape == (4,) + + @pytest.mark.parametrize("jacobian", jacobian_fn) + def test_jacobian_single_measurement_probs_multiple_param_single_array( + self, dev_name, diff_method, gradient_kwargs, shots, jacobian, interface + ): + """For a multi dimensional measurement (probs), check that a single tuple is returned containing arrays with + the correct dimension""" + dev = qml.device(dev_name, wires=2, shots=shots) + + @qnode(dev, interface=interface, diff_method=diff_method, **gradient_kwargs) + def circuit(a): + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + return qml.probs(wires=[0, 1]) + + a = jax.numpy.array([0.1, 0.2]) + + jac = jacobian(circuit)(a) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(jac) == num_copies + for j in jac: + assert isinstance(j, jax.numpy.ndarray) + assert j.shape == (4, 2) + + @pytest.mark.parametrize("jacobian", jacobian_fn) + def test_jacobian_expval_expval_multiple_params( + self, dev_name, diff_method, gradient_kwargs, shots, jacobian, interface + ): + """The hessian of multiple measurements with multiple params return a tuple of arrays.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + par_0 = jax.numpy.array(0.1) + par_1 = jax.numpy.array(0.2) + + @qnode(dev, interface=interface, diff_method=diff_method, max_diff=1, **gradient_kwargs) + def circuit(x, y): + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)), qml.expval(qml.PauliZ(0)) + + jac = jacobian(circuit, argnums=[0, 1])(par_0, par_1) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(jac) == num_copies + for j in jac: + assert isinstance(j, tuple) + + assert isinstance(j[0], tuple) + assert len(j[0]) == 2 + assert isinstance(j[0][0], jax.numpy.ndarray) + assert j[0][0].shape == () + assert isinstance(j[0][1], jax.numpy.ndarray) + assert j[0][1].shape == () + + assert isinstance(j[1], tuple) + assert len(j[1]) == 2 + assert isinstance(j[1][0], jax.numpy.ndarray) + assert j[1][0].shape == () + assert isinstance(j[1][1], jax.numpy.ndarray) + assert j[1][1].shape == () + + @pytest.mark.parametrize("jacobian", jacobian_fn) + def test_jacobian_expval_expval_multiple_params_array( + self, dev_name, diff_method, gradient_kwargs, shots, jacobian, interface + ): + """The jacobian of multiple measurements with a multiple params array return a single array.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + @qnode(dev, interface=interface, diff_method=diff_method, **gradient_kwargs) + def circuit(a): + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)), qml.expval(qml.PauliZ(0)) + + a = jax.numpy.array([0.1, 0.2]) + + jac = jacobian(circuit)(a) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(jac) == num_copies + for j in jac: + assert isinstance(j, tuple) + assert len(j) == 2 # measurements + + assert isinstance(j[0], jax.numpy.ndarray) + assert j[0].shape == (2,) + + assert isinstance(j[1], jax.numpy.ndarray) + assert j[1].shape == (2,) + + @pytest.mark.parametrize("jacobian", jacobian_fn) + def test_jacobian_var_var_multiple_params( + self, dev_name, diff_method, gradient_kwargs, shots, jacobian, interface + ): + """The hessian of multiple measurements with multiple params return a tuple of arrays.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + par_0 = jax.numpy.array(0.1) + par_1 = jax.numpy.array(0.2) + + @qnode(dev, interface=interface, diff_method=diff_method, **gradient_kwargs) + def circuit(x, y): + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.var(qml.PauliZ(0) @ qml.PauliX(1)), qml.var(qml.PauliZ(0)) + + jac = jacobian(circuit, argnums=[0, 1])(par_0, par_1) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(jac) == num_copies + for j in jac: + assert isinstance(j, tuple) + assert len(j) == 2 + + assert isinstance(j[0], tuple) + assert len(j[0]) == 2 + assert isinstance(j[0][0], jax.numpy.ndarray) + assert j[0][0].shape == () + assert isinstance(j[0][1], jax.numpy.ndarray) + assert j[0][1].shape == () + + assert isinstance(j[1], tuple) + assert len(j[1]) == 2 + assert isinstance(j[1][0], jax.numpy.ndarray) + assert j[1][0].shape == () + assert isinstance(j[1][1], jax.numpy.ndarray) + assert j[1][1].shape == () + + @pytest.mark.parametrize("jacobian", jacobian_fn) + def test_jacobian_var_var_multiple_params_array( + self, dev_name, diff_method, gradient_kwargs, shots, jacobian, interface + ): + """The jacobian of multiple measurements with a multiple params array return a single array.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + @qnode(dev, interface=interface, diff_method=diff_method, **gradient_kwargs) + def circuit(a): + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + return qml.var(qml.PauliZ(0) @ qml.PauliX(1)), qml.var(qml.PauliZ(0)) + + a = jax.numpy.array([0.1, 0.2]) + + jac = jacobian(circuit)(a) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(jac) == num_copies + for j in jac: + assert isinstance(j, tuple) + assert len(j) == 2 # measurements + + assert isinstance(j[0], jax.numpy.ndarray) + assert j[0].shape == (2,) + + assert isinstance(j[1], jax.numpy.ndarray) + assert j[1].shape == (2,) + + @pytest.mark.parametrize("jacobian", jacobian_fn) + def test_jacobian_multiple_measurement_single_param( + self, dev_name, diff_method, gradient_kwargs, shots, jacobian, interface + ): + """The jacobian of multiple measurements with a single params return an array.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + @qnode(dev, interface=interface, diff_method=diff_method, **gradient_kwargs) + def circuit(a): + qml.RY(a, wires=0) + qml.RX(0.2, wires=0) + return qml.expval(qml.PauliZ(0)), qml.probs(wires=[0, 1]) + + a = jax.numpy.array(0.1) + + jac = jacobian(circuit)(a) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(jac) == num_copies + for j in jac: + assert isinstance(jac, tuple) + assert len(j) == 2 + + assert isinstance(j[0], jax.numpy.ndarray) + assert j[0].shape == () + + assert isinstance(j[1], jax.numpy.ndarray) + assert j[1].shape == (4,) + + @pytest.mark.parametrize("jacobian", jacobian_fn) + def test_jacobian_multiple_measurement_multiple_param( + self, dev_name, diff_method, gradient_kwargs, shots, jacobian, interface + ): + """The jacobian of multiple measurements with a multiple params return a tuple of arrays.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + @qnode(dev, interface=interface, diff_method=diff_method, **gradient_kwargs) + def circuit(a, b): + qml.RY(a, wires=0) + qml.RX(b, wires=0) + return qml.expval(qml.PauliZ(0)), qml.probs(wires=[0, 1]) + + a = np.array(0.1, requires_grad=True) + b = np.array(0.2, requires_grad=True) + + jac = jacobian(circuit, argnums=[0, 1])(a, b) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(jac) == num_copies + for j in jac: + assert isinstance(j, tuple) + assert len(j) == 2 + + assert isinstance(j[0], tuple) + assert len(j[0]) == 2 + assert isinstance(j[0][0], jax.numpy.ndarray) + assert j[0][0].shape == () + assert isinstance(j[0][1], jax.numpy.ndarray) + assert j[0][1].shape == () + + assert isinstance(j[1], tuple) + assert len(j[1]) == 2 + assert isinstance(j[1][0], jax.numpy.ndarray) + assert j[1][0].shape == (4,) + assert isinstance(j[1][1], jax.numpy.ndarray) + assert j[1][1].shape == (4,) + + @pytest.mark.parametrize("jacobian", jacobian_fn) + def test_jacobian_multiple_measurement_multiple_param_array( + self, dev_name, diff_method, gradient_kwargs, shots, jacobian, interface + ): + """The jacobian of multiple measurements with a multiple params array return a single array.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + @qnode(dev, interface=interface, diff_method=diff_method, **gradient_kwargs) + def circuit(a): + qml.RY(a[0], wires=0) + qml.RX(a[1], wires=0) + return qml.expval(qml.PauliZ(0)), qml.probs(wires=[0, 1]) + + a = jax.numpy.array([0.1, 0.2]) + + jac = jacobian(circuit)(a) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(jac) == num_copies + for j in jac: + assert isinstance(j, tuple) + assert len(j) == 2 # measurements + + assert isinstance(j[0], jax.numpy.ndarray) + assert j[0].shape == (2,) + + assert isinstance(j[1], jax.numpy.ndarray) + assert j[1].shape == (4, 2) + + def test_hessian_expval_multiple_params( + self, dev_name, diff_method, gradient_kwargs, shots, interface + ): + """The hessian of single a measurement with multiple params return a tuple of arrays.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + par_0 = jax.numpy.array(0.1) + par_1 = jax.numpy.array(0.2) + + @qnode(dev, interface=interface, diff_method=diff_method, max_diff=2, **gradient_kwargs) + def circuit(x, y): + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) + + hess = jax.hessian(circuit, argnums=[0, 1])(par_0, par_1) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(hess) == num_copies + for h in hess: + assert isinstance(hess, tuple) + assert len(h) == 2 + + assert isinstance(h[0], tuple) + assert len(h[0]) == 2 + assert isinstance(h[0][0], jax.numpy.ndarray) + assert h[0][0].shape == () + assert h[0][1].shape == () + + assert isinstance(h[1], tuple) + assert len(h[1]) == 2 + assert isinstance(h[1][0], jax.numpy.ndarray) + assert h[1][0].shape == () + assert h[1][1].shape == () + + def test_hessian_expval_multiple_param_array( + self, dev_name, diff_method, gradient_kwargs, shots, interface + ): + """The hessian of single measurement with a multiple params array return a single array.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + params = jax.numpy.array([0.1, 0.2]) + + @qnode(dev, interface=interface, diff_method=diff_method, max_diff=2, **gradient_kwargs) + def circuit(x): + qml.RX(x[0], wires=[0]) + qml.RY(x[1], wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) + + hess = jax.hessian(circuit)(params) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(hess) == num_copies + for h in hess: + assert isinstance(h, jax.numpy.ndarray) + assert h.shape == (2, 2) + + def test_hessian_var_multiple_params( + self, dev_name, diff_method, gradient_kwargs, shots, interface + ): + """The hessian of single a measurement with multiple params return a tuple of arrays.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + par_0 = jax.numpy.array(0.1) + par_1 = jax.numpy.array(0.2) + + @qnode(dev, interface=interface, diff_method=diff_method, max_diff=2, **gradient_kwargs) + def circuit(x, y): + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.var(qml.PauliZ(0) @ qml.PauliX(1)) + + hess = jax.hessian(circuit, argnums=[0, 1])(par_0, par_1) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(hess) == num_copies + for h in hess: + assert isinstance(h, tuple) + assert len(h) == 2 + + assert isinstance(h[0], tuple) + assert len(h[0]) == 2 + assert isinstance(h[0][0], jax.numpy.ndarray) + assert h[0][0].shape == () + assert h[0][1].shape == () + + assert isinstance(h[1], tuple) + assert len(h[1]) == 2 + assert isinstance(h[1][0], jax.numpy.ndarray) + assert h[1][0].shape == () + assert h[1][1].shape == () + + def test_hessian_var_multiple_param_array( + self, dev_name, diff_method, gradient_kwargs, shots, interface + ): + """The hessian of single measurement with a multiple params array return a single array.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + params = jax.numpy.array([0.1, 0.2]) + + @qnode(dev, interface=interface, diff_method=diff_method, max_diff=2, **gradient_kwargs) + def circuit(x): + qml.RX(x[0], wires=[0]) + qml.RY(x[1], wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.var(qml.PauliZ(0) @ qml.PauliX(1)) + + hess = jax.hessian(circuit)(params) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(hess) == num_copies + for h in hess: + assert isinstance(h, jax.numpy.ndarray) + assert h.shape == (2, 2) + + def test_hessian_probs_expval_multiple_params( + self, dev_name, diff_method, gradient_kwargs, shots, interface + ): + """The hessian of multiple measurements with multiple params return a tuple of arrays.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + par_0 = jax.numpy.array(0.1) + par_1 = jax.numpy.array(0.2) + + @qnode(dev, interface=interface, diff_method=diff_method, max_diff=2, **gradient_kwargs) + def circuit(x, y): + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)), qml.probs(wires=[0]) + + hess = jax.hessian(circuit, argnums=[0, 1])(par_0, par_1) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(hess) == num_copies + for h in hess: + assert isinstance(h, tuple) + assert len(h) == 2 + + assert isinstance(h[0], tuple) + assert len(h[0]) == 2 + assert isinstance(h[0][0], tuple) + assert len(h[0][0]) == 2 + assert isinstance(h[0][0][0], jax.numpy.ndarray) + assert h[0][0][0].shape == () + assert isinstance(h[0][0][1], jax.numpy.ndarray) + assert h[0][0][1].shape == () + assert isinstance(h[0][1], tuple) + assert len(h[0][1]) == 2 + assert isinstance(h[0][1][0], jax.numpy.ndarray) + assert h[0][1][0].shape == () + assert isinstance(h[0][1][1], jax.numpy.ndarray) + assert h[0][1][1].shape == () + + assert isinstance(h[1], tuple) + assert len(h[1]) == 2 + assert isinstance(h[1][0], tuple) + assert len(h[1][0]) == 2 + assert isinstance(h[1][0][0], jax.numpy.ndarray) + assert h[1][0][0].shape == (2,) + assert isinstance(h[1][0][1], jax.numpy.ndarray) + assert h[1][0][1].shape == (2,) + assert isinstance(h[1][1], tuple) + assert len(h[1][1]) == 2 + assert isinstance(h[1][1][0], jax.numpy.ndarray) + assert h[1][1][0].shape == (2,) + assert isinstance(h[1][1][1], jax.numpy.ndarray) + assert h[1][1][1].shape == (2,) + + def test_hessian_expval_probs_multiple_param_array( + self, dev_name, diff_method, gradient_kwargs, shots, interface + ): + """The hessian of multiple measurements with a multiple param array return a single array.""" + if diff_method == "adjoint": + pytest.skip("Test does not supports adjoint because second order diff.") + + dev = qml.device(dev_name, wires=2, shots=shots) + + params = jax.numpy.array([0.1, 0.2]) + + @qnode(dev, interface=interface, diff_method=diff_method, max_diff=2, **gradient_kwargs) + def circuit(x): + qml.RX(x[0], wires=[0]) + qml.RY(x[1], wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)), qml.probs(wires=[0]) + + hess = jax.hessian(circuit)(params) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(hess) == num_copies + for h in hess: + assert isinstance(h, tuple) + assert len(h) == 2 + + assert isinstance(h[0], jax.numpy.ndarray) + assert h[0].shape == (2, 2) + + assert isinstance(h[1], jax.numpy.ndarray) + assert h[1].shape == (2, 2, 2) + + def test_hessian_probs_var_multiple_params( + self, dev_name, diff_method, gradient_kwargs, shots, interface + ): + """The hessian of multiple measurements with multiple params return a tuple of arrays.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + par_0 = qml.numpy.array(0.1) + par_1 = qml.numpy.array(0.2) + + @qnode(dev, interface=interface, diff_method=diff_method, max_diff=2, **gradient_kwargs) + def circuit(x, y): + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.var(qml.PauliZ(0) @ qml.PauliX(1)), qml.probs(wires=[0]) + + hess = jax.hessian(circuit, argnums=[0, 1])(par_0, par_1) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(hess) == num_copies + for h in hess: + assert isinstance(h, tuple) + assert len(h) == 2 + + assert isinstance(h[0], tuple) + assert len(h[0]) == 2 + assert isinstance(h[0][0], tuple) + assert len(h[0][0]) == 2 + assert isinstance(h[0][0][0], jax.numpy.ndarray) + assert h[0][0][0].shape == () + assert isinstance(h[0][0][1], jax.numpy.ndarray) + assert h[0][0][1].shape == () + assert isinstance(h[0][1], tuple) + assert len(h[0][1]) == 2 + assert isinstance(h[0][1][0], jax.numpy.ndarray) + assert h[0][1][0].shape == () + assert isinstance(h[0][1][1], jax.numpy.ndarray) + assert h[0][1][1].shape == () + + assert isinstance(h[1], tuple) + assert len(h[1]) == 2 + assert isinstance(h[1][0], tuple) + assert len(h[1][0]) == 2 + assert isinstance(h[1][0][0], jax.numpy.ndarray) + assert h[1][0][0].shape == (2,) + assert isinstance(h[1][0][1], jax.numpy.ndarray) + assert h[1][0][1].shape == (2,) + assert isinstance(h[1][1], tuple) + assert len(h[1][1]) == 2 + assert isinstance(h[1][1][0], jax.numpy.ndarray) + assert h[1][1][0].shape == (2,) + assert isinstance(h[1][1][1], jax.numpy.ndarray) + assert h[1][1][1].shape == (2,) + + def test_hessian_var_probs_multiple_param_array( + self, dev_name, diff_method, gradient_kwargs, shots, interface + ): + """The hessian of multiple measurements with a multiple param array return a single array.""" + dev = qml.device(dev_name, wires=2, shots=shots) + + params = jax.numpy.array([0.1, 0.2]) + + @qnode(dev, interface=interface, diff_method=diff_method, max_diff=2, **gradient_kwargs) + def circuit(x): + qml.RX(x[0], wires=[0]) + qml.RY(x[1], wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.var(qml.PauliZ(0) @ qml.PauliX(1)), qml.probs(wires=[0]) + + hess = jax.hessian(circuit)(params) + + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(hess) == num_copies + for h in hess: + assert isinstance(h, tuple) + assert len(h) == 2 + + assert isinstance(h[0], jax.numpy.ndarray) + assert h[0].shape == (2, 2) + + assert isinstance(h[1], jax.numpy.ndarray) + assert h[1].shape == (2, 2, 2) + + +qubit_device_and_diff_method = [ + ["default.qubit", "finite-diff", {"h": 10e-2}], + ["default.qubit", "parameter-shift", {}], +] + +shots_large = [(1000000, 900000, 800000), (1000000, (900000, 2))] + + +@flaky(max_runs=5) +@pytest.mark.parametrize("shots", shots_large) +@pytest.mark.parametrize( + "interface,dev_name,diff_method,gradient_kwargs", interface_and_qubit_device_and_diff_method +) +class TestReturnShotVectorIntegration: + """Tests for the integration of shots with the Jax interface.""" + + @pytest.mark.parametrize("jacobian", jacobian_fn) + def test_single_expectation_value( + self, dev_name, diff_method, gradient_kwargs, shots, jacobian, interface + ): + """Tests correct output shape and evaluation for a tape + with a single expval output""" + dev = qml.device(dev_name, wires=2, shots=shots) + x = jax.numpy.array(0.543) + y = jax.numpy.array(-0.654) + + @qnode(dev, interface=interface, diff_method=diff_method, **gradient_kwargs) + def circuit(x, y): + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0) @ qml.PauliX(1)) + + expected = np.array([[-np.sin(y) * np.sin(x), np.cos(y) * np.cos(x)]]) + all_res = jacobian(circuit, argnums=[0, 1])(x, y) + + assert isinstance(all_res, tuple) + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(all_res) == num_copies + + for res in all_res: + assert isinstance(res[0], jax.numpy.ndarray) + assert res[0].shape == () + + assert isinstance(res[1], jax.numpy.ndarray) + assert res[1].shape == () + tol = TOLS[diff_method] + assert np.allclose(res, expected, atol=tol, rtol=0) + + @pytest.mark.parametrize("jacobian", jacobian_fn) + def test_prob_expectation_values( + self, dev_name, diff_method, gradient_kwargs, shots, jacobian, interface + ): + """Tests correct output shape and evaluation for a tape + with prob and expval outputs""" + dev = qml.device(dev_name, wires=2, shots=shots) + x = jax.numpy.array(0.543) + y = jax.numpy.array(-0.654) + + @qnode(dev, interface=interface, diff_method=diff_method, **gradient_kwargs) + def circuit(x, y): + qml.RX(x, wires=[0]) + qml.RY(y, wires=[1]) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0)), qml.probs(wires=[0, 1]) + + all_res = jacobian(circuit, argnums=[0, 1])(x, y) + + tol = TOLS[diff_method] + + assert isinstance(all_res, tuple) + num_copies = sum( + [1 for x in shots if isinstance(x, int)] + [x[1] for x in shots if isinstance(x, tuple)] + ) + assert len(all_res) == num_copies + + for res in all_res: + assert isinstance(res, tuple) + assert len(res) == 2 + + assert isinstance(res[0], tuple) + assert len(res[0]) == 2 + assert np.allclose(res[0][0], -np.sin(x), atol=tol, rtol=0) + assert isinstance(res[0][0], jax.numpy.ndarray) + assert np.allclose(res[0][1], 0, atol=tol, rtol=0) + assert isinstance(res[0][1], jax.numpy.ndarray) + + assert isinstance(res[1], tuple) + assert len(res[1]) == 2 + assert np.allclose( + res[1][0], + [ + -(np.cos(y / 2) ** 2 * np.sin(x)) / 2, + -(np.sin(x) * np.sin(y / 2) ** 2) / 2, + (np.sin(x) * np.sin(y / 2) ** 2) / 2, + (np.cos(y / 2) ** 2 * np.sin(x)) / 2, + ], + atol=tol, + rtol=0, + ) + assert isinstance(res[1][0], jax.numpy.ndarray) + assert np.allclose( + res[1][1], + [ + -(np.cos(x / 2) ** 2 * np.sin(y)) / 2, + (np.cos(x / 2) ** 2 * np.sin(y)) / 2, + (np.sin(x / 2) ** 2 * np.sin(y)) / 2, + -(np.sin(x / 2) ** 2 * np.sin(y)) / 2, + ], + atol=tol, + rtol=0, + ) + assert isinstance(res[1][1], jax.numpy.ndarray) diff --git a/tests/interfaces/default_qubit_integration/test_set_shots.py b/tests/interfaces/default_qubit_integration/test_set_shots.py new file mode 100644 index 00000000000..efe8c9cf7ca --- /dev/null +++ b/tests/interfaces/default_qubit_integration/test_set_shots.py @@ -0,0 +1,31 @@ +# Copyright 2018-2023 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 workflow.set_shots +""" + +import pytest + +import pennylane as qml +from pennylane.workflow import set_shots + + +def test_shots_new_device_interface(): + """Test that calling set_shots on a device implementing the new interface leaves it + untouched. + """ + dev = qml.devices.DefaultQubit() + with pytest.raises(ValueError): + with set_shots(dev, 10): + pass diff --git a/tests/interfaces/default_qubit_2_integration/test_tensorflow_default_qubit_2.py b/tests/interfaces/default_qubit_integration/test_tensorflow.py similarity index 100% rename from tests/interfaces/default_qubit_2_integration/test_tensorflow_default_qubit_2.py rename to tests/interfaces/default_qubit_integration/test_tensorflow.py diff --git a/tests/interfaces/default_qubit_2_integration/test_tensorflow_autograph_qnode_shot_vector_default_qubit_2.py b/tests/interfaces/default_qubit_integration/test_tensorflow_autograph_qnode_shot_vector.py similarity index 100% rename from tests/interfaces/default_qubit_2_integration/test_tensorflow_autograph_qnode_shot_vector_default_qubit_2.py rename to tests/interfaces/default_qubit_integration/test_tensorflow_autograph_qnode_shot_vector.py diff --git a/tests/interfaces/default_qubit_2_integration/test_tensorflow_qnode_default_qubit_2.py b/tests/interfaces/default_qubit_integration/test_tensorflow_qnode.py similarity index 100% rename from tests/interfaces/default_qubit_2_integration/test_tensorflow_qnode_default_qubit_2.py rename to tests/interfaces/default_qubit_integration/test_tensorflow_qnode.py diff --git a/tests/interfaces/default_qubit_2_integration/test_tensorflow_qnode_shot_vector_default_qubit_2.py b/tests/interfaces/default_qubit_integration/test_tensorflow_qnode_shot_vector.py similarity index 100% rename from tests/interfaces/default_qubit_2_integration/test_tensorflow_qnode_shot_vector_default_qubit_2.py rename to tests/interfaces/default_qubit_integration/test_tensorflow_qnode_shot_vector.py diff --git a/tests/interfaces/default_qubit_2_integration/test_torch_default_qubit_2.py b/tests/interfaces/default_qubit_integration/test_torch.py similarity index 100% rename from tests/interfaces/default_qubit_2_integration/test_torch_default_qubit_2.py rename to tests/interfaces/default_qubit_integration/test_torch.py diff --git a/tests/interfaces/default_qubit_2_integration/test_torch_qnode_default_qubit_2.py b/tests/interfaces/default_qubit_integration/test_torch_qnode.py similarity index 100% rename from tests/interfaces/default_qubit_2_integration/test_torch_qnode_default_qubit_2.py rename to tests/interfaces/default_qubit_integration/test_torch_qnode.py diff --git a/tests/interfaces/test_autograd.py b/tests/interfaces/legacy_devices_integration/test_autograd_legacy.py similarity index 100% rename from tests/interfaces/test_autograd.py rename to tests/interfaces/legacy_devices_integration/test_autograd_legacy.py diff --git a/tests/interfaces/test_autograd_qnode.py b/tests/interfaces/legacy_devices_integration/test_autograd_qnode_legacy.py similarity index 100% rename from tests/interfaces/test_autograd_qnode.py rename to tests/interfaces/legacy_devices_integration/test_autograd_qnode_legacy.py diff --git a/tests/interfaces/test_autograd_qnode_shot_vector.py b/tests/interfaces/legacy_devices_integration/test_autograd_qnode_shot_vector_legacy.py similarity index 100% rename from tests/interfaces/test_autograd_qnode_shot_vector.py rename to tests/interfaces/legacy_devices_integration/test_autograd_qnode_shot_vector_legacy.py diff --git a/tests/interfaces/test_execute.py b/tests/interfaces/legacy_devices_integration/test_execute_legacy.py similarity index 100% rename from tests/interfaces/test_execute.py rename to tests/interfaces/legacy_devices_integration/test_execute_legacy.py diff --git a/tests/interfaces/test_jax_jit.py b/tests/interfaces/legacy_devices_integration/test_jax_jit_legacy.py similarity index 100% rename from tests/interfaces/test_jax_jit.py rename to tests/interfaces/legacy_devices_integration/test_jax_jit_legacy.py diff --git a/tests/interfaces/test_jax_jit_qnode.py b/tests/interfaces/legacy_devices_integration/test_jax_jit_qnode_legacy.py similarity index 100% rename from tests/interfaces/test_jax_jit_qnode.py rename to tests/interfaces/legacy_devices_integration/test_jax_jit_qnode_legacy.py diff --git a/tests/interfaces/test_jax.py b/tests/interfaces/legacy_devices_integration/test_jax_legacy.py similarity index 100% rename from tests/interfaces/test_jax.py rename to tests/interfaces/legacy_devices_integration/test_jax_legacy.py diff --git a/tests/interfaces/test_jax_qnode.py b/tests/interfaces/legacy_devices_integration/test_jax_qnode_legacy.py similarity index 100% rename from tests/interfaces/test_jax_qnode.py rename to tests/interfaces/legacy_devices_integration/test_jax_qnode_legacy.py diff --git a/tests/interfaces/test_jax_qnode_shot_vector.py b/tests/interfaces/legacy_devices_integration/test_jax_qnode_shot_vector_legacy.py similarity index 100% rename from tests/interfaces/test_jax_qnode_shot_vector.py rename to tests/interfaces/legacy_devices_integration/test_jax_qnode_shot_vector_legacy.py diff --git a/tests/interfaces/test_set_shots.py b/tests/interfaces/legacy_devices_integration/test_set_shots_legacy.py similarity index 83% rename from tests/interfaces/test_set_shots.py rename to tests/interfaces/legacy_devices_integration/test_set_shots_legacy.py index 5e6a53b83d3..6e9739a631f 100644 --- a/tests/interfaces/test_set_shots.py +++ b/tests/interfaces/legacy_devices_integration/test_set_shots_legacy.py @@ -15,23 +15,12 @@ Tests for workflow.set_shots """ -import pytest import pennylane as qml from pennylane.measurements import Shots from pennylane.workflow import set_shots -def test_shots_new_device_interface(): - """Test that calling set_shots on a device implementing the new interface leaves it - untouched. - """ - dev = qml.devices.DefaultQubit() - with pytest.raises(ValueError): - with set_shots(dev, 10): - pass - - def test_set_with_shots_class(): """Test that shots can be set on the old device interface with a Shots class.""" diff --git a/tests/interfaces/test_tensorflow_autograph_qnode_shot_vector.py b/tests/interfaces/legacy_devices_integration/test_tensorflow_autograph_qnode_shot_vector_legacy.py similarity index 100% rename from tests/interfaces/test_tensorflow_autograph_qnode_shot_vector.py rename to tests/interfaces/legacy_devices_integration/test_tensorflow_autograph_qnode_shot_vector_legacy.py diff --git a/tests/interfaces/test_tensorflow.py b/tests/interfaces/legacy_devices_integration/test_tensorflow_legacy.py similarity index 100% rename from tests/interfaces/test_tensorflow.py rename to tests/interfaces/legacy_devices_integration/test_tensorflow_legacy.py diff --git a/tests/interfaces/test_tensorflow_qnode.py b/tests/interfaces/legacy_devices_integration/test_tensorflow_qnode_legacy.py similarity index 100% rename from tests/interfaces/test_tensorflow_qnode.py rename to tests/interfaces/legacy_devices_integration/test_tensorflow_qnode_legacy.py diff --git a/tests/interfaces/test_tensorflow_qnode_shot_vector.py b/tests/interfaces/legacy_devices_integration/test_tensorflow_qnode_shot_vector_legacy.py similarity index 100% rename from tests/interfaces/test_tensorflow_qnode_shot_vector.py rename to tests/interfaces/legacy_devices_integration/test_tensorflow_qnode_shot_vector_legacy.py diff --git a/tests/interfaces/test_torch.py b/tests/interfaces/legacy_devices_integration/test_torch_legacy.py similarity index 100% rename from tests/interfaces/test_torch.py rename to tests/interfaces/legacy_devices_integration/test_torch_legacy.py diff --git a/tests/interfaces/test_torch_qnode.py b/tests/interfaces/legacy_devices_integration/test_torch_qnode_legacy.py similarity index 100% rename from tests/interfaces/test_torch_qnode.py rename to tests/interfaces/legacy_devices_integration/test_torch_qnode_legacy.py diff --git a/tests/interfaces/test_transform_program_integration.py b/tests/interfaces/test_transform_program_integration.py index 4e82f725f54..704424dff8b 100644 --- a/tests/interfaces/test_transform_program_integration.py +++ b/tests/interfaces/test_transform_program_integration.py @@ -169,7 +169,7 @@ def split_sum_terms(tape): def test_chained_preprocessing(self): """Test a transform program with two transforms where their order affects the output.""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.qubit", wires=2) def null_postprocessing(results): return results[0] diff --git a/tests/math/test_functions.py b/tests/math/test_functions.py index ec0eb076419..789bf5e2568 100644 --- a/tests/math/test_functions.py +++ b/tests/math/test_functions.py @@ -2189,7 +2189,7 @@ def circuit(weights): def test_jax(self, tol): """Test that the covariance matrix computes the correct result, and is differentiable, using the JAX interface""" - dev = qml.device("default.qubit.jax", wires=3) + dev = qml.device("default.qubit", wires=3) @qml.qnode(dev, interface="jax", diff_method="backprop") def circuit(weights): diff --git a/tests/measurements/test_state.py b/tests/measurements/test_state.py index 07c8b8d5fe6..6dce16479d2 100644 --- a/tests/measurements/test_state.py +++ b/tests/measurements/test_state.py @@ -17,7 +17,7 @@ import pennylane as qml from pennylane import numpy as pnp -from pennylane.devices import DefaultQubitLegacy +from pennylane.devices import DefaultMixed from pennylane.math.matrix_manipulation import _permute_dense_matrix from pennylane.math.quantum import reduce_dm, reduce_statevector from pennylane.measurements import ( @@ -361,7 +361,7 @@ def func(x): def test_no_state_capability(self, monkeypatch): """Test if an error is raised for devices that are not capable of returning the state. This is tested by changing the capability of default.qubit""" - dev = qml.device("default.qubit.legacy", wires=1) + dev = qml.device("default.mixed", wires=1) capabilities = dev.capabilities().copy() capabilities["returns_state"] = False @@ -370,7 +370,7 @@ def func(): return state() with monkeypatch.context() as m: - m.setattr(DefaultQubitLegacy, "capabilities", lambda *args, **kwargs: capabilities) + m.setattr(DefaultMixed, "capabilities", lambda *args, **kwargs: capabilities) with pytest.raises(qml.QuantumFunctionError, match="The current device is not capable"): func() @@ -410,9 +410,9 @@ def test_default_qubit_tf(self, diff_method): """Test that the returned state is equal to the expected returned state for all of PennyLane's built in statevector devices""" - dev = qml.device("default.qubit.tf", wires=4) + dev = qml.device("default.qubit", wires=4) - @qml.qnode(dev, diff_method=diff_method) + @qml.qnode(dev, interface="tf", diff_method=diff_method) def func(): for i in range(4): qml.Hadamard(i) @@ -429,9 +429,9 @@ def test_default_qubit_autograd(self, diff_method): """Test that the returned state is equal to the expected returned state for all of PennyLane's built in statevector devices""" - dev = qml.device("default.qubit.autograd", wires=4) + dev = qml.device("default.qubit", wires=4) - @qml.qnode(dev, diff_method=diff_method) + @qml.qnode(dev, interface="autograd", diff_method=diff_method) def func(): for i in range(4): qml.Hadamard(i) @@ -444,11 +444,11 @@ def func(): @pytest.mark.tf def test_gradient_with_passthru_tf(self): - """Test that the gradient of the state is accessible when using default.qubit.tf with the - backprop diff_method.""" + """Test that the gradient of the state is accessible when using default.qubit with the + tf interface and backprop diff_method.""" import tensorflow as tf - dev = qml.device("default.qubit.tf", wires=1) + dev = qml.device("default.qubit", wires=1) @qml.qnode(dev, interface="tf", diff_method="backprop") def func(x): @@ -466,10 +466,10 @@ def func(x): @pytest.mark.autograd def test_gradient_with_passthru_autograd(self): - """Test that the gradient of the state is accessible when using default.qubit.autograd - with the backprop diff_method.""" + """Test that the gradient of the state is accessible when using default.qubit + with autograd interface and the backprop diff_method.""" - dev = qml.device("default.qubit.autograd", wires=1) + dev = qml.device("default.qubit", wires=1) @qml.qnode(dev, interface="autograd", diff_method="backprop") def func(x): @@ -1014,7 +1014,7 @@ def func(): def test_no_state_capability(self, monkeypatch): """Test if an error is raised for devices that are not capable of returning the density matrix. This is tested by changing the capability of default.qubit""" - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.mixed", wires=2) capabilities = dev.capabilities().copy() capabilities["returns_state"] = False @@ -1023,7 +1023,7 @@ def func(): return density_matrix(0) with monkeypatch.context() as m: - m.setattr(DefaultQubitLegacy, "capabilities", lambda *args, **kwargs: capabilities) + m.setattr(DefaultMixed, "capabilities", lambda *args, **kwargs: capabilities) with pytest.raises( qml.QuantumFunctionError, match="The current device is not capable" " of returning the state", diff --git a/tests/ops/op_math/test_evolution.py b/tests/ops/op_math/test_evolution.py index 6143e10226c..8033c180f64 100644 --- a/tests/ops/op_math/test_evolution.py +++ b/tests/ops/op_math/test_evolution.py @@ -172,7 +172,7 @@ def circ_param_shift(x): Evolution(base, -0.5 * x) return qml.expval(qml.PauliZ(0)) - @qml.qnode(qml.device("default.qubit.jax", wires=1), interface="jax") + @qml.qnode(qml.device("default.qubit"), interface="jax") def circ(x): Evolution(qml.PauliX(0), -0.5 * x) return qml.expval(qml.PauliZ(0)) diff --git a/tests/ops/qubit/test_parametric_ops.py b/tests/ops/qubit/test_parametric_ops.py index 6780cfe21a1..b20b5cc94bf 100644 --- a/tests/ops/qubit/test_parametric_ops.py +++ b/tests/ops/qubit/test_parametric_ops.py @@ -3464,9 +3464,9 @@ def test_simplify_rotations_grad_jax(self, op): import jax import jax.numpy as jnp - dev = qml.device("default.qubit.jax", wires=2) + dev = qml.device("default.qubit") - @qml.qnode(dev) + @qml.qnode(dev, interface="jax") def circuit(simplify, wires, *params, **hyperparams): if simplify: qml.simplify(op(*params, wires=wires, **hyperparams)) diff --git a/tests/ops/qubit/test_qchem_ops.py b/tests/ops/qubit/test_qchem_ops.py index ce3be78b948..f652da4c5a0 100644 --- a/tests/ops/qubit/test_qchem_ops.py +++ b/tests/ops/qubit/test_qchem_ops.py @@ -273,10 +273,10 @@ def test_autograd(self, excitation): """Tests that operations are computed correctly using the autograd interface""" - dev = qml.device("default.qubit.autograd", wires=2) + dev = qml.device("default.qubit") state = np.array([0, -1 / np.sqrt(2), 1 / np.sqrt(2), 0]) - @qml.qnode(dev) + @qml.qnode(dev, interface="autograd") def circuit(phi): qml.PauliX(wires=0) excitation(phi, wires=[0, 1]) @@ -298,9 +298,9 @@ def test_autograd_grad(self, diff_method, excitation, phi): """Tests that gradients are computed correctly using the autograd interface""" - dev = qml.device("default.qubit.autograd", wires=2) + dev = qml.device("default.qubit") - @qml.qnode(dev, diff_method=diff_method) + @qml.qnode(dev, diff_method=diff_method, interface="autograd") def circuit(phi): qml.PauliX(wires=0) excitation(phi, wires=[0, 1]) @@ -324,9 +324,9 @@ def test_tf(self, excitation, phi, diff_method): import tensorflow as tf - dev = qml.device("default.qubit.tf", wires=2) + dev = qml.device("default.qubit") - @qml.qnode(dev, diff_method=diff_method) + @qml.qnode(dev, diff_method=diff_method, interface="tf") def circuit(phi): qml.PauliX(wires=0) excitation(phi, wires=[0, 1]) @@ -355,9 +355,9 @@ def test_jax(self, excitation, phi, diff_method): import jax - dev = qml.device("default.qubit.jax", wires=2) + dev = qml.device("default.qubit") - @qml.qnode(dev, diff_method=diff_method) + @qml.qnode(dev, diff_method=diff_method, interface="jax") def circuit(phi): qml.PauliX(wires=0) excitation(phi, wires=[0, 1]) @@ -505,12 +505,12 @@ def test_autograd(self, excitation): """Tests that operations are computed correctly using the autograd interface""" - dev = qml.device("default.qubit.autograd", wires=4) + dev = qml.device("default.qubit") state = np.array( [0, 0, 0, -1 / np.sqrt(2), 0, 0, 0, 0, 0, 0, 0, 0, 1 / np.sqrt(2), 0, 0, 0] ) - @qml.qnode(dev) + @qml.qnode(dev, interface="autograd") def circuit(phi): qml.PauliX(wires=0) qml.PauliX(wires=1) @@ -528,12 +528,12 @@ def test_tf(self, excitation): """Tests that operations are computed correctly using the tensorflow interface""" - dev = qml.device("default.qubit.tf", wires=4) + dev = qml.device("default.qubit") state = np.array( [0, 0, 0, -1 / np.sqrt(2), 0, 0, 0, 0, 0, 0, 0, 0, 1 / np.sqrt(2), 0, 0, 0] ) - @qml.qnode(dev) + @qml.qnode(dev, interface="tf") def circuit(phi): qml.PauliX(wires=0) qml.PauliX(wires=1) @@ -551,12 +551,12 @@ def test_jax(self, excitation): """Tests that operations are computed correctly using the jax interface""" - dev = qml.device("default.qubit.jax", wires=4) + dev = qml.device("default.qubit") state = np.array( [0, 0, 0, -1 / np.sqrt(2), 0, 0, 0, 0, 0, 0, 0, 0, 1 / np.sqrt(2), 0, 0, 0] ) - @qml.qnode(dev) + @qml.qnode(dev, interface="jax") def circuit(phi): qml.PauliX(wires=0) qml.PauliX(wires=1) @@ -579,9 +579,9 @@ def test_autograd_grad(self, excitation, phi): """Tests that gradients are computed correctly using the autograd interface""" - dev = qml.device("default.qubit.autograd", wires=4) + dev = qml.device("default.qubit") - @qml.qnode(dev) + @qml.qnode(dev, interface="autograd") def circuit(phi): qml.PauliX(wires=0) qml.PauliX(wires=1) @@ -607,9 +607,9 @@ def test_tf_grad(self, excitation, phi, diff_method): import tensorflow as tf - dev = qml.device("default.qubit.tf", wires=4) + dev = qml.device("default.qubit") - @qml.qnode(dev, diff_method=diff_method) + @qml.qnode(dev, diff_method=diff_method, interface="tf") def circuit(phi): qml.PauliX(wires=0) qml.PauliX(wires=1) @@ -639,9 +639,9 @@ def test_jax_grad(self, excitation, phi, diff_method): import jax - dev = qml.device("default.qubit.jax", wires=4) + dev = qml.device("default.qubit") - @qml.qnode(dev, diff_method=diff_method) + @qml.qnode(dev, diff_method=diff_method, interface="jax") def circuit(phi): qml.PauliX(wires=0) qml.PauliX(wires=1) @@ -774,7 +774,7 @@ def test_autograd(self): """Tests that operations are computed correctly using the autograd interface""" - dev = qml.device("default.qubit.autograd", wires=4) + dev = qml.device("default.qubit") state = np.array( [ 0.0 + 0.0j, @@ -796,7 +796,7 @@ def test_autograd(self): ] ) - @qml.qnode(dev) + @qml.qnode(dev, interface="autograd") def circuit(phi): qml.PauliX(wires=0) qml.PauliX(wires=1) @@ -811,7 +811,7 @@ def test_tf(self): """Tests that operations are computed correctly using the tensorflow interface""" - dev = qml.device("default.qubit.tf", wires=4) + dev = qml.device("default.qubit") state = np.array( [ 0.0 + 0.0j, @@ -833,7 +833,7 @@ def test_tf(self): ] ) - @qml.qnode(dev) + @qml.qnode(dev, interface="tf") def circuit(phi): qml.PauliX(wires=0) qml.PauliX(wires=1) @@ -848,7 +848,7 @@ def test_jax(self): """Tests that operations are computed correctly using the jax interface""" - dev = qml.device("default.qubit.jax", wires=4) + dev = qml.device("default.qubit") state = np.array( [ 0.0 + 0.0j, @@ -870,7 +870,7 @@ def test_jax(self): ] ) - @qml.qnode(dev) + @qml.qnode(dev, interface="jax") def circuit(phi): qml.PauliX(wires=0) qml.PauliX(wires=1) @@ -885,7 +885,7 @@ def test_torch(self): """Tests that operations are computed correctly using the torch interface""" - dev = qml.device("default.qubit.torch", wires=4) + dev = qml.device("default.qubit") state = np.array( [ 0.0 + 0.0j, @@ -907,7 +907,7 @@ def test_torch(self): ] ) - @qml.qnode(dev) + @qml.qnode(dev, interface="torch") def circuit(phi): qml.PauliX(wires=0) qml.PauliX(wires=1) @@ -930,10 +930,14 @@ def test_autograd_grad(self, phi, diff_method): """Tests that gradients are computed correctly using the autograd interface""" - dev = qml.device("default.qubit.autograd", wires=4) + dev = qml.device("default.qubit") - circuit_0 = qml.QNode(self.grad_circuit_0, dev, diff_method=diff_method) - circuit_1 = qml.QNode(self.grad_circuit_1, dev, diff_method=diff_method) + circuit_0 = qml.QNode( + self.grad_circuit_0, dev, interface="autograd", diff_method=diff_method + ) + circuit_1 = qml.QNode( + self.grad_circuit_1, dev, interface="autograd", diff_method=diff_method + ) total = lambda phi: 1.1 * circuit_0(phi) + 0.7 * circuit_1(phi) assert np.allclose(qml.grad(total)(phi), self.expected_grad_fn(phi)) @@ -950,10 +954,10 @@ def test_tf_grad(self, phi, diff_method): import tensorflow as tf - dev = qml.device("default.qubit.tf", wires=4) + dev = qml.device("default.qubit") - circuit_0 = qml.QNode(self.grad_circuit_0, dev, diff_method=diff_method) - circuit_1 = qml.QNode(self.grad_circuit_1, dev, diff_method=diff_method) + circuit_0 = qml.QNode(self.grad_circuit_0, dev, interface="tf", diff_method=diff_method) + circuit_1 = qml.QNode(self.grad_circuit_1, dev, interface="tf", diff_method=diff_method) total = lambda phi: 1.1 * circuit_0(phi) + 0.7 * circuit_1(phi) phi_t = tf.Variable(phi, dtype=tf.float64) @@ -976,10 +980,10 @@ def test_jax_grad(self, phi, diff_method): import jax - dev = qml.device("default.qubit.jax", wires=4) + dev = qml.device("default.qubit") - circuit_0 = qml.QNode(self.grad_circuit_0, dev, diff_method=diff_method) - circuit_1 = qml.QNode(self.grad_circuit_1, dev, diff_method=diff_method) + circuit_0 = qml.QNode(self.grad_circuit_0, dev, interface="jax", diff_method=diff_method) + circuit_1 = qml.QNode(self.grad_circuit_1, dev, interface="jax", diff_method=diff_method) total = lambda phi: 1.1 * circuit_0(phi) + 0.7 * circuit_1(phi) phi_j = jax.numpy.array(phi) @@ -998,10 +1002,10 @@ def test_torch_grad(self, phi, diff_method): import torch - dev = qml.device("default.qubit.torch", wires=4) + dev = qml.device("default.qubit") - circuit_0 = qml.QNode(self.grad_circuit_0, dev, diff_method=diff_method) - circuit_1 = qml.QNode(self.grad_circuit_1, dev, diff_method=diff_method) + circuit_0 = qml.QNode(self.grad_circuit_0, dev, interface="torch", diff_method=diff_method) + circuit_1 = qml.QNode(self.grad_circuit_1, dev, interface="torch", diff_method=diff_method) total = lambda phi: 1.1 * circuit_0(phi) + 0.7 * circuit_1(phi) phi_t = torch.tensor(phi, dtype=torch.complex128, requires_grad=True) @@ -1105,7 +1109,7 @@ def test_autograd(self): """Tests that operations are computed correctly using the autograd interface""" - dev = qml.device("default.qubit.autograd", wires=2) + dev = qml.device("default.qubit") state = np.array( [ 0, @@ -1115,7 +1119,7 @@ def test_autograd(self): ] ) - @qml.qnode(dev) + @qml.qnode(dev, interface="autograd") def circuit(phi): qml.PauliX(wires=0) qml.FermionicSWAP(phi, wires=[0, 1]) @@ -1137,9 +1141,9 @@ def test_autograd_grad(self, diff_method, phi): """Tests that gradients are computed correctly using the autograd interface""" - dev = qml.device("default.qubit.autograd", wires=2) + dev = qml.device("default.qubit") - @qml.qnode(dev, diff_method=diff_method) + @qml.qnode(dev, interface="autograd", diff_method=diff_method) def circuit(phi): qml.PauliX(wires=0) qml.FermionicSWAP(phi, wires=[0, 1]) @@ -1163,9 +1167,9 @@ def test_tf(self, phi, diff_method): import tensorflow as tf - dev = qml.device("default.qubit.tf", wires=2) + dev = qml.device("default.qubit") - @qml.qnode(dev, diff_method=diff_method) + @qml.qnode(dev, diff_method=diff_method, interface="tf") def circuit(phi): qml.PauliX(wires=0) qml.FermionicSWAP(phi, wires=[0, 1]) @@ -1194,9 +1198,9 @@ def test_jax(self, phi, diff_method): import jax - dev = qml.device("default.qubit.jax", wires=2) + dev = qml.device("default.qubit") - @qml.qnode(dev, diff_method=diff_method) + @qml.qnode(dev, interface="jax", diff_method=diff_method) def circuit(phi): qml.PauliX(wires=0) qml.FermionicSWAP(phi, wires=[0, 1]) diff --git a/tests/pulse/test_transmon.py b/tests/pulse/test_transmon.py index eb280f04bab..6a0631d3abc 100644 --- a/tests/pulse/test_transmon.py +++ b/tests/pulse/test_transmon.py @@ -510,7 +510,7 @@ def fb(p, t): Hd = transmon_drive(amplitude=fa, phase=fb, freq=0.5, wires=[0]) H = Hi + Hd - dev = qml.device("default.qubit.jax", wires=wires) + dev = qml.device("default.qubit") ts = jnp.array([0.0, 3.0]) H_obj = sum(qml.PauliZ(i) for i in range(2)) diff --git a/tests/qnn/test_keras.py b/tests/qnn/test_keras.py index af8d457cd9e..f4f9769edc2 100644 --- a/tests/qnn/test_keras.py +++ b/tests/qnn/test_keras.py @@ -503,7 +503,7 @@ def test_gradients(self, get_circuit, output_dim, n_qubits): # pylint: disable= def test_backprop_gradients(self, mocker): # pylint: disable=no-self-use """Test if KerasLayer is compatible with the backprop diff method.""" - dev = qml.device("default.qubit.tf", wires=2) + dev = qml.device("default.qubit") @qml.qnode(dev, interface="tf", diff_method="backprop") def f(inputs, weights): @@ -813,9 +813,9 @@ def test_no_attribute(): @pytest.mark.tf def test_batch_input_single_measure(tol): """Test input batching in keras""" - dev = qml.device("default.qubit.tf", wires=4) + dev = qml.device("default.qubit") - @qml.qnode(dev, diff_method="parameter-shift") + @qml.qnode(dev, interface="tf", diff_method="parameter-shift") def circuit(x, weights): qml.AngleEmbedding(x, wires=range(4), rotation="Y") qml.RY(weights[0], wires=0) @@ -830,7 +830,6 @@ def circuit(x, weights): res = layer(x) assert res.shape == (10, 2) - assert dev.num_executions == 1 for x_, r in zip(x, res): assert qml.math.allclose(r, circuit(x_, layer.qnode_weights["weights"]), atol=tol) @@ -842,9 +841,9 @@ def circuit(x, weights): @pytest.mark.tf def test_batch_input_multi_measure(tol): """Test input batching in keras for multiple measurements""" - dev = qml.device("default.qubit.tf", wires=4) + dev = qml.device("default.qubit") - @qml.qnode(dev, diff_method="parameter-shift") + @qml.qnode(dev, interface="tf", diff_method="parameter-shift") def circuit(x, weights): qml.AngleEmbedding(x, wires=range(4), rotation="Y") qml.RY(weights[0], wires=0) @@ -859,7 +858,6 @@ def circuit(x, weights): res = layer(x) assert res.shape == (10, 5) - assert dev.num_executions == 1 for x_, r in zip(x, res): exp = tf.experimental.numpy.hstack(circuit(x_, layer.qnode_weights["weights"])) diff --git a/tests/qnn/test_qnn_torch.py b/tests/qnn/test_qnn_torch.py index a25ee7d6949..64aeb9b1a9c 100644 --- a/tests/qnn/test_qnn_torch.py +++ b/tests/qnn/test_qnn_torch.py @@ -805,9 +805,9 @@ def circ(inputs, w0): # pylint: disable=unused-argument @pytest.mark.torch def test_batch_input_single_measure(tol): """Test input batching in torch""" - dev = qml.device("default.qubit.torch", wires=4) + dev = qml.device("default.qubit") - @qml.qnode(dev, diff_method="parameter-shift") + @qml.qnode(dev, interface="torch", diff_method="parameter-shift") def circuit(x, weights): qml.AngleEmbedding(x, wires=range(4), rotation="Y") qml.RY(weights[0], wires=0) @@ -820,7 +820,6 @@ def circuit(x, weights): res = layer(x) assert res.shape == (10, 2) - assert dev.num_executions == 1 for x_, r in zip(x, res): assert qml.math.allclose(r, circuit(x_, layer.qnode_weights["weights"]), atol=tol) @@ -832,9 +831,9 @@ def circuit(x, weights): @pytest.mark.torch def test_batch_input_multi_measure(tol): """Test input batching in torch for multiple measurements""" - dev = qml.device("default.qubit.torch", wires=4) + dev = qml.device("default.qubit") - @qml.qnode(dev, diff_method="parameter-shift") + @qml.qnode(dev, interface="torch", diff_method="parameter-shift") def circuit(x, weights): qml.AngleEmbedding(x, wires=range(4), rotation="Y") qml.RY(weights[0], wires=0) @@ -847,7 +846,6 @@ def circuit(x, weights): res = layer(x) assert res.shape == (10, 5) - assert dev.num_executions == 1 for x_, r in zip(x, res): exp = torch.hstack(circuit(x_, layer.qnode_weights["weights"])) diff --git a/tests/test_device.py b/tests/test_device.py index 1639f76ed57..e8ec7cc997f 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -642,7 +642,7 @@ def test_default_expand_with_initial_state(self, op, decomp): prep = [op] ops = [qml.AngleEmbedding(features=[0.1], wires=[0], rotation="Z"), op, qml.PauliZ(wires=2)] - dev = qml.device("default.qubit.legacy", wires=3) + dev = qml.device("default.mixed", wires=3) tape = qml.tape.QuantumTape(ops=prep + ops, measurements=[], shots=100) new_tape = dev.default_expand_fn(tape) @@ -998,7 +998,7 @@ def test_outdated_API(self, monkeypatch): with monkeypatch.context() as m: m.setattr(qml, "version", lambda: "0.0.1") with pytest.raises(DeviceError, match="plugin requires PennyLane versions"): - qml.device("default.qubit.legacy", wires=0) + qml.device("default.mixed", wires=0) def test_refresh_entrypoints(self, monkeypatch): """Test that new entrypoints are found by the refresh_devices function""" @@ -1055,7 +1055,7 @@ def test_hot_refresh_entrypoints(self, monkeypatch): def test_shot_vector_property(self): """Tests shot vector initialization.""" - dev = qml.device("default.qubit.legacy", wires=1, shots=[1, 3, 3, 4, 4, 4, 3]) + dev = qml.device("default.mixed", wires=1, shots=[1, 3, 3, 4, 4, 4, 3]) shot_vector = dev.shot_vector assert len(shot_vector) == 4 assert shot_vector[0].shots == 1 diff --git a/tests/test_operation.py b/tests/test_operation.py index 4750b8feb3f..a9938b66817 100644 --- a/tests/test_operation.py +++ b/tests/test_operation.py @@ -1073,7 +1073,7 @@ def test_all_wires_defined_but_init_with_one(self): """Test that an exception is raised if the class is defined with ALL wires, but then instantiated with only one""" - dev1 = qml.device("default.qubit.legacy", wires=2) + dev1 = qml.device("default.qubit", wires=2) class DummyOp(qml.operation.Operation): r"""Dummy custom operator""" diff --git a/tests/test_qubit_device.py b/tests/test_qubit_device.py index 2b2387ff6ed..787ed809fbf 100644 --- a/tests/test_qubit_device.py +++ b/tests/test_qubit_device.py @@ -1211,7 +1211,7 @@ def test_device_executions(self): """Test the number of times a qubit device is executed over a QNode's lifetime is tracked by `num_executions`""" - dev_1 = qml.device("default.qubit.legacy", wires=2) + dev_1 = qml.device("default.mixed", wires=2) def circuit_1(x, y): qml.RX(x, wires=[0]) @@ -1227,7 +1227,7 @@ def circuit_1(x, y): assert dev_1.num_executions == num_evals_1 # test a second instance of a default qubit device - dev_2 = qml.device("default.qubit.legacy", wires=2) + dev_2 = qml.device("default.mixed", wires=2) def circuit_2(x): qml.RX(x, wires=[0]) @@ -1272,7 +1272,7 @@ def test_device_executions(self): """Test the number of times a qubit device is executed over a QNode's lifetime is tracked by `num_executions`""" - dev_1 = qml.device("default.qubit.legacy", wires=2) + dev_1 = qml.device("default.mixed", wires=2) def circuit_1(x, y): qml.RX(x, wires=[0]) @@ -1285,10 +1285,10 @@ def circuit_1(x, y): for _ in range(num_evals_1): node_1(0.432, np.array([0.12, 0.5, 3.2])) - assert dev_1.num_executions == num_evals_1 + assert dev_1.num_executions == num_evals_1 * 3 # test a second instance of a default qubit device - dev_2 = qml.device("default.qubit.legacy", wires=2) + dev_2 = qml.device("default.mixed", wires=2) assert dev_2.num_executions == 0 @@ -1302,7 +1302,7 @@ def circuit_2(x, y): for _ in range(num_evals_2): node_2(np.array([0.432, 0.61, 8.2]), 0.12) - assert dev_2.num_executions == num_evals_2 + assert dev_2.num_executions == num_evals_2 * 3 # test a new circuit on an existing instance of a qubit device def circuit_3(x, y): @@ -1315,7 +1315,7 @@ def circuit_3(x, y): for _ in range(num_evals_3): node_3(np.array([0.432, 0.2]), np.array([0.12, 1.214])) - assert dev_1.num_executions == num_evals_1 + num_evals_3 + assert dev_1.num_executions == num_evals_1 * 3 + num_evals_3 * 2 class TestBatchExecution: @@ -1504,7 +1504,7 @@ def test_tracker_multi_execution(self, dev_name): @pytest.mark.autograd def test_tracker_grad(self): """Test that the tracker can track resources through a gradient computation""" - dev = qml.device("default.qubit.legacy", wires=1, shots=100) + dev = qml.device("default.qubit", wires=1, shots=100) @qml.qnode(dev, diff_method="parameter-shift") def circuit(x): @@ -1540,8 +1540,9 @@ def test_samples_to_counts_with_nan(self): """Test that the counts function disregards failed measurements (samples including NaN values) when totalling counts""" # generate 1000 samples for 2 wires, randomly distributed between 0 and 1 - device = qml.device("default.qubit.legacy", wires=2, shots=1000) - device._state = [0.5 + 0.0j, 0.5 + 0.0j, 0.5 + 0.0j, 0.5 + 0.0j] + device = qml.device("default.mixed", wires=2, shots=1000) + sv = [0.5 + 0.0j, 0.5 + 0.0j, 0.5 + 0.0j, 0.5 + 0.0j] + device._state = np.outer(sv, sv) device._samples = device.generate_samples() samples = device.sample(qml.measurements.CountsMP()) @@ -1568,9 +1569,12 @@ def test_samples_to_counts_with_many_wires(self, all_outcomes): # generate 1000 samples for 10 wires, randomly distributed between 0 and 1 n_wires = 10 shots = 100 - device = qml.device("default.qubit.legacy", wires=n_wires, shots=shots) - state = np.random.rand(*([2] * n_wires)) - device._state = state / np.linalg.norm(state) + device = qml.device("default.mixed", wires=n_wires, shots=shots) + + sv = np.random.rand(*([2] * n_wires)) + state = sv / np.linalg.norm(sv) + + device._state = np.outer(state, state) device._samples = device.generate_samples() samples = device.sample(qml.measurements.CountsMP(all_outcomes=all_outcomes)) diff --git a/tests/transforms/test_qcut.py b/tests/transforms/test_qcut.py index 6439bb04e2d..1bd9b284663 100644 --- a/tests/transforms/test_qcut.py +++ b/tests/transforms/test_qcut.py @@ -4072,7 +4072,7 @@ def test_simple_cut_circuit_torch_trace(self, mocker, use_opt_einsum): # TODO: this passes with default.qubit locally, but fails on CI # possibly an architecture-specific issue - dev = qml.device("default.qubit.legacy", wires=2) + dev = qml.device("default.qubit", wires=2) @qml.qnode(dev, interface="torch") def circuit(x): From 13eeea36bf61c95e86e2657a9e2301b8669bdc05 Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Tue, 16 Jul 2024 15:36:34 -0400 Subject: [PATCH 08/38] add changelog and deprecation note --- doc/development/deprecations.rst | 6 ++++++ doc/releases/changelog-dev.md | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/doc/development/deprecations.rst b/doc/development/deprecations.rst index df57bdb6981..31a51cca8ea 100644 --- a/doc/development/deprecations.rst +++ b/doc/development/deprecations.rst @@ -9,6 +9,12 @@ deprecations are listed below. Pending deprecations -------------------- +* All of the legacy devices (any with the name ``default.qubit.{legacy,autograd,torch,tf,jax}``) are deprecated. Use ``default.qubit`` instead, + as it supports backpropagation for the many backends the legacy devices support. + + - Deprecated and Duplicated in v0.38 + - Will be removed in v0.39 + * The functions ``qml.qinfo.classical_fisher`` and ``qml.qinfo.quantum_fisher`` are deprecated since they are being migrated to the ``qml.gradients`` module. Therefore, ``qml.gradients.classical_fisher`` and ``qml.gradients.quantum_fisher`` should be used instead. diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 6543ac9c4b2..a95d003f28d 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -53,6 +53,10 @@ Instead, use `pennylane.gradients.classical_fisher` and `pennylane.gradients.quantum_fisher`. [(#5985)](https://github.com/PennyLaneAI/pennylane/pull/5985) +* The legacy devices `default.qubit.{default.qubit.{legacy,autograd,torch,tf,jax}}` are deprecated. + Instead, use ``default.qubit`` as it now supports backpropagation through the several backends. + [(#5997)](https://github.com/PennyLaneAI/pennylane/pull/5997) +

Documentation 📝

* Improves the docstring for `QuantumScript.expand` and `qml.tape.tape.expand_tape`. From e257c29301a862e2a02b9489cc9cf66204197c0a Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Tue, 16 Jul 2024 17:22:16 -0400 Subject: [PATCH 09/38] printing failing warning checks --- tests/test_qnode_legacy.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_qnode_legacy.py b/tests/test_qnode_legacy.py index 5627a455c86..8e7fef597e0 100644 --- a/tests/test_qnode_legacy.py +++ b/tests/test_qnode_legacy.py @@ -636,6 +636,8 @@ def circuit(params): # Two warnings. One for the device and one for the interface assert len(record) == 2 + + print([w.message for w in record]) assert all(isinstance(w.message, qml.PennyLaneDeprecationWarning) for w in record) def test_not_giving_mode_kwarg_does_not_raise_warning(self): From b9f405c681ab701576facd42d105229d8d7e417a Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Tue, 16 Jul 2024 17:41:07 -0400 Subject: [PATCH 10/38] maybe this time --- tests/test_qnode_legacy.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_qnode_legacy.py b/tests/test_qnode_legacy.py index 8e7fef597e0..6987f2d2cd6 100644 --- a/tests/test_qnode_legacy.py +++ b/tests/test_qnode_legacy.py @@ -636,9 +636,8 @@ def circuit(params): # Two warnings. One for the device and one for the interface assert len(record) == 2 - - print([w.message for w in record]) - assert all(isinstance(w.message, qml.PennyLaneDeprecationWarning) for w in record) + for w in record: + assert isinstance(w.message, qml.PennyLaneDeprecationWarning) def test_not_giving_mode_kwarg_does_not_raise_warning(self): """Test that not providing a value for mode does not raise a warning From 0a1f9f966f7f1d156d8441be02d4119f31bc63de Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Tue, 16 Jul 2024 23:14:51 -0400 Subject: [PATCH 11/38] fix typo in deprecations doc --- doc/development/deprecations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/development/deprecations.rst b/doc/development/deprecations.rst index 31a51cca8ea..704c4f8ec26 100644 --- a/doc/development/deprecations.rst +++ b/doc/development/deprecations.rst @@ -12,7 +12,7 @@ Pending deprecations * All of the legacy devices (any with the name ``default.qubit.{legacy,autograd,torch,tf,jax}``) are deprecated. Use ``default.qubit`` instead, as it supports backpropagation for the many backends the legacy devices support. - - Deprecated and Duplicated in v0.38 + - Deprecated in v0.38 - Will be removed in v0.39 * The functions ``qml.qinfo.classical_fisher`` and ``qml.qinfo.quantum_fisher`` are deprecated since they are being migrated From 3a63fadee714c7d252c0be529362af8a047bfa5a Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Tue, 16 Jul 2024 23:26:26 -0400 Subject: [PATCH 12/38] remove no longer needed comment --- tests/transforms/test_qcut.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/transforms/test_qcut.py b/tests/transforms/test_qcut.py index 1bd9b284663..1c05a626739 100644 --- a/tests/transforms/test_qcut.py +++ b/tests/transforms/test_qcut.py @@ -4070,8 +4070,6 @@ def test_simple_cut_circuit_torch_trace(self, mocker, use_opt_einsum): import torch - # TODO: this passes with default.qubit locally, but fails on CI - # possibly an architecture-specific issue dev = qml.device("default.qubit", wires=2) @qml.qnode(dev, interface="torch") From d14594127271a36d262949a1b9633ac4f8f7e911 Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Wed, 17 Jul 2024 00:34:35 -0400 Subject: [PATCH 13/38] more robust classical control draw tests --- pennylane/drawer/tape_mpl.py | 1 + tests/drawer/test_tape_mpl.py | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/pennylane/drawer/tape_mpl.py b/pennylane/drawer/tape_mpl.py index d73e4bcc68f..2626cde639d 100644 --- a/pennylane/drawer/tape_mpl.py +++ b/pennylane/drawer/tape_mpl.py @@ -459,6 +459,7 @@ def tape_mpl( if update_style := (has_mpl and style != "rcParams"): restore_params = mpl.rcParams.copy() _set_style(style) + try: return _tape_mpl( tape, diff --git a/tests/drawer/test_tape_mpl.py b/tests/drawer/test_tape_mpl.py index c8568c6293f..a8da3a71d57 100644 --- a/tests/drawer/test_tape_mpl.py +++ b/tests/drawer/test_tape_mpl.py @@ -844,7 +844,7 @@ def test_single_measure_multiple_conds(self): qml.cond(m0, qml.PauliY)(0) tape = qml.tape.QuantumScript.from_queue(q) - _, ax = qml.drawer.tape_mpl(tape) + _, ax = qml.drawer.tape_mpl(tape, style="black_white") assert len(ax.patches) == 5 # three for measure, two for boxes @@ -857,11 +857,11 @@ def test_single_measure_multiple_conds(self): # probably not a good way to test this, but the best I can figure out assert pe1._gc == { - "linewidth": 5 * plt.rcParams["lines.linewidth"], + "linewidth": 5 * 1.5, # lines.linewidth for black white style "foreground": "black", # lines.color for black white style } assert pe2._gc == { - "linewidth": 3 * plt.rcParams["lines.linewidth"], + "linewidth": 3 * 1.5, # lines.linewidth for black white style "foreground": "white", # figure.facecolor for black white sytle } plt.close() @@ -875,7 +875,7 @@ def test_combo_measurement(self): qml.cond(m0 & m1, qml.PauliY)(0) tape = qml.tape.QuantumScript.from_queue(q) - _, ax = qml.drawer.tape_mpl(tape) + _, ax = qml.drawer.tape_mpl(tape, style="black_white") assert len(ax.patches) == 7 # three for 2 measurements, one for box [_, _, cwire1, cwire2, eraser] = ax.lines @@ -891,11 +891,11 @@ def test_combo_measurement(self): # probably not a good way to test this, but the best I can figure out assert pe1._gc == { - "linewidth": 5 * plt.rcParams["lines.linewidth"], + "linewidth": 5 * 1.5, # lines.linewidth for black white style "foreground": "black", # lines.color for black white style } assert pe2._gc == { - "linewidth": 3 * plt.rcParams["lines.linewidth"], + "linewidth": 3 * 1.5, # lines.linewidth for black white style "foreground": "white", # figure.facecolor for black white sytle } @@ -918,7 +918,7 @@ def test_combo_measurement_non_terminal(self): qml.cond(m1, qml.T)(1) tape = qml.tape.QuantumScript.from_queue(q) - _, ax = qml.drawer.tape_mpl(tape) + _, ax = qml.drawer.tape_mpl(tape, style="black_white") [_, _, cwire1, cwire2, eraser] = ax.lines @@ -933,11 +933,11 @@ def test_combo_measurement_non_terminal(self): # probably not a good way to test this, but the best I can figure out assert pe1._gc == { - "linewidth": 5 * plt.rcParams["lines.linewidth"], + "linewidth": 5 * 1.5, # lines.linewidth for black white style "foreground": "black", # lines.color for black white style } assert pe2._gc == { - "linewidth": 3 * plt.rcParams["lines.linewidth"], + "linewidth": 3 * 1.5, # lines.linewidth for black white style "foreground": "white", # figure.facecolor for black white sytle } @@ -950,11 +950,11 @@ def test_combo_measurement_non_terminal(self): def test_single_mcm_measure(self): """Test a final measurement of a mid circuit measurement.""" - + plt.rcParams["lines.linewidth"] = 20 with qml.queuing.AnnotatedQueue() as q: m0 = qml.measure(0) qml.expval(m0) - _, ax = tape_mpl(qml.tape.QuantumScript.from_queue(q)) + _, ax = tape_mpl(qml.tape.QuantumScript.from_queue(q), style="black_white") assert len(ax.patches) == 6 # two measurement boxes assert ax.patches[3].get_x() == 1 - 0.75 / 2 + 0.2 # 1 - box_length/2 + pad @@ -974,11 +974,11 @@ def test_single_mcm_measure(self): # probably not a good way to test this, but the best I can figure out assert pe1._gc == { - "linewidth": 5 * plt.rcParams["lines.linewidth"], + "linewidth": 5 * 1.5, # lines.linewidth for black white style "foreground": "black", # lines.color for black white style } assert pe2._gc == { - "linewidth": 3 * plt.rcParams["lines.linewidth"], + "linewidth": 3 * 1.5, # lines.linewidth for black white style "foreground": "white", # figure.facecolor for black white sytle } @@ -992,7 +992,7 @@ def test_multiple_mcm_measure(self): _ = qml.measure(0) qml.sample([m0, m1]) qml.expval(m2) - _, ax = qml.drawer.tape_mpl(qml.tape.QuantumScript.from_queue(q)) + _, ax = qml.drawer.tape_mpl(qml.tape.QuantumScript.from_queue(q), style="black_white") [_, cwire0, cwire1, cwire2] = ax.lines assert cwire0.get_xdata() == [0, 0, 0, 5, 5, 5] From a61191706674962f50fa5ca79b2bb066cd78d28f Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Wed, 17 Jul 2024 00:51:59 -0400 Subject: [PATCH 14/38] what --- tests/test_qnode_legacy.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/test_qnode_legacy.py b/tests/test_qnode_legacy.py index 6987f2d2cd6..a54f5bc904c 100644 --- a/tests/test_qnode_legacy.py +++ b/tests/test_qnode_legacy.py @@ -626,12 +626,13 @@ def test_autograd_interface_device_switched_no_warnings(self): except for thee deprecation warnings.""" dev = qml.device("default.qubit.legacy", wires=1) - @qml.qnode(dev, interface="autograd") - def circuit(params): - qml.RX(params, wires=0) - return qml.expval(qml.PauliZ(0)) - with warnings.catch_warnings(record=True) as record: + + @qml.qnode(dev, interface="autograd") + def circuit(params): + qml.RX(params, wires=0) + return qml.expval(qml.PauliZ(0)) + circuit(qml.numpy.array(0.1, requires_grad=True)) # Two warnings. One for the device and one for the interface From ba6ff262a7a8764b9659a0c4dc74c985ea9965ea Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Wed, 17 Jul 2024 00:54:56 -0400 Subject: [PATCH 15/38] another one --- tests/test_qnode_legacy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_qnode_legacy.py b/tests/test_qnode_legacy.py index a54f5bc904c..78f27d698fe 100644 --- a/tests/test_qnode_legacy.py +++ b/tests/test_qnode_legacy.py @@ -638,7 +638,7 @@ def circuit(params): # Two warnings. One for the device and one for the interface assert len(record) == 2 for w in record: - assert isinstance(w.message, qml.PennyLaneDeprecationWarning) + assert w.category == qml.PennyLaneDeprecationWarning def test_not_giving_mode_kwarg_does_not_raise_warning(self): """Test that not providing a value for mode does not raise a warning @@ -647,7 +647,7 @@ def test_not_giving_mode_kwarg_does_not_raise_warning(self): qml.QNode(lambda f: f, qml.device("default.qubit.legacy", wires=1)) assert len(record) == 1 - assert isinstance(record[0].message, qml.PennyLaneDeprecationWarning) + assert record[0].category == qml.PennyLaneDeprecationWarning class TestTapeConstruction: From 3e4dc46f31ef513437a7a86c1c766948b5b6f669 Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Wed, 17 Jul 2024 15:07:05 -0400 Subject: [PATCH 16/38] fix --- tests/test_qnode_legacy.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_qnode_legacy.py b/tests/test_qnode_legacy.py index 78f27d698fe..14dd315c14f 100644 --- a/tests/test_qnode_legacy.py +++ b/tests/test_qnode_legacy.py @@ -623,7 +623,7 @@ def circuit(params): def test_autograd_interface_device_switched_no_warnings(self): """Test that checks that no warning is raised for device switch when you define an interface, - except for thee deprecation warnings.""" + except for the deprecation warnings.""" dev = qml.device("default.qubit.legacy", wires=1) with warnings.catch_warnings(record=True) as record: @@ -636,9 +636,7 @@ def circuit(params): circuit(qml.numpy.array(0.1, requires_grad=True)) # Two warnings. One for the device and one for the interface - assert len(record) == 2 - for w in record: - assert w.category == qml.PennyLaneDeprecationWarning + assert [rc.category for rc in record].count(qml.PennyLaneDeprecationWarning) == 2 def test_not_giving_mode_kwarg_does_not_raise_warning(self): """Test that not providing a value for mode does not raise a warning From c8d44754a5f11b52815a72bd80e2c3ea3b86aeca Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Wed, 17 Jul 2024 16:09:06 -0400 Subject: [PATCH 17/38] fix --- tests/test_qnode_legacy.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/tests/test_qnode_legacy.py b/tests/test_qnode_legacy.py index 14dd315c14f..2933ef7f1f7 100644 --- a/tests/test_qnode_legacy.py +++ b/tests/test_qnode_legacy.py @@ -623,20 +623,15 @@ def circuit(params): def test_autograd_interface_device_switched_no_warnings(self): """Test that checks that no warning is raised for device switch when you define an interface, - except for the deprecation warnings.""" + except for the deprecation warnings which will be caught by the fixture.""" dev = qml.device("default.qubit.legacy", wires=1) - with warnings.catch_warnings(record=True) as record: - - @qml.qnode(dev, interface="autograd") - def circuit(params): - qml.RX(params, wires=0) - return qml.expval(qml.PauliZ(0)) - - circuit(qml.numpy.array(0.1, requires_grad=True)) + @qml.qnode(dev, interface="autograd") + def circuit(params): + qml.RX(params, wires=0) + return qml.expval(qml.PauliZ(0)) - # Two warnings. One for the device and one for the interface - assert [rc.category for rc in record].count(qml.PennyLaneDeprecationWarning) == 2 + circuit(qml.numpy.array(0.1, requires_grad=True)) def test_not_giving_mode_kwarg_does_not_raise_warning(self): """Test that not providing a value for mode does not raise a warning From ca3ee221336d12c62f962fbf536b3b9e25bdf120 Mon Sep 17 00:00:00 2001 From: Ahmed Darwish Date: Thu, 18 Jul 2024 23:21:21 -0400 Subject: [PATCH 18/38] Update doc/releases/changelog-dev.md Co-authored-by: Thomas R. Bromley <49409390+trbromley@users.noreply.github.com> --- doc/releases/changelog-dev.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index b7a9aafd8fa..67759b72873 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -57,7 +57,7 @@ [(#5985)](https://github.com/PennyLaneAI/pennylane/pull/5985) * The legacy devices `default.qubit.{default.qubit.{legacy,autograd,torch,tf,jax}}` are deprecated. - Instead, use ``default.qubit`` as it now supports backpropagation through the several backends. + Instead, use `default.qubit` as it now supports backpropagation through the several backends. [(#5997)](https://github.com/PennyLaneAI/pennylane/pull/5997)

Documentation 📝

From 1b2bbfe2349629a5286119d376d3b6563d9ade62 Mon Sep 17 00:00:00 2001 From: Ahmed Darwish Date: Thu, 18 Jul 2024 23:21:32 -0400 Subject: [PATCH 19/38] Update doc/releases/changelog-dev.md Co-authored-by: Thomas R. Bromley <49409390+trbromley@users.noreply.github.com> --- doc/releases/changelog-dev.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 67759b72873..aaab464f74b 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -56,7 +56,7 @@ Instead, use `pennylane.gradients.classical_fisher` and `pennylane.gradients.quantum_fisher`. [(#5985)](https://github.com/PennyLaneAI/pennylane/pull/5985) -* The legacy devices `default.qubit.{default.qubit.{legacy,autograd,torch,tf,jax}}` are deprecated. +* The legacy devices `default.qubit.{default.qubit.{autograd,torch,tf,jax,legacy}}` are deprecated. Instead, use `default.qubit` as it now supports backpropagation through the several backends. [(#5997)](https://github.com/PennyLaneAI/pennylane/pull/5997) From 5c3038939dcca01017cf652821a40aba6602184e Mon Sep 17 00:00:00 2001 From: Ahmed Darwish Date: Thu, 18 Jul 2024 23:21:39 -0400 Subject: [PATCH 20/38] Update doc/development/deprecations.rst Co-authored-by: Thomas R. Bromley <49409390+trbromley@users.noreply.github.com> --- doc/development/deprecations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/development/deprecations.rst b/doc/development/deprecations.rst index 704c4f8ec26..71f40a3833a 100644 --- a/doc/development/deprecations.rst +++ b/doc/development/deprecations.rst @@ -9,7 +9,7 @@ deprecations are listed below. Pending deprecations -------------------- -* All of the legacy devices (any with the name ``default.qubit.{legacy,autograd,torch,tf,jax}``) are deprecated. Use ``default.qubit`` instead, +* All of the legacy devices (any with the name ``default.qubit.{autograd,torch,tf,jax,legacy}``) are deprecated. Use ``default.qubit`` instead, as it supports backpropagation for the many backends the legacy devices support. - Deprecated in v0.38 From 7c14991b263e24c7f44208e4711d1d57bfce4689 Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Fri, 19 Jul 2024 01:17:36 -0400 Subject: [PATCH 21/38] consolidate warning capture fixtures of devs and {ham,sum}_expand --- tests/transforms/test_hamiltonian_expand.py | 30 ++++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tests/transforms/test_hamiltonian_expand.py b/tests/transforms/test_hamiltonian_expand.py index 894bf4b65a9..b3f2ebad99d 100644 --- a/tests/transforms/test_hamiltonian_expand.py +++ b/tests/transforms/test_hamiltonian_expand.py @@ -15,6 +15,7 @@ Unit tests for the ``hamiltonian_expand`` transform. """ import functools +import warnings import numpy as np import pytest @@ -91,11 +92,15 @@ class TestHamiltonianExpand: """Tests for the hamiltonian_expand transform""" @pytest.fixture(scope="function", autouse=True) - def capture_warnings(self, recwarn): - yield - if len(recwarn) > 0: - for w in recwarn: - assert isinstance(w.message, qml.PennyLaneDeprecationWarning) + def capture_warnings(self): + with pytest.warns(qml.PennyLaneDeprecationWarning) as record: + yield + + for w in record: + assert isinstance(w.message, qml.PennyLaneDeprecationWarning) + if "qml.transforms.hamiltonian_expand is deprecated" not in str(w.message): + warnings.warn(w.message, w.category) + else: assert "qml.transforms.hamiltonian_expand is deprecated" in str(w.message) def test_ham_with_no_terms_raises(self): @@ -527,11 +532,15 @@ class TestSumExpand: """Tests for the sum_expand transform""" @pytest.fixture(scope="function", autouse=True) - def capture_warnings(self, recwarn): - yield - if len(recwarn) > 0: - for w in recwarn: - assert isinstance(w.message, qml.PennyLaneDeprecationWarning) + def capture_warnings(self): + with pytest.warns(qml.PennyLaneDeprecationWarning) as record: + yield + + for w in record: + assert isinstance(w.message, qml.PennyLaneDeprecationWarning) + if "qml.transforms.sum_expand is deprecated" not in str(w.message): + warnings.warn(w.message, w.category) + else: assert "qml.transforms.sum_expand is deprecated" in str(w.message) def test_observables_on_same_wires(self): @@ -560,6 +569,7 @@ def test_sums(self, qscript, output): assert all(qml.math.allclose(o, e) for o, e in zip(output, expval)) @pytest.mark.parametrize(("qscript", "output"), zip(SUM_QSCRIPTS, SUM_OUTPUTS)) + @pytest.mark.filterwarnings("ignore:Use of 'default.qubit.legacy' is deprecated") def test_sums_legacy_opmath(self, qscript, output): """Tests that the sum_expand transform returns the correct value""" dev_old = qml.device("default.qubit.legacy", wires=4) From 5ced67fdb2fc8b94aea4e19fcd1eba34560424af Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Fri, 19 Jul 2024 01:22:03 -0400 Subject: [PATCH 22/38] consolidate warning capture fixtures of devs and tape_expand tests --- tests/transforms/test_tape_expand.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/transforms/test_tape_expand.py b/tests/transforms/test_tape_expand.py index 56fee694894..5f790e08014 100644 --- a/tests/transforms/test_tape_expand.py +++ b/tests/transforms/test_tape_expand.py @@ -17,6 +17,8 @@ # pylint: disable=too-few-public-methods, invalid-unary-operand-type, no-member, # pylint: disable=arguments-differ, arguments-renamed, +import warnings + import numpy as np import pytest @@ -24,15 +26,6 @@ from pennylane.wires import Wires -@pytest.fixture(scope="function", autouse=True) -def capture_warnings(recwarn): - yield - if len(recwarn) > 0: - for w in recwarn: - assert isinstance(w.message, qml.PennyLaneDeprecationWarning) - assert "'expansion_strategy' attribute is deprecated" in str(w.message) - - class TestCreateExpandFn: """Test creating expansion functions from stopping criteria.""" @@ -438,6 +431,18 @@ def custom_basic_entangler_layers(weights, wires, **kwargs): class TestCreateCustomDecompExpandFn: """Tests for the custom_decomps argument for devices""" + @pytest.fixture(scope="function", autouse=True) + def capture_warnings(self): + with pytest.warns(qml.PennyLaneDeprecationWarning) as record: + yield + + for w in record: + assert isinstance(w.message, qml.PennyLaneDeprecationWarning) + if "'expansion_strategy' attribute is deprecated" not in str(w.message): + warnings.warn(w.message, w.category) + else: + assert "'expansion_strategy' attribute is deprecated" in str(w.message) + @pytest.mark.parametrize("device_name", ["default.qubit", "default.qubit.legacy"]) def test_string_and_operator_allowed(self, device_name): """Test that the custom_decomps dictionary accepts both strings and operator classes as keys.""" @@ -910,7 +915,7 @@ def test_custom_decomp_used_twice(self): custom_decomps = {"MultiRZ": qml.MultiRZ.compute_decomposition} dev = qml.device("lightning.qubit", wires=2, custom_decomps=custom_decomps) - @qml.qnode(dev, diff_method="adjoint") + @qml.qnode(dev, diff_method="adjoint", expansion_strategy="gradient") def cost(theta): qml.Hadamard(wires=0) qml.Hadamard(wires=1) From 4778f7f72e835f3689ba6a977ad283cafefd74b4 Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Fri, 19 Jul 2024 01:35:10 -0400 Subject: [PATCH 23/38] minor typo in changelog --- doc/releases/changelog-dev.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index d069bfd36d2..49dd8128a40 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -113,7 +113,7 @@ Instead, use `pennylane.gradients.classical_fisher` and `pennylane.gradients.quantum_fisher`. [(#5985)](https://github.com/PennyLaneAI/pennylane/pull/5985) -* The legacy devices `default.qubit.{default.qubit.{autograd,torch,tf,jax,legacy}}` are deprecated. +* The legacy devices `default.qubit.{autograd,torch,tf,jax,legacy}` are deprecated. Instead, use `default.qubit` as it now supports backpropagation through the several backends. [(#5997)](https://github.com/PennyLaneAI/pennylane/pull/5997) From 6339beabb8411fb593df9d0287f18f06a89a86f3 Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Fri, 19 Jul 2024 01:41:29 -0400 Subject: [PATCH 24/38] test fix --- tests/transforms/test_tape_expand.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/transforms/test_tape_expand.py b/tests/transforms/test_tape_expand.py index 5f790e08014..7fe681a1cea 100644 --- a/tests/transforms/test_tape_expand.py +++ b/tests/transforms/test_tape_expand.py @@ -437,10 +437,10 @@ def capture_warnings(self): yield for w in record: - assert isinstance(w.message, qml.PennyLaneDeprecationWarning) if "'expansion_strategy' attribute is deprecated" not in str(w.message): warnings.warn(w.message, w.category) else: + assert isinstance(w.message, qml.PennyLaneDeprecationWarning) assert "'expansion_strategy' attribute is deprecated" in str(w.message) @pytest.mark.parametrize("device_name", ["default.qubit", "default.qubit.legacy"]) From 624167a29823e327dfeb6958ae7ae397ab7239c1 Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Fri, 19 Jul 2024 11:40:12 -0400 Subject: [PATCH 25/38] fix tape_mpl test --- tests/drawer/test_tape_mpl.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/drawer/test_tape_mpl.py b/tests/drawer/test_tape_mpl.py index a8da3a71d57..c1953e17ada 100644 --- a/tests/drawer/test_tape_mpl.py +++ b/tests/drawer/test_tape_mpl.py @@ -936,10 +936,12 @@ def test_combo_measurement_non_terminal(self): "linewidth": 5 * 1.5, # lines.linewidth for black white style "foreground": "black", # lines.color for black white style } - assert pe2._gc == { - "linewidth": 3 * 1.5, # lines.linewidth for black white style - "foreground": "white", # figure.facecolor for black white sytle - } + + assert pe2._gc["linewidth"] == 3 * 1.5 # lines.linewidth for black white style + assert pe2._gc["foreground"].lower() in ( + "white", + "#f0f0f0", + ) # figure.facecolor for black white sytle assert eraser.get_xdata() == (1.8, 2.2) assert eraser.get_ydata() == (2, 2) From fd42815720a9540399c3472f989588b23548644d Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Fri, 19 Jul 2024 12:25:35 -0400 Subject: [PATCH 26/38] more tape_mpl fixes --- tests/drawer/test_tape_mpl.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/tests/drawer/test_tape_mpl.py b/tests/drawer/test_tape_mpl.py index c1953e17ada..d1196c934f8 100644 --- a/tests/drawer/test_tape_mpl.py +++ b/tests/drawer/test_tape_mpl.py @@ -894,15 +894,19 @@ def test_combo_measurement(self): "linewidth": 5 * 1.5, # lines.linewidth for black white style "foreground": "black", # lines.color for black white style } - assert pe2._gc == { - "linewidth": 3 * 1.5, # lines.linewidth for black white style - "foreground": "white", # figure.facecolor for black white sytle - } + assert pe2._gc["linewidth"] == 3 * 1.5 # lines.linewidth for black white style + assert pe2._gc["foreground"].lower() in ( + "white", + "#f0f0f0", + ) # figure.facecolor for black white style assert eraser.get_xdata() == (1.8, 2) assert eraser.get_ydata() == (2, 2) - assert eraser.get_color() == plt.rcParams["figure.facecolor"] - assert eraser.get_linewidth() == 3 * plt.rcParams["lines.linewidth"] + assert eraser.get_color().lower() in ( + "white", + "#f0f0f0", + ) # figure.facecolor for black white style + assert eraser.get_linewidth() == 3 * 1.5 # lines.linewidth for black white style plt.close() @@ -945,8 +949,11 @@ def test_combo_measurement_non_terminal(self): assert eraser.get_xdata() == (1.8, 2.2) assert eraser.get_ydata() == (2, 2) - assert eraser.get_color() == plt.rcParams["figure.facecolor"] - assert eraser.get_linewidth() == 3 * plt.rcParams["lines.linewidth"] + assert eraser.get_color().lower() in ( + "white", + "#f0f0f0", + ) # figure.facecolor for black white style + assert eraser.get_linewidth() == 3 * 1.5 # lines.linewidth for black white style plt.close() From 73d151a156c32caa360bcf727a31de247ee1f025 Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Sun, 21 Jul 2024 17:02:35 -0400 Subject: [PATCH 27/38] more robust fixture setup and undoing draw test changes --- pennylane/devices/default_qubit_autograd.py | 2 +- pennylane/devices/default_qubit_jax.py | 2 +- pennylane/devices/default_qubit_tf.py | 2 +- pennylane/devices/default_qubit_torch.py | 2 +- tests/conftest.py | 19 ++++--- tests/drawer/test_tape_mpl.py | 57 +++++++++------------ tests/transforms/test_tape_expand.py | 11 ++-- 7 files changed, 48 insertions(+), 47 deletions(-) diff --git a/pennylane/devices/default_qubit_autograd.py b/pennylane/devices/default_qubit_autograd.py index ef228327d54..abcc6e0452f 100644 --- a/pennylane/devices/default_qubit_autograd.py +++ b/pennylane/devices/default_qubit_autograd.py @@ -38,7 +38,7 @@ class DefaultQubitAutograd(DefaultQubitLegacy): pip install autograd .. warning:: - This device is deprecated. Use :class:`~.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``, which now supports backpropagation. + This device is deprecated. Use :class:`~pennylane.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``, which now supports backpropagation. **Example** diff --git a/pennylane/devices/default_qubit_jax.py b/pennylane/devices/default_qubit_jax.py index 68e0da05b0a..0cae4827e41 100644 --- a/pennylane/devices/default_qubit_jax.py +++ b/pennylane/devices/default_qubit_jax.py @@ -52,7 +52,7 @@ class DefaultQubitJax(DefaultQubitLegacy): pip install jax jaxlib .. warning:: - This device is deprecated. Use :class:`~.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``, which now supports backpropagation. + This device is deprecated. Use :class:`~pennylane.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``, which now supports backpropagation. **Example** diff --git a/pennylane/devices/default_qubit_tf.py b/pennylane/devices/default_qubit_tf.py index 160af64c0d9..2171aa7592e 100644 --- a/pennylane/devices/default_qubit_tf.py +++ b/pennylane/devices/default_qubit_tf.py @@ -59,7 +59,7 @@ class DefaultQubitTF(DefaultQubitLegacy): pip install tensorflow>=2.0 .. warning:: - This device is deprecated. Use :class:`~.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``, which now supports backpropagation. + This device is deprecated. Use :class:`~pennylane.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``, which now supports backpropagation. **Example** diff --git a/pennylane/devices/default_qubit_torch.py b/pennylane/devices/default_qubit_torch.py index 59253c2c4cc..3ed26025b60 100644 --- a/pennylane/devices/default_qubit_torch.py +++ b/pennylane/devices/default_qubit_torch.py @@ -60,7 +60,7 @@ class DefaultQubitTorch(DefaultQubitLegacy): pip install torch>=1.8.0 .. warning:: - This device is deprecated. Use :class:`~.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``, which now supports backpropagation. + This device is deprecated. Use :class:`~pennylane.devices.DefaultQubit` instead; for example through ``qml.device("default.qubit")``, which now supports backpropagation. **Example** diff --git a/tests/conftest.py b/tests/conftest.py index 3812ade33f3..3094a2fd784 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,6 +19,7 @@ import os import pathlib import sys +import warnings import numpy as np import pytest @@ -50,14 +51,20 @@ def set_numpy_seed(): @pytest.fixture(scope="function", autouse=True) -def capture_legacy_device_deprecation_warnings(recwarn): - yield +def capture_legacy_device_deprecation_warnings(): + with warnings.catch_warnings(record=True) as recwarn: + warnings.simplefilter("always") + yield + + for w in recwarn: + if isinstance(w, qml.PennyLaneDeprecationWarning): + assert "Use of 'default.qubit." in str(w.message) + assert "is deprecated" in str(w.message) + assert "use 'default.qubit'" in str(w.message) for w in recwarn: - if isinstance(w.message, qml.PennyLaneDeprecationWarning): - assert "Use of 'default.qubit." in str(w.message) - assert "is deprecated" in str(w.message) - assert "use 'default.qubit'" in str(w.message) + if "Use of 'default.qubit." not in str(w.message): + warnings.warn(message=w.message, category=w.category) @pytest.fixture(scope="session") diff --git a/tests/drawer/test_tape_mpl.py b/tests/drawer/test_tape_mpl.py index d1196c934f8..c8568c6293f 100644 --- a/tests/drawer/test_tape_mpl.py +++ b/tests/drawer/test_tape_mpl.py @@ -844,7 +844,7 @@ def test_single_measure_multiple_conds(self): qml.cond(m0, qml.PauliY)(0) tape = qml.tape.QuantumScript.from_queue(q) - _, ax = qml.drawer.tape_mpl(tape, style="black_white") + _, ax = qml.drawer.tape_mpl(tape) assert len(ax.patches) == 5 # three for measure, two for boxes @@ -857,11 +857,11 @@ def test_single_measure_multiple_conds(self): # probably not a good way to test this, but the best I can figure out assert pe1._gc == { - "linewidth": 5 * 1.5, # lines.linewidth for black white style + "linewidth": 5 * plt.rcParams["lines.linewidth"], "foreground": "black", # lines.color for black white style } assert pe2._gc == { - "linewidth": 3 * 1.5, # lines.linewidth for black white style + "linewidth": 3 * plt.rcParams["lines.linewidth"], "foreground": "white", # figure.facecolor for black white sytle } plt.close() @@ -875,7 +875,7 @@ def test_combo_measurement(self): qml.cond(m0 & m1, qml.PauliY)(0) tape = qml.tape.QuantumScript.from_queue(q) - _, ax = qml.drawer.tape_mpl(tape, style="black_white") + _, ax = qml.drawer.tape_mpl(tape) assert len(ax.patches) == 7 # three for 2 measurements, one for box [_, _, cwire1, cwire2, eraser] = ax.lines @@ -891,22 +891,18 @@ def test_combo_measurement(self): # probably not a good way to test this, but the best I can figure out assert pe1._gc == { - "linewidth": 5 * 1.5, # lines.linewidth for black white style + "linewidth": 5 * plt.rcParams["lines.linewidth"], "foreground": "black", # lines.color for black white style } - assert pe2._gc["linewidth"] == 3 * 1.5 # lines.linewidth for black white style - assert pe2._gc["foreground"].lower() in ( - "white", - "#f0f0f0", - ) # figure.facecolor for black white style + assert pe2._gc == { + "linewidth": 3 * plt.rcParams["lines.linewidth"], + "foreground": "white", # figure.facecolor for black white sytle + } assert eraser.get_xdata() == (1.8, 2) assert eraser.get_ydata() == (2, 2) - assert eraser.get_color().lower() in ( - "white", - "#f0f0f0", - ) # figure.facecolor for black white style - assert eraser.get_linewidth() == 3 * 1.5 # lines.linewidth for black white style + assert eraser.get_color() == plt.rcParams["figure.facecolor"] + assert eraser.get_linewidth() == 3 * plt.rcParams["lines.linewidth"] plt.close() @@ -922,7 +918,7 @@ def test_combo_measurement_non_terminal(self): qml.cond(m1, qml.T)(1) tape = qml.tape.QuantumScript.from_queue(q) - _, ax = qml.drawer.tape_mpl(tape, style="black_white") + _, ax = qml.drawer.tape_mpl(tape) [_, _, cwire1, cwire2, eraser] = ax.lines @@ -937,33 +933,28 @@ def test_combo_measurement_non_terminal(self): # probably not a good way to test this, but the best I can figure out assert pe1._gc == { - "linewidth": 5 * 1.5, # lines.linewidth for black white style + "linewidth": 5 * plt.rcParams["lines.linewidth"], "foreground": "black", # lines.color for black white style } - - assert pe2._gc["linewidth"] == 3 * 1.5 # lines.linewidth for black white style - assert pe2._gc["foreground"].lower() in ( - "white", - "#f0f0f0", - ) # figure.facecolor for black white sytle + assert pe2._gc == { + "linewidth": 3 * plt.rcParams["lines.linewidth"], + "foreground": "white", # figure.facecolor for black white sytle + } assert eraser.get_xdata() == (1.8, 2.2) assert eraser.get_ydata() == (2, 2) - assert eraser.get_color().lower() in ( - "white", - "#f0f0f0", - ) # figure.facecolor for black white style - assert eraser.get_linewidth() == 3 * 1.5 # lines.linewidth for black white style + assert eraser.get_color() == plt.rcParams["figure.facecolor"] + assert eraser.get_linewidth() == 3 * plt.rcParams["lines.linewidth"] plt.close() def test_single_mcm_measure(self): """Test a final measurement of a mid circuit measurement.""" - plt.rcParams["lines.linewidth"] = 20 + with qml.queuing.AnnotatedQueue() as q: m0 = qml.measure(0) qml.expval(m0) - _, ax = tape_mpl(qml.tape.QuantumScript.from_queue(q), style="black_white") + _, ax = tape_mpl(qml.tape.QuantumScript.from_queue(q)) assert len(ax.patches) == 6 # two measurement boxes assert ax.patches[3].get_x() == 1 - 0.75 / 2 + 0.2 # 1 - box_length/2 + pad @@ -983,11 +974,11 @@ def test_single_mcm_measure(self): # probably not a good way to test this, but the best I can figure out assert pe1._gc == { - "linewidth": 5 * 1.5, # lines.linewidth for black white style + "linewidth": 5 * plt.rcParams["lines.linewidth"], "foreground": "black", # lines.color for black white style } assert pe2._gc == { - "linewidth": 3 * 1.5, # lines.linewidth for black white style + "linewidth": 3 * plt.rcParams["lines.linewidth"], "foreground": "white", # figure.facecolor for black white sytle } @@ -1001,7 +992,7 @@ def test_multiple_mcm_measure(self): _ = qml.measure(0) qml.sample([m0, m1]) qml.expval(m2) - _, ax = qml.drawer.tape_mpl(qml.tape.QuantumScript.from_queue(q), style="black_white") + _, ax = qml.drawer.tape_mpl(qml.tape.QuantumScript.from_queue(q)) [_, cwire0, cwire1, cwire2] = ax.lines assert cwire0.get_xdata() == [0, 0, 0, 5, 5, 5] diff --git a/tests/transforms/test_tape_expand.py b/tests/transforms/test_tape_expand.py index 7fe681a1cea..b51e87601d6 100644 --- a/tests/transforms/test_tape_expand.py +++ b/tests/transforms/test_tape_expand.py @@ -433,15 +433,18 @@ class TestCreateCustomDecompExpandFn: @pytest.fixture(scope="function", autouse=True) def capture_warnings(self): - with pytest.warns(qml.PennyLaneDeprecationWarning) as record: + with pytest.warns( + qml.PennyLaneDeprecationWarning, + ) as record: yield + assert any( + "'expansion_strategy' attribute is deprecated" in str(w.message) for w in record + ) + for w in record: if "'expansion_strategy' attribute is deprecated" not in str(w.message): warnings.warn(w.message, w.category) - else: - assert isinstance(w.message, qml.PennyLaneDeprecationWarning) - assert "'expansion_strategy' attribute is deprecated" in str(w.message) @pytest.mark.parametrize("device_name", ["default.qubit", "default.qubit.legacy"]) def test_string_and_operator_allowed(self, device_name): From 0247cf7ac329f83e63bb38123fd4f3467d133c76 Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Sun, 21 Jul 2024 17:06:28 -0400 Subject: [PATCH 28/38] fix minor formatting typo --- pennylane/pulse/parametrized_evolution.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pennylane/pulse/parametrized_evolution.py b/pennylane/pulse/parametrized_evolution.py index 65fc9748fbe..e71377b0a69 100644 --- a/pennylane/pulse/parametrized_evolution.py +++ b/pennylane/pulse/parametrized_evolution.py @@ -322,11 +322,11 @@ def circuit(param, time): >>> circuit(param, time) Array([[1. , 0. ], - [0.98977406, 0.01022594], - [0.95990416, 0.04009584], - [0.91236167, 0.08763833], - [0.84996865, 0.15003133], - [0.77614817, 0.22385181]], dtype=float64) + [0.98977406, 0.01022594], + [0.95990416, 0.04009584], + [0.91236167, 0.08763833], + [0.84996865, 0.15003133], + [0.77614817, 0.22385181]], dtype=float64) **Computing complementary time evolution** From 7925f8fe0da114d72c5420d1b2afdf09a79a863b Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Mon, 22 Jul 2024 13:55:36 -0400 Subject: [PATCH 29/38] revert changes to hardware_hamiltonian example --- pennylane/pulse/hardware_hamiltonian.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pennylane/pulse/hardware_hamiltonian.py b/pennylane/pulse/hardware_hamiltonian.py index 57006e24dca..b1b22e0c63b 100644 --- a/pennylane/pulse/hardware_hamiltonian.py +++ b/pennylane/pulse/hardware_hamiltonian.py @@ -106,9 +106,9 @@ def circuit(params): >>> params = [2.4] >>> circuit(params) - Array(-0.17375104, dtype=float64) + Array(0.32495208, dtype=float64) >>> jax.grad(circuit)(params) - [Array(13.66916253, dtype=float64, weak_type=True)] + [Array(1.31956098, dtype=float64, weak_type=True)] We can also create a Hamiltonian with multiple local drives. The following circuit corresponds to the evolution where an additional local drive that changes in time is acting on wires ``[0, 1]`` is added to the Hamiltonian: From 1b0ce0e0dba15bbc145685a73503902afc73b6e5 Mon Sep 17 00:00:00 2001 From: Ahmed Darwish Date: Mon, 22 Jul 2024 16:01:24 -0400 Subject: [PATCH 30/38] Update pennylane/devices/default_qubit_legacy.py Co-authored-by: Mudit Pandey --- pennylane/devices/default_qubit_legacy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/devices/default_qubit_legacy.py b/pennylane/devices/default_qubit_legacy.py index 49c7d31fbe2..44916ddc85d 100644 --- a/pennylane/devices/default_qubit_legacy.py +++ b/pennylane/devices/default_qubit_legacy.py @@ -84,7 +84,7 @@ class DefaultQubitLegacy(QubitDevice): .. warning:: This is the legacy implementation of DefaultQubit and is deprecated. It has been replaced by - :class:`~.devices.DefaultQubit`, which can be accessed with the familiar constructor, + :class:`~pennylane.devices.DefaultQubit`, which can be accessed with the familiar constructor, ``qml.device("default.qubit")``, and now supports backpropagation. This change will not alter device behaviour for most workflows, but may have implications for From 4b10db6f987d9fe0a9a2790c29b14df3eed05fce Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Tue, 23 Jul 2024 11:27:27 -0400 Subject: [PATCH 31/38] restructuring and bringing back a test --- .../test_autograd.py | 0 .../test_autograd_qnode.py | 0 .../test_autograd_qnode_shot_vector.py | 0 .../test_execute.py | 0 .../test_jax.py | 0 .../test_jax_jit.py | 0 .../test_jax_jit_qnode.py | 0 .../test_jax_qnode.py | 0 .../test_jax_qnode_shot_vector.py | 0 .../test_set_shots.py | 0 .../test_tensorflow.py | 0 ..._tensorflow_autograph_qnode_shot_vector.py | 0 .../test_tensorflow_qnode.py | 0 .../test_tensorflow_qnode_shot_vector.py | 0 .../test_torch.py | 0 .../test_torch_qnode.py | 0 tests/test_debugging.py | 50 ++++++++++++++++++- 17 files changed, 49 insertions(+), 1 deletion(-) rename tests/interfaces/{default_qubit_integration => }/test_autograd.py (100%) rename tests/interfaces/{default_qubit_integration => }/test_autograd_qnode.py (100%) rename tests/interfaces/{default_qubit_integration => }/test_autograd_qnode_shot_vector.py (100%) rename tests/interfaces/{default_qubit_integration => }/test_execute.py (100%) rename tests/interfaces/{default_qubit_integration => }/test_jax.py (100%) rename tests/interfaces/{default_qubit_integration => }/test_jax_jit.py (100%) rename tests/interfaces/{default_qubit_integration => }/test_jax_jit_qnode.py (100%) rename tests/interfaces/{default_qubit_integration => }/test_jax_qnode.py (100%) rename tests/interfaces/{default_qubit_integration => }/test_jax_qnode_shot_vector.py (100%) rename tests/interfaces/{default_qubit_integration => }/test_set_shots.py (100%) rename tests/interfaces/{default_qubit_integration => }/test_tensorflow.py (100%) rename tests/interfaces/{default_qubit_integration => }/test_tensorflow_autograph_qnode_shot_vector.py (100%) rename tests/interfaces/{default_qubit_integration => }/test_tensorflow_qnode.py (100%) rename tests/interfaces/{default_qubit_integration => }/test_tensorflow_qnode_shot_vector.py (100%) rename tests/interfaces/{default_qubit_integration => }/test_torch.py (100%) rename tests/interfaces/{default_qubit_integration => }/test_torch_qnode.py (100%) diff --git a/tests/interfaces/default_qubit_integration/test_autograd.py b/tests/interfaces/test_autograd.py similarity index 100% rename from tests/interfaces/default_qubit_integration/test_autograd.py rename to tests/interfaces/test_autograd.py diff --git a/tests/interfaces/default_qubit_integration/test_autograd_qnode.py b/tests/interfaces/test_autograd_qnode.py similarity index 100% rename from tests/interfaces/default_qubit_integration/test_autograd_qnode.py rename to tests/interfaces/test_autograd_qnode.py diff --git a/tests/interfaces/default_qubit_integration/test_autograd_qnode_shot_vector.py b/tests/interfaces/test_autograd_qnode_shot_vector.py similarity index 100% rename from tests/interfaces/default_qubit_integration/test_autograd_qnode_shot_vector.py rename to tests/interfaces/test_autograd_qnode_shot_vector.py diff --git a/tests/interfaces/default_qubit_integration/test_execute.py b/tests/interfaces/test_execute.py similarity index 100% rename from tests/interfaces/default_qubit_integration/test_execute.py rename to tests/interfaces/test_execute.py diff --git a/tests/interfaces/default_qubit_integration/test_jax.py b/tests/interfaces/test_jax.py similarity index 100% rename from tests/interfaces/default_qubit_integration/test_jax.py rename to tests/interfaces/test_jax.py diff --git a/tests/interfaces/default_qubit_integration/test_jax_jit.py b/tests/interfaces/test_jax_jit.py similarity index 100% rename from tests/interfaces/default_qubit_integration/test_jax_jit.py rename to tests/interfaces/test_jax_jit.py diff --git a/tests/interfaces/default_qubit_integration/test_jax_jit_qnode.py b/tests/interfaces/test_jax_jit_qnode.py similarity index 100% rename from tests/interfaces/default_qubit_integration/test_jax_jit_qnode.py rename to tests/interfaces/test_jax_jit_qnode.py diff --git a/tests/interfaces/default_qubit_integration/test_jax_qnode.py b/tests/interfaces/test_jax_qnode.py similarity index 100% rename from tests/interfaces/default_qubit_integration/test_jax_qnode.py rename to tests/interfaces/test_jax_qnode.py diff --git a/tests/interfaces/default_qubit_integration/test_jax_qnode_shot_vector.py b/tests/interfaces/test_jax_qnode_shot_vector.py similarity index 100% rename from tests/interfaces/default_qubit_integration/test_jax_qnode_shot_vector.py rename to tests/interfaces/test_jax_qnode_shot_vector.py diff --git a/tests/interfaces/default_qubit_integration/test_set_shots.py b/tests/interfaces/test_set_shots.py similarity index 100% rename from tests/interfaces/default_qubit_integration/test_set_shots.py rename to tests/interfaces/test_set_shots.py diff --git a/tests/interfaces/default_qubit_integration/test_tensorflow.py b/tests/interfaces/test_tensorflow.py similarity index 100% rename from tests/interfaces/default_qubit_integration/test_tensorflow.py rename to tests/interfaces/test_tensorflow.py diff --git a/tests/interfaces/default_qubit_integration/test_tensorflow_autograph_qnode_shot_vector.py b/tests/interfaces/test_tensorflow_autograph_qnode_shot_vector.py similarity index 100% rename from tests/interfaces/default_qubit_integration/test_tensorflow_autograph_qnode_shot_vector.py rename to tests/interfaces/test_tensorflow_autograph_qnode_shot_vector.py diff --git a/tests/interfaces/default_qubit_integration/test_tensorflow_qnode.py b/tests/interfaces/test_tensorflow_qnode.py similarity index 100% rename from tests/interfaces/default_qubit_integration/test_tensorflow_qnode.py rename to tests/interfaces/test_tensorflow_qnode.py diff --git a/tests/interfaces/default_qubit_integration/test_tensorflow_qnode_shot_vector.py b/tests/interfaces/test_tensorflow_qnode_shot_vector.py similarity index 100% rename from tests/interfaces/default_qubit_integration/test_tensorflow_qnode_shot_vector.py rename to tests/interfaces/test_tensorflow_qnode_shot_vector.py diff --git a/tests/interfaces/default_qubit_integration/test_torch.py b/tests/interfaces/test_torch.py similarity index 100% rename from tests/interfaces/default_qubit_integration/test_torch.py rename to tests/interfaces/test_torch.py diff --git a/tests/interfaces/default_qubit_integration/test_torch_qnode.py b/tests/interfaces/test_torch_qnode.py similarity index 100% rename from tests/interfaces/default_qubit_integration/test_torch_qnode.py rename to tests/interfaces/test_torch_qnode.py diff --git a/tests/test_debugging.py b/tests/test_debugging.py index e241974600d..bdd10a4cfd5 100644 --- a/tests/test_debugging.py +++ b/tests/test_debugging.py @@ -181,7 +181,12 @@ def circuit(): return qml.expval(qml.PauliZ(0)) - qml.snapshots(circuit)(shots=200) + with ( + pytest.warns(UserWarning, match="Requested state or density matrix with finite shots") + if isinstance(dev, qml.devices.default_qutrit.DefaultQutrit) + else nullcontext() + ): + qml.snapshots(circuit)(shots=200) @pytest.mark.parametrize("diff_method", [None, "parameter-shift"]) def test_all_state_measurement_snapshot_pure_qubit_dev(self, dev, diff_method): @@ -273,6 +278,49 @@ def circuit(): _compare_numpy_dicts(result, expected) + @pytest.mark.parametrize("diff_method", [None, "backprop", "parameter-shift", "adjoint"]) + def test_default_qubit_legacy_only_supports_state(self, diff_method): + with pytest.warns(qml.PennyLaneDeprecationWarning, match="Use of 'default.qubit"): + dev = qml.device("default.qubit.legacy", wires=2) + + assert qml.debugging.snapshot._is_snapshot_compatible(dev) + + @qml.qnode(dev, diff_method=diff_method) + def circuit_faulty(): + qml.Hadamard(wires=0) + qml.Snapshot("important_expval", measurement=qml.expval(qml.PauliX(0))) + qml.CNOT(wires=[0, 1]) + qml.Snapshot() + return qml.expval(qml.PauliX(0)) + + circuit_faulty() + assert dev._debugger is None + if diff_method is not None: + assert circuit_faulty.interface == "auto" + + with pytest.raises(NotImplementedError, match="only supports `qml.state` measurements"): + qml.snapshots(circuit_faulty)() + + @qml.qnode(dev, diff_method=diff_method) + def circuit(): + qml.Hadamard(wires=0) + qml.CNOT(wires=[0, 1]) + qml.Snapshot() + return qml.expval(qml.PauliX(0)) + + expected = { + 0: np.array([1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2)]), + "execution_results": np.array(0), + } + + result = qml.snapshots(circuit)() + _compare_numpy_dicts(result, expected) + + if diff_method not in ("backprop", "adjoint"): + result_shots = qml.snapshots(circuit)(shots=200) + expected["execution_results"] = np.array(-0.04) + _compare_numpy_dicts(result_shots, expected) + # pylint: disable=protected-access @pytest.mark.parametrize("method", [None, "parameter-shift"]) def test_default_mixed(self, method): From 2d6784a62dc74fdbdd2cea6d9a7368761b6eaf9b Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Tue, 23 Jul 2024 14:41:59 -0400 Subject: [PATCH 32/38] computationally less expensive shots for TF shots tests --- .../test_tensorflow_autograph_qnode_shot_vector_legacy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/interfaces/legacy_devices_integration/test_tensorflow_autograph_qnode_shot_vector_legacy.py b/tests/interfaces/legacy_devices_integration/test_tensorflow_autograph_qnode_shot_vector_legacy.py index ab3147dfc95..15dd7e6fe3f 100644 --- a/tests/interfaces/legacy_devices_integration/test_tensorflow_autograph_qnode_shot_vector_legacy.py +++ b/tests/interfaces/legacy_devices_integration/test_tensorflow_autograph_qnode_shot_vector_legacy.py @@ -384,7 +384,7 @@ def circuit(x, y): assert h.shape == (2, num_copies) -shots_and_num_copies = [((1000000, 900000, 800000), 3), ((1000000, (900000, 2)), 3)] +shots_and_num_copies = [((20000, 18000, 16000), 3), ((20000, (18000, 2)), 3)] @pytest.mark.parametrize("shots,num_copies", shots_and_num_copies) From d4eaf6cee1cd007e90e6dc6025eeb416548ae975 Mon Sep 17 00:00:00 2001 From: albi3ro Date: Tue, 23 Jul 2024 17:40:56 -0400 Subject: [PATCH 33/38] add print statements to try and debug failing drawer test --- pennylane/drawer/mpldrawer.py | 1 + tests/drawer/test_tape_mpl.py | 1 + 2 files changed, 2 insertions(+) diff --git a/pennylane/drawer/mpldrawer.py b/pennylane/drawer/mpldrawer.py index 75684f5bd67..3abaa76d5ce 100644 --- a/pennylane/drawer/mpldrawer.py +++ b/pennylane/drawer/mpldrawer.py @@ -931,6 +931,7 @@ def classical_wire(self, layers, wires) -> None: greater than the number of quantum wires will be scaled as classical wires. """ + print("linewidth in MPLDrawer: ", plt.rcParams["lines.linewidth"]) outer_stroke = path_effects.Stroke( linewidth=5 * plt.rcParams["lines.linewidth"], foreground=plt.rcParams["lines.color"] ) diff --git a/tests/drawer/test_tape_mpl.py b/tests/drawer/test_tape_mpl.py index c8568c6293f..078aac20c07 100644 --- a/tests/drawer/test_tape_mpl.py +++ b/tests/drawer/test_tape_mpl.py @@ -856,6 +856,7 @@ def test_single_measure_multiple_conds(self): [pe1, pe2] = cwire.get_path_effects() # probably not a good way to test this, but the best I can figure out + print("lines.linewidth in test: ", plt.rcParams["lines.linewidth"]) assert pe1._gc == { "linewidth": 5 * plt.rcParams["lines.linewidth"], "foreground": "black", # lines.color for black white style From 67f0944b09a3e543756eee9e7a493568b1d95dbe Mon Sep 17 00:00:00 2001 From: albi3ro Date: Wed, 24 Jul 2024 10:37:06 -0400 Subject: [PATCH 34/38] hardcode expected linewidth value, remove print statements --- pennylane/drawer/mpldrawer.py | 1 - tests/drawer/test_tape_mpl.py | 29 ++++++++++++++--------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/pennylane/drawer/mpldrawer.py b/pennylane/drawer/mpldrawer.py index 3946761ff04..ea2f2425d06 100644 --- a/pennylane/drawer/mpldrawer.py +++ b/pennylane/drawer/mpldrawer.py @@ -930,7 +930,6 @@ def classical_wire(self, layers, wires) -> None: greater than the number of quantum wires will be scaled as classical wires. """ - print("linewidth in MPLDrawer: ", plt.rcParams["lines.linewidth"]) outer_stroke = path_effects.Stroke( linewidth=5 * plt.rcParams["lines.linewidth"], foreground=plt.rcParams["lines.color"] ) diff --git a/tests/drawer/test_tape_mpl.py b/tests/drawer/test_tape_mpl.py index 078aac20c07..6f2c27b2c89 100644 --- a/tests/drawer/test_tape_mpl.py +++ b/tests/drawer/test_tape_mpl.py @@ -844,7 +844,7 @@ def test_single_measure_multiple_conds(self): qml.cond(m0, qml.PauliY)(0) tape = qml.tape.QuantumScript.from_queue(q) - _, ax = qml.drawer.tape_mpl(tape) + _, ax = qml.drawer.tape_mpl(tape, style="black-white") assert len(ax.patches) == 5 # three for measure, two for boxes @@ -856,13 +856,12 @@ def test_single_measure_multiple_conds(self): [pe1, pe2] = cwire.get_path_effects() # probably not a good way to test this, but the best I can figure out - print("lines.linewidth in test: ", plt.rcParams["lines.linewidth"]) assert pe1._gc == { - "linewidth": 5 * plt.rcParams["lines.linewidth"], + "linewidth": 5 * 1.5, # hardcoded value to black-white linewidth "foreground": "black", # lines.color for black white style } assert pe2._gc == { - "linewidth": 3 * plt.rcParams["lines.linewidth"], + "linewidth": 3 * 1.5, # hardcoded value to black-white linewidth "foreground": "white", # figure.facecolor for black white sytle } plt.close() @@ -876,7 +875,7 @@ def test_combo_measurement(self): qml.cond(m0 & m1, qml.PauliY)(0) tape = qml.tape.QuantumScript.from_queue(q) - _, ax = qml.drawer.tape_mpl(tape) + _, ax = qml.drawer.tape_mpl(tape, style="black-white") assert len(ax.patches) == 7 # three for 2 measurements, one for box [_, _, cwire1, cwire2, eraser] = ax.lines @@ -892,18 +891,18 @@ def test_combo_measurement(self): # probably not a good way to test this, but the best I can figure out assert pe1._gc == { - "linewidth": 5 * plt.rcParams["lines.linewidth"], + "linewidth": 5 * 1.5, # hardcoded value to black-white linewidth "foreground": "black", # lines.color for black white style } assert pe2._gc == { - "linewidth": 3 * plt.rcParams["lines.linewidth"], + "linewidth": 3 * 1.5, # hardcoded value to black-white linewidth "foreground": "white", # figure.facecolor for black white sytle } assert eraser.get_xdata() == (1.8, 2) assert eraser.get_ydata() == (2, 2) assert eraser.get_color() == plt.rcParams["figure.facecolor"] - assert eraser.get_linewidth() == 3 * plt.rcParams["lines.linewidth"] + assert eraser.get_linewidth() == 3 * 1.5 # hardcoded value to black-white linewidth plt.close() @@ -919,7 +918,7 @@ def test_combo_measurement_non_terminal(self): qml.cond(m1, qml.T)(1) tape = qml.tape.QuantumScript.from_queue(q) - _, ax = qml.drawer.tape_mpl(tape) + _, ax = qml.drawer.tape_mpl(tape, style="black-white") [_, _, cwire1, cwire2, eraser] = ax.lines @@ -934,18 +933,18 @@ def test_combo_measurement_non_terminal(self): # probably not a good way to test this, but the best I can figure out assert pe1._gc == { - "linewidth": 5 * plt.rcParams["lines.linewidth"], + "linewidth": 5 * 1.5, # hardcoded value to black-white linewidth "foreground": "black", # lines.color for black white style } assert pe2._gc == { - "linewidth": 3 * plt.rcParams["lines.linewidth"], + "linewidth": 3 * 1.5, # hardcoded value to black-white linewidth "foreground": "white", # figure.facecolor for black white sytle } assert eraser.get_xdata() == (1.8, 2.2) assert eraser.get_ydata() == (2, 2) assert eraser.get_color() == plt.rcParams["figure.facecolor"] - assert eraser.get_linewidth() == 3 * plt.rcParams["lines.linewidth"] + assert eraser.get_linewidth() == 3 * 1.5 # hardcoded value to black-white linewidth plt.close() @@ -955,7 +954,7 @@ def test_single_mcm_measure(self): with qml.queuing.AnnotatedQueue() as q: m0 = qml.measure(0) qml.expval(m0) - _, ax = tape_mpl(qml.tape.QuantumScript.from_queue(q)) + _, ax = tape_mpl(qml.tape.QuantumScript.from_queue(q), style="black-white") assert len(ax.patches) == 6 # two measurement boxes assert ax.patches[3].get_x() == 1 - 0.75 / 2 + 0.2 # 1 - box_length/2 + pad @@ -975,11 +974,11 @@ def test_single_mcm_measure(self): # probably not a good way to test this, but the best I can figure out assert pe1._gc == { - "linewidth": 5 * plt.rcParams["lines.linewidth"], + "linewidth": 5 * 1.5, # hardcoded value to black-white linewidth "foreground": "black", # lines.color for black white style } assert pe2._gc == { - "linewidth": 3 * plt.rcParams["lines.linewidth"], + "linewidth": 3 * 1.5, # hardcoded value to black-white linewidth "foreground": "white", # figure.facecolor for black white sytle } From 7572bdae3ce878e4fb3de56e2cb9e54b7bbf10fa Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Wed, 24 Jul 2024 11:31:46 -0400 Subject: [PATCH 35/38] fix typo in style name --- tests/drawer/test_tape_mpl.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/drawer/test_tape_mpl.py b/tests/drawer/test_tape_mpl.py index 6f2c27b2c89..3d25d0087b8 100644 --- a/tests/drawer/test_tape_mpl.py +++ b/tests/drawer/test_tape_mpl.py @@ -844,7 +844,7 @@ def test_single_measure_multiple_conds(self): qml.cond(m0, qml.PauliY)(0) tape = qml.tape.QuantumScript.from_queue(q) - _, ax = qml.drawer.tape_mpl(tape, style="black-white") + _, ax = qml.drawer.tape_mpl(tape, style="black_white") assert len(ax.patches) == 5 # three for measure, two for boxes @@ -857,11 +857,11 @@ def test_single_measure_multiple_conds(self): # probably not a good way to test this, but the best I can figure out assert pe1._gc == { - "linewidth": 5 * 1.5, # hardcoded value to black-white linewidth + "linewidth": 5 * 1.5, # hardcoded value to black_white linewidth "foreground": "black", # lines.color for black white style } assert pe2._gc == { - "linewidth": 3 * 1.5, # hardcoded value to black-white linewidth + "linewidth": 3 * 1.5, # hardcoded value to black_white linewidth "foreground": "white", # figure.facecolor for black white sytle } plt.close() @@ -875,7 +875,7 @@ def test_combo_measurement(self): qml.cond(m0 & m1, qml.PauliY)(0) tape = qml.tape.QuantumScript.from_queue(q) - _, ax = qml.drawer.tape_mpl(tape, style="black-white") + _, ax = qml.drawer.tape_mpl(tape, style="black_white") assert len(ax.patches) == 7 # three for 2 measurements, one for box [_, _, cwire1, cwire2, eraser] = ax.lines @@ -891,18 +891,18 @@ def test_combo_measurement(self): # probably not a good way to test this, but the best I can figure out assert pe1._gc == { - "linewidth": 5 * 1.5, # hardcoded value to black-white linewidth + "linewidth": 5 * 1.5, # hardcoded value to black_white linewidth "foreground": "black", # lines.color for black white style } assert pe2._gc == { - "linewidth": 3 * 1.5, # hardcoded value to black-white linewidth + "linewidth": 3 * 1.5, # hardcoded value to black_white linewidth "foreground": "white", # figure.facecolor for black white sytle } assert eraser.get_xdata() == (1.8, 2) assert eraser.get_ydata() == (2, 2) assert eraser.get_color() == plt.rcParams["figure.facecolor"] - assert eraser.get_linewidth() == 3 * 1.5 # hardcoded value to black-white linewidth + assert eraser.get_linewidth() == 3 * 1.5 # hardcoded value to black_white linewidth plt.close() @@ -918,7 +918,7 @@ def test_combo_measurement_non_terminal(self): qml.cond(m1, qml.T)(1) tape = qml.tape.QuantumScript.from_queue(q) - _, ax = qml.drawer.tape_mpl(tape, style="black-white") + _, ax = qml.drawer.tape_mpl(tape, style="black_white") [_, _, cwire1, cwire2, eraser] = ax.lines @@ -933,18 +933,18 @@ def test_combo_measurement_non_terminal(self): # probably not a good way to test this, but the best I can figure out assert pe1._gc == { - "linewidth": 5 * 1.5, # hardcoded value to black-white linewidth + "linewidth": 5 * 1.5, # hardcoded value to black_white linewidth "foreground": "black", # lines.color for black white style } assert pe2._gc == { - "linewidth": 3 * 1.5, # hardcoded value to black-white linewidth + "linewidth": 3 * 1.5, # hardcoded value to black_white linewidth "foreground": "white", # figure.facecolor for black white sytle } assert eraser.get_xdata() == (1.8, 2.2) assert eraser.get_ydata() == (2, 2) assert eraser.get_color() == plt.rcParams["figure.facecolor"] - assert eraser.get_linewidth() == 3 * 1.5 # hardcoded value to black-white linewidth + assert eraser.get_linewidth() == 3 * 1.5 # hardcoded value to black_white linewidth plt.close() @@ -954,7 +954,7 @@ def test_single_mcm_measure(self): with qml.queuing.AnnotatedQueue() as q: m0 = qml.measure(0) qml.expval(m0) - _, ax = tape_mpl(qml.tape.QuantumScript.from_queue(q), style="black-white") + _, ax = tape_mpl(qml.tape.QuantumScript.from_queue(q), style="black_white") assert len(ax.patches) == 6 # two measurement boxes assert ax.patches[3].get_x() == 1 - 0.75 / 2 + 0.2 # 1 - box_length/2 + pad @@ -974,11 +974,11 @@ def test_single_mcm_measure(self): # probably not a good way to test this, but the best I can figure out assert pe1._gc == { - "linewidth": 5 * 1.5, # hardcoded value to black-white linewidth + "linewidth": 5 * 1.5, # hardcoded value to black_white linewidth "foreground": "black", # lines.color for black white style } assert pe2._gc == { - "linewidth": 3 * 1.5, # hardcoded value to black-white linewidth + "linewidth": 3 * 1.5, # hardcoded value to black_white linewidth "foreground": "white", # figure.facecolor for black white sytle } From 1eb9d896f3401714767aa7dde59c0cc0f4e421eb Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Wed, 24 Jul 2024 14:53:46 -0400 Subject: [PATCH 36/38] hardcode eraser color in test_tape_mpl --- tests/drawer/test_tape_mpl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/drawer/test_tape_mpl.py b/tests/drawer/test_tape_mpl.py index 3d25d0087b8..60d1b997004 100644 --- a/tests/drawer/test_tape_mpl.py +++ b/tests/drawer/test_tape_mpl.py @@ -901,7 +901,7 @@ def test_combo_measurement(self): assert eraser.get_xdata() == (1.8, 2) assert eraser.get_ydata() == (2, 2) - assert eraser.get_color() == plt.rcParams["figure.facecolor"] + assert eraser.get_color() == "white" # hardcoded value to black_white color assert eraser.get_linewidth() == 3 * 1.5 # hardcoded value to black_white linewidth plt.close() @@ -943,7 +943,7 @@ def test_combo_measurement_non_terminal(self): assert eraser.get_xdata() == (1.8, 2.2) assert eraser.get_ydata() == (2, 2) - assert eraser.get_color() == plt.rcParams["figure.facecolor"] + assert eraser.get_color() == "white" # hardcoded value to black_white color assert eraser.get_linewidth() == 3 * 1.5 # hardcoded value to black_white linewidth plt.close() From 1ef372932ee5e55c29f00863a38bc09a8dc54569 Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Wed, 24 Jul 2024 16:26:15 -0400 Subject: [PATCH 37/38] fixes to tf tests and runtime improvements --- .../test_autograd_qnode_shot_vector_legacy.py | 33 ++++++++++++------ ...flow_autograph_qnode_shot_vector_legacy.py | 30 +++++++++++----- ...est_tensorflow_qnode_shot_vector_legacy.py | 2 +- ..._tensorflow_autograph_qnode_shot_vector.py | 34 ++++++++++++------- .../test_tensorflow_qnode_shot_vector.py | 2 +- 5 files changed, 68 insertions(+), 33 deletions(-) diff --git a/tests/interfaces/legacy_devices_integration/test_autograd_qnode_shot_vector_legacy.py b/tests/interfaces/legacy_devices_integration/test_autograd_qnode_shot_vector_legacy.py index 6aee767d01e..24d1d824ced 100644 --- a/tests/interfaces/legacy_devices_integration/test_autograd_qnode_shot_vector_legacy.py +++ b/tests/interfaces/legacy_devices_integration/test_autograd_qnode_shot_vector_legacy.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Integration tests for using the Autograd interface with shot vectors and with a QNode""" -# pylint: disable=too-many-arguments +# pylint: disable=too-many-arguments,redefined-outer-name import pytest @@ -25,13 +25,17 @@ shots_and_num_copies = [(((5, 2), 1, 10), 4), ((1, 10, (5, 2)), 4)] shots_and_num_copies_hess = [(((5, 1), 10), 2), ((10, (5, 1)), 2)] -SEED_FOR_SPSA = 42 -spsa_kwargs = {"h": 0.05, "num_directions": 20, "sampler_rng": np.random.default_rng(SEED_FOR_SPSA)} + +kwargs = { + "finite-diff": {"h": 0.05}, + "parameter-shift": {}, + "spsa": {"h": 0.05, "num_directions": 20}, +} qubit_device_and_diff_method = [ - ["default.qubit.legacy", "finite-diff", {"h": 0.05}], - ["default.qubit.legacy", "parameter-shift", {}], - ["default.qubit.legacy", "spsa", spsa_kwargs], + ["default.qubit.legacy", "finite-diff"], + ["default.qubit.legacy", "parameter-shift"], + ["default.qubit.legacy", "spsa"], ] TOLS = { @@ -41,8 +45,16 @@ } +@pytest.fixture +def gradient_kwargs(request): + diff_method = request.node.funcargs["diff_method"] + return kwargs[diff_method] | ( + {"sampler_rng": np.random.default_rng(42)} if diff_method == "spsa" else {} + ) + + @pytest.mark.parametrize("shots,num_copies", shots_and_num_copies) -@pytest.mark.parametrize("dev_name,diff_method,gradient_kwargs", qubit_device_and_diff_method) +@pytest.mark.parametrize("dev_name,diff_method", qubit_device_and_diff_method) class TestReturnWithShotVectors: """Class to test the shape of the Jacobian/Hessian with different return types and shot vectors.""" @@ -369,7 +381,7 @@ def cost(a): @pytest.mark.slow @pytest.mark.parametrize("shots,num_copies", shots_and_num_copies_hess) -@pytest.mark.parametrize("dev_name,diff_method,gradient_kwargs", qubit_device_and_diff_method) +@pytest.mark.parametrize("dev_name,diff_method", qubit_device_and_diff_method) class TestReturnShotVectorHessian: """Class to test the shape of the Hessian with different return types and shot vectors.""" @@ -553,12 +565,11 @@ def cost2(x): assert hess.shape == (num_copies, 3, 2, 2) -shots_and_num_copies = [((1000000, 900000, 800000), 3), ((1000000, (900000, 2)), 3)] +shots_and_num_copies = [((30000, 28000, 26000), 3), ((30000, (28000, 2)), 3)] -@pytest.mark.skip("failing in CI for inscrutable reasons, passes locally") @pytest.mark.parametrize("shots,num_copies", shots_and_num_copies) -@pytest.mark.parametrize("dev_name,diff_method,gradient_kwargs", qubit_device_and_diff_method) +@pytest.mark.parametrize("dev_name,diff_method", qubit_device_and_diff_method) class TestReturnShotVectorIntegration: """Tests for the integration of shots with the autograd interface.""" diff --git a/tests/interfaces/legacy_devices_integration/test_tensorflow_autograph_qnode_shot_vector_legacy.py b/tests/interfaces/legacy_devices_integration/test_tensorflow_autograph_qnode_shot_vector_legacy.py index 15dd7e6fe3f..f5b6e37a85b 100644 --- a/tests/interfaces/legacy_devices_integration/test_tensorflow_autograph_qnode_shot_vector_legacy.py +++ b/tests/interfaces/legacy_devices_integration/test_tensorflow_autograph_qnode_shot_vector_legacy.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Integration tests for using the TF interface with shot vectors and with a QNode""" -# pylint: disable=too-many-arguments,too-few-public-methods +# pylint: disable=too-many-arguments,too-few-public-methods,redefined-outer-name import pytest import pennylane as qml @@ -26,11 +26,17 @@ shots_and_num_copies = [((1, (5, 2), 10), 4)] shots_and_num_copies_hess = [((10, (5, 1)), 2)] -spsa_kwargs = {"h": 10e-2, "num_directions": 30, "sampler_rng": np.random.default_rng(42)} + +kwargs = { + "finite-diff": {"h": 10e-2}, + "parameter-shift": {}, + "spsa": {"h": 10e-2, "num_directions": 30}, +} + qubit_device_and_diff_method = [ - ["default.qubit.legacy", "finite-diff", {"h": 10e-2}], - ["default.qubit.legacy", "parameter-shift", {}], - ["default.qubit.legacy", "spsa", spsa_kwargs], + ["default.qubit.legacy", "finite-diff"], + ["default.qubit.legacy", "parameter-shift"], + ["default.qubit.legacy", "spsa"], ] TOLS = { @@ -40,8 +46,16 @@ } +@pytest.fixture +def gradient_kwargs(request): + diff_method = request.node.funcargs["diff_method"] + return kwargs[diff_method] | ( + {"sampler_rng": np.random.default_rng(42)} if diff_method == "spsa" else {} + ) + + @pytest.mark.parametrize("shots,num_copies", shots_and_num_copies) -@pytest.mark.parametrize("dev_name,diff_method,gradient_kwargs", qubit_device_and_diff_method) +@pytest.mark.parametrize("dev_name,diff_method", qubit_device_and_diff_method) @pytest.mark.parametrize( "decorator,interface", [(tf.function, "tf"), (lambda x: x, "tf-autograph")], @@ -337,7 +351,7 @@ def circuit(a): @pytest.mark.slow @pytest.mark.parametrize("shots,num_copies", shots_and_num_copies_hess) -@pytest.mark.parametrize("dev_name,diff_method,gradient_kwargs", qubit_device_and_diff_method) +@pytest.mark.parametrize("dev_name,diff_method", qubit_device_and_diff_method) @pytest.mark.parametrize( "decorator,interface", [(tf.function, "tf"), (lambda x: x, "tf-autograph")], @@ -388,7 +402,7 @@ def circuit(x, y): @pytest.mark.parametrize("shots,num_copies", shots_and_num_copies) -@pytest.mark.parametrize("dev_name,diff_method,gradient_kwargs", qubit_device_and_diff_method) +@pytest.mark.parametrize("dev_name,diff_method", qubit_device_and_diff_method) @pytest.mark.parametrize( "decorator,interface", [(tf.function, "tf"), (lambda x: x, "tf-autograph")], diff --git a/tests/interfaces/legacy_devices_integration/test_tensorflow_qnode_shot_vector_legacy.py b/tests/interfaces/legacy_devices_integration/test_tensorflow_qnode_shot_vector_legacy.py index 614c71ee7d4..bea3056b7c5 100644 --- a/tests/interfaces/legacy_devices_integration/test_tensorflow_qnode_shot_vector_legacy.py +++ b/tests/interfaces/legacy_devices_integration/test_tensorflow_qnode_shot_vector_legacy.py @@ -453,7 +453,7 @@ def circuit(x): assert hess.shape == (num_copies, 3, 2, 2) -shots_and_num_copies = [((1000000, 900000, 800000), 3), ((1000000, (900000, 2)), 3)] +shots_and_num_copies = [((20000, 18000, 16000), 3), ((20000, (18000, 2)), 3)] @pytest.mark.parametrize("shots,num_copies", shots_and_num_copies) diff --git a/tests/interfaces/test_tensorflow_autograph_qnode_shot_vector.py b/tests/interfaces/test_tensorflow_autograph_qnode_shot_vector.py index c831fce26cc..f0978ab1e86 100644 --- a/tests/interfaces/test_tensorflow_autograph_qnode_shot_vector.py +++ b/tests/interfaces/test_tensorflow_autograph_qnode_shot_vector.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Integration tests for using the TF interface with shot vectors and with a QNode""" -# pylint: disable=too-many-arguments,too-few-public-methods,unexpected-keyword-arg +# pylint: disable=too-many-arguments,too-few-public-methods,unexpected-keyword-arg,redefined-outer-name import pytest import pennylane as qml @@ -27,14 +27,16 @@ shots_and_num_copies = [((1, (5, 2), 10), 4)] shots_and_num_copies_hess = [((10, (5, 1)), 2)] +kwargs = { + "finite-diff": {"h": 10e-2}, + "parameter-shift": {}, + "spsa": {"h": 10e-2, "num_directions": 20}, +} + qubit_device_and_diff_method = [ - [DefaultQubit(), "finite-diff", {"h": 10e-2}], - [DefaultQubit(), "parameter-shift", {}], - [ - DefaultQubit(), - "spsa", - {"h": 10e-2, "num_directions": 20, "sampler_rng": np.random.default_rng(42)}, - ], + [DefaultQubit(), "finite-diff"], + [DefaultQubit(), "parameter-shift"], + [DefaultQubit(), "spsa"], ] TOLS = { @@ -44,8 +46,16 @@ } +@pytest.fixture +def gradient_kwargs(request): + diff_method = request.node.funcargs["diff_method"] + return kwargs[diff_method] | ( + {"sampler_rng": np.random.default_rng(42)} if diff_method == "spsa" else {} + ) + + @pytest.mark.parametrize("shots,num_copies", shots_and_num_copies) -@pytest.mark.parametrize("dev,diff_method,gradient_kwargs", qubit_device_and_diff_method) +@pytest.mark.parametrize("dev,diff_method", qubit_device_and_diff_method) @pytest.mark.parametrize( "decorator,interface", [(tf.function, "tf"), (lambda x: x, "tf-autograph")], @@ -329,7 +339,7 @@ def circuit(a, **_): @pytest.mark.slow @pytest.mark.parametrize("shots,num_copies", shots_and_num_copies_hess) -@pytest.mark.parametrize("dev,diff_method,gradient_kwargs", qubit_device_and_diff_method) +@pytest.mark.parametrize("dev,diff_method", qubit_device_and_diff_method) @pytest.mark.parametrize( "decorator,interface", [(tf.function, "tf"), (lambda x: x, "tf-autograph")], @@ -374,11 +384,11 @@ def circuit(x, y, **_): assert h.shape == (2, num_copies) -shots_and_num_copies = [((1000000, 900000, 800000), 3), ((1000000, (900000, 2)), 3)] +shots_and_num_copies = [((30000, 28000, 26000), 3), ((20000, (18000, 2)), 3)] @pytest.mark.parametrize("shots,num_copies", shots_and_num_copies) -@pytest.mark.parametrize("dev,diff_method,gradient_kwargs", qubit_device_and_diff_method) +@pytest.mark.parametrize("dev,diff_method", qubit_device_and_diff_method) @pytest.mark.parametrize( "decorator,interface", [(tf.function, "tf"), (lambda x: x, "tf-autograph")], diff --git a/tests/interfaces/test_tensorflow_qnode_shot_vector.py b/tests/interfaces/test_tensorflow_qnode_shot_vector.py index a3a5606fc78..c0340aaab9a 100644 --- a/tests/interfaces/test_tensorflow_qnode_shot_vector.py +++ b/tests/interfaces/test_tensorflow_qnode_shot_vector.py @@ -438,7 +438,7 @@ def circuit(x): assert hess.shape == (num_copies, 3, 2, 2) -shots_and_num_copies = [((1000000, 900000, 800000), 3), ((1000000, (900000, 2)), 3)] +shots_and_num_copies = [((30000, 28000, 26000), 3), ((30000, (28000, 2)), 3)] @pytest.mark.parametrize("shots,num_copies", shots_and_num_copies) From 4a2d5e812a00ae45c449be6cd3eade096f395b06 Mon Sep 17 00:00:00 2001 From: Shiro-Raven Date: Wed, 24 Jul 2024 16:49:56 -0400 Subject: [PATCH 38/38] more shots --- tests/interfaces/test_tensorflow_autograph_qnode_shot_vector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/interfaces/test_tensorflow_autograph_qnode_shot_vector.py b/tests/interfaces/test_tensorflow_autograph_qnode_shot_vector.py index f0978ab1e86..695438310c1 100644 --- a/tests/interfaces/test_tensorflow_autograph_qnode_shot_vector.py +++ b/tests/interfaces/test_tensorflow_autograph_qnode_shot_vector.py @@ -384,7 +384,7 @@ def circuit(x, y, **_): assert h.shape == (2, num_copies) -shots_and_num_copies = [((30000, 28000, 26000), 3), ((20000, (18000, 2)), 3)] +shots_and_num_copies = [((30000, 28000, 26000), 3), ((30000, (28000, 2)), 3)] @pytest.mark.parametrize("shots,num_copies", shots_and_num_copies)