diff --git a/doc/development/deprecations.rst b/doc/development/deprecations.rst index b7e58cb2279..ff58671d78f 100644 --- a/doc/development/deprecations.rst +++ b/doc/development/deprecations.rst @@ -68,30 +68,41 @@ Pending deprecations New operator arithmetic deprecations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The v0.36 release completes the main phase of PennyLane's switchover to an updated approach for handling -arithmetic operations between operators, check out the :ref:`Updated operators ` page -for more details. The old system is still accessible via :func:`~.disable_new_opmath`. However, the -old system will be removed in an upcoming release and should be treated as deprecated. The following -functionality will explicitly raise a deprecation warning when used: +In PennyLane v0.39, the legacy operator arithmetic system has been deprecated. Check out the :ref:`Updated operators ` page +for details on how to port your legacy code to the new system. The old system is still accessible via :func:`~.disable_new_opmath`, though +it is not recommended, as the old system is deprecated and will be removed in the v0.40 release. The following functionality will explicitly +raise a deprecation warning when used: + +* In PennyLane v0.39, legacy operator arithmetic has been deprecated. This includes :func:`~pennylane.operation.enable_new_opmath`, + :func:`~pennylane.operation.disable_new_opmath`, :class:`~pennylane.ops.Hamiltonian`, and :class:`~pennylane.operation.Tensor`. Note + that when new operator arithmetic is enabled, ``qml.Hamiltonian`` will continue to dispatch to :class:`~pennylane.ops.LinearCombination`; + this behaviour is not deprecated. + + - Deprecated in v0.39 + - Will be removed in v0.40 + +* :meth:`~pennylane.pauli.PauliSentence.hamiltonian` and :meth:`~pennylane.pauli.PauliWord.hamiltonian` are deprecated. Instead, please use + :meth:`~pennylane.pauli.PauliSentence.operation` and :meth:`~pennylane.pauli.PauliWord.operation` respectively. + + - Deprecated in v0.39 + - Will be removed in v0.40 + +* :func:`pennylane.pauli.simplify` is deprecated. Instead, please use :func:`pennylane.simplify` or :meth:`~pennylane.operation.Operator.simplify`. + + - Deprecated in v0.39 + - Will be removed in v0.40 * ``op.ops`` and ``op.coeffs`` will be deprecated in the future. Use :meth:`~.Operator.terms` instead. - Added and deprecated for ``Sum`` and ``Prod`` instances in v0.35 -* Accessing ``qml.ops.Hamiltonian`` is deprecated because it points to the old version of the class - that may not be compatible with the new approach to operator arithmetic. Instead, using - ``qml.Hamiltonian`` is recommended because it dispatches to the :class:`~.LinearCombination` class - when the new approach to operator arithmetic is enabled. This will allow you to continue to use - ``qml.Hamiltonian`` with existing code without needing to make any changes. - - - Use of ``qml.ops.Hamiltonian`` is deprecated in v0.36 - * Accessing terms of a tensor product (e.g., ``op = X(0) @ X(1)``) via ``op.obs`` is deprecated with new operator arithmetic. A user should use :class:`op.operands <~.CompositeOp>` instead. - Deprecated in v0.36 + Other deprecations ~~~~~~~~~~~~~~~~~~ diff --git a/doc/news/new_opmath.rst b/doc/news/new_opmath.rst index c90df6d8978..20f86fb05fa 100644 --- a/doc/news/new_opmath.rst +++ b/doc/news/new_opmath.rst @@ -34,7 +34,12 @@ Summary of the update The opt-in feature ``qml.operation.enable_new_opmath()`` is now the default. Ideally, your code should not break. If it still does, it likely only requires some minor changes. For that, see the :ref:`Troubleshooting_opmath` section. - You can still opt-out and run legacy code via ``qml.operation.disable_new_opmath()``. + You can still opt-out and run legacy code via ``qml.operation.disable_new_opmath()``, though it is deprecated, and thus, + not recommended. + +.. warning:: + + In PennyLane v0.39, legacy operator arithmetic is deprecated and will be removed in v0.40. * The underlying system for performing arithmetic with operators has been changed. Arithmetic can be carried out using diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 3d1fae85ca9..e3dede0db1c 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -148,6 +148,20 @@

Deprecations πŸ‘‹

+* Legacy operator arithmetic has been deprecated. This includes `qml.ops.Hamiltonian`, `qml.operation.Tensor`, + `qml.operation.enable_new_opmath`, `qml.operation.disable_new_opmath`, and `qml.operation.convert_to_legacy_H`. + Note that when new operator arithmetic is enabled, ``qml.Hamiltonian`` will continue to dispatch to + `qml.ops.LinearCombination`; this behaviour is not deprecated. For more information, check out the + [updated operator troubleshooting page](https://docs.pennylane.ai/en/stable/news/new_opmath.html). + [(#6287)](https://github.com/PennyLaneAI/pennylane/pull/6287) + +* `qml.pauli.PauliSentence.hamiltonian` and `qml.pauli.PauliWord.hamiltonian` are deprecated. Instead, please use + `qml.pauli.PauliSentence.operation` and `qml.pauli.PauliWord.operation` respectively. + [(#6287)](https://github.com/PennyLaneAI/pennylane/pull/6287) + +* `qml.pauli.simplify()` is deprecated. Instead, please use `qml.simplify(op)` or `op.simplify()`. + [(#6287)](https://github.com/PennyLaneAI/pennylane/pull/6287) + * The `qml.BasisStatePreparation` template is deprecated. Instead, use `qml.BasisState`. [(#6021)](https://github.com/PennyLaneAI/pennylane/pull/6021) diff --git a/pennylane/devices/tests/conftest.py b/pennylane/devices/tests/conftest.py index 044fa957a7c..bcd48a3f164 100755 --- a/pennylane/devices/tests/conftest.py +++ b/pennylane/devices/tests/conftest.py @@ -14,6 +14,7 @@ """Contains shared fixtures for the device tests.""" import argparse import os +from warnings import warn import numpy as np import pytest @@ -226,6 +227,12 @@ def disable_opmath_if_requested(request): disable_opmath = request.config.getoption("--disable-opmath") # value from yaml file is a string, convert to boolean if eval(disable_opmath): + warn( + "Disabling the new Operator arithmetic system for legacy support. " + "If you need help troubleshooting your code, please visit " + "https://docs.pennylane.ai/en/stable/news/new_opmath.html", + UserWarning, + ) qml.operation.disable_new_opmath(warn=False) diff --git a/pennylane/operation.py b/pennylane/operation.py index 37b69f087c7..7fab8083d86 100644 --- a/pennylane/operation.py +++ b/pennylane/operation.py @@ -2123,6 +2123,14 @@ def __init__(self, *args): # pylint: disable=super-init-not-called self._pauli_rep = None self.queue(init=True) + warnings.warn( + "qml.operation.Tensor uses the old approach to operator arithmetic, which will become " + "unavailable in version 0.40 of PennyLane. If you are experiencing issues, visit " + "https://docs.pennylane.ai/en/stable/news/new_opmath.html or contact the PennyLane " + "team on the discussion forum: https://discuss.pennylane.ai/.", + qml.PennyLaneDeprecationWarning, + ) + wires = [op.wires for op in self.obs] if len(wires) != len(set(wires)): warnings.warn( @@ -3039,6 +3047,11 @@ def enable_new_opmath(warn=True): """ Change dunder methods to return arithmetic operators instead of Hamiltonians and Tensors + .. warning:: + + Using legacy operator arithmetic is deprecated, and will be removed in PennyLane v0.40. + For further details, see :doc:`Updated Operators `. + Args: warn (bool): Whether or not to emit a warning for re-enabling new opmath. Default is ``True``. @@ -3054,9 +3067,11 @@ def enable_new_opmath(warn=True): """ if warn: warnings.warn( - "Re-enabling the new Operator arithmetic system after disabling it is not advised. " - "Please visit https://docs.pennylane.ai/en/stable/news/new_opmath.html for help troubleshooting.", - UserWarning, + "Toggling the new approach to operator arithmetic is deprecated. From version 0.40 of " + "PennyLane, only the new approach to operator arithmetic will be available. If you are " + "experiencing issues, visit https://docs.pennylane.ai/en/stable/news/new_opmath.html " + "or contact the PennyLane team on the discussion forum: https://discuss.pennylane.ai/.", + qml.PennyLaneDeprecationWarning, ) global __use_new_opmath __use_new_opmath = True @@ -3066,6 +3081,11 @@ def disable_new_opmath(warn=True): """ Change dunder methods to return Hamiltonians and Tensors instead of arithmetic operators + .. warning:: + + Using legacy operator arithmetic is deprecated, and will be removed in PennyLane v0.40. + For further details, see :doc:`Updated Operators `. + Args: warn (bool): Whether or not to emit a warning for disabling new opmath. Default is ``True``. @@ -3081,10 +3101,11 @@ def disable_new_opmath(warn=True): """ if warn: warnings.warn( - "Disabling the new Operator arithmetic system for legacy support. " - "If you need help troubleshooting your code, please visit " - "https://docs.pennylane.ai/en/stable/news/new_opmath.html", - UserWarning, + "Disabling the new approach to operator arithmetic is deprecated. From version 0.40 of " + "PennyLane, only the new approach to operator arithmetic will be available. If you are " + "experiencing issues, visit https://docs.pennylane.ai/en/stable/news/new_opmath.html " + "or contact the PennyLane team on the discussion forum: https://discuss.pennylane.ai/.", + qml.PennyLaneDeprecationWarning, ) global __use_new_opmath __use_new_opmath = False @@ -3094,6 +3115,11 @@ def active_new_opmath(): """ Function that checks if the new arithmetic operator dunders are active + .. warning:: + + Using legacy operator arithmetic is deprecated, and will be removed in PennyLane v0.40. + For further details, see :doc:`Updated Operators `. + Returns: bool: Returns ``True`` if the new arithmetic operator dunders are active @@ -3136,37 +3162,53 @@ def convert_to_opmath(op): @contextmanager -def disable_new_opmath_cm(): +def disable_new_opmath_cm(warn=True): r"""Allows to use the old operator arithmetic within a temporary context using the `with` statement.""" + if warn: + warnings.warn( + "Disabling the new approach to operator arithmetic is deprecated. From version 0.40 of " + "PennyLane, only the new approach to operator arithmetic will be available. If you are " + "experiencing issues, visit https://docs.pennylane.ai/en/stable/news/new_opmath.html " + "or contact the PennyLane team on the discussion forum: https://discuss.pennylane.ai/.", + qml.PennyLaneDeprecationWarning, + ) was_active = qml.operation.active_new_opmath() try: if was_active: - disable_new_opmath(warn=False) + disable_new_opmath(warn=False) # Only warn once yield except Exception as e: raise e finally: if was_active: - enable_new_opmath(warn=False) + enable_new_opmath(warn=False) # Only warn once else: - disable_new_opmath(warn=False) + disable_new_opmath(warn=False) # Only warn once @contextmanager -def enable_new_opmath_cm(): +def enable_new_opmath_cm(warn=True): r"""Allows to use the new operator arithmetic within a temporary context using the `with` statement.""" + if warn: + warnings.warn( + "Toggling the new approach to operator arithmetic is deprecated. From version 0.40 of " + "PennyLane, only the new approach to operator arithmetic will be available. If you are " + "experiencing issues, visit https://docs.pennylane.ai/en/stable/news/new_opmath.html " + "or contact the PennyLane team on the discussion forum: https://discuss.pennylane.ai/.", + qml.PennyLaneDeprecationWarning, + ) was_active = qml.operation.active_new_opmath() if not was_active: - enable_new_opmath(warn=False) + enable_new_opmath(warn=False) # Only warn once yield if was_active: - enable_new_opmath(warn=False) + enable_new_opmath(warn=False) # Only warn once else: - disable_new_opmath(warn=False) + disable_new_opmath(warn=False) # Only warn once # pylint: disable=too-many-branches @@ -3249,13 +3291,19 @@ def convert_to_legacy_H(op): Arithmetic operators include :class:`~pennylane.ops.op_math.Prod`, :class:`~pennylane.ops.op_math.Sum` and :class:`~pennylane.ops.op_math.SProd`. + .. warning:: + + Using legacy operator arithmetic is deprecated, and will be removed in PennyLane v0.40. + For further details, see :doc:`Updated Operators `. + Args: op (Operator): The operator instance to convert. Returns: Operator: The operator as a :class:`~pennylane.Hamiltonian` instance """ - with disable_new_opmath_cm(): + with disable_new_opmath_cm(warn=False): + # Suppress warning because constructing Hamiltonian will raise a warning anyway res = convert_to_H(op) return res diff --git a/pennylane/ops/qubit/hamiltonian.py b/pennylane/ops/qubit/hamiltonian.py index 73e75bf76d8..f2e2d18b86c 100644 --- a/pennylane/ops/qubit/hamiltonian.py +++ b/pennylane/ops/qubit/hamiltonian.py @@ -75,8 +75,9 @@ class Hamiltonian(Observable): .. warning:: - As of ``v0.36``, ``qml.Hamiltonian`` dispatches to :class:`~.pennylane.ops.op_math.LinearCombination` - by default. For further details, see :doc:`Updated Operators `. + As of ``v0.39``, ``qml.ops.Hamiltonian`` is deprecated. When using the new operator arithmetic, + ``qml.Hamiltonian`` will dispatch to :class:`~pennylane.ops.op_math.LinearCombination`. See + :doc:`Updated Operators ` for more details. Args: coeffs (tensor_like): coefficients of the Hamiltonian expression @@ -140,7 +141,7 @@ class Hamiltonian(Observable): The following code examples show the behaviour of ``qml.Hamiltonian`` using old operator arithmetic. See :doc:`Updated Operators ` for more details. The old - behaviour can be reactivated by calling + behaviour can be reactivated by calling the deprecated >>> qml.operation.disable_new_opmath() @@ -253,13 +254,13 @@ def __init__( method: Literal["lf", "rlf"] = "rlf", id: str = None, ): - if qml.operation.active_new_opmath(): - warn( - "Using 'qml.ops.Hamiltonian' with new operator arithmetic is deprecated. " - "Instead, use 'qml.Hamiltonian'. " - "Please visit https://docs.pennylane.ai/en/stable/news/new_opmath.html for more information and help troubleshooting.", - qml.PennyLaneDeprecationWarning, - ) + warn( + "qml.ops.Hamiltonian uses the old approach to operator arithmetic, which will become " + "unavailable in version 0.40 of PennyLane. If you are experiencing issues, visit " + "https://docs.pennylane.ai/en/stable/news/new_opmath.html or contact the PennyLane " + "team on the discussion forum: https://discuss.pennylane.ai/.", + qml.PennyLaneDeprecationWarning, + ) if qml.math.shape(coeffs)[0] != len(observables): raise ValueError( diff --git a/pennylane/pauli/pauli_arithmetic.py b/pennylane/pauli/pauli_arithmetic.py index ae24889990f..6fcfe6ff36a 100644 --- a/pennylane/pauli/pauli_arithmetic.py +++ b/pennylane/pauli/pauli_arithmetic.py @@ -15,6 +15,7 @@ # pylint:disable=protected-access from copy import copy from functools import lru_cache, reduce +from warnings import warn import numpy as np from scipy import sparse @@ -82,7 +83,7 @@ def _cached_sparse_data(op): elif op == "Y": data = np.array([-1.0j, 1.0j], dtype=np.complex128) indices = np.array([1, 0], dtype=np.int64) - elif op == "Z": + else: # op == "Z" data = np.array([1.0, -1.0], dtype=np.complex128) indices = np.array([0, 1], dtype=np.int64) return data, indices @@ -518,7 +519,19 @@ def operation(self, wire_order=None, get_as_tensor=False): return factors[0] if len(factors) == 1 else Prod(*factors, _pauli_rep=pauli_rep) def hamiltonian(self, wire_order=None): - """Return :class:`~pennylane.Hamiltonian` representing the PauliWord.""" + """Return :class:`~pennylane.Hamiltonian` representing the PauliWord. + + .. warning:: + + :meth:`~pennylane.pauli.PauliWord.hamiltonian` is deprecated. Instead, please use + :meth:`~pennylane.pauli.PauliWord.operation` + + """ + warn( + "PauliWord.hamiltonian() is deprecated. Please use PauliWord.operation() instead.", + qml.PennyLaneDeprecationWarning, + ) + if len(self) == 0: if wire_order in (None, [], Wires([])): raise ValueError("Can't get the Hamiltonian for an empty PauliWord.") @@ -1022,7 +1035,19 @@ def operation(self, wire_order=None): return summands[0] if len(summands) == 1 else Sum(*summands, _pauli_rep=self) def hamiltonian(self, wire_order=None): - """Returns a native PennyLane :class:`~pennylane.Hamiltonian` representing the PauliSentence.""" + """Returns a native PennyLane :class:`~pennylane.Hamiltonian` representing the PauliSentence. + + .. warning:: + + :meth:`~pennylane.pauli.PauliSentence.hamiltonian` is deprecated. Instead, please use + :meth:`~pennylane.pauli.PauliSentence.operation` + + """ + warn( + "PauliSentence.hamiltonian() is deprecated. Please use PauliSentence.operation() instead.", + qml.PennyLaneDeprecationWarning, + ) + if len(self) == 0: if wire_order in (None, [], Wires([])): raise ValueError("Can't get the Hamiltonian for an empty PauliSentence.") diff --git a/pennylane/pauli/utils.py b/pennylane/pauli/utils.py index cfe81dff3e3..7365e3131e1 100644 --- a/pennylane/pauli/utils.py +++ b/pennylane/pauli/utils.py @@ -23,6 +23,7 @@ from functools import lru_cache, singledispatch from itertools import product from typing import Union +from warnings import warn import numpy as np @@ -1211,6 +1212,11 @@ def simplify(h, cutoff=1.0e-12): The Hamiltonian terms with identical Pauli words are added together and eliminated if the overall coefficient is smaller than a cutoff value. + .. warning:: + + :func:`~pennylane.pauli.simplify` is deprecated. Instead, please use :func:`pennylane.simplify` + or :meth:`~pennylane.operation.Operator.simplify`. + Args: h (Hamiltonian): PennyLane Hamiltonian cutoff (float): cutoff value for discarding the negligible terms @@ -1225,6 +1231,11 @@ def simplify(h, cutoff=1.0e-12): >>> print(simplify(h)) (1.0) [X0 Y1] """ + warn( + "qml.pauli.simplify() has been deprecated. Instead, please use " + "qml.simplify(op) or op.simplify().", + qml.PennyLaneDeprecationWarning, + ) wiremap = dict(zip(h.wires, range(len(h.wires) + 1))) c, o = [], [] diff --git a/pennylane/transforms/decompositions/clifford_t_transform.py b/pennylane/transforms/decompositions/clifford_t_transform.py index 9473a5c115a..8d8e8edd2d4 100644 --- a/pennylane/transforms/decompositions/clifford_t_transform.py +++ b/pennylane/transforms/decompositions/clifford_t_transform.py @@ -100,10 +100,10 @@ def pauli_group(x): qml.pauli.pauli_sentence(qml.prod(*pauli)) for pauli in product(*(pauli_group(idx) for idx in op.wires)) ] - pauli_hams = (pauli_sen.hamiltonian(wire_order=op.wires) for pauli_sen in pauli_sens) + pauli_ops = (pauli_sen.operation(wire_order=op.wires) for pauli_sen in pauli_sens) # Perform U@P@U^\dagger and check if the result exists in set P - for pauli_prod in product([pauli_terms], pauli_hams, [pauli_terms_adj]): + for pauli_prod in product([pauli_terms], pauli_ops, [pauli_terms_adj]): # hopefully op_math.prod scales better than matrix multiplication, i.e., O((2^N)^3) upu = qml.pauli.pauli_sentence(qml.prod(*pauli_prod)) upu.simplify() diff --git a/tests/circuit_graph/test_circuit_graph_hash.py b/tests/circuit_graph/test_circuit_graph_hash.py index 0157343eaf2..6fb966c25ad 100644 --- a/tests/circuit_graph/test_circuit_graph_hash.py +++ b/tests/circuit_graph/test_circuit_graph_hash.py @@ -23,23 +23,21 @@ from pennylane.wires import Wires -@pytest.mark.usefixtures("use_legacy_opmath") class TestCircuitGraphHash: """Test the creation of a hash on a CircuitGraph""" - with qml.operation.disable_new_opmath_cm(): - numeric_queues = [ - ([qml.RX(0.3, wires=[0])], [], "RX!0.3![0]|||"), - ( - [ - qml.RX(0.3, wires=[0]), - qml.RX(0.4, wires=[1]), - qml.RX(0.5, wires=[2]), - ], - [], - "RX!0.3![0]RX!0.4![1]RX!0.5![2]|||", - ), - ] + numeric_queues = [ + ([qml.RX(0.3, wires=[0])], [], "RX!0.3![0]|||"), + ( + [ + qml.RX(0.3, wires=[0]), + qml.RX(0.4, wires=[1]), + qml.RX(0.5, wires=[2]), + ], + [], + "RX!0.3![0]RX!0.4![1]RX!0.5![2]|||", + ), + ] @pytest.mark.parametrize("queue, observable_queue, expected_string", numeric_queues) def test_serialize_numeric_arguments(self, queue, observable_queue, expected_string): @@ -50,38 +48,37 @@ def test_serialize_numeric_arguments(self, queue, observable_queue, expected_str assert circuit_graph_1.serialize() == circuit_graph_2.serialize() assert expected_string == circuit_graph_1.serialize() - with qml.operation.disable_new_opmath_cm(): - returntype1 = qml.expval - returntype2 = qml.var - - observable1 = qml.PauliZ(wires=[0]) - observable2 = qml.Hermitian(np.array([[1, 0], [0, -1]]), wires=[0]) - observable3 = Tensor(qml.PauliZ(0) @ qml.PauliZ(1)) - - numeric_observable_queue = [ - (returntype1, observable1, "|||ObservableReturnTypes.Expectation!PauliZ[0]"), - ( - returntype1, - observable2, - "|||ObservableReturnTypes.Expectation!Hermitian![[ 1 0]\n [ 0 -1]]![0]", - ), - ( - returntype1, - observable3, - "|||ObservableReturnTypes.Expectation!['PauliZ', 'PauliZ'][0, 1]", - ), - (returntype2, observable1, "|||ObservableReturnTypes.Variance!PauliZ[0]"), - ( - returntype2, - observable2, - "|||ObservableReturnTypes.Variance!Hermitian![[ 1 0]\n [ 0 -1]]![0]", - ), - ( - returntype2, - observable3, - "|||ObservableReturnTypes.Variance!['PauliZ', 'PauliZ'][0, 1]", - ), - ] + returntype1 = qml.expval + returntype2 = qml.var + + observable1 = qml.PauliZ(wires=[0]) + observable2 = qml.Hermitian(np.array([[1, 0], [0, -1]]), wires=[0]) + observable3 = Tensor(qml.PauliZ(0), qml.PauliZ(1)) + + numeric_observable_queue = [ + (returntype1, observable1, "|||ObservableReturnTypes.Expectation!PauliZ[0]"), + ( + returntype1, + observable2, + "|||ObservableReturnTypes.Expectation!Hermitian![[ 1 0]\n [ 0 -1]]![0]", + ), + ( + returntype1, + observable3, + "|||ObservableReturnTypes.Expectation!['PauliZ', 'PauliZ'][0, 1]", + ), + (returntype2, observable1, "|||ObservableReturnTypes.Variance!PauliZ[0]"), + ( + returntype2, + observable2, + "|||ObservableReturnTypes.Variance!Hermitian![[ 1 0]\n [ 0 -1]]![0]", + ), + ( + returntype2, + observable3, + "|||ObservableReturnTypes.Variance!['PauliZ', 'PauliZ'][0, 1]", + ), + ] @pytest.mark.parametrize("obs, op, expected_string", numeric_observable_queue) def test_serialize_numeric_arguments_observables_expval_var(self, obs, op, expected_string): diff --git a/tests/conftest.py b/tests/conftest.py index 2c6772e6bdb..d885f74338f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -15,10 +15,10 @@ Pytest configuration file for PennyLane test suite. """ # pylint: disable=unused-import -import contextlib import os import pathlib import sys +from warnings import filterwarnings, warn import numpy as np import pytest @@ -155,25 +155,26 @@ def disable_opmath_if_requested(request): disable_opmath = request.config.getoption("--disable-opmath") # value from yaml file is a string, convert to boolean if eval(disable_opmath): - qml.operation.disable_new_opmath(warn=True) - - -@pytest.fixture(scope="function") -def use_legacy_opmath(): - with disable_new_opmath_cm() as cm: - yield cm - - -# pylint: disable=contextmanager-generator-missing-cleanup -@pytest.fixture(scope="function") -def use_new_opmath(): - with enable_new_opmath_cm() as cm: - yield cm + warn( + "Disabling the new Operator arithmetic system for legacy support. " + "If you need help troubleshooting your code, please visit " + "https://docs.pennylane.ai/en/stable/news/new_opmath.html", + UserWarning, + ) + qml.operation.disable_new_opmath(warn=False) + + # Suppressing warnings so that Hamiltonians and Tensors constructed outside tests + # don't raise deprecation warnings + filterwarnings("ignore", "qml.ops.Hamiltonian", qml.PennyLaneDeprecationWarning) + filterwarnings("ignore", "qml.operation.Tensor", qml.PennyLaneDeprecationWarning) + filterwarnings("ignore", "qml.pauli.simplify", qml.PennyLaneDeprecationWarning) + filterwarnings("ignore", "PauliSentence.hamiltonian", qml.PennyLaneDeprecationWarning) + filterwarnings("ignore", "PauliWord.hamiltonian", qml.PennyLaneDeprecationWarning) @pytest.fixture(params=[disable_new_opmath_cm, enable_new_opmath_cm], scope="function") def use_legacy_and_new_opmath(request): - with request.param() as cm: + with request.param(warn=False) as cm: yield cm diff --git a/tests/devices/default_qubit/test_default_qubit_preprocessing.py b/tests/devices/default_qubit/test_default_qubit_preprocessing.py index 89691125852..11a4bf001c2 100644 --- a/tests/devices/default_qubit/test_default_qubit_preprocessing.py +++ b/tests/devices/default_qubit/test_default_qubit_preprocessing.py @@ -575,8 +575,10 @@ def test_preprocess_check_validity_fail(self): with pytest.raises(qml.DeviceError, match="Operator NoMatNoDecompOp"): program(tapes) - with qml.operation.disable_new_opmath_cm(): - invalid_tape_adjoint_test_cases = [ + @pytest.mark.usefixtures("legacy_opmath_only") + @pytest.mark.parametrize( + "ops, measurement, message", + [ ( [qml.RX(0.1, wires=0)], [qml.probs(op=qml.PauliX(0))], @@ -584,15 +586,10 @@ def test_preprocess_check_validity_fail(self): ), ( [qml.RX(0.1, wires=0)], - [qml.expval(qml.Hamiltonian([1], [qml.PauliZ(0)]))], + [qml.expval(qml.ops.Hamiltonian([1], [qml.PauliZ(0)]))], "not supported on adjoint", ), - ] - - @pytest.mark.usefixtures("use_legacy_opmath") - @pytest.mark.parametrize( - "ops, measurement, message", - invalid_tape_adjoint_test_cases, + ], ) @pytest.mark.filterwarnings("ignore:Differentiating with respect to") def test_preprocess_invalid_tape_adjoint_legacy_opmath(self, ops, measurement, message): @@ -880,7 +877,7 @@ def test_u3_non_trainable_params(self): assert res.trainable_params == [0, 1, 2, 3, 4] @pytest.mark.usefixtures( - "use_legacy_opmath" + "legacy_opmath_only" ) # this is only an issue for legacy Hamiltonian that does not define a matrix method def test_unsupported_obs_legacy_opmath(self): """Test that the correct error is raised if a Hamiltonian measurement is differentiated""" diff --git a/tests/devices/qubit/test_measure.py b/tests/devices/qubit/test_measure.py index f4903e05137..8c6b7959463 100644 --- a/tests/devices/qubit/test_measure.py +++ b/tests/devices/qubit/test_measure.py @@ -97,7 +97,7 @@ def test_sum_sum_of_terms_when_backprop(self): state = qml.numpy.zeros(2) assert get_measurement_function(qml.expval(S), state) is sum_of_terms_method - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_hamiltonian_with_multi_wire_obs(self): """Check that a Hamiltonian with a multi-wire observable uses the sum of terms method.""" diff --git a/tests/devices/test_legacy_device.py b/tests/devices/test_legacy_device.py index 037f1da076e..9b3e8e48aa4 100644 --- a/tests/devices/test_legacy_device.py +++ b/tests/devices/test_legacy_device.py @@ -320,7 +320,7 @@ def test_check_validity_on_valid_queue(self, mock_device_supporting_paulis): # Raises an error if queue or observables are invalid dev.check_validity(queue, observables) - @pytest.mark.usefixtures("use_new_opmath") + @pytest.mark.usefixtures("new_opmath_only") def test_check_validity_containing_prod(self, mock_device_supporting_prod): """Tests that the function Device.check_validity works with Prod""" @@ -338,7 +338,7 @@ def test_check_validity_containing_prod(self, mock_device_supporting_prod): dev.check_validity(queue, observables) - @pytest.mark.usefixtures("use_new_opmath") + @pytest.mark.usefixtures("new_opmath_only") def test_prod_containing_unsupported_nested_observables(self, mock_device_supporting_prod): """Tests that the observables nested within Prod are checked for validity""" @@ -356,7 +356,7 @@ def test_prod_containing_unsupported_nested_observables(self, mock_device_suppor with pytest.raises(qml.DeviceError, match="Observable PauliY not supported"): dev.check_validity(queue, unsupported_nested_observables) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_check_validity_on_tensor_support_legacy_opmath(self, mock_device_supporting_paulis): """Tests the function Device.check_validity with tensor support capability""" dev = mock_device_supporting_paulis() @@ -373,7 +373,7 @@ def test_check_validity_on_tensor_support_legacy_opmath(self, mock_device_suppor with pytest.raises(qml.DeviceError, match="Tensor observables not supported"): dev.check_validity(queue, observables) - @pytest.mark.usefixtures("use_new_opmath") + @pytest.mark.usefixtures("new_opmath_only") def test_check_validity_on_prod_support(self, mock_device_supporting_paulis): """Tests the function Device.check_validity with prod support capability""" dev = mock_device_supporting_paulis() @@ -390,7 +390,7 @@ def test_check_validity_on_prod_support(self, mock_device_supporting_paulis): with pytest.raises(qml.DeviceError, match="Observable Prod not supported"): dev.check_validity(queue, observables) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_check_validity_on_invalid_observable_with_tensor_support(self, monkeypatch): """Tests the function Device.check_validity with tensor support capability but with an invalid observable""" diff --git a/tests/devices/test_preprocess.py b/tests/devices/test_preprocess.py index 50ddfbab75d..bb8e6ef6c5b 100644 --- a/tests/devices/test_preprocess.py +++ b/tests/devices/test_preprocess.py @@ -254,17 +254,7 @@ def test_invalid_tensor_observable(self): with pytest.raises(qml.DeviceError, match="not supported on device"): validate_observables(tape, lambda obj: obj.name == "PauliX") - @pytest.mark.usefixtures("use_legacy_opmath") - def test_invalid_tensor_observable_legacy(self): - """Test that expand_fn throws an error when a tensor includes invalid obserables""" - tape = QuantumScript( - ops=[qml.PauliX(0), qml.PauliY(1)], - measurements=[qml.expval(qml.PauliX(0) @ qml.GellMann(wires=1, index=2))], - ) - with pytest.raises(qml.DeviceError, match="not supported on device"): - validate_observables(tape, lambda obj: obj.name == "PauliX") - - @pytest.mark.usefixtures("use_legacy_opmath") # only required for legacy observables + @pytest.mark.usefixtures("legacy_opmath_only") # only required for legacy observables def test_valid_tensor_observable_legacy_opmath(self): """Test that a valid tensor ovservable passes without error.""" tape = QuantumScript([], [qml.expval(qml.PauliZ(0) @ qml.PauliY(1))]) diff --git a/tests/fermi/test_bravyi_kitaev.py b/tests/fermi/test_bravyi_kitaev.py index 805f0fec19e..07a1fea5187 100644 --- a/tests/fermi/test_bravyi_kitaev.py +++ b/tests/fermi/test_bravyi_kitaev.py @@ -427,7 +427,7 @@ def test_error_is_raised_for_dimension_mismatch(): ), ] -with qml.operation.disable_new_opmath_cm(): +with qml.operation.disable_new_opmath_cm(warn=False): FERMI_WORDS_AND_OPS_LEGACY = [ ( FermiWord({(0, 0): "+"}), @@ -832,7 +832,7 @@ def test_error_is_raised_for_dimension_mismatch(): ] -@pytest.mark.usefixtures("use_new_opmath") +@pytest.mark.usefixtures("new_opmath_only") @pytest.mark.parametrize("fermionic_op, n_qubits, result", FERMI_WORDS_AND_OPS + FERMI_OPS_COMPLEX) def test_bravyi_kitaev_fermi_word_ps(fermionic_op, n_qubits, result): """Test that the parity_transform function returns the correct qubit operator.""" @@ -847,7 +847,7 @@ def test_bravyi_kitaev_fermi_word_ps(fermionic_op, n_qubits, result): assert qubit_op == expected_op -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") @pytest.mark.parametrize( "fermionic_op, n_qubits, result", FERMI_WORDS_AND_OPS_LEGACY + FERMI_OPS_COMPLEX_LEGACY ) @@ -864,7 +864,7 @@ def test_bravyi_kitaev_fermi_word_ps_legacy(fermionic_op, n_qubits, result): assert qubit_op == expected_op -@pytest.mark.usefixtures("use_new_opmath") +@pytest.mark.usefixtures("new_opmath_only") @pytest.mark.parametrize("fermionic_op, n_qubits, result", FERMI_WORDS_AND_OPS) def test_bravyi_kitaev_fermi_word_operation(fermionic_op, n_qubits, result): wires = fermionic_op.wires or [0] @@ -877,7 +877,7 @@ def test_bravyi_kitaev_fermi_word_operation(fermionic_op, n_qubits, result): qml.assert_equal(qubit_op.simplify(), expected_op.simplify()) -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") @pytest.mark.parametrize("fermionic_op, n_qubits, result", FERMI_WORDS_AND_OPS_LEGACY) def test_bravyi_kitaev_fermi_word_operation_legacy(fermionic_op, n_qubits, result): wires = fermionic_op.wires or [0] diff --git a/tests/fermi/test_fermi_mapping.py b/tests/fermi/test_fermi_mapping.py index 0222dde82e4..3853ce26a99 100644 --- a/tests/fermi/test_fermi_mapping.py +++ b/tests/fermi/test_fermi_mapping.py @@ -349,7 +349,7 @@ ] -with qml.operation.disable_new_opmath_cm(): +with qml.operation.disable_new_opmath_cm(warn=False): FERMI_WORDS_AND_OPS_LEGACY = [ ( FermiWord({(0, 0): "+"}), @@ -678,7 +678,7 @@ ] -@pytest.mark.usefixtures("use_new_opmath") +@pytest.mark.usefixtures("new_opmath_only") @pytest.mark.parametrize("fermionic_op, result", FERMI_WORDS_AND_OPS + FERMI_WORDS_AND_OPS_EXTENDED) def test_jordan_wigner_fermi_word_ps(fermionic_op, result): """Test that the jordan_wigner function returns the correct qubit operator.""" @@ -693,7 +693,7 @@ def test_jordan_wigner_fermi_word_ps(fermionic_op, result): assert qubit_op == expected_op -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") @pytest.mark.parametrize( "fermionic_op, result", FERMI_WORDS_AND_OPS_LEGACY + FERMI_WORDS_AND_OPS_EXTENDED_LEGACY ) @@ -710,7 +710,7 @@ def test_jordan_wigner_fermi_word_ps_legacy(fermionic_op, result): assert qubit_op == expected_op -@pytest.mark.usefixtures("use_new_opmath") +@pytest.mark.usefixtures("new_opmath_only") # TODO: if qml.equal is extended to compare layers of nested ops, also test with FERMI_WORDS_AND_OPS_EXTENDED @pytest.mark.parametrize("fermionic_op, result", FERMI_WORDS_AND_OPS) def test_jordan_wigner_fermi_word_operation(fermionic_op, result): @@ -724,7 +724,7 @@ def test_jordan_wigner_fermi_word_operation(fermionic_op, result): qml.assert_equal(qubit_op.simplify(), expected_op.simplify()) -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") # TODO: if qml.equal is extended to compare layers of nested ops, also test with FERMI_WORDS_AND_OPS_EXTENDED @pytest.mark.parametrize("fermionic_op, result", FERMI_WORDS_AND_OPS_LEGACY) def test_jordan_wigner_fermi_word_operation_legacy(fermionic_op, result): diff --git a/tests/fermi/test_parity_mapping.py b/tests/fermi/test_parity_mapping.py index 65820a93232..537be708afe 100644 --- a/tests/fermi/test_parity_mapping.py +++ b/tests/fermi/test_parity_mapping.py @@ -437,7 +437,7 @@ def test_error_is_raised_for_dimension_mismatch(): ), ] -with qml.operation.disable_new_opmath_cm(): +with qml.operation.disable_new_opmath_cm(warn=False): FERMI_WORDS_AND_OPS_LEGACY = [ ( FermiWord({(0, 0): "+"}), @@ -852,7 +852,7 @@ def test_error_is_raised_for_dimension_mismatch(): ] -@pytest.mark.usefixtures("use_new_opmath") +@pytest.mark.usefixtures("new_opmath_only") @pytest.mark.parametrize("fermionic_op, n_qubits, result", FERMI_WORDS_AND_OPS + FERMI_OPS_COMPLEX) def test_parity_transform_fermi_word_ps(fermionic_op, n_qubits, result): """Test that the parity_transform function returns the correct qubit operator.""" @@ -867,7 +867,7 @@ def test_parity_transform_fermi_word_ps(fermionic_op, n_qubits, result): assert qubit_op == expected_op -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") @pytest.mark.parametrize( "fermionic_op, n_qubits, result", FERMI_WORDS_AND_OPS_LEGACY + FERMI_OPS_COMPLEX_LEGACY ) @@ -884,7 +884,7 @@ def test_parity_transform_fermi_word_ps_legacy(fermionic_op, n_qubits, result): assert qubit_op == expected_op -@pytest.mark.usefixtures("use_new_opmath") +@pytest.mark.usefixtures("new_opmath_only") @pytest.mark.parametrize("fermionic_op, n_qubits, result", FERMI_WORDS_AND_OPS) def test_parity_transform_fermi_word_operation(fermionic_op, n_qubits, result): wires = fermionic_op.wires or [0] @@ -897,7 +897,7 @@ def test_parity_transform_fermi_word_operation(fermionic_op, n_qubits, result): qml.assert_equal(qubit_op.simplify(), expected_op.simplify()) -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") @pytest.mark.parametrize("fermionic_op, n_qubits, result", FERMI_WORDS_AND_OPS_LEGACY) def test_parity_transform_fermi_word_operation_legacy(fermionic_op, n_qubits, result): wires = fermionic_op.wires or [0] diff --git a/tests/gradients/core/test_adjoint_diff.py b/tests/gradients/core/test_adjoint_diff.py index 6d9e585b1a8..ec907a5827f 100644 --- a/tests/gradients/core/test_adjoint_diff.py +++ b/tests/gradients/core/test_adjoint_diff.py @@ -55,7 +55,7 @@ def test_finite_shots_warns(self): ): dev.adjoint_jacobian(tape) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_hamiltonian_error_legacy_opmath(self, dev): """Test that error is raised for qml.Hamiltonian""" diff --git a/tests/gradients/core/test_metric_tensor.py b/tests/gradients/core/test_metric_tensor.py index 364516d6079..39841d0c06f 100644 --- a/tests/gradients/core/test_metric_tensor.py +++ b/tests/gradients/core/test_metric_tensor.py @@ -1538,7 +1538,8 @@ def circuit_multi_block(x, z): qml.metric_tensor(circuit_multi_block, approx="block-diag")(x, z) qml.metric_tensor(circuit_multi_block, approx="block-diag", aux_wire="aux_wire")(x, z) - assert len(recwarn) == 0 + if qml.operation.active_new_opmath(): + assert len(recwarn) == 0 def test_raises_circuit_that_uses_missing_wire(): diff --git a/tests/gradients/core/test_pulse_gradient.py b/tests/gradients/core/test_pulse_gradient.py index b427f5ee23b..f3154689ba0 100644 --- a/tests/gradients/core/test_pulse_gradient.py +++ b/tests/gradients/core/test_pulse_gradient.py @@ -182,7 +182,7 @@ def test_with_general_ob(self, ham, params, time, ob): # Check that the inserted exponential is correct qml.assert_equal(qml.exp(qml.dot([-1j * exp_shift], [ob])), _ops[1]) - @pytest.mark.usefixtures("use_legacy_opmath") # this is only an issue with legacy Hamiltonian + @pytest.mark.usefixtures("legacy_opmath_only") # this is only an issue with legacy Hamiltonian def test_warnings_legacy_opmath(self): """Test that a warning is raised for computing eigenvalues of a Hamiltonian for more than four wires but not for fewer wires.""" @@ -201,7 +201,12 @@ def test_warnings_legacy_opmath(self): [0.4, 0.2], [qml.operation.Tensor(*[qml.PauliY(i) for i in range(4)]), qml.PauliX(0)] ) with warnings.catch_warnings(): - warnings.simplefilter("error") + warnings.filterwarnings("error") + warnings.filterwarnings( + "ignore", + "qml.operation.Tensor uses the old approach", + qml.PennyLaneDeprecationWarning, + ) _split_evol_ops(op, ob, tau=0.4) diff --git a/tests/ops/functions/conftest.py b/tests/ops/functions/conftest.py index 692745b48b6..43316f0ccf0 100644 --- a/tests/ops/functions/conftest.py +++ b/tests/ops/functions/conftest.py @@ -16,6 +16,7 @@ Generates parametrizations of operators to test in test_assert_valid.py. """ +import warnings from inspect import getmembers, isclass import numpy as np @@ -50,7 +51,6 @@ qml.adjoint(qml.PauliX(0)), qml.adjoint(qml.RX(1.1, 0)), Tensor(qml.PauliX(0), qml.PauliX(1)), - qml.operation.convert_to_legacy_H(qml.Hamiltonian([1.1, 2.2], [qml.PauliX(0), qml.PauliZ(0)])), qml.ops.LinearCombination([1.1, 2.2], [qml.PauliX(0), qml.PauliZ(0)]), qml.s_prod(1.1, qml.RX(1.1, 0)), qml.prod(qml.PauliX(0), qml.PauliY(1), qml.PauliZ(0)), @@ -66,6 +66,14 @@ ] """Valid operator instances that could not be auto-generated.""" +with warnings.catch_warnings(): + warnings.filterwarnings("ignore", "qml.ops.Hamiltonian uses", qml.PennyLaneDeprecationWarning) + _INSTANCES_TO_TEST.append( + qml.operation.convert_to_legacy_H( + qml.Hamiltonian([1.1, 2.2], [qml.PauliX(0), qml.PauliZ(0)]) + ), + ) + _INSTANCES_TO_FAIL = [ ( diff --git a/tests/ops/functions/test_assert_valid.py b/tests/ops/functions/test_assert_valid.py index e426b91a405..5c347487d9d 100644 --- a/tests/ops/functions/test_assert_valid.py +++ b/tests/ops/functions/test_assert_valid.py @@ -379,7 +379,7 @@ def test_generated_list_of_ops(class_to_validate, str_wires): def test_explicit_list_of_ops(valid_instance): """Test the validity of operators that could not be auto-generated.""" if valid_instance.name == "Hamiltonian": - with qml.operation.disable_new_opmath_cm(): + with qml.operation.disable_new_opmath_cm(warn=False): assert_valid(valid_instance) else: assert_valid(valid_instance) diff --git a/tests/ops/functions/test_bind_new_parameters.py b/tests/ops/functions/test_bind_new_parameters.py index e5018db212c..94872282052 100644 --- a/tests/ops/functions/test_bind_new_parameters.py +++ b/tests/ops/functions/test_bind_new_parameters.py @@ -14,6 +14,7 @@ """ This module contains unit tests for ``qml.bind_parameters``. """ +import warnings import numpy as np import pytest @@ -184,7 +185,10 @@ def test_controlled_sequence(): qml.assert_equal(new_op.base, qml.RX(0.5, wires=3)) -with qml.operation.disable_new_opmath_cm(): +with warnings.catch_warnings(): + warnings.filterwarnings("ignore", "qml.ops.Hamiltonian uses", qml.PennyLaneDeprecationWarning) + warnings.filterwarnings("ignore", "qml.operation.Tensor uses", qml.PennyLaneDeprecationWarning) + TEST_BIND_LEGACY_HAMILTONIAN = [ ( qml.ops.Hamiltonian( @@ -204,8 +208,21 @@ def test_controlled_sequence(): ), ] + TEST_BIND_LEGACY_TENSOR = [ + ( + Tensor(qml.Hermitian(Y, wires=0), qml.PauliZ(1)), + [X], + Tensor(qml.Hermitian(X, wires=0), qml.PauliZ(1)), + ), + ( + Tensor(qml.Hermitian(qml.math.kron(X, Z), wires=[0, 1]), qml.Hermitian(I, wires=2)), + [qml.math.kron(I, I), Z], + Tensor(qml.Hermitian(qml.math.kron(I, I), wires=[0, 1]), qml.Hermitian(Z, wires=2)), + ), + (Tensor(qml.PauliZ(0), qml.PauliX(1)), [], Tensor(qml.PauliZ(0), qml.PauliX(1))), + ] + -@pytest.mark.usefixtures("use_legacy_opmath") @pytest.mark.parametrize( "H, new_coeffs, expected_H", TEST_BIND_LEGACY_HAMILTONIAN, @@ -220,6 +237,18 @@ def test_hamiltonian_legacy_opmath(H, new_coeffs, expected_H): assert new_H is not H +@pytest.mark.parametrize("op, new_params, expected_op", TEST_BIND_LEGACY_TENSOR) +def test_tensor(op, new_params, expected_op): + """Test that `bind_new_parameters` with `Tensor` returns a new + operator with the new parameters without mutating the original + operator.""" + new_op = bind_new_parameters(op, new_params) + + qml.assert_equal(new_op, expected_op) + assert new_op is not op + assert all(n_obs is not obs for n_obs, obs in zip(new_op.obs, op.obs)) + + TEST_BIND_LINEARCOMBINATION = [ ( # LinearCombination with only data being the coeffs qml.ops.LinearCombination( @@ -321,33 +350,6 @@ def test_hamiltonian_grouping_indices(): assert new_H.data == (2.3, 3.4) -@pytest.mark.parametrize( - "op, new_params, expected_op", - [ - ( - Tensor(qml.Hermitian(Y, wires=0), qml.PauliZ(1)), - [X], - Tensor(qml.Hermitian(X, wires=0), qml.PauliZ(1)), - ), - ( - Tensor(qml.Hermitian(qml.math.kron(X, Z), wires=[0, 1]), qml.Hermitian(I, wires=2)), - [qml.math.kron(I, I), Z], - Tensor(qml.Hermitian(qml.math.kron(I, I), wires=[0, 1]), qml.Hermitian(Z, wires=2)), - ), - (Tensor(qml.PauliZ(0), qml.PauliX(1)), [], Tensor(qml.PauliZ(0), qml.PauliX(1))), - ], -) -def test_tensor(op, new_params, expected_op): - """Test that `bind_new_parameters` with `Tensor` returns a new - operator with the new parameters without mutating the original - operator.""" - new_op = bind_new_parameters(op, new_params) - - qml.assert_equal(new_op, expected_op) - assert new_op is not op - assert all(n_obs is not obs for n_obs, obs in zip(new_op.obs, op.obs)) - - old_hamiltonian = qml.Hamiltonian( [0.1, 0.2, 0.3], [qml.PauliX(0), qml.PauliZ(0) @ qml.PauliX(1), qml.PauliZ(2)] ) diff --git a/tests/ops/functions/test_eigvals.py b/tests/ops/functions/test_eigvals.py index c95aaac0e3d..569e9cc1969 100644 --- a/tests/ops/functions/test_eigvals.py +++ b/tests/ops/functions/test_eigvals.py @@ -118,7 +118,7 @@ def test_ctrl(self): expected = np.linalg.eigvals(qml.matrix(qml.CNOT(wires=[0, 1]))) assert np.allclose(np.sort(res), np.sort(expected)) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_tensor_product_legacy_opmath(self): """Test a tensor product""" res = qml.eigvals(qml.PauliX(0) @ qml.Identity(1) @ qml.PauliZ(1)) @@ -140,7 +140,7 @@ def test_hamiltonian(self): expected = np.linalg.eigvalsh(reduce(np.kron, [Z, Y]) - 0.5 * reduce(np.kron, [I, X])) assert np.allclose(res, expected) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_hamiltonian_legacy_opmath(self): """Test that the matrix of a Hamiltonian is correctly returned""" ham = qml.PauliZ(0) @ qml.PauliY(1) - 0.5 * qml.PauliX(1) diff --git a/tests/ops/functions/test_equal.py b/tests/ops/functions/test_equal.py index f512376dc08..6ed3a953b7e 100644 --- a/tests/ops/functions/test_equal.py +++ b/tests/ops/functions/test_equal.py @@ -1464,7 +1464,7 @@ def test_shadow_expval_list_versus_operator(self): assert not qml.equal(m1, m2) -@pytest.mark.usefixtures("use_legacy_opmath") # TODO update qml.equal with new opmath +@pytest.mark.usefixtures("legacy_opmath_only") # TODO update qml.equal with new opmath class TestObservablesComparisons: """Tests comparisons between Hamiltonians, Tensors and PauliX/Y/Z operators""" @@ -2141,7 +2141,7 @@ def test_s_prod_base_op_comparison_with_trainability(self): assert not qml.equal(op1, op2, check_interface=False, check_trainability=True) -@pytest.mark.usefixtures("use_new_opmath") +@pytest.mark.usefixtures("new_opmath_only") class TestProdComparisons: """Tests comparisons between Prod operators""" @@ -2247,7 +2247,7 @@ def test_prod_global_phase(self): assert qml.equal(p1, p2) -@pytest.mark.usefixtures("use_new_opmath") +@pytest.mark.usefixtures("new_opmath_only") class TestSumComparisons: """Tests comparisons between Sum operators""" diff --git a/tests/ops/functions/test_generator.py b/tests/ops/functions/test_generator.py index 7976634e453..aa91323fc3e 100644 --- a/tests/ops/functions/test_generator.py +++ b/tests/ops/functions/test_generator.py @@ -341,30 +341,30 @@ class TestObservableReturn: """Tests for format="observable". This format preserves the initial generator encoded in the operator.""" - @pytest.mark.usefixtures("use_legacy_opmath") - def test_observable(self): - """Test a generator that returns a single observable is correct""" + @pytest.mark.usefixtures("legacy_opmath_only") + def test_observable_legacy_opmath(self): + """Test a generator that returns a single observable is correct with opmath disabled""" gen = qml.generator(ObservableOp, format="observable")(0.5, wires=0) assert gen.name == "Hamiltonian" assert gen.compare(ObservableOp(0.5, wires=0).generator()) - @pytest.mark.usefixtures("use_new_opmath") - def test_observable_opmath(self): - """Test a generator that returns a single observable is correct with opmath enabled""" + @pytest.mark.usefixtures("new_opmath_only") + def test_observable(self): + """Test a generator that returns a single observable is correct""" gen = qml.generator(ObservableOp, format="observable")(0.5, wires=0) assert gen.name == "SProd" qml.assert_equal(gen, ObservableOp(0.5, wires=0).generator()) - @pytest.mark.usefixtures("use_legacy_opmath") - def test_tensor_observable(self): - """Test a generator that returns a tensor observable is correct""" + @pytest.mark.usefixtures("legacy_opmath_only") + def test_tensor_observable_legacy_opmath(self): + """Test a generator that returns a tensor observable is correct with opmath disabled""" gen = qml.generator(TensorOp, format="observable")(0.5, wires=[0, 1]) assert gen.name == "Hamiltonian" assert gen.compare(TensorOp(0.5, wires=[0, 1]).generator()) - @pytest.mark.usefixtures("use_new_opmath") - def test_tensor_observable_opmath(self): - """Test a generator that returns a tensor observable is correct with opmath enabled""" + @pytest.mark.usefixtures("new_opmath_only") + def test_tensor_observable(self): + """Test a generator that returns a tensor observable is correct""" gen = qml.generator(TensorOp, format="observable")(0.5, wires=[0, 1]) assert gen.name == "Prod" qml.assert_equal(gen, TensorOp(0.5, wires=[0, 1]).generator()) @@ -402,14 +402,13 @@ def test_observable_no_coeff(self): assert isinstance(gen, qml.Hamiltonian) assert gen.compare(qml.Hamiltonian([1.0], [qml.PhaseShift(0.5, wires=0).generator()])) - @pytest.mark.usefixtures("use_legacy_opmath") def test_observable(self): """Test a generator that returns a single observable is correct""" gen = qml.generator(ObservableOp, format="hamiltonian")(0.5, wires=0) assert isinstance(gen, qml.Hamiltonian) assert gen.compare(ObservableOp(0.5, wires=0).generator()) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_tensor_observable(self): """Test a generator that returns a tensor observable is correct""" gen = qml.generator(TensorOp, format="hamiltonian")(0.5, wires=[0, 1]) diff --git a/tests/ops/op_math/test_adjoint.py b/tests/ops/op_math/test_adjoint.py index 725537e8bcf..da4a446098c 100644 --- a/tests/ops/op_math/test_adjoint.py +++ b/tests/ops/op_math/test_adjoint.py @@ -76,7 +76,7 @@ class CustomOp(qml.operation.Operation): assert "grad_recipe" in dir(op) assert "control_wires" in dir(op) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_observable(self): """Test that when the base is an Observable, Adjoint will also inherit from Observable.""" @@ -180,7 +180,7 @@ def test_template_base(self): assert op.wires == qml.wires.Wires((0, 1)) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_hamiltonian_base(self): """Test adjoint initialization for a hamiltonian.""" with pytest.warns(UserWarning, match="Tensor object acts on overlapping"): @@ -320,7 +320,7 @@ def test_queue_category(self): op = Adjoint(qml.PauliX(0)) assert op._queue_category == "_ops" # pylint: disable=protected-access - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_queue_category_None(self): """Test that the queue category `None` for some observables carries over.""" op = Adjoint(qml.PauliX(0) @ qml.PauliY(1)) @@ -868,7 +868,7 @@ def test_single_op_defined_outside_queue_eager(self): assert len(q) == 1 assert q.queue[0] is out - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_single_observable(self): """Test passing a single preconstructed observable in a queuing context.""" diff --git a/tests/ops/op_math/test_controlled.py b/tests/ops/op_math/test_controlled.py index 40e315449b1..1cc3c5241f9 100644 --- a/tests/ops/op_math/test_controlled.py +++ b/tests/ops/op_math/test_controlled.py @@ -495,33 +495,6 @@ def test_generator(self): expected.matrix(wire_order=["a", "b", "c"]), op.matrix(wire_order=["a", "b", "c"]) ) - @pytest.mark.usefixtures("use_legacy_opmath") - def test_generator_legacy_opmath(self): - """Test that the generator is a tensor product of projectors and the base's generator.""" - - base = qml.RZ(-0.123, wires="a") - control_values = [0, 1] - op = Controlled(base, ("b", "c"), control_values=control_values) - - base_gen, base_gen_coeff = qml.generator(base, format="prefactor") - gen_tensor, gen_coeff = qml.generator(op, format="prefactor") - - assert base_gen_coeff == gen_coeff - - for wire, val in zip(op.control_wires, control_values): - ob = list(op for op in gen_tensor.operands if op.wires == qml.wires.Wires(wire)) - assert len(ob) == 1 - assert ob[0].data == ([val],) - - ob = list(op for op in gen_tensor.operands if op.wires == base.wires) - assert len(ob) == 1 - assert ob[0].__class__ is base_gen.__class__ - - expected = qml.exp(op.generator(), 1j * op.data[0]) - assert qml.math.allclose( - expected.matrix(wire_order=["a", "b", "c"]), op.matrix(wire_order=["a", "b", "c"]) - ) - def test_diagonalizing_gates(self): """Test that the Controlled diagonalizing gates is the same as the base diagonalizing gates.""" base = qml.PauliX(0) diff --git a/tests/ops/op_math/test_evolution.py b/tests/ops/op_math/test_evolution.py index 8033c180f64..4cb61166573 100644 --- a/tests/ops/op_math/test_evolution.py +++ b/tests/ops/op_math/test_evolution.py @@ -71,7 +71,7 @@ def test_generator(self): U = Evolution(qml.PauliX(0), 3) assert U.base == U.generator() - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_num_params_for_parametric_base_legacy_opmath(self): base_op = 0.5 * qml.PauliY(0) + qml.PauliZ(0) @ qml.PauliX(1) op = Evolution(base_op, 1.23) @@ -79,7 +79,7 @@ def test_num_params_for_parametric_base_legacy_opmath(self): assert base_op.num_params == 2 assert op.num_params == 1 - @pytest.mark.usefixtures("use_new_opmath") + @pytest.mark.usefixtures("new_opmath_only") def test_num_params_for_parametric_base(self): base_op = 0.5 * qml.PauliY(0) + qml.PauliZ(0) @ qml.PauliX(1) op = Evolution(base_op, 1.23) diff --git a/tests/ops/op_math/test_linear_combination.py b/tests/ops/op_math/test_linear_combination.py index 748f2bfb48a..b75b7d01fd9 100644 --- a/tests/ops/op_math/test_linear_combination.py +++ b/tests/ops/op_math/test_linear_combination.py @@ -31,46 +31,25 @@ from pennylane.wires import Wires -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") def test_switching(): """Test that switching to new from old opmath changes the dispatch of qml.Hamiltonian""" Ham = qml.Hamiltonian([1.0, 2.0, 3.0], [X(0), X(0) @ X(1), X(2)]) assert isinstance(Ham, qml.Hamiltonian) assert not isinstance(Ham, qml.ops.LinearCombination) - with enable_new_opmath_cm(): + with enable_new_opmath_cm(warn=False): LC = qml.Hamiltonian([1.0, 2.0, 3.0], [X(0), X(0) @ X(1), X(2)]) assert isinstance(LC, qml.Hamiltonian) assert isinstance(LC, qml.ops.LinearCombination) -@pytest.mark.usefixtures("use_legacy_and_new_opmath") -class TestParityWithHamiltonian: +def test_isinstance_Hamiltonian(): """Test that Hamiltonian and LinearCombination can be used interchangeably when new opmath is disabled or enabled""" + H = qml.Hamiltonian([1.0, 2.0, 3.0], [X(0), X(0) @ X(1), X(2)]) + assert isinstance(H, qml.Hamiltonian) - def test_isinstance_Hamiltonian(self): - H = qml.Hamiltonian([1.0, 2.0, 3.0], [X(0), X(0) @ X(1), X(2)]) - assert isinstance(H, qml.Hamiltonian) - - -@pytest.mark.usefixtures("new_opmath_only") -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)]) - - 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) - - assert res - - -@pytest.mark.usefixtures("legacy_opmath_only") def test_mixed_legacy_warning_Hamiltonian_legacy(): """Test that mixing legacy ops and LinearCombination.compare raises a warning in legacy opmath""" @@ -576,7 +555,7 @@ def circuit2(param): dev = qml.device("default.qubit", wires=2) -@pytest.mark.usefixtures("use_new_opmath") +@pytest.mark.usefixtures("new_opmath_only") class TestLinearCombination: """Test the LinearCombination class""" diff --git a/tests/ops/op_math/test_pow_op.py b/tests/ops/op_math/test_pow_op.py index 15ad53f3c97..cf13dddd7f1 100644 --- a/tests/ops/op_math/test_pow_op.py +++ b/tests/ops/op_math/test_pow_op.py @@ -164,7 +164,7 @@ class CustomObs(qml.operation.Observable): # check the dir assert "grad_recipe" not in dir(ob) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_observable_legacy_opmath(self, power_method): """Test that when the base is an Observable, Pow will also inherit from Observable.""" @@ -258,7 +258,7 @@ def test_template_base(self, power_method): assert op.wires == qml.wires.Wires((0, 1)) assert op.num_wires == 2 - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_hamiltonian_base(self, power_method): """Test pow initialization for a hamiltonian.""" base = qml.Hamiltonian([2.0, 1.0], [qml.PauliX(0) @ qml.PauliY(0), qml.PauliZ("b")]) @@ -423,7 +423,7 @@ def test_queue_category(self, power_method): op: Pow = power_method(base=qml.PauliX(0), z=3.5) assert op._queue_category == "_ops" # pylint: disable=protected-access - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_queue_category_None(self, power_method): """Test that the queue category `None` for some observables carries over.""" op: Pow = power_method(base=qml.PauliX(0) @ qml.PauliY(1), z=-1.1) diff --git a/tests/ops/op_math/test_sum.py b/tests/ops/op_math/test_sum.py index c63fb0a4753..d6bc1668f9f 100644 --- a/tests/ops/op_math/test_sum.py +++ b/tests/ops/op_math/test_sum.py @@ -344,7 +344,7 @@ def test_repr(self, op, repr_true): # qml.sum(*[0.5 * X(i) for i in range(10)]) # multiline output needs fixing of https://github.com/PennyLaneAI/pennylane/issues/5162 before working ) - @pytest.mark.usefixtures("use_new_opmath") + @pytest.mark.usefixtures("new_opmath_only") @pytest.mark.parametrize("op", SUM_REPR_EVAL) def test_eval_sum(self, op): """Test that string representations of Sum can be evaluated and yield the same operator""" diff --git a/tests/ops/qubit/test_hamiltonian.py b/tests/ops/qubit/test_hamiltonian.py index b977a2f9f7a..8b27b2cb7b4 100644 --- a/tests/ops/qubit/test_hamiltonian.py +++ b/tests/ops/qubit/test_hamiltonian.py @@ -14,6 +14,8 @@ """ Tests for the Hamiltonian class. """ +import warnings + # pylint: disable=too-many-public-methods, superfluous-parens, unnecessary-dunder-call from collections.abc import Iterable from unittest.mock import patch @@ -26,6 +28,11 @@ from pennylane import numpy as pnp from pennylane.wires import Wires +pytestmark = pytest.mark.filterwarnings( + "ignore:qml.ops.Hamiltonian uses:pennylane.PennyLaneDeprecationWarning" +) + + # Make test data in different interfaces, if installed COEFFS_PARAM_INTERFACE = [ ([-0.05, 0.17], 1.7, "autograd"), @@ -65,7 +72,7 @@ COEFFS = [(0.5, 1.2, -0.7), (2.2, -0.2, 0.0), (0.33,)] -with qml.operation.disable_new_opmath_cm(): +with qml.operation.disable_new_opmath_cm(warn=False): OBSERVABLES = [ (qml.PauliZ(0), qml.PauliY(0), qml.PauliZ(1)), @@ -130,533 +137,574 @@ ((1.0,), (qml.PauliZ(0), qml.PauliY(0))), ] - simplify_hamiltonians = [ - ( - qml.Hamiltonian( - [1, 1, 1], [qml.PauliX(0) @ qml.Identity(1), qml.PauliX(0), qml.PauliX(1)] + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", "qml.ops.Hamiltonian uses", qml.PennyLaneDeprecationWarning + ) + + simplify_hamiltonians = [ + ( + qml.Hamiltonian( + [1, 1, 1], [qml.PauliX(0) @ qml.Identity(1), qml.PauliX(0), qml.PauliX(1)] + ), + qml.Hamiltonian([2, 1], [qml.PauliX(0), qml.PauliX(1)]), ), - qml.Hamiltonian([2, 1], [qml.PauliX(0), qml.PauliX(1)]), - ), - ( - qml.Hamiltonian( - [-1, 1, 1], [qml.PauliX(0) @ qml.Identity(1), qml.PauliX(0), qml.PauliX(1)] + ( + qml.Hamiltonian( + [-1, 1, 1], [qml.PauliX(0) @ qml.Identity(1), qml.PauliX(0), qml.PauliX(1)] + ), + qml.Hamiltonian([1], [qml.PauliX(1)]), ), - qml.Hamiltonian([1], [qml.PauliX(1)]), - ), - ( - qml.Hamiltonian( - [1, 0.5], - [qml.PauliX(0) @ qml.PauliY(1), qml.PauliY(1) @ qml.Identity(2) @ qml.PauliX(0)], + ( + qml.Hamiltonian( + [1, 0.5], + [ + qml.PauliX(0) @ qml.PauliY(1), + qml.PauliY(1) @ qml.Identity(2) @ qml.PauliX(0), + ], + ), + qml.Hamiltonian([1.5], [qml.PauliX(0) @ qml.PauliY(1)]), ), - qml.Hamiltonian([1.5], [qml.PauliX(0) @ qml.PauliY(1)]), - ), - ( - qml.Hamiltonian( - [1, 1, 0.5], - [ - qml.Hermitian(np.array([[1, 0], [0, -1]]), "a"), - qml.PauliX("b") @ qml.PauliY(1.3), - qml.PauliY(1.3) @ qml.Identity(-0.9) @ qml.PauliX("b"), - ], + ( + qml.Hamiltonian( + [1, 1, 0.5], + [ + qml.Hermitian(np.array([[1, 0], [0, -1]]), "a"), + qml.PauliX("b") @ qml.PauliY(1.3), + qml.PauliY(1.3) @ qml.Identity(-0.9) @ qml.PauliX("b"), + ], + ), + qml.Hamiltonian( + [1, 1.5], + [ + qml.Hermitian(np.array([[1, 0], [0, -1]]), "a"), + qml.PauliX("b") @ qml.PauliY(1.3), + ], + ), ), - qml.Hamiltonian( - [1, 1.5], - [ - qml.Hermitian(np.array([[1, 0], [0, -1]]), "a"), - qml.PauliX("b") @ qml.PauliY(1.3), - ], + # Simplifies to zero Hamiltonian + ( + qml.Hamiltonian( + [1, -0.5, -0.5], [qml.PauliX(0) @ qml.Identity(1), qml.PauliX(0), qml.PauliX(0)] + ), + qml.Hamiltonian([], []), ), - ), - # Simplifies to zero Hamiltonian - ( - qml.Hamiltonian( - [1, -0.5, -0.5], [qml.PauliX(0) @ qml.Identity(1), qml.PauliX(0), qml.PauliX(0)] + ( + qml.Hamiltonian( + [1, -1], + [ + qml.PauliX(4) @ qml.Identity(0) @ qml.PauliX(1), + qml.PauliX(4) @ qml.PauliX(1), + ], + ), + qml.Hamiltonian([], []), ), - qml.Hamiltonian([], []), - ), - ( - qml.Hamiltonian( - [1, -1], - [qml.PauliX(4) @ qml.Identity(0) @ qml.PauliX(1), qml.PauliX(4) @ qml.PauliX(1)], + ( + qml.Hamiltonian([0], [qml.Identity(0)]), + qml.Hamiltonian([0], [qml.Identity(0)]), ), - qml.Hamiltonian([], []), - ), - ( - qml.Hamiltonian([0], [qml.Identity(0)]), - qml.Hamiltonian([0], [qml.Identity(0)]), - ), - ] + ] - equal_hamiltonians = [ - ( - qml.Hamiltonian([1, 1], [qml.PauliX(0) @ qml.Identity(1), qml.PauliZ(0)]), - qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliZ(0)]), - True, - ), - ( - qml.Hamiltonian( - [1, 1], [qml.PauliX(0) @ qml.Identity(1), qml.PauliY(2) @ qml.PauliZ(0)] - ), - qml.Hamiltonian( - [1, 1], [qml.PauliX(0), qml.PauliZ(0) @ qml.PauliY(2) @ qml.Identity(1)] + equal_hamiltonians = [ + ( + qml.Hamiltonian([1, 1], [qml.PauliX(0) @ qml.Identity(1), qml.PauliZ(0)]), + qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliZ(0)]), + True, ), - True, - ), - ( - qml.Hamiltonian( - [1, 1, 1], [qml.PauliX(0) @ qml.Identity(1), qml.PauliZ(0), qml.Identity(1)] + ( + qml.Hamiltonian( + [1, 1], [qml.PauliX(0) @ qml.Identity(1), qml.PauliY(2) @ qml.PauliZ(0)] + ), + qml.Hamiltonian( + [1, 1], [qml.PauliX(0), qml.PauliZ(0) @ qml.PauliY(2) @ qml.Identity(1)] + ), + True, ), - qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliZ(0)]), - False, - ), - ( - qml.Hamiltonian([1], [qml.PauliZ(0) @ qml.PauliX(1)]), - qml.PauliZ(0) @ qml.PauliX(1), - True, - ), - (qml.Hamiltonian([1], [qml.PauliZ(0)]), qml.PauliZ(0), True), - ( - qml.Hamiltonian( - [1, 1, 1], - [ - qml.Hermitian(np.array([[1, 0], [0, -1]]), "b") @ qml.Identity(7), - qml.PauliZ(3), - qml.Identity(1.2), - ], + ( + qml.Hamiltonian( + [1, 1, 1], [qml.PauliX(0) @ qml.Identity(1), qml.PauliZ(0), qml.Identity(1)] + ), + qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliZ(0)]), + False, ), - qml.Hamiltonian( - [1, 1, 1], - [qml.Hermitian(np.array([[1, 0], [0, -1]]), "b"), qml.PauliZ(3), qml.Identity(1.2)], + ( + qml.Hamiltonian([1], [qml.PauliZ(0) @ qml.PauliX(1)]), + qml.PauliZ(0) @ qml.PauliX(1), + True, ), - True, - ), - ( - qml.Hamiltonian([1, 1], [qml.PauliZ(3) @ qml.Identity(1.2), qml.PauliZ(3)]), - qml.Hamiltonian([2], [qml.PauliZ(3)]), - True, - ), - ] - - add_hamiltonians = [ - ( - qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), - qml.Hamiltonian([0.5, 0.3, 1], [qml.PauliX(0), qml.PauliX(1), qml.PauliX(2)]), - qml.Hamiltonian( - [1.5, 1.2, 1.1, 0.3], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2), qml.PauliX(1)] + (qml.Hamiltonian([1], [qml.PauliZ(0)]), qml.PauliZ(0), True), + ( + qml.Hamiltonian( + [1, 1, 1], + [ + qml.Hermitian(np.array([[1, 0], [0, -1]]), "b") @ qml.Identity(7), + qml.PauliZ(3), + qml.Identity(1.2), + ], + ), + qml.Hamiltonian( + [1, 1, 1], + [ + qml.Hermitian(np.array([[1, 0], [0, -1]]), "b"), + qml.PauliZ(3), + qml.Identity(1.2), + ], + ), + True, ), - ), - ( - qml.Hamiltonian( - [1.3, 0.2, 0.7], [qml.PauliX(0) @ qml.PauliX(1), qml.Hadamard(1), qml.PauliX(2)] + ( + qml.Hamiltonian([1, 1], [qml.PauliZ(3) @ qml.Identity(1.2), qml.PauliZ(3)]), + qml.Hamiltonian([2], [qml.PauliZ(3)]), + True, ), - qml.Hamiltonian( - [0.5, 0.3, 1.6], [qml.PauliX(0), qml.PauliX(1) @ qml.PauliX(0), qml.PauliX(2)] + ] + + add_hamiltonians = [ + ( + qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), + qml.Hamiltonian([0.5, 0.3, 1], [qml.PauliX(0), qml.PauliX(1), qml.PauliX(2)]), + qml.Hamiltonian( + [1.5, 1.2, 1.1, 0.3], + [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2), qml.PauliX(1)], + ), ), - qml.Hamiltonian( - [1.6, 0.2, 2.3, 0.5], - [qml.PauliX(0) @ qml.PauliX(1), qml.Hadamard(1), qml.PauliX(2), qml.PauliX(0)], + ( + qml.Hamiltonian( + [1.3, 0.2, 0.7], [qml.PauliX(0) @ qml.PauliX(1), qml.Hadamard(1), qml.PauliX(2)] + ), + qml.Hamiltonian( + [0.5, 0.3, 1.6], [qml.PauliX(0), qml.PauliX(1) @ qml.PauliX(0), qml.PauliX(2)] + ), + qml.Hamiltonian( + [1.6, 0.2, 2.3, 0.5], + [qml.PauliX(0) @ qml.PauliX(1), qml.Hadamard(1), qml.PauliX(2), qml.PauliX(0)], + ), ), - ), - ( - qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)]), - qml.Hamiltonian( - [0.5, 0.5], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)] + ( + qml.Hamiltonian( + [1, 1], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)] + ), + qml.Hamiltonian( + [0.5, 0.5], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)] + ), + qml.Hamiltonian( + [1.5, 1.5], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)] + ), ), - qml.Hamiltonian( - [1.5, 1.5], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)] + ( + qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), + qml.PauliX(0) @ qml.Identity(1), + qml.Hamiltonian([2, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), ), - ), - ( - qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), - qml.PauliX(0) @ qml.Identity(1), - qml.Hamiltonian([2, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), - ), - ( - qml.Hamiltonian( - [1.3, 0.2, 0.7], [qml.PauliX(0) @ qml.PauliX(1), qml.Hadamard(1), qml.PauliX(2)] + ( + qml.Hamiltonian( + [1.3, 0.2, 0.7], [qml.PauliX(0) @ qml.PauliX(1), qml.Hadamard(1), qml.PauliX(2)] + ), + qml.Hadamard(1), + qml.Hamiltonian( + [1.3, 1.2, 0.7], [qml.PauliX(0) @ qml.PauliX(1), qml.Hadamard(1), qml.PauliX(2)] + ), ), - qml.Hadamard(1), - qml.Hamiltonian( - [1.3, 1.2, 0.7], [qml.PauliX(0) @ qml.PauliX(1), qml.Hadamard(1), qml.PauliX(2)] + ( + qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX("b"), qml.PauliZ(3.1), qml.PauliX(1.6)]), + qml.PauliX("b") @ qml.Identity(5), + qml.Hamiltonian([2, 1.2, 0.1], [qml.PauliX("b"), qml.PauliZ(3.1), qml.PauliX(1.6)]), ), - ), - ( - qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX("b"), qml.PauliZ(3.1), qml.PauliX(1.6)]), - qml.PauliX("b") @ qml.Identity(5), - qml.Hamiltonian([2, 1.2, 0.1], [qml.PauliX("b"), qml.PauliZ(3.1), qml.PauliX(1.6)]), - ), - # Case where arguments coeffs and ops to the Hamiltonian are iterables other than lists - ( - qml.Hamiltonian((1, 1.2, 0.1), (qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2))), - qml.Hamiltonian( - np.array([0.5, 0.3, 1]), np.array([qml.PauliX(0), qml.PauliX(1), qml.PauliX(2)]) + # Case where arguments coeffs and ops to the Hamiltonian are iterables other than lists + ( + qml.Hamiltonian((1, 1.2, 0.1), (qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2))), + qml.Hamiltonian( + np.array([0.5, 0.3, 1]), np.array([qml.PauliX(0), qml.PauliX(1), qml.PauliX(2)]) + ), + qml.Hamiltonian( + (1.5, 1.2, 1.1, 0.3), + np.array([qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2), qml.PauliX(1)]), + ), ), - qml.Hamiltonian( - (1.5, 1.2, 1.1, 0.3), - np.array([qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2), qml.PauliX(1)]), + # Case where the 1st hamiltonian doesn't contain all wires + ( + qml.Hamiltonian([1.23, -3.45], [qml.PauliX(0), qml.PauliY(1)]), + qml.Hamiltonian([6.78], [qml.PauliZ(2)]), + qml.Hamiltonian([1.23, -3.45, 6.78], [qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2)]), ), - ), - # Case where the 1st hamiltonian doesn't contain all wires - ( - qml.Hamiltonian([1.23, -3.45], [qml.PauliX(0), qml.PauliY(1)]), - qml.Hamiltonian([6.78], [qml.PauliZ(2)]), - qml.Hamiltonian([1.23, -3.45, 6.78], [qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2)]), - ), - ] - - add_zero_hamiltonians = [ - qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), - qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)]), - qml.Hamiltonian( - [1.5, 1.2, 1.1, 0.3], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2), qml.PauliX(1)] - ), - ] + ] - iadd_zero_hamiltonians = [ - # identical hamiltonians - ( + add_zero_hamiltonians = [ qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), - qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), - ), - ( - qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)]), qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)]), - ), - ( qml.Hamiltonian( [1.5, 1.2, 1.1, 0.3], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2), qml.PauliX(1)] ), - qml.Hamiltonian( - [1.5, 1.2, 1.1, 0.3], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2), qml.PauliX(1)] + ] + + iadd_zero_hamiltonians = [ + # identical hamiltonians + ( + qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), + qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), ), - ), - ] + ( + qml.Hamiltonian( + [1, 1], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)] + ), + qml.Hamiltonian( + [1, 1], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)] + ), + ), + ( + qml.Hamiltonian( + [1.5, 1.2, 1.1, 0.3], + [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2), qml.PauliX(1)], + ), + qml.Hamiltonian( + [1.5, 1.2, 1.1, 0.3], + [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2), qml.PauliX(1)], + ), + ), + ] - sub_hamiltonians = [ - ( - qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), - qml.Hamiltonian([0.5, 0.3, 1.6], [qml.PauliX(0), qml.PauliX(1), qml.PauliX(2)]), - qml.Hamiltonian( - [0.5, 1.2, -1.5, -0.3], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2), qml.PauliX(1)] + sub_hamiltonians = [ + ( + qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), + qml.Hamiltonian([0.5, 0.3, 1.6], [qml.PauliX(0), qml.PauliX(1), qml.PauliX(2)]), + qml.Hamiltonian( + [0.5, 1.2, -1.5, -0.3], + [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2), qml.PauliX(1)], + ), ), - ), - ( - qml.Hamiltonian( - [1.3, 0.2, 1], [qml.PauliX(0) @ qml.PauliX(1), qml.Hadamard(1), qml.PauliX(2)] + ( + qml.Hamiltonian( + [1.3, 0.2, 1], [qml.PauliX(0) @ qml.PauliX(1), qml.Hadamard(1), qml.PauliX(2)] + ), + qml.Hamiltonian( + [0.5, 0.3, 1], [qml.PauliX(0), qml.PauliX(1) @ qml.PauliX(0), qml.PauliX(2)] + ), + qml.Hamiltonian( + [1, 0.2, -0.5], [qml.PauliX(0) @ qml.PauliX(1), qml.Hadamard(1), qml.PauliX(0)] + ), ), - qml.Hamiltonian( - [0.5, 0.3, 1], [qml.PauliX(0), qml.PauliX(1) @ qml.PauliX(0), qml.PauliX(2)] + ( + qml.Hamiltonian( + [1, 1], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)] + ), + qml.Hamiltonian( + [0.5, 0.5], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)] + ), + qml.Hamiltonian( + [0.5, 0.5], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)] + ), ), - qml.Hamiltonian( - [1, 0.2, -0.5], [qml.PauliX(0) @ qml.PauliX(1), qml.Hadamard(1), qml.PauliX(0)] + ( + qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), + qml.PauliX(0) @ qml.Identity(1), + qml.Hamiltonian([1.2, 0.1], [qml.PauliZ(1), qml.PauliX(2)]), ), - ), - ( - qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)]), - qml.Hamiltonian( - [0.5, 0.5], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)] + ( + qml.Hamiltonian( + [1.3, 0.2, 0.7], [qml.PauliX(0) @ qml.PauliX(1), qml.Hadamard(1), qml.PauliX(2)] + ), + qml.Hadamard(1), + qml.Hamiltonian( + [1.3, -0.8, 0.7], + [qml.PauliX(0) @ qml.PauliX(1), qml.Hadamard(1), qml.PauliX(2)], + ), ), - qml.Hamiltonian( - [0.5, 0.5], [qml.PauliX(0), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)] + ( + qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX("b"), qml.PauliZ(3.1), qml.PauliX(1.6)]), + qml.PauliX("b") @ qml.Identity(1), + qml.Hamiltonian([1.2, 0.1], [qml.PauliZ(3.1), qml.PauliX(1.6)]), ), - ), - ( - qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), - qml.PauliX(0) @ qml.Identity(1), - qml.Hamiltonian([1.2, 0.1], [qml.PauliZ(1), qml.PauliX(2)]), - ), - ( - qml.Hamiltonian( - [1.3, 0.2, 0.7], [qml.PauliX(0) @ qml.PauliX(1), qml.Hadamard(1), qml.PauliX(2)] + # The result is the zero Hamiltonian + ( + qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), + qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), + qml.Hamiltonian([], []), ), - qml.Hadamard(1), - qml.Hamiltonian( - [1.3, -0.8, 0.7], [qml.PauliX(0) @ qml.PauliX(1), qml.Hadamard(1), qml.PauliX(2)] + ( + qml.Hamiltonian([1, 2], [qml.PauliX(4), qml.PauliZ(2)]), + qml.Hamiltonian([1, 2], [qml.PauliX(4), qml.PauliZ(2)]), + qml.Hamiltonian([], []), ), - ), - ( - qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX("b"), qml.PauliZ(3.1), qml.PauliX(1.6)]), - qml.PauliX("b") @ qml.Identity(1), - qml.Hamiltonian([1.2, 0.1], [qml.PauliZ(3.1), qml.PauliX(1.6)]), - ), - # The result is the zero Hamiltonian - ( - qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), - qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), - qml.Hamiltonian([], []), - ), - ( - qml.Hamiltonian([1, 2], [qml.PauliX(4), qml.PauliZ(2)]), - qml.Hamiltonian([1, 2], [qml.PauliX(4), qml.PauliZ(2)]), - qml.Hamiltonian([], []), - ), - # Case where arguments coeffs and ops to the Hamiltonian are iterables other than lists - ( - qml.Hamiltonian((1, 1.2, 0.1), (qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2))), - qml.Hamiltonian( - np.array([0.5, 0.3, 1.6]), np.array([qml.PauliX(0), qml.PauliX(1), qml.PauliX(2)]) + # Case where arguments coeffs and ops to the Hamiltonian are iterables other than lists + ( + qml.Hamiltonian((1, 1.2, 0.1), (qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2))), + qml.Hamiltonian( + np.array([0.5, 0.3, 1.6]), + np.array([qml.PauliX(0), qml.PauliX(1), qml.PauliX(2)]), + ), + qml.Hamiltonian( + (0.5, 1.2, -1.5, -0.3), + np.array([qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2), qml.PauliX(1)]), + ), ), - qml.Hamiltonian( - (0.5, 1.2, -1.5, -0.3), - np.array([qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2), qml.PauliX(1)]), + # Case where the 1st hamiltonian doesn't contain all wires + ( + qml.Hamiltonian([1.23, -3.45], [qml.PauliX(0), qml.PauliY(1)]), + qml.Hamiltonian([6.78], [qml.PauliZ(2)]), + qml.Hamiltonian( + [1.23, -3.45, -6.78], [qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2)] + ), ), - ), - # Case where the 1st hamiltonian doesn't contain all wires - ( - qml.Hamiltonian([1.23, -3.45], [qml.PauliX(0), qml.PauliY(1)]), - qml.Hamiltonian([6.78], [qml.PauliZ(2)]), - qml.Hamiltonian([1.23, -3.45, -6.78], [qml.PauliX(0), qml.PauliY(1), qml.PauliZ(2)]), - ), - ] + ] - mul_hamiltonians = [ - ( - 0.5, - qml.Hamiltonian( - [1, 2], [qml.PauliX(0), qml.PauliZ(1)] - ), # Case where the types of the coefficient and the scalar differ - qml.Hamiltonian([0.5, 1.0], [qml.PauliX(0), qml.PauliZ(1)]), - ), - ( - 3, - qml.Hamiltonian([1.5, 0.5], [qml.PauliX(0), qml.PauliZ(1)]), - qml.Hamiltonian([4.5, 1.5], [qml.PauliX(0), qml.PauliZ(1)]), - ), - ( - -1.3, - qml.Hamiltonian([1, -0.3], [qml.PauliX(0), qml.PauliZ(1) @ qml.PauliZ(2)]), - qml.Hamiltonian([-1.3, 0.39], [qml.PauliX(0), qml.PauliZ(1) @ qml.PauliZ(2)]), - ), - ( - -1.3, - qml.Hamiltonian( - [1, -0.3], - [qml.Hermitian(np.array([[1, 0], [0, -1]]), "b"), qml.PauliZ(23) @ qml.PauliZ(0)], + mul_hamiltonians = [ + ( + 0.5, + qml.Hamiltonian( + [1, 2], [qml.PauliX(0), qml.PauliZ(1)] + ), # Case where the types of the coefficient and the scalar differ + qml.Hamiltonian([0.5, 1.0], [qml.PauliX(0), qml.PauliZ(1)]), ), - qml.Hamiltonian( - [-1.3, 0.39], - [qml.Hermitian(np.array([[1, 0], [0, -1]]), "b"), qml.PauliZ(23) @ qml.PauliZ(0)], + ( + 3, + qml.Hamiltonian([1.5, 0.5], [qml.PauliX(0), qml.PauliZ(1)]), + qml.Hamiltonian([4.5, 1.5], [qml.PauliX(0), qml.PauliZ(1)]), ), - ), - # The result is the zero Hamiltonian - ( - 0, - qml.Hamiltonian([1], [qml.PauliX(0)]), - qml.Hamiltonian([0], [qml.PauliX(0)]), - ), - ( - 0, - qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), - qml.Hamiltonian([0, 0, 0], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), - ), - # Case where arguments coeffs and ops to the Hamiltonian are iterables other than lists - ( - 3, - qml.Hamiltonian((1.5, 0.5), (qml.PauliX(0), qml.PauliZ(1))), - qml.Hamiltonian(np.array([4.5, 1.5]), np.array([qml.PauliX(0), qml.PauliZ(1)])), - ), - ] - - matmul_hamiltonians = [ - ( - qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliZ(1)]), - qml.Hamiltonian([0.5, 0.5], [qml.PauliZ(2), qml.PauliZ(3)]), - qml.Hamiltonian( - [0.5, 0.5, 0.5, 0.5], - [ - qml.PauliX(0) @ qml.PauliZ(2), - qml.PauliX(0) @ qml.PauliZ(3), - qml.PauliZ(1) @ qml.PauliZ(2), - qml.PauliZ(1) @ qml.PauliZ(3), - ], + ( + -1.3, + qml.Hamiltonian([1, -0.3], [qml.PauliX(0), qml.PauliZ(1) @ qml.PauliZ(2)]), + qml.Hamiltonian([-1.3, 0.39], [qml.PauliX(0), qml.PauliZ(1) @ qml.PauliZ(2)]), ), - ), - ( - qml.Hamiltonian([0.5, 0.25], [qml.PauliX(0) @ qml.PauliX(1), qml.PauliZ(0)]), - qml.Hamiltonian([1, 1], [qml.PauliX(3) @ qml.PauliZ(2), qml.PauliZ(2)]), - qml.Hamiltonian( - [0.5, 0.5, 0.25, 0.25], - [ - qml.PauliX(0) @ qml.PauliX(1) @ qml.PauliX(3) @ qml.PauliZ(2), - qml.PauliX(0) @ qml.PauliX(1) @ qml.PauliZ(2), - qml.PauliZ(0) @ qml.PauliX(3) @ qml.PauliZ(2), - qml.PauliZ(0) @ qml.PauliZ(2), - ], + ( + -1.3, + qml.Hamiltonian( + [1, -0.3], + [ + qml.Hermitian(np.array([[1, 0], [0, -1]]), "b"), + qml.PauliZ(23) @ qml.PauliZ(0), + ], + ), + qml.Hamiltonian( + [-1.3, 0.39], + [ + qml.Hermitian(np.array([[1, 0], [0, -1]]), "b"), + qml.PauliZ(23) @ qml.PauliZ(0), + ], + ), ), - ), - ( - qml.Hamiltonian( - [1, 1], [qml.PauliX("b"), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)] + # The result is the zero Hamiltonian + ( + 0, + qml.Hamiltonian([1], [qml.PauliX(0)]), + qml.Hamiltonian([0], [qml.PauliX(0)]), ), - qml.Hamiltonian([2, 2], [qml.PauliZ(1.2), qml.PauliY("c")]), - qml.Hamiltonian( - [2, 2, 2, 2], - [ - qml.PauliX("b") @ qml.PauliZ(1.2), - qml.PauliX("b") @ qml.PauliY("c"), - qml.Hermitian(np.array([[1, 0], [0, -1]]), 0) @ qml.PauliZ(1.2), - qml.Hermitian(np.array([[1, 0], [0, -1]]), 0) @ qml.PauliY("c"), - ], + ( + 0, + qml.Hamiltonian([1, 1.2, 0.1], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), + qml.Hamiltonian([0, 0, 0], [qml.PauliX(0), qml.PauliZ(1), qml.PauliX(2)]), ), - ), - ( - qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliZ(1)]), - qml.PauliX(2), - qml.Hamiltonian([1, 1], [qml.PauliX(0) @ qml.PauliX(2), qml.PauliZ(1) @ qml.PauliX(2)]), - ), - # Case where arguments coeffs and ops to the Hamiltonian are iterables other than lists - ( - qml.Hamiltonian((1, 1), (qml.PauliX(0), qml.PauliZ(1))), - qml.Hamiltonian(np.array([0.5, 0.5]), np.array([qml.PauliZ(2), qml.PauliZ(3)])), - qml.Hamiltonian( - (0.5, 0.5, 0.5, 0.5), - np.array( + # Case where arguments coeffs and ops to the Hamiltonian are iterables other than lists + ( + 3, + qml.Hamiltonian((1.5, 0.5), (qml.PauliX(0), qml.PauliZ(1))), + qml.Hamiltonian(np.array([4.5, 1.5]), np.array([qml.PauliX(0), qml.PauliZ(1)])), + ), + ] + + matmul_hamiltonians = [ + ( + qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliZ(1)]), + qml.Hamiltonian([0.5, 0.5], [qml.PauliZ(2), qml.PauliZ(3)]), + qml.Hamiltonian( + [0.5, 0.5, 0.5, 0.5], [ qml.PauliX(0) @ qml.PauliZ(2), qml.PauliX(0) @ qml.PauliZ(3), qml.PauliZ(1) @ qml.PauliZ(2), qml.PauliZ(1) @ qml.PauliZ(3), - ] + ], ), ), - ), - ] - - rmatmul_hamiltonians = [ - ( - qml.Hamiltonian([0.5, 0.5], [qml.PauliZ(2), qml.PauliZ(3)]), - qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliZ(1)]), - qml.Hamiltonian( - [0.5, 0.5, 0.5, 0.5], - [ - qml.PauliX(0) @ qml.PauliZ(2), - qml.PauliX(0) @ qml.PauliZ(3), - qml.PauliZ(1) @ qml.PauliZ(2), - qml.PauliZ(1) @ qml.PauliZ(3), - ], + ( + qml.Hamiltonian([0.5, 0.25], [qml.PauliX(0) @ qml.PauliX(1), qml.PauliZ(0)]), + qml.Hamiltonian([1, 1], [qml.PauliX(3) @ qml.PauliZ(2), qml.PauliZ(2)]), + qml.Hamiltonian( + [0.5, 0.5, 0.25, 0.25], + [ + qml.PauliX(0) @ qml.PauliX(1) @ qml.PauliX(3) @ qml.PauliZ(2), + qml.PauliX(0) @ qml.PauliX(1) @ qml.PauliZ(2), + qml.PauliZ(0) @ qml.PauliX(3) @ qml.PauliZ(2), + qml.PauliZ(0) @ qml.PauliZ(2), + ], + ), ), - ), - ( - qml.Hamiltonian([1, 1], [qml.PauliX(3) @ qml.PauliZ(2), qml.PauliZ(2)]), - qml.Hamiltonian([0.5, 0.25], [qml.PauliX(0) @ qml.PauliX(1), qml.PauliZ(0)]), - qml.Hamiltonian( - [0.5, 0.5, 0.25, 0.25], - [ - qml.PauliX(0) @ qml.PauliX(1) @ qml.PauliX(3) @ qml.PauliZ(2), - qml.PauliX(0) @ qml.PauliX(1) @ qml.PauliZ(2), - qml.PauliZ(0) @ qml.PauliX(3) @ qml.PauliZ(2), - qml.PauliZ(0) @ qml.PauliZ(2), - ], + ( + qml.Hamiltonian( + [1, 1], [qml.PauliX("b"), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)] + ), + qml.Hamiltonian([2, 2], [qml.PauliZ(1.2), qml.PauliY("c")]), + qml.Hamiltonian( + [2, 2, 2, 2], + [ + qml.PauliX("b") @ qml.PauliZ(1.2), + qml.PauliX("b") @ qml.PauliY("c"), + qml.Hermitian(np.array([[1, 0], [0, -1]]), 0) @ qml.PauliZ(1.2), + qml.Hermitian(np.array([[1, 0], [0, -1]]), 0) @ qml.PauliY("c"), + ], + ), ), - ), - ( - qml.Hamiltonian([2, 2], [qml.PauliZ(1.2), qml.PauliY("c")]), - qml.Hamiltonian( - [1, 1], [qml.PauliX("b"), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)] + ( + qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliZ(1)]), + qml.PauliX(2), + qml.Hamiltonian( + [1, 1], [qml.PauliX(0) @ qml.PauliX(2), qml.PauliZ(1) @ qml.PauliX(2)] + ), ), - qml.Hamiltonian( - [2, 2, 2, 2], - [ - qml.PauliX("b") @ qml.PauliZ(1.2), - qml.PauliX("b") @ qml.PauliY("c"), - qml.Hermitian(np.array([[1, 0], [0, -1]]), 0) @ qml.PauliZ(1.2), - qml.Hermitian(np.array([[1, 0], [0, -1]]), 0) @ qml.PauliY("c"), - ], + # Case where arguments coeffs and ops to the Hamiltonian are iterables other than lists + ( + qml.Hamiltonian((1, 1), (qml.PauliX(0), qml.PauliZ(1))), + qml.Hamiltonian(np.array([0.5, 0.5]), np.array([qml.PauliZ(2), qml.PauliZ(3)])), + qml.Hamiltonian( + (0.5, 0.5, 0.5, 0.5), + np.array( + [ + qml.PauliX(0) @ qml.PauliZ(2), + qml.PauliX(0) @ qml.PauliZ(3), + qml.PauliZ(1) @ qml.PauliZ(2), + qml.PauliZ(1) @ qml.PauliZ(3), + ] + ), + ), ), - ), - ( - qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliZ(1)]), - qml.PauliX(2), - qml.Hamiltonian([1, 1], [qml.PauliX(2) @ qml.PauliX(0), qml.PauliX(2) @ qml.PauliZ(1)]), - ), - # Case where arguments coeffs and ops to the Hamiltonian are iterables other than lists - ( - qml.Hamiltonian(np.array([0.5, 0.5]), np.array([qml.PauliZ(2), qml.PauliZ(3)])), - qml.Hamiltonian((1, 1), (qml.PauliX(0), qml.PauliZ(1))), - qml.Hamiltonian( - (0.5, 0.5, 0.5, 0.5), - np.array( + ] + + rmatmul_hamiltonians = [ + ( + qml.Hamiltonian([0.5, 0.5], [qml.PauliZ(2), qml.PauliZ(3)]), + qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliZ(1)]), + qml.Hamiltonian( + [0.5, 0.5, 0.5, 0.5], [ qml.PauliX(0) @ qml.PauliZ(2), qml.PauliX(0) @ qml.PauliZ(3), qml.PauliZ(1) @ qml.PauliZ(2), qml.PauliZ(1) @ qml.PauliZ(3), - ] + ], + ), + ), + ( + qml.Hamiltonian([1, 1], [qml.PauliX(3) @ qml.PauliZ(2), qml.PauliZ(2)]), + qml.Hamiltonian([0.5, 0.25], [qml.PauliX(0) @ qml.PauliX(1), qml.PauliZ(0)]), + qml.Hamiltonian( + [0.5, 0.5, 0.25, 0.25], + [ + qml.PauliX(0) @ qml.PauliX(1) @ qml.PauliX(3) @ qml.PauliZ(2), + qml.PauliX(0) @ qml.PauliX(1) @ qml.PauliZ(2), + qml.PauliZ(0) @ qml.PauliX(3) @ qml.PauliZ(2), + qml.PauliZ(0) @ qml.PauliZ(2), + ], + ), + ), + ( + qml.Hamiltonian([2, 2], [qml.PauliZ(1.2), qml.PauliY("c")]), + qml.Hamiltonian( + [1, 1], [qml.PauliX("b"), qml.Hermitian(np.array([[1, 0], [0, -1]]), 0)] + ), + qml.Hamiltonian( + [2, 2, 2, 2], + [ + qml.PauliX("b") @ qml.PauliZ(1.2), + qml.PauliX("b") @ qml.PauliY("c"), + qml.Hermitian(np.array([[1, 0], [0, -1]]), 0) @ qml.PauliZ(1.2), + qml.Hermitian(np.array([[1, 0], [0, -1]]), 0) @ qml.PauliY("c"), + ], + ), + ), + ( + qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliZ(1)]), + qml.PauliX(2), + qml.Hamiltonian( + [1, 1], [qml.PauliX(2) @ qml.PauliX(0), qml.PauliX(2) @ qml.PauliZ(1)] + ), + ), + # Case where arguments coeffs and ops to the Hamiltonian are iterables other than lists + ( + qml.Hamiltonian(np.array([0.5, 0.5]), np.array([qml.PauliZ(2), qml.PauliZ(3)])), + qml.Hamiltonian((1, 1), (qml.PauliX(0), qml.PauliZ(1))), + qml.Hamiltonian( + (0.5, 0.5, 0.5, 0.5), + np.array( + [ + qml.PauliX(0) @ qml.PauliZ(2), + qml.PauliX(0) @ qml.PauliZ(3), + qml.PauliZ(1) @ qml.PauliZ(2), + qml.PauliZ(1) @ qml.PauliZ(3), + ] + ), ), ), - ), - ] - - big_hamiltonian_coeffs = np.array( - [ - -0.04207898, - 0.17771287, - 0.17771287, - -0.24274281, - -0.24274281, - 0.17059738, - 0.04475014, - -0.04475014, - -0.04475014, - 0.04475014, - 0.12293305, - 0.16768319, - 0.16768319, - 0.12293305, - 0.17627641, ] - ) - big_hamiltonian_ops = [ - qml.Identity(wires=[0]), - qml.PauliZ(wires=[0]), - qml.PauliZ(wires=[1]), - qml.PauliZ(wires=[2]), - qml.PauliZ(wires=[3]), - qml.PauliZ(wires=[0]) @ qml.PauliZ(wires=[1]), - qml.PauliY(wires=[0]) - @ qml.PauliX(wires=[1]) - @ qml.PauliX(wires=[2]) - @ qml.PauliY(wires=[3]), - qml.PauliY(wires=[0]) - @ qml.PauliY(wires=[1]) - @ qml.PauliX(wires=[2]) - @ qml.PauliX(wires=[3]), - qml.PauliX(wires=[0]) - @ qml.PauliX(wires=[1]) - @ qml.PauliY(wires=[2]) - @ qml.PauliY(wires=[3]), - qml.PauliX(wires=[0]) - @ qml.PauliY(wires=[1]) - @ qml.PauliY(wires=[2]) - @ qml.PauliX(wires=[3]), - qml.PauliZ(wires=[0]) @ qml.PauliZ(wires=[2]), - qml.PauliZ(wires=[0]) @ qml.PauliZ(wires=[3]), - qml.PauliZ(wires=[1]) @ qml.PauliZ(wires=[2]), - qml.PauliZ(wires=[1]) @ qml.PauliZ(wires=[3]), - qml.PauliZ(wires=[2]) @ qml.PauliZ(wires=[3]), - ] + big_hamiltonian_coeffs = np.array( + [ + -0.04207898, + 0.17771287, + 0.17771287, + -0.24274281, + -0.24274281, + 0.17059738, + 0.04475014, + -0.04475014, + -0.04475014, + 0.04475014, + 0.12293305, + 0.16768319, + 0.16768319, + 0.12293305, + 0.17627641, + ] + ) - big_hamiltonian = qml.Hamiltonian(big_hamiltonian_coeffs, big_hamiltonian_ops) + big_hamiltonian_ops = [ + qml.Identity(wires=[0]), + qml.PauliZ(wires=[0]), + qml.PauliZ(wires=[1]), + qml.PauliZ(wires=[2]), + qml.PauliZ(wires=[3]), + qml.PauliZ(wires=[0]) @ qml.PauliZ(wires=[1]), + qml.PauliY(wires=[0]) + @ qml.PauliX(wires=[1]) + @ qml.PauliX(wires=[2]) + @ qml.PauliY(wires=[3]), + qml.PauliY(wires=[0]) + @ qml.PauliY(wires=[1]) + @ qml.PauliX(wires=[2]) + @ qml.PauliX(wires=[3]), + qml.PauliX(wires=[0]) + @ qml.PauliX(wires=[1]) + @ qml.PauliY(wires=[2]) + @ qml.PauliY(wires=[3]), + qml.PauliX(wires=[0]) + @ qml.PauliY(wires=[1]) + @ qml.PauliY(wires=[2]) + @ qml.PauliX(wires=[3]), + qml.PauliZ(wires=[0]) @ qml.PauliZ(wires=[2]), + qml.PauliZ(wires=[0]) @ qml.PauliZ(wires=[3]), + qml.PauliZ(wires=[1]) @ qml.PauliZ(wires=[2]), + qml.PauliZ(wires=[1]) @ qml.PauliZ(wires=[3]), + qml.PauliZ(wires=[2]) @ qml.PauliZ(wires=[3]), + ] - big_hamiltonian_grad = ( - np.array( - [ - [ - [6.52084595e-18, -2.11464420e-02, -1.16576858e-02], - [-8.22589330e-18, -5.20597922e-02, -1.85365365e-02], - [-2.73850768e-17, 1.14202988e-01, -5.45041403e-03], - [-1.27514307e-17, -1.10465531e-01, 5.19489457e-02], - ], + big_hamiltonian = qml.Hamiltonian(big_hamiltonian_coeffs, big_hamiltonian_ops) + + big_hamiltonian_grad = ( + np.array( [ - [-2.45428288e-02, 8.38921555e-02, -2.00641818e-17], - [-2.21085973e-02, 7.39332741e-04, -1.25580654e-17], - [9.62058625e-03, -1.51398765e-01, 2.02129847e-03], - [1.10020832e-03, -3.49066271e-01, 2.13669117e-03], - ], - ] - ), - ) + [ + [6.52084595e-18, -2.11464420e-02, -1.16576858e-02], + [-8.22589330e-18, -5.20597922e-02, -1.85365365e-02], + [-2.73850768e-17, 1.14202988e-01, -5.45041403e-03], + [-1.27514307e-17, -1.10465531e-01, 5.19489457e-02], + ], + [ + [-2.45428288e-02, 8.38921555e-02, -2.00641818e-17], + [-2.21085973e-02, 7.39332741e-04, -1.25580654e-17], + [9.62058625e-03, -1.51398765e-01, 2.02129847e-03], + [1.10020832e-03, -3.49066271e-01, 2.13669117e-03], + ], + ] + ), + ) def circuit1(param): @@ -688,22 +736,16 @@ def test_matmul_queuing(): @pytest.mark.usefixtures("use_legacy_and_new_opmath") -def test_deprecation_with_new_opmath(recwarn): - """Test that a warning is raised if attempting to create a Hamiltonian with new operator - arithmetic enabled.""" - if qml.operation.active_new_opmath(): - with pytest.warns( - qml.PennyLaneDeprecationWarning, - match="Using 'qml.ops.Hamiltonian' with new operator arithmetic is deprecated", - ): - _ = qml.ops.Hamiltonian([1.0], [qml.X(0)]) - - else: - _ = qml.Hamiltonian([1.0], [qml.X(0)]) - assert len(recwarn) == 0 +def test_deprecation(): + """Test that a warning is raised if attempting to create a legacy Hamiltonian.""" + with pytest.warns( + qml.PennyLaneDeprecationWarning, + match="qml.ops.Hamiltonian uses the old approach", + ): + _ = qml.ops.Hamiltonian([1.0], [qml.X(0)]) -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") class TestHamiltonian: """Test the Hamiltonian class""" @@ -1108,7 +1150,7 @@ def test_hamiltonian_no_pauli_rep(self): assert h.pauli_rep is None -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") class TestHamiltonianCoefficients: """Test the creation of a Hamiltonian""" @@ -1130,7 +1172,7 @@ def test_simplify(self, coeffs): @pytest.mark.tf -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") class TestHamiltonianArithmeticTF: """Tests creation of Hamiltonians using arithmetic operations with TensorFlow tensor coefficients.""" @@ -1203,7 +1245,7 @@ def test_hamiltonian_matmul(self): assert H.compare(H1 @ H2) -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") class TestHamiltonianArithmeticTorch: """Tests creation of Hamiltonians using arithmetic operations with torch tensor coefficients.""" @@ -1280,7 +1322,7 @@ def test_hamiltonian_matmul(self): assert H.compare(H1 @ H2) -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") class TestHamiltonianArithmeticAutograd: """Tests creation of Hamiltonians using arithmetic operations with autograd tensor coefficients.""" @@ -1357,7 +1399,7 @@ def test_hamiltonian_matmul(self): assert H.compare(H1 @ H2) -with qml.operation.disable_new_opmath_cm(): +with qml.operation.disable_new_opmath_cm(warn=False): TEST_SPARSE_MATRIX = [ ( [1, -0.45], @@ -1481,7 +1523,7 @@ def test_hamiltonian_matmul(self): ] -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") class TestHamiltonianSparseMatrix: """Tests for sparse matrix representation.""" @@ -1519,7 +1561,7 @@ def test_observable_error(self): @pytest.mark.jax -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") class TestHamiltonianArithmeticJax: """Tests creation of Hamiltonians using arithmetic operations with jax tensor coefficients.""" @@ -1593,7 +1635,7 @@ def test_hamiltonian_matmul(self): assert H.compare(H1 @ H2) -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") class TestGrouping: """Tests for the grouping functionality""" @@ -1700,7 +1742,7 @@ def test_grouping_method_can_be_set(self): assert set(H3.grouping_indices) == set(((0, 1), (2,))) -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") class TestHamiltonianEvaluation: """Test the usage of a Hamiltonian as an observable""" @@ -1750,7 +1792,7 @@ def circuit(): assert pars == [0.1, 3.0] -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") class TestHamiltonianDifferentiation: """Test that the Hamiltonian coefficients are differentiable""" diff --git a/tests/ops/qubit/test_parametric_ops.py b/tests/ops/qubit/test_parametric_ops.py index c33eaa8fe32..f9fe0e6d9ab 100644 --- a/tests/ops/qubit/test_parametric_ops.py +++ b/tests/ops/qubit/test_parametric_ops.py @@ -3054,7 +3054,7 @@ def test_pauli_rot_identity_torch(self, torch_device, theta): exp = torch.tensor(np.diag([val, val]), device=torch_device) assert qml.math.allclose(mat, exp) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_pauli_rot_generator_legacy_opmath(self): """Test that the generator of the PauliRot operation is correctly returned.""" @@ -3066,7 +3066,7 @@ def test_pauli_rot_generator_legacy_opmath(self): assert gen.operands[0].name == expected.obs[0].name assert gen.operands[1].wires == expected.obs[1].wires - @pytest.mark.usefixtures("use_new_opmath") + @pytest.mark.usefixtures("new_opmath_only") def test_pauli_rot_generator(self): """Test that the generator of the PauliRot operation is correctly returned.""" diff --git a/tests/ops/qutrit/test_qutrit_observables.py b/tests/ops/qutrit/test_qutrit_observables.py index de7f4915a96..be2ac895869 100644 --- a/tests/ops/qutrit/test_qutrit_observables.py +++ b/tests/ops/qutrit/test_qutrit_observables.py @@ -372,7 +372,7 @@ def test_matrix(self, index, mat, eigs, tol): assert np.allclose(res_static, mat) assert np.allclose(res_dynamic, mat) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_obs_data(self): """Test that the _obs_data() method of qml.GellMann returns the correct observable data.""" diff --git a/tests/optimize/test_momentum_qng.py b/tests/optimize/test_momentum_qng.py index 663c72ffcf6..75482f8ed31 100644 --- a/tests/optimize/test_momentum_qng.py +++ b/tests/optimize/test_momentum_qng.py @@ -333,4 +333,5 @@ def gradient(params): # check final cost assert np.allclose(circuit(x, y), qml.eigvals(H).min(), atol=tol, rtol=0) - assert len(recwarn) == 0 + if qml.operation.active_new_opmath(): + assert len(recwarn) == 0 diff --git a/tests/optimize/test_qng.py b/tests/optimize/test_qng.py index fc3f35e2129..6ab7838528a 100644 --- a/tests/optimize/test_qng.py +++ b/tests/optimize/test_qng.py @@ -308,4 +308,5 @@ def gradient(params): # check final cost assert np.allclose(circuit(x, y), qml.eigvals(H).min(), atol=tol, rtol=0) - assert len(recwarn) == 0 + if qml.operation.active_new_opmath(): + assert len(recwarn) == 0 diff --git a/tests/optimize/test_spsa.py b/tests/optimize/test_spsa.py index f0422602ffd..a8d8289c379 100644 --- a/tests/optimize/test_spsa.py +++ b/tests/optimize/test_spsa.py @@ -441,43 +441,6 @@ def cost(params): ): opt.step(cost, params) - @pytest.mark.usefixtures("use_legacy_opmath") - @pytest.mark.slow - def test_lightning_device_legacy_opmath(self): - """Test SPSAOptimizer implementation with lightning.qubit device.""" - coeffs = [0.2, -0.543, 0.4514] - obs = [ - qml.PauliX(0) @ qml.PauliZ(1), - qml.PauliZ(0) @ qml.Hadamard(2), - qml.PauliX(3) @ qml.PauliZ(1), - ] - H = qml.Hamiltonian(coeffs, obs) - num_qubits = 4 - dev = qml.device("lightning.qubit", wires=num_qubits) - - @qml.qnode(dev) - def cost_fun(params, num_qubits=1): - qml.BasisState([1, 1, 0, 0], wires=range(num_qubits)) - for i in range(num_qubits): - qml.Rot(*params[i], wires=0) - qml.CNOT(wires=[2, 3]) - qml.CNOT(wires=[2, 0]) - qml.CNOT(wires=[3, 1]) - return qml.expval(H) - - init_params = np.random.normal(0, np.pi, (num_qubits, 3), requires_grad=True) - params = init_params - - init_energy = cost_fun(init_params, num_qubits) - - max_iterations = 100 - opt = qml.SPSAOptimizer(maxiter=max_iterations) - for _ in range(max_iterations): - params, energy = opt.step_and_cost(cost_fun, params, num_qubits=num_qubits) - - assert np.all(params != init_params) - assert energy < init_energy - @pytest.mark.slow def test_lightning_device(self): """Test SPSAOptimizer implementation with lightning.qubit device.""" diff --git a/tests/pauli/grouping/test_pauli_group_observables.py b/tests/pauli/grouping/test_pauli_group_observables.py index b70f8425018..c36dd7e94ff 100644 --- a/tests/pauli/grouping/test_pauli_group_observables.py +++ b/tests/pauli/grouping/test_pauli_group_observables.py @@ -459,7 +459,7 @@ def test_return_list_coefficients(self): _, grouped_coeffs = group_observables(obs, coeffs) assert isinstance(grouped_coeffs[0], list) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_return_new_opmath_legacy_opmath(self): """Test that using new opmath causes grouped observables to have Prods instead of Tensors""" @@ -473,7 +473,7 @@ def test_return_new_opmath_legacy_opmath(self): assert all(isinstance(o, Tensor) for g in old_groups for o in g) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_return_deactive_opmath_prod(self): """Test that using new opmath causes grouped observables to have Prods instead of Tensors""" diff --git a/tests/pauli/test_conversion.py b/tests/pauli/test_conversion.py index 1e009aed642..5e8267104b8 100644 --- a/tests/pauli/test_conversion.py +++ b/tests/pauli/test_conversion.py @@ -46,7 +46,7 @@ test_diff_matrix2 = [[[-2, -2 + 1j], [-2 - 1j, 0]], [[2.5, -0.5], [-0.5, 2.5]]] with warnings.catch_warnings(): - warnings.simplefilter("ignore") + warnings.filterwarnings("ignore", "qml.ops.Hamiltonian uses", qml.PennyLaneDeprecationWarning) hamiltonian_ps = ( ( qml.ops.Hamiltonian([], []), @@ -132,7 +132,7 @@ def test_hide_identity_true_all_identities(self): for tensor in tensors: assert all(isinstance(o, Identity) for o in tensor.obs) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") @pytest.mark.parametrize("hide_identity", [True, False]) @pytest.mark.parametrize("hamiltonian", test_hamiltonians) def test_observable_types_legacy_opmath(self, hamiltonian, hide_identity): @@ -142,7 +142,7 @@ def test_observable_types_legacy_opmath(self, hamiltonian, hide_identity): _, decomposed_obs = qml.pauli_decompose(hamiltonian, hide_identity).terms() assert all((isinstance(o, allowed_obs) for o in decomposed_obs)) - @pytest.mark.usefixtures("use_new_opmath") + @pytest.mark.usefixtures("new_opmath_only") @pytest.mark.parametrize("hide_identity", [True, False]) @pytest.mark.parametrize("hamiltonian", test_hamiltonians) def test_observable_types(self, hamiltonian, hide_identity): @@ -275,7 +275,7 @@ def test_hide_identity_true_all_identities(self): for tensor in tensors: assert all(isinstance(o, Identity) for o in tensor.obs) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") @pytest.mark.parametrize("hide_identity", [True, False]) @pytest.mark.parametrize("hamiltonian", test_hamiltonians) def test_observable_types_legacy_opmath(self, hamiltonian, hide_identity): @@ -288,7 +288,7 @@ def test_observable_types_legacy_opmath(self, hamiltonian, hide_identity): ).terms() assert all((isinstance(o, allowed_obs) for o in decomposed_obs)) - @pytest.mark.usefixtures("use_new_opmath") + @pytest.mark.usefixtures("new_opmath_only") @pytest.mark.parametrize("hide_identity", [True, False]) @pytest.mark.parametrize("hamiltonian", test_hamiltonians) def test_observable_types(self, hamiltonian, hide_identity): @@ -339,7 +339,7 @@ def test_to_paulisentence(self, hamiltonian): assert np.allclose(hamiltonian, ps.to_mat(range(num_qubits))) # pylint: disable = consider-using-generator - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") @pytest.mark.parametrize("hide_identity", [True, False]) @pytest.mark.parametrize("matrix", test_general_matrix) def test_observable_types_general_legacy_opmath(self, matrix, hide_identity): @@ -368,7 +368,7 @@ def test_observable_types_general_legacy_opmath(self, matrix, hide_identity): assert all(len(tensor.obs) == num_qubits for tensor in tensors) # pylint: disable = consider-using-generator - @pytest.mark.usefixtures("use_new_opmath") + @pytest.mark.usefixtures("new_opmath_only") @pytest.mark.parametrize("hide_identity", [True, False]) @pytest.mark.parametrize("matrix", test_general_matrix) def test_observable_types_general(self, matrix, hide_identity): @@ -553,6 +553,9 @@ def test_tensor_raises_error(self): with pytest.raises(ValueError, match="Op must be a linear combination of"): pauli_sentence(op) + @pytest.mark.filterwarnings( + "ignore:qml.ops.Hamiltonian uses:pennylane.PennyLaneDeprecationWarning" + ) @pytest.mark.usefixtures("use_legacy_and_new_opmath") @pytest.mark.parametrize("op, ps", hamiltonian_ps) def test_hamiltonian(self, op, ps): diff --git a/tests/pauli/test_pauli_arithmetic.py b/tests/pauli/test_pauli_arithmetic.py index 120be030c47..6aee89c5dc9 100644 --- a/tests/pauli/test_pauli_arithmetic.py +++ b/tests/pauli/test_pauli_arithmetic.py @@ -450,7 +450,7 @@ def test_operation_empty_nowires(self): ), ) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") @pytest.mark.parametrize("pw, h", tup_pw_hamiltonian) def test_hamiltonian(self, pw, h): """Test that a PauliWord can be cast to a Hamiltonian.""" @@ -458,20 +458,25 @@ def test_hamiltonian(self, pw, h): h = qml.operation.convert_to_legacy_H(h) assert pw_h.compare(h) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_hamiltonian_empty(self): """Test that an empty PauliWord with wire_order returns Identity Hamiltonian.""" op = PauliWord({}).hamiltonian(wire_order=[0, 1]) id = qml.Hamiltonian([1], [qml.Identity(wires=[0, 1])]) assert op.compare(id) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_hamiltonian_empty_error(self): """Test that a ValueError is raised if an empty PauliWord is cast to a Hamiltonian.""" with pytest.raises(ValueError, match="Can't get the Hamiltonian for an empty PauliWord."): pw4.hamiltonian() + def test_hamiltonian_deprecation(self): + """Test that the correct deprecation warning is raised when calling hamiltonian()""" + with pytest.warns(qml.PennyLaneDeprecationWarning, match="PauliWord.hamiltonian"): + _ = pw1.hamiltonian() + def test_pickling(self): """Check that pauliwords can be pickled and unpickled.""" pw = PauliWord({2: "X", 3: "Y", 4: "Z"}) @@ -1039,7 +1044,7 @@ def test_operation_wire_order(self): ), ) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") @pytest.mark.parametrize("ps, h", tup_ps_hamiltonian) def test_hamiltonian(self, ps, h): """Test that a PauliSentence can be cast to a Hamiltonian.""" @@ -1047,14 +1052,14 @@ def test_hamiltonian(self, ps, h): h = qml.operation.convert_to_legacy_H(h) assert ps_h.compare(h) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_hamiltonian_empty(self): """Test that an empty PauliSentence with wire_order returns Identity.""" op = ps5.hamiltonian(wire_order=[0, 1]) id = qml.Hamiltonian([], []) assert op.compare(id) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_hamiltonian_empty_error(self): """Test that a ValueError is raised if an empty PauliSentence is cast to a Hamiltonian.""" @@ -1063,7 +1068,7 @@ def test_hamiltonian_empty_error(self): ): ps5.hamiltonian() - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_hamiltonian_wire_order(self): """Test that the wire_order parameter is used when the pauli representation is empty""" op = ps5.hamiltonian(wire_order=["a", "b"]) @@ -1071,6 +1076,11 @@ def test_hamiltonian_wire_order(self): qml.assert_equal(op, id) + def test_hamiltonian_deprecation(self): + """Test that the correct deprecation warning is raised when calling hamiltonian()""" + with pytest.warns(qml.PennyLaneDeprecationWarning, match="PauliSentence.hamiltonian"): + _ = ps1.hamiltonian() + def test_pickling(self): """Check that paulisentences can be pickled and unpickled.""" word1 = PauliWord({2: "X", 3: "Y", 4: "Z"}) @@ -1416,7 +1426,9 @@ def test_pauli_word_private_commutator(self, op1, op2, true_word, true_coeff): @pytest.mark.parametrize("convert1", [_id, _pw_to_ps]) @pytest.mark.parametrize("convert2", [_id, _pw_to_ps]) @pytest.mark.parametrize("op1, op2, true_res", data_pauli_relations_different_types) - def test_pauli_word_comm_different_types(self, op1, op2, true_res, convert1, convert2): + def test_pauli_word_comm_different_types( + self, op1, op2, true_res, convert1, convert2 + ): # pylint: disable=too-many-positional-arguments """Test native comm in between a PauliSentence and either of PauliWord, PauliSentence, Operator""" op1 = convert1(op1) op2 = convert2(op2) @@ -1429,7 +1441,9 @@ def test_pauli_word_comm_different_types(self, op1, op2, true_res, convert1, con @pytest.mark.parametrize("convert1", [_id, _pw_to_ps]) @pytest.mark.parametrize("convert2", [_pauli_to_op]) @pytest.mark.parametrize("op1, op2, true_res", data_pauli_relations_different_types) - def test_pauli_word_comm_different_types_with_ops(self, op1, op2, true_res, convert1, convert2): + def test_pauli_word_comm_different_types_with_ops( + self, op1, op2, true_res, convert1, convert2 + ): # pylint: disable=too-many-positional-arguments """Test native comm in between a PauliWord, PauliSentence and Operator""" op1 = convert1(op1) op2 = convert2(op2) diff --git a/tests/pauli/test_pauli_utils.py b/tests/pauli/test_pauli_utils.py index 84bc4d91590..cfba6dc1195 100644 --- a/tests/pauli/test_pauli_utils.py +++ b/tests/pauli/test_pauli_utils.py @@ -14,6 +14,8 @@ """ Unit tests for the :mod:`pauli` utility functions in ``pauli/utils.py``. """ +import warnings + # pylint: disable=too-few-public-methods,too-many-public-methods import numpy as np import pytest @@ -224,7 +226,7 @@ def test_observables_to_binary_matrix_n_qubits_arg(self): ValueError, observables_to_binary_matrix, observables, n_qubits_invalid ) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_is_qwc(self): """Determining if two Pauli words are qubit-wise commuting.""" @@ -370,7 +372,7 @@ def test_are_identical_pauli_words(self): assert not are_identical_pauli_words(pauli_word_7, pauli_word_4) assert not are_identical_pauli_words(pauli_word_6, pauli_word_4) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_are_identical_pauli_words_hamiltonian_unsupported(self): """Test that using Hamiltonians that are valid Pauli words with are_identical_pauli_words always returns False""" @@ -455,10 +457,14 @@ def test_pauli_word_to_string_tensor(self): op = qml.operation.Tensor(qml.Z(0), qml.Y(1), qml.X(2)) assert pauli_word_to_string(op) == "ZYX" - with qml.operation.disable_new_opmath_cm(): - PAULI_WORD_STRINGS_LEGACY = _make_pauli_word_strings() + with qml.operation.disable_new_opmath_cm(warn=False): + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", "qml.ops.Hamiltonian uses", qml.PennyLaneDeprecationWarning + ) + PAULI_WORD_STRINGS_LEGACY = _make_pauli_word_strings() - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") @pytest.mark.parametrize("pauli_word,wire_map,expected_string", PAULI_WORD_STRINGS_LEGACY) def test_pauli_word_to_string_legacy_opmath(self, pauli_word, wire_map, expected_string): """Test that Pauli words are correctly converted into strings.""" @@ -471,7 +477,7 @@ def test_pauli_word_to_string_invalid_input(self, non_pauli_word): with pytest.raises(TypeError): pauli_word_to_string(non_pauli_word) - @pytest.mark.usefixtures("use_new_opmath") + @pytest.mark.usefixtures("new_opmath_only") @pytest.mark.parametrize( "pauli_string,wire_map,expected_pauli", [ @@ -793,21 +799,7 @@ def test_scaling(self, n): """Test if the number of groups is equal to 3**n""" assert len(partition_pauli_group(n)) == 3**n - @pytest.mark.usefixtures("use_legacy_opmath") @pytest.mark.parametrize("n", range(1, 6)) - def test_is_qwc_legacy_opmath(self, n): - """Test if each group contains only qubit-wise commuting terms""" - for group in partition_pauli_group(n): - size = len(group) - for i in range(size): - for j in range(i, size): - s1 = group[i] - s2 = group[j] - w1 = string_to_pauli_word(s1) - w2 = string_to_pauli_word(s2) - assert is_commuting(w1, w2) - - @pytest.mark.parametrize("n", range(2, 6)) def test_is_qwc(self, n): """Test if each group contains only qubit-wise commuting terms""" for group in partition_pauli_group(n): @@ -999,7 +991,7 @@ def test_diagonalize_qwc_pauli_words_catch_when_not_qwc(self, not_qwc_grouping): assert pytest.raises(ValueError, diagonalize_qwc_pauli_words, not_qwc_grouping) @pytest.mark.usefixtures( - "use_legacy_opmath" + "legacy_opmath_only" ) # Handling a LinearCombination is not a problem under new opmath anymore def test_diagonalize_qwc_pauli_words_catch_invalid_type(self): """Test for ValueError raise when diagonalize_qwc_pauli_words is given a list @@ -1012,7 +1004,7 @@ def test_diagonalize_qwc_pauli_words_catch_invalid_type(self): class TestObservableHF: - with qml.operation.disable_new_opmath_cm(): + with qml.operation.disable_new_opmath_cm(warn=False): HAMILTONIAN_SIMPLIFY = [ ( qml.Hamiltonian( @@ -1052,82 +1044,90 @@ class TestObservableHF: ), ] - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") @pytest.mark.parametrize(("hamiltonian", "result"), HAMILTONIAN_SIMPLIFY) def test_simplify(self, hamiltonian, result): r"""Test that simplify returns the correct hamiltonian.""" h = simplify(hamiltonian) assert h.compare(result) + def test_simplify_deprecation(self): + """Test that a deprecation warning is raised when using simplify""" + with pytest.warns(qml.PennyLaneDeprecationWarning, match="qml.ops.Hamiltonian"): + h = qml.ops.Hamiltonian([1.5, 2.5], [qml.X(0), qml.Z(0)]) + + with pytest.warns(qml.PennyLaneDeprecationWarning, match="qml.pauli.simplify"): + _ = simplify(h) + +@pytest.mark.usefixtures("legacy_opmath_only") class TestTapering: - with qml.operation.disable_new_opmath_cm(): - terms_bin_mat_data = [ - ( + + terms_bin_mat_data = [ + ( + [ + qml.Identity(wires=[0]), + qml.PauliZ(wires=[0]), + qml.PauliZ(wires=[1]), + qml.PauliZ(wires=[2]), + qml.PauliZ(wires=[3]), + qml.PauliZ(wires=[0]) @ qml.PauliZ(wires=[1]), + qml.PauliY(wires=[0]) + @ qml.PauliX(wires=[1]) + @ qml.PauliX(wires=[2]) + @ qml.PauliY(wires=[3]), + qml.PauliY(wires=[0]) + @ qml.PauliY(wires=[1]) + @ qml.PauliX(wires=[2]) + @ qml.PauliX(wires=[3]), + qml.PauliX(wires=[0]) + @ qml.PauliX(wires=[1]) + @ qml.PauliY(wires=[2]) + @ qml.PauliY(wires=[3]), + qml.PauliX(wires=[0]) + @ qml.PauliY(wires=[1]) + @ qml.PauliY(wires=[2]) + @ qml.PauliX(wires=[3]), + qml.PauliZ(wires=[0]) @ qml.PauliZ(wires=[2]), + qml.PauliZ(wires=[0]) @ qml.PauliZ(wires=[3]), + qml.PauliZ(wires=[1]) @ qml.PauliZ(wires=[2]), + qml.PauliZ(wires=[1]) @ qml.PauliZ(wires=[3]), + qml.PauliZ(wires=[2]) @ qml.PauliZ(wires=[3]), + ], + 4, + np.array( [ - qml.Identity(wires=[0]), - qml.PauliZ(wires=[0]), - qml.PauliZ(wires=[1]), - qml.PauliZ(wires=[2]), - qml.PauliZ(wires=[3]), - qml.PauliZ(wires=[0]) @ qml.PauliZ(wires=[1]), - qml.PauliY(wires=[0]) - @ qml.PauliX(wires=[1]) - @ qml.PauliX(wires=[2]) - @ qml.PauliY(wires=[3]), - qml.PauliY(wires=[0]) - @ qml.PauliY(wires=[1]) - @ qml.PauliX(wires=[2]) - @ qml.PauliX(wires=[3]), - qml.PauliX(wires=[0]) - @ qml.PauliX(wires=[1]) - @ qml.PauliY(wires=[2]) - @ qml.PauliY(wires=[3]), - qml.PauliX(wires=[0]) - @ qml.PauliY(wires=[1]) - @ qml.PauliY(wires=[2]) - @ qml.PauliX(wires=[3]), - qml.PauliZ(wires=[0]) @ qml.PauliZ(wires=[2]), - qml.PauliZ(wires=[0]) @ qml.PauliZ(wires=[3]), - qml.PauliZ(wires=[1]) @ qml.PauliZ(wires=[2]), - qml.PauliZ(wires=[1]) @ qml.PauliZ(wires=[3]), - qml.PauliZ(wires=[2]) @ qml.PauliZ(wires=[3]), - ], - 4, - np.array( - [ - [0, 0, 0, 0, 0, 0, 0, 0], - [1, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 1, 0, 0, 0, 0], - [1, 1, 0, 0, 0, 0, 0, 0], - [1, 0, 0, 1, 1, 1, 1, 1], - [1, 1, 0, 0, 1, 1, 1, 1], - [0, 0, 1, 1, 1, 1, 1, 1], - [0, 1, 1, 0, 1, 1, 1, 1], - [1, 0, 1, 0, 0, 0, 0, 0], - [1, 0, 0, 1, 0, 0, 0, 0], - [0, 1, 1, 0, 0, 0, 0, 0], - [0, 1, 0, 1, 0, 0, 0, 0], - [0, 0, 1, 1, 0, 0, 0, 0], - ] - ), + [0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 1, 1, 1, 1, 1], + [1, 1, 0, 0, 1, 1, 1, 1], + [0, 0, 1, 1, 1, 1, 1, 1], + [0, 1, 1, 0, 1, 1, 1, 1], + [1, 0, 1, 0, 0, 0, 0, 0], + [1, 0, 0, 1, 0, 0, 0, 0], + [0, 1, 1, 0, 0, 0, 0, 0], + [0, 1, 0, 1, 0, 0, 0, 0], + [0, 0, 1, 1, 0, 0, 0, 0], + ] ), - ( - [ - qml.PauliZ(wires=["a"]) @ qml.PauliX(wires=["b"]), - qml.PauliZ(wires=["a"]) @ qml.PauliY(wires=["c"]), - qml.PauliX(wires=["a"]) @ qml.PauliY(wires=["d"]), - ], - 4, - np.array( - [[1, 0, 0, 0, 0, 1, 0, 0], [1, 0, 1, 0, 0, 0, 1, 0], [0, 0, 0, 1, 1, 0, 0, 1]] - ), + ), + ( + [ + qml.PauliZ(wires=["a"]) @ qml.PauliX(wires=["b"]), + qml.PauliZ(wires=["a"]) @ qml.PauliY(wires=["c"]), + qml.PauliX(wires=["a"]) @ qml.PauliY(wires=["d"]), + ], + 4, + np.array( + [[1, 0, 0, 0, 0, 1, 0, 0], [1, 0, 1, 0, 0, 0, 1, 0], [0, 0, 0, 1, 1, 0, 0, 1]] ), - ] + ), + ] - @pytest.mark.usefixtures("use_legacy_opmath") @pytest.mark.parametrize(("terms", "num_qubits", "result"), terms_bin_mat_data) def test_binary_matrix_from_pws(self, terms, num_qubits, result): r"""Test that _binary_matrix_from_pws returns the correct result.""" diff --git a/tests/pytest.ini b/tests/pytest.ini index 1897c3c65e4..2cdb4b8ef8f 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -26,5 +26,10 @@ filterwarnings = ignore:Call to deprecated create function:DeprecationWarning ignore:the imp module is deprecated:DeprecationWarning error::pennylane.PennyLaneDeprecationWarning + ignore:qml.ops.Hamiltonian uses the old approach:pennylane.PennyLaneDeprecationWarning + ignore:qml.operation.Tensor uses the old approach:pennylane.PennyLaneDeprecationWarning + ignore:qml.pauli.simplify:pennylane.PennyLaneDeprecationWarning + ignore:PauliSentence.hamiltonian:pennylane.PennyLaneDeprecationWarning + ignore:PauliWord.hamiltonian:pennylane.PennyLaneDeprecationWarning addopts = --benchmark-disable xfail_strict=true diff --git a/tests/resource/test_specs.py b/tests/resource/test_specs.py index c3df9c9f936..00af26777f5 100644 --- a/tests/resource/test_specs.py +++ b/tests/resource/test_specs.py @@ -209,11 +209,11 @@ def circ(): specs = qml.specs(circ)() assert specs["resources"].num_gates == 1 - assert specs["num_diagonalizing_gates"] == 1 + assert specs["num_diagonalizing_gates"] == (1 if qml.operation.active_new_opmath() else 0) specs = qml.specs(circ, level="device")() assert specs["resources"].num_gates == 3 - assert specs["num_diagonalizing_gates"] == 3 + assert specs["num_diagonalizing_gates"] == (3 if qml.operation.active_new_opmath() else 0) def test_splitting_transforms(self): coeffs = [0.2, -0.543, 0.1] diff --git a/tests/shadow/test_shadow_class.py b/tests/shadow/test_shadow_class.py index aabec6012a0..e9fcb8ef90a 100644 --- a/tests/shadow/test_shadow_class.py +++ b/tests/shadow/test_shadow_class.py @@ -341,7 +341,6 @@ def test_max_entangled_expval(self): assert actual.dtype == np.float64 assert qml.math.allclose(actual, expected, atol=1e-1) - @pytest.mark.usefixtures("use_legacy_opmath") def test_non_pauli_error(self): """Test that an error is raised when a non-Pauli observable is passed""" circuit = hadamard_circuit(3) @@ -350,7 +349,11 @@ def test_non_pauli_error(self): H = qml.Hadamard(0) @ qml.Hadamard(2) - msg = "Observable must be a linear combination of Pauli observables" + msg = ( + "Observable must have a valid pauli representation" + if qml.operation.active_new_opmath() + else "Observable must be a linear combination of Pauli observables" + ) with pytest.raises(ValueError, match=msg): shadow.expval(H, k=10) diff --git a/tests/tape/test_tape.py b/tests/tape/test_tape.py index 734df641229..218a7dbf8f7 100644 --- a/tests/tape/test_tape.py +++ b/tests/tape/test_tape.py @@ -145,7 +145,7 @@ def test_tensor_observables_rmatmul(self): assert tape.measurements[0].return_type is qml.measurements.Expectation assert tape.measurements[0].obs is t_obs2 - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_tensor_observables_tensor_init(self): """Test that tensor observables are correctly processed from the annotated queue. Here, we test multiple tensor observables constructed via explicit diff --git a/tests/test_operation.py b/tests/test_operation.py index 7d14d85a951..25334ad1e89 100644 --- a/tests/test_operation.py +++ b/tests/test_operation.py @@ -16,6 +16,7 @@ """ import copy import itertools +import warnings from functools import reduce import numpy as np @@ -1356,7 +1357,16 @@ def test_label_for_operations_with_id(self): assert '"test_with_id"' not in op.label(decimals=2) -@pytest.mark.usefixtures("use_legacy_opmath") +# This test is outside the TestTensor class because that class only runs when legacy op math +# is enabled. We want this test to run in normal CI as well. +def test_tensor_deprecation(): + """Test that a deprecation warning is raised when initializing a Tensor""" + ops = [qml.Z(0), qml.Z(1)] + with pytest.warns(qml.PennyLaneDeprecationWarning, match="qml.operation.Tensor"): + _ = Tensor(*ops) + + +@pytest.mark.usefixtures("legacy_opmath_only") class TestTensor: """Unit tests for the Tensor class""" @@ -1854,7 +1864,7 @@ def test_multiplication_matrix(self, tol, classes): herm_matrix = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) - with qml.operation.disable_new_opmath_cm(): + with qml.operation.disable_new_opmath_cm(warn=False): tensor_obs = [ (qml.PauliZ(0) @ qml.Identity(1) @ qml.PauliZ(2), [qml.PauliZ(0), qml.PauliZ(2)]), ( @@ -1885,7 +1895,7 @@ def test_non_identity_obs(self, tensor_observable, expected): assert isinstance(obs, type(expected[idx])) assert obs.wires == expected[idx].wires - with qml.operation.disable_new_opmath_cm(): + with qml.operation.disable_new_opmath_cm(warn=False): tensor_obs_pruning = [ (qml.PauliZ(0) @ qml.Identity(1) @ qml.PauliZ(2), qml.PauliZ(0) @ qml.PauliZ(2)), ( @@ -2076,120 +2086,128 @@ def f(state): assert qml.math.allclose(res, expected) -with qml.operation.disable_new_opmath_cm(): - equal_obs = [ - (qml.PauliZ(0), qml.PauliZ(0), True), - (qml.PauliZ(0) @ qml.PauliX(1), qml.PauliZ(0) @ qml.PauliX(1) @ qml.Identity(2), True), - (qml.PauliZ("b"), qml.PauliZ("b") @ qml.Identity(1.3), True), - (qml.PauliZ(0) @ qml.Identity(1), qml.PauliZ(0), True), - (qml.PauliZ(0), qml.PauliZ(1) @ qml.Identity(0), False), - ( - qml.Hermitian(np.array([[0, 1], [1, 0]]), 0), - qml.Identity(1) @ qml.Hermitian(np.array([[0, 1], [1, 0]]), 0), - True, - ), - (qml.PauliZ("a") @ qml.PauliX(1), qml.PauliX(1) @ qml.PauliZ("a"), True), - (qml.PauliZ("a"), qml.Hamiltonian([1], [qml.PauliZ("a")]), True), - ] - - add_obs = [ - (qml.PauliZ(0) @ qml.Identity(1), qml.PauliZ(0), qml.Hamiltonian([2], [qml.PauliZ(0)])), - ( - qml.PauliZ(0), - qml.PauliZ(0) @ qml.PauliX(1), - qml.Hamiltonian([1, 1], [qml.PauliZ(0), qml.PauliZ(0) @ qml.PauliX(1)]), - ), - ( - qml.PauliZ("b") @ qml.Identity(1), - qml.Hamiltonian([3], [qml.PauliZ("b")]), - qml.Hamiltonian([4], [qml.PauliZ("b")]), - ), - ( - qml.PauliX(0) @ qml.PauliZ(1), - qml.PauliZ(1) @ qml.Identity(2) @ qml.PauliX(0), - qml.Hamiltonian([2], [qml.PauliX(0) @ qml.PauliZ(1)]), - ), - ( - qml.Hermitian(np.array([[1, 0], [0, -1]]), 1.2), - qml.Hamiltonian([3], [qml.Hermitian(np.array([[1, 0], [0, -1]]), 1.2)]), - qml.Hamiltonian([4], [qml.Hermitian(np.array([[1, 0], [0, -1]]), 1.2)]), - ), - ] +with qml.operation.disable_new_opmath_cm(warn=False): + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", "qml.ops.Hamiltonian uses", qml.PennyLaneDeprecationWarning + ) + warnings.filterwarnings( + "ignore", "qml.operation.Tensor uses", qml.PennyLaneDeprecationWarning + ) - add_zero_obs = [ - qml.PauliX(0), - qml.Hermitian(np.array([[1, 0], [0, -1]]), 1.2), - qml.PauliX(0) @ qml.Hadamard(2), - # qml.Projector(np.array([1, 1]), wires=[0, 1]), - # qml.SparseHamiltonian(csr_matrix(np.array([[1, 0], [-1.5, 0]])), 1), - # CVObservables - qml.Identity(1), - cv.NumberOperator(wires=[1]), - cv.TensorN(wires=[1]), - cv.QuadX(wires=[1]), - cv.QuadP(wires=[1]), - # cv.QuadOperator(1.234, wires=0), - # cv.FockStateProjector([1,2,3], wires=[0, 1, 2]), - cv.PolyXP(np.array([1.0, 2.0, 3.0]), wires=[0]), - ] + equal_obs = [ + (qml.PauliZ(0), qml.PauliZ(0), True), + (qml.PauliZ(0) @ qml.PauliX(1), qml.PauliZ(0) @ qml.PauliX(1) @ qml.Identity(2), True), + (qml.PauliZ("b"), qml.PauliZ("b") @ qml.Identity(1.3), True), + (qml.PauliZ(0) @ qml.Identity(1), qml.PauliZ(0), True), + (qml.PauliZ(0), qml.PauliZ(1) @ qml.Identity(0), False), + ( + qml.Hermitian(np.array([[0, 1], [1, 0]]), 0), + qml.Identity(1) @ qml.Hermitian(np.array([[0, 1], [1, 0]]), 0), + True, + ), + (qml.PauliZ("a") @ qml.PauliX(1), qml.PauliX(1) @ qml.PauliZ("a"), True), + (qml.PauliZ("a"), qml.Hamiltonian([1], [qml.PauliZ("a")]), True), + ] - mul_obs = [ - (qml.PauliZ(0), 3, qml.Hamiltonian([3], [qml.PauliZ(0)])), - (qml.PauliZ(0) @ qml.Identity(1), 3, qml.Hamiltonian([3], [qml.PauliZ(0)])), - ( - qml.PauliZ(0) @ qml.PauliX(1), - 4.5, - qml.Hamiltonian([4.5], [qml.PauliZ(0) @ qml.PauliX(1)]), - ), - ( - qml.Hermitian(np.array([[1, 0], [0, -1]]), "c"), - 3, - qml.Hamiltonian([3], [qml.Hermitian(np.array([[1, 0], [0, -1]]), "c")]), - ), - ] + add_obs = [ + (qml.PauliZ(0) @ qml.Identity(1), qml.PauliZ(0), qml.Hamiltonian([2], [qml.PauliZ(0)])), + ( + qml.PauliZ(0), + qml.PauliZ(0) @ qml.PauliX(1), + qml.Hamiltonian([1, 1], [qml.PauliZ(0), qml.PauliZ(0) @ qml.PauliX(1)]), + ), + ( + qml.PauliZ("b") @ qml.Identity(1), + qml.Hamiltonian([3], [qml.PauliZ("b")]), + qml.Hamiltonian([4], [qml.PauliZ("b")]), + ), + ( + qml.PauliX(0) @ qml.PauliZ(1), + qml.PauliZ(1) @ qml.Identity(2) @ qml.PauliX(0), + qml.Hamiltonian([2], [qml.PauliX(0) @ qml.PauliZ(1)]), + ), + ( + qml.Hermitian(np.array([[1, 0], [0, -1]]), 1.2), + qml.Hamiltonian([3], [qml.Hermitian(np.array([[1, 0], [0, -1]]), 1.2)]), + qml.Hamiltonian([4], [qml.Hermitian(np.array([[1, 0], [0, -1]]), 1.2)]), + ), + ] - matmul_obs = [ - (qml.PauliX(0), qml.PauliZ(1), Tensor(qml.PauliX(0), qml.PauliZ(1))), # obs @ obs - ( - qml.PauliX(0), - qml.PauliZ(1) @ qml.PauliY(2), - Tensor(qml.PauliX(0), qml.PauliZ(1), qml.PauliY(2)), - ), # obs @ tensor - ( + add_zero_obs = [ qml.PauliX(0), - qml.Hamiltonian([1.0], [qml.PauliY(1)]), - qml.Hamiltonian([1.0], [qml.PauliX(0) @ qml.PauliY(1)]), - ), # obs @ hamiltonian - ] + qml.Hermitian(np.array([[1, 0], [0, -1]]), 1.2), + qml.PauliX(0) @ qml.Hadamard(2), + # qml.Projector(np.array([1, 1]), wires=[0, 1]), + # qml.SparseHamiltonian(csr_matrix(np.array([[1, 0], [-1.5, 0]])), 1), + # CVObservables + qml.Identity(1), + cv.NumberOperator(wires=[1]), + cv.TensorN(wires=[1]), + cv.QuadX(wires=[1]), + cv.QuadP(wires=[1]), + # cv.QuadOperator(1.234, wires=0), + # cv.FockStateProjector([1,2,3], wires=[0, 1, 2]), + cv.PolyXP(np.array([1.0, 2.0, 3.0]), wires=[0]), + ] - sub_obs = [ - (qml.PauliZ(0) @ qml.Identity(1), qml.PauliZ(0), qml.Hamiltonian([], [])), - ( - qml.PauliZ(0), - qml.PauliZ(0) @ qml.PauliX(1), - qml.Hamiltonian([1, -1], [qml.PauliZ(0), qml.PauliZ(0) @ qml.PauliX(1)]), - ), - ( - qml.PauliZ(0) @ qml.Identity(1), - qml.Hamiltonian([3], [qml.PauliZ(0)]), - qml.Hamiltonian([-2], [qml.PauliZ(0)]), - ), - ( - qml.PauliX(0) @ qml.PauliZ(1), - qml.PauliZ(3) @ qml.Identity(2) @ qml.PauliX(0), - qml.Hamiltonian( - [1, -1], [qml.PauliX(0) @ qml.PauliZ(1), qml.PauliZ(3) @ qml.PauliX(0)] + mul_obs = [ + (qml.PauliZ(0), 3, qml.Hamiltonian([3], [qml.PauliZ(0)])), + (qml.PauliZ(0) @ qml.Identity(1), 3, qml.Hamiltonian([3], [qml.PauliZ(0)])), + ( + qml.PauliZ(0) @ qml.PauliX(1), + 4.5, + qml.Hamiltonian([4.5], [qml.PauliZ(0) @ qml.PauliX(1)]), ), - ), - ( - qml.Hermitian(np.array([[1, 0], [0, -1]]), 1.2), - qml.Hamiltonian([3], [qml.Hermitian(np.array([[1, 0], [0, -1]]), 1.2)]), - qml.Hamiltonian([-2], [qml.Hermitian(np.array([[1, 0], [0, -1]]), 1.2)]), - ), - ] + ( + qml.Hermitian(np.array([[1, 0], [0, -1]]), "c"), + 3, + qml.Hamiltonian([3], [qml.Hermitian(np.array([[1, 0], [0, -1]]), "c")]), + ), + ] + + matmul_obs = [ + (qml.PauliX(0), qml.PauliZ(1), Tensor(qml.PauliX(0), qml.PauliZ(1))), # obs @ obs + ( + qml.PauliX(0), + qml.PauliZ(1) @ qml.PauliY(2), + Tensor(qml.PauliX(0), qml.PauliZ(1), qml.PauliY(2)), + ), # obs @ tensor + ( + qml.PauliX(0), + qml.Hamiltonian([1.0], [qml.PauliY(1)]), + qml.Hamiltonian([1.0], [qml.PauliX(0) @ qml.PauliY(1)]), + ), # obs @ hamiltonian + ] + + sub_obs = [ + (qml.PauliZ(0) @ qml.Identity(1), qml.PauliZ(0), qml.Hamiltonian([], [])), + ( + qml.PauliZ(0), + qml.PauliZ(0) @ qml.PauliX(1), + qml.Hamiltonian([1, -1], [qml.PauliZ(0), qml.PauliZ(0) @ qml.PauliX(1)]), + ), + ( + qml.PauliZ(0) @ qml.Identity(1), + qml.Hamiltonian([3], [qml.PauliZ(0)]), + qml.Hamiltonian([-2], [qml.PauliZ(0)]), + ), + ( + qml.PauliX(0) @ qml.PauliZ(1), + qml.PauliZ(3) @ qml.Identity(2) @ qml.PauliX(0), + qml.Hamiltonian( + [1, -1], [qml.PauliX(0) @ qml.PauliZ(1), qml.PauliZ(3) @ qml.PauliX(0)] + ), + ), + ( + qml.Hermitian(np.array([[1, 0], [0, -1]]), 1.2), + qml.Hamiltonian([3], [qml.Hermitian(np.array([[1, 0], [0, -1]]), 1.2)]), + qml.Hamiltonian([-2], [qml.Hermitian(np.array([[1, 0], [0, -1]]), 1.2)]), + ), + ] -@pytest.mark.usefixtures("use_legacy_opmath") +@pytest.mark.usefixtures("legacy_opmath_only") class TestTensorObservableOperations: """Tests arithmetic operations between observables/tensors""" @@ -2646,7 +2664,7 @@ def test_composed(self): ] -@pytest.mark.usefixtures("use_new_opmath") +@pytest.mark.usefixtures("new_opmath_only") class TestNewOpMath: """Tests dunder operations with new operator arithmetic enabled.""" @@ -2771,7 +2789,7 @@ def test_mul_does_auto_simplify(self): class TestHamiltonianLinearCombinationAlias: """Unit tests for using qml.Hamiltonian as an alias for LinearCombination""" - @pytest.mark.usefixtures("use_new_opmath") + @pytest.mark.usefixtures("new_opmath_only") def test_hamiltonian_linear_combination_alias_enabled(self): """Test that qml.Hamiltonian is an alias for LinearCombination with new operator arithmetic enabled""" @@ -2783,7 +2801,7 @@ def test_hamiltonian_linear_combination_alias_enabled(self): assert not isinstance(op, qml.ops.qubit.Hamiltonian) assert not isinstance(op, qml.ops.qubit.hamiltonian.Hamiltonian) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_hamiltonian_linear_combination_alias_disabled(self): """Test that qml.Hamiltonian is not an alias for LinearCombination with new operator arithmetic disabled""" @@ -2832,31 +2850,40 @@ def test_symmetric_matrix_early_return(op, mocker): assert np.allclose(actual, manually_expanded) -@pytest.mark.usefixtures("use_new_opmath") +@pytest.mark.usefixtures("use_legacy_and_new_opmath") def test_op_arithmetic_toggle(): - """Tests toggling op arithmetic on and off, and that it is on by default.""" - assert qml.operation.active_new_opmath() + """Tests toggling op arithmetic on and off""" - with qml.operation.enable_new_opmath_cm(): - assert qml.operation.active_new_opmath() - assert isinstance(qml.PauliX(0) @ qml.PauliZ(1), Prod) + with pytest.warns(qml.PennyLaneDeprecationWarning, match="Toggling the new approach"): + with qml.operation.enable_new_opmath_cm(): + assert qml.operation.active_new_opmath() + assert isinstance(qml.PauliX(0) @ qml.PauliZ(1), Prod) - with qml.operation.disable_new_opmath_cm(): - assert not qml.operation.active_new_opmath() - assert isinstance(qml.PauliX(0) @ qml.PauliZ(1), Tensor) + with pytest.warns(qml.PennyLaneDeprecationWarning, match="Disabling the new approach"): + with qml.operation.disable_new_opmath_cm(): + assert not qml.operation.active_new_opmath() + assert isinstance(qml.PauliX(0) @ qml.PauliZ(1), Tensor) + + +@pytest.mark.usefixtures("new_opmath_only") +def test_op_arithmetic_default(): + """Test that new op math is enabled by default""" + assert qml.operation.active_new_opmath() -@pytest.mark.usefixtures("use_new_opmath") +@pytest.mark.usefixtures("new_opmath_only") def test_disable_enable_new_opmath(): """Test that disabling and re-enabling new opmath works and raises the correct warning""" - with pytest.warns(UserWarning, match="Disabling the new Operator arithmetic"): + with pytest.warns( + qml.PennyLaneDeprecationWarning, match="Disabling the new approach to operator arithmetic" + ): qml.operation.disable_new_opmath() assert not qml.operation.active_new_opmath() with pytest.warns( - UserWarning, - match="Re-enabling the new Operator arithmetic system after disabling it is not advised.", + qml.PennyLaneDeprecationWarning, + match="Toggling the new approach to operator arithmetic", ): qml.operation.enable_new_opmath() @@ -2872,7 +2899,7 @@ class FlipAndRotate(qml.operation.Operation): num_wires = qml.operation.AnyWires grad_method = "A" - # pylint: disable=too-many-arguments + # pylint: disable=too-many-arguments,too-many-positional-arguments def __init__(self, angle, wire_rot, wire_flip=None, do_flip=False, id=None): if do_flip and wire_flip is None: raise ValueError("Expected a wire to flip; got None.") @@ -2936,83 +2963,84 @@ class CustomOperator(qml.operation.Operator): qml.assert_equal(new_op, CustomOperator(2.3, wires=0)) -@pytest.mark.usefixtures("use_new_opmath") +@pytest.mark.usefixtures("new_opmath_only") def test_use_new_opmath_fixture(): """Test that the fixture for using new opmath in a context works as expected""" assert qml.operation.active_new_opmath() -@pytest.mark.usefixtures("use_legacy_opmath") -def test_use_legacy_opmath_fixture(): +@pytest.mark.usefixtures("legacy_opmath_only") +def test_legacy_opmath_only_fixture(): """Test that the fixture for using new opmath in a context works as expected""" assert not qml.operation.active_new_opmath() -CONVERT_HAMILTONIAN = [ - ( - [1.5, 0.5, 1, 1], - [ - qml.Identity(1), - Tensor(qml.Z(1), qml.Z(2)), - Tensor(qml.X(1), qml.Y(2)), - qml.Hadamard(1), - ], - ), - ([0.5], [qml.X(1)]), - ([1], [Tensor(qml.X(0), qml.Y(1))]), - ( - [-0.5, 0.4, -0.3, 0.2], - [ - qml.Identity(0, 1), - Tensor(qml.X(1), qml.Y(2)), - qml.Identity(1), - Tensor(qml.Z(1), qml.Z(2)), - ], - ), - ( - [0.0625, 0.0625, -0.0625, 0.0625, -0.0625, 0.0625, -0.0625, -0.0625], - [ - Tensor(qml.Hadamard(0), qml.X(1), qml.X(2), qml.Y(3)), - Tensor(qml.X(0), qml.X(1), qml.Y(2), qml.X(3)), - Tensor(qml.X(0), qml.Y(1), qml.X(2), qml.X(3)), - Tensor(qml.X(0), qml.Y(1), qml.Y(2), qml.Y(3)), - Tensor(qml.Y(0), qml.X(1), qml.X(2), qml.X(3)), - Tensor(qml.Y(0), qml.X(1), qml.Hadamard(2), qml.Y(3)), - Tensor(qml.Y(0), qml.Y(1), qml.X(2), qml.Y(3)), - Tensor(qml.Y(0), qml.Y(1), qml.Y(2), qml.Hadamard(3)), - ], - ), -] +with warnings.catch_warnings(): + warnings.filterwarnings("ignore", "qml.operation.Tensor uses", qml.PennyLaneDeprecationWarning) + + CONVERT_HAMILTONIAN = [ + ( + [1.5, 0.5, 1, 1], + [ + qml.Identity(1), + Tensor(qml.Z(1), qml.Z(2)), + Tensor(qml.X(1), qml.Y(2)), + qml.Hadamard(1), + ], + ), + ([0.5], [qml.X(1)]), + ([1], [Tensor(qml.X(0), qml.Y(1))]), + ( + [-0.5, 0.4, -0.3, 0.2], + [ + qml.Identity(0, 1), + Tensor(qml.X(1), qml.Y(2)), + qml.Identity(1), + Tensor(qml.Z(1), qml.Z(2)), + ], + ), + ( + [0.0625, 0.0625, -0.0625, 0.0625, -0.0625, 0.0625, -0.0625, -0.0625], + [ + Tensor(qml.Hadamard(0), qml.X(1), qml.X(2), qml.Y(3)), + Tensor(qml.X(0), qml.X(1), qml.Y(2), qml.X(3)), + Tensor(qml.X(0), qml.Y(1), qml.X(2), qml.X(3)), + Tensor(qml.X(0), qml.Y(1), qml.Y(2), qml.Y(3)), + Tensor(qml.Y(0), qml.X(1), qml.X(2), qml.X(3)), + Tensor(qml.Y(0), qml.X(1), qml.Hadamard(2), qml.Y(3)), + Tensor(qml.Y(0), qml.Y(1), qml.X(2), qml.Y(3)), + Tensor(qml.Y(0), qml.Y(1), qml.Y(2), qml.Hadamard(3)), + ], + ), + ] -@pytest.mark.usefixtures("use_new_opmath") +@pytest.mark.usefixtures("new_opmath_only") @pytest.mark.parametrize("coeffs, obs", CONVERT_HAMILTONIAN) def test_convert_to_hamiltonian(coeffs, obs): """Test that arithmetic operators can be converted to Hamiltonian instances""" opmath_instance = qml.dot(coeffs, obs) - converted_opmath = convert_to_legacy_H(opmath_instance) - with qml.operation.disable_new_opmath_cm(): - assert isinstance(converted_opmath, qml.Hamiltonian) - with pytest.warns( - qml.PennyLaneDeprecationWarning, match="with new operator arithmetic is deprecated" + qml.PennyLaneDeprecationWarning, match="qml.ops.Hamiltonian uses the old approach" ): + converted_opmath = convert_to_legacy_H(opmath_instance) hamiltonian_instance = qml.ops.Hamiltonian(coeffs, obs) + assert isinstance(converted_opmath, qml.ops.Hamiltonian) qml.assert_equal(converted_opmath, hamiltonian_instance) +@pytest.mark.usefixtures("legacy_opmath_only") @pytest.mark.parametrize( "coeffs, obs", [([1], [qml.Hadamard(1)]), ([0.5, 0.5], [qml.Identity(1), qml.Identity(1)])] ) def test_convert_to_hamiltonian_trivial(coeffs, obs): """Test that non-arithmetic operator after simplification is returned as an Observable""" - with qml.operation.disable_new_opmath_cm(): - opmath_instance = qml.dot(coeffs, obs) - converted_opmath = convert_to_legacy_H(opmath_instance) - assert isinstance(converted_opmath, qml.operation.Observable) + opmath_instance = qml.dot(coeffs, obs) + converted_opmath = convert_to_legacy_H(opmath_instance) + assert isinstance(converted_opmath, qml.operation.Observable) @pytest.mark.parametrize( @@ -3031,7 +3059,8 @@ def test_convert_to_hamiltonian_error(coeffs, obs): convert_to_legacy_H(qml.dot(coeffs, obs)) -@pytest.mark.usefixtures("use_new_opmath") +@pytest.mark.usefixtures("new_opmath_only") +@pytest.mark.filterwarnings("ignore::pennylane.PennyLaneDeprecationWarning") def test_convert_to_H(): operator = ( 2 * qml.X(0) @@ -3039,7 +3068,7 @@ def test_convert_to_H(): + qml.Y(1) @ qml.Z(2) @ (2 * qml.X(3)) + 2 * (qml.Hadamard(3) + 3 * qml.Z(2)) ) - with qml.operation.disable_new_opmath_cm(): + with qml.operation.disable_new_opmath_cm(warn=False): legacy_H = qml.operation.convert_to_H(operator) linear_combination = qml.operation.convert_to_H(operator) @@ -3099,7 +3128,13 @@ def test_convert_to_opmath_queueing(make_op): """Tests that converting to opmath dequeues the original operation""" with qml.queuing.AnnotatedQueue() as q: - original_op = make_op() + if not qml.operation.active_new_opmath(): + with pytest.warns( + qml.PennyLaneDeprecationWarning, match="qml.ops.Hamiltonian uses the old approach" + ): + original_op = make_op() + else: + original_op = make_op() new_op = qml.operation.convert_to_opmath(original_op) assert len(q.queue) == 1 diff --git a/tests/test_qaoa.py b/tests/test_qaoa.py index e4ad4599d8d..da8d948e4e0 100644 --- a/tests/test_qaoa.py +++ b/tests/test_qaoa.py @@ -1139,7 +1139,7 @@ class TestUtils: """Tests that the utility functions are working properly""" # pylint: disable=protected-access - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") @pytest.mark.parametrize( ("hamiltonian", "value"), ( @@ -1150,55 +1150,9 @@ class TestUtils: ), ) def test_diagonal_terms(self, hamiltonian, value): - hamiltonian = qml.operation.convert_to_legacy_H(hamiltonian) assert qaoa.layers._diagonal_terms(hamiltonian) == value -def make_mixer_layer_test_cases(): - return [ - [ - qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliX(1)]), - [qml.PauliRot(2, "X", wires=[0]), qml.PauliRot(2, "X", wires=[1])], - ], - [ - qml.X(0) + qml.X(1), - [qml.PauliRot(2, "X", wires=[0]), qml.PauliRot(2, "X", wires=[1])], - ], - [ - qaoa.xy_mixer(Graph([(0, 1), (1, 2), (2, 0)])), - [ - qml.PauliRot(1.0, "XX", wires=[0, 1]), - qml.PauliRot(1.0, "YY", wires=[0, 1]), - qml.PauliRot(1.0, "XX", wires=[0, 2]), - qml.PauliRot(1.0, "YY", wires=[0, 2]), - qml.PauliRot(1.0, "XX", wires=[1, 2]), - qml.PauliRot(1.0, "YY", wires=[1, 2]), - ], - ], - ] - - -def make_cost_layer_test_cases(): - return [ - [ - qml.Hamiltonian([1, 1], [qml.PauliZ(0), qml.PauliZ(1)]), - [qml.PauliRot(2, "Z", wires=[0]), qml.PauliRot(2, "Z", wires=[1])], - ], - [ - qml.Z(0) + qml.Z(1), - [qml.PauliRot(2, "Z", wires=[0]), qml.PauliRot(2, "Z", wires=[1])], - ], - [ - qaoa.maxcut(Graph([(0, 1), (1, 2), (2, 0)]))[0], - [ - qml.PauliRot(1.0, "ZZ", wires=[0, 1]), - qml.PauliRot(1.0, "ZZ", wires=[0, 2]), - qml.PauliRot(1.0, "ZZ", wires=[1, 2]), - ], - ], - ] - - class TestLayers: """Tests that the cost and mixer layers are being constructed properly""" @@ -1230,43 +1184,64 @@ def test_cost_layer_errors(self): ): qaoa.cost_layer(0.1, hamiltonian) - mixer_layer_test_cases = make_mixer_layer_test_cases() + mixer_layer_test_cases = [ + [ + qml.Hamiltonian([1, 1], [qml.PauliX(0), qml.PauliX(1)]), + [qml.PauliRot(2, "X", wires=[0]), qml.PauliRot(2, "X", wires=[1])], + ], + [ + qml.X(0) + qml.X(1), + [qml.PauliRot(2, "X", wires=[0]), qml.PauliRot(2, "X", wires=[1])], + ], + [ + qaoa.xy_mixer(Graph([(0, 1), (1, 2), (2, 0)])), + [ + qml.PauliRot(1.0, "XX", wires=[0, 1]), + qml.PauliRot(1.0, "YY", wires=[0, 1]), + qml.PauliRot(1.0, "XX", wires=[0, 2]), + qml.PauliRot(1.0, "YY", wires=[0, 2]), + qml.PauliRot(1.0, "XX", wires=[1, 2]), + qml.PauliRot(1.0, "YY", wires=[1, 2]), + ], + ], + ] @pytest.mark.parametrize(("mixer", "gates"), mixer_layer_test_cases) def test_mixer_layer_output(self, mixer, gates): """Tests that the gates of the mixer layer are correct""" alpha = 1 - with qml.tape.OperationRecorder() as rec: - qaoa.mixer_layer(alpha, mixer) - - rec = rec.expand() - - for i, j in zip(rec.operations, gates): - prep = [i.name, i.parameters, i.wires] - target = [j.name, j.parameters, j.wires] - assert prep == target - - with qml.operation.disable_new_opmath_cm(): - mixer_layer_test_cases_legacy = make_mixer_layer_test_cases() - - @pytest.mark.usefixtures("use_legacy_opmath") - @pytest.mark.parametrize(("mixer", "gates"), mixer_layer_test_cases_legacy) - def test_mixer_layer_output_legacy_opmath(self, mixer, gates): - """Tests that the gates of the mixer layer are correct""" + with qml.queuing.AnnotatedQueue() as q: + out = qaoa.mixer_layer(alpha, mixer) - alpha = 1 - with qml.tape.OperationRecorder() as rec: - qaoa.mixer_layer(alpha, mixer) + expected = qml.ApproxTimeEvolution(mixer, alpha, 1) + qml.assert_equal(out, expected) - rec = rec.expand() + assert q.queue[0] is out + assert len(q) == 1 + decomp = out.decomposition() - for i, j in zip(rec.operations, gates): - prep = [i.name, i.parameters, i.wires] - target = [j.name, j.parameters, j.wires] - assert prep == target + for i, j in zip(decomp, gates): + qml.assert_equal(i, j) - cost_layer_test_cases = make_cost_layer_test_cases() + cost_layer_test_cases = [ + [ + qml.Hamiltonian([1, 1], [qml.PauliZ(0), qml.PauliZ(1)]), + [qml.PauliRot(2, "Z", wires=[0]), qml.PauliRot(2, "Z", wires=[1])], + ], + [ + qml.Z(0) + qml.Z(1), + [qml.PauliRot(2, "Z", wires=[0]), qml.PauliRot(2, "Z", wires=[1])], + ], + [ + qaoa.maxcut(Graph([(0, 1), (1, 2), (2, 0)]))[0], + [ + qml.PauliRot(1.0, "ZZ", wires=[0, 1]), + qml.PauliRot(1.0, "ZZ", wires=[0, 2]), + qml.PauliRot(1.0, "ZZ", wires=[1, 2]), + ], + ], + ] @pytest.mark.parametrize( ("cost", "gates"), @@ -1290,27 +1265,6 @@ def test_cost_layer_output(self, cost, gates): for i, j in zip(decomp, gates): qml.assert_equal(i, j) - with qml.operation.disable_new_opmath_cm(): - cost_layer_test_cases_legacy = make_cost_layer_test_cases() - - @pytest.mark.usefixtures("use_legacy_opmath") - @pytest.mark.parametrize(("cost", "gates"), cost_layer_test_cases_legacy) - def test_cost_layer_output_legacy_opmath(self, cost, gates): - """Tests that the gates of the cost layer is correct""" - - gamma = 1 - - with qml.tape.OperationRecorder() as rec: - cost = qml.operation.convert_to_legacy_H(cost) - qaoa.cost_layer(gamma, cost) - - rec = rec.expand() - - for i, j in zip(rec.operations, gates): - prep = [i.name, i.parameters, i.wires] - target = [j.name, j.parameters, j.wires] - assert prep == target - class TestIntegration: """Test integration of the QAOA module with PennyLane""" @@ -1518,31 +1472,6 @@ def test_partial_cycle_mixer_incomplete(self, g): assert all(op.wires == op_e.wires for op, op_e in zip(h.ops, ops_expected)) assert all(op.name == op_e.name for op, op_e in zip(h.ops, ops_expected)) - @pytest.mark.usefixtures("use_legacy_opmath") - @pytest.mark.parametrize( - "g", - [nx.complete_graph(4).to_directed(), rx.generators.directed_mesh_graph(4, [0, 1, 2, 3])], - ) - def test_partial_cycle_mixer_incomplete_legacy_opmath(self, g): - """Test if the _partial_cycle_mixer function returns the expected Hamiltonian for a fixed - example""" - g.remove_edge(2, 1) # remove an egde to make graph incomplete - edge = (0, 1) - - h = _partial_cycle_mixer(g, edge) - - ops_expected = [ - qml.PauliX(0) @ qml.PauliX(2) @ qml.PauliX(9), - qml.PauliY(0) @ qml.PauliY(2) @ qml.PauliX(9), - qml.PauliY(0) @ qml.PauliX(2) @ qml.PauliY(9), - qml.PauliX(0) @ qml.PauliY(2) @ qml.PauliY(9), - ] - coeffs_expected = [0.25, 0.25, 0.25, -0.25] - - assert h.coeffs == coeffs_expected - assert all(op.wires == op_e.wires for op, op_e in zip(h.ops, ops_expected)) - assert all(op.name == op_e.name for op, op_e in zip(h.ops, ops_expected)) - @pytest.mark.parametrize("g", [nx.complete_graph(4), rx.generators.mesh_graph(4, [0, 1, 2, 3])]) def test_partial_cycle_mixer_error(self, g): """Test if the _partial_cycle_mixer raises ValueError""" @@ -1946,25 +1875,6 @@ def test_inner_out_flow_constraint_hamiltonian(self, g): expected_hamiltonian = qml.Hamiltonian(expected_coeffs, expected_ops) assert h.compare(expected_hamiltonian) - @pytest.mark.usefixtures("use_legacy_opmath") - @pytest.mark.parametrize( - "g", [nx.complete_graph(3).to_directed(), rx.generators.directed_mesh_graph(3, [0, 1, 2])] - ) - def test_inner_out_flow_constraint_hamiltonian_legacy_opmath(self, g): - """Test if the _inner_out_flow_constraint_hamiltonian function returns the expected result - on a manually-calculated example of a 3-node complete digraph relative to the 0 node""" - h = _inner_out_flow_constraint_hamiltonian(g, 0) - expected_ops = [ - qml.Identity(0), - qml.PauliZ(1) @ qml.PauliZ(0), - qml.PauliZ(0), - qml.PauliZ(1), - ] - expected_coeffs = [2, 2, -2, -2] - - expected_hamiltonian = qml.Hamiltonian(expected_coeffs, expected_ops) - assert h.compare(expected_hamiltonian) - @pytest.mark.parametrize("g", [nx.complete_graph(3), rx.generators.mesh_graph(3, [0, 1, 2])]) def test_inner_out_flow_constraint_hamiltonian_error(self, g): """Test if the _inner_out_flow_constraint_hamiltonian function raises ValueError""" @@ -1999,7 +1909,7 @@ def test_inner_net_flow_constraint_hamiltonian(self, g): @pytest.mark.parametrize( "g", [nx.complete_graph(3).to_directed(), rx.generators.directed_mesh_graph(3, [0, 1, 2])] ) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_inner_net_flow_constraint_hamiltonian_legacy_opmath(self, g): """Test if the _inner_net_flow_constraint_hamiltonian function returns the expected result on a manually-calculated example of a 3-node complete digraph relative to the 0 node""" @@ -2030,7 +1940,7 @@ def test_inner_net_flow_constraint_hamiltonian_error(self, g): @pytest.mark.parametrize( "g", [nx.complete_graph(3).to_directed(), rx.generators.directed_mesh_graph(3, [0, 1, 2])] ) - @pytest.mark.usefixtures("use_new_opmath") + @pytest.mark.usefixtures("new_opmath_only") def test_inner_out_flow_constraint_hamiltonian_non_complete(self, g): """Test if the _inner_out_flow_constraint_hamiltonian function returns the expected result on a manually-calculated example of a 3-node complete digraph relative to the 0 node, with @@ -2049,7 +1959,7 @@ def test_inner_out_flow_constraint_hamiltonian_non_complete(self, g): @pytest.mark.parametrize( "g", [nx.complete_graph(3).to_directed(), rx.generators.directed_mesh_graph(3, [0, 1, 2])] ) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_inner_out_flow_constraint_hamiltonian_non_complete_legacy_opmath(self, g): """Test if the _inner_out_flow_constraint_hamiltonian function returns the expected result on a manually-calculated example of a 3-node complete digraph relative to the 0 node, with @@ -2092,7 +2002,7 @@ def test_inner_net_flow_constraint_hamiltonian_non_complete(self, g): @pytest.mark.parametrize( "g", [nx.complete_graph(3).to_directed(), rx.generators.directed_mesh_graph(3, [0, 1, 2])] ) - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_inner_net_flow_constraint_hamiltonian_non_complete_legacy_opmath(self, g): """Test if the _inner_net_flow_constraint_hamiltonian function returns the expected result on a manually-calculated example of a 3-node complete digraph relative to the 0 node, with the (1, 0) edge removed""" @@ -2176,56 +2086,6 @@ def cost(params): elif max(num_edges_leaving_node.values()) <= 1: assert energy == min(energies_bitstrings)[0] - @pytest.mark.parametrize( - "g", [nx.complete_graph(3).to_directed(), rx.generators.directed_mesh_graph(3, [0, 1, 2])] - ) - @pytest.mark.usefixtures("use_legacy_opmath") - def test_out_flow_constraint_legacy_opmath(self, g): - """Test the out-flow constraint Hamiltonian is minimised by states that correspond to - subgraphs that only ever have 0 or 1 edge leaving each node - """ - h = out_flow_constraint(g) - m = wires_to_edges(g) - wires = len(g.edge_list() if isinstance(g, rx.PyDiGraph) else g.edges) - - # We use PL to find the energies corresponding to each possible bitstring - dev = qml.device("default.qubit", wires=wires) - - # pylint: disable=unused-argument - def states(basis_state, **kwargs): - qml.BasisState(basis_state, wires=range(wires)) - - @qml.qnode(dev) - def cost(params): - states(params) - return qml.expval(h) - - # Calculate the set of all bitstrings - bitstrings = itertools.product([0, 1], repeat=wires) - - # Calculate the corresponding energies - energies_bitstrings = ((cost(np.array(bitstring)), bitstring) for bitstring in bitstrings) - - for energy, bs in energies_bitstrings: - # convert binary string to wires then wires to edges - wires_ = tuple(i for i, s in enumerate(bs) if s != 0) - edges = tuple(m[w] for w in wires_) - - # find the number of edges leaving each node - if isinstance(g, rx.PyDiGraph): - num_edges_leaving_node = {node: 0 for node in g.nodes()} - else: - num_edges_leaving_node = {node: 0 for node in g.nodes} - for e in edges: - num_edges_leaving_node[e[0]] += 1 - - # check that if the max number of edges is <=1 it corresponds to a state that minimizes - # the out_flow_constraint Hamiltonian - if max(num_edges_leaving_node.values()) > 1: - assert energy > min(energies_bitstrings)[0] - elif max(num_edges_leaving_node.values()) <= 1: - assert energy == min(energies_bitstrings)[0] - @pytest.mark.parametrize("g", [nx.complete_graph(3), rx.generators.mesh_graph(3, [0, 1, 2])]) def test_out_flow_constraint_undirected_raises_error(self, g): """Test `out_flow_constraint` raises ValueError if input graph is not directed""" @@ -2284,59 +2144,6 @@ def cost(basis_state): else: assert energy > min(energies_states)[0] - @pytest.mark.parametrize( - "g", [nx.complete_graph(3).to_directed(), rx.generators.directed_mesh_graph(3, [0, 1, 2])] - ) - @pytest.mark.usefixtures("use_legacy_opmath") - def test_net_flow_constraint_legacy_opmath(self, g): - """Test if the net_flow_constraint Hamiltonian is minimized by states that correspond to a - collection of edges with zero flow""" - h = net_flow_constraint(g) - m = wires_to_edges(g) - wires = len(g.edge_list() if isinstance(g, rx.PyDiGraph) else g.edges) - - # We use PL to find the energies corresponding to each possible bitstring - dev = qml.device("default.qubit", wires=wires) - - @qml.qnode(dev) - def cost(basis_state): - qml.BasisState(basis_state, wires=range(wires)) - return qml.expval(h) - - # Calculate the set of all bitstrings - states = itertools.product([0, 1], repeat=wires) - - # Calculate the corresponding energies - energies_states = ((cost(np.array(state)), state) for state in states) - - # We now have the energies of each bitstring/state. We also want to calculate the net flow of - # the corresponding edges - for energy, state in energies_states: - # This part converts from a binary string of wires selected to graph edges - wires_ = tuple(i for i, s in enumerate(state) if s != 0) - edges = tuple(m[w] for w in wires_) - - # Calculates the number of edges entering and leaving a given node - if isinstance(g, rx.PyDiGraph): - in_flows = np.zeros(len(g.nodes())) - out_flows = np.zeros(len(g.nodes())) - else: - in_flows = np.zeros(len(g.nodes)) - out_flows = np.zeros(len(g.nodes)) - - for e in edges: - in_flows[e[0]] += 1 - out_flows[e[1]] += 1 - - net_flow = np.sum(np.abs(in_flows - out_flows)) - - # The test requires that a set of edges with zero net flow must have a corresponding - # bitstring that minimized the energy of the Hamiltonian - if net_flow == 0: - assert energy == min(energies_states)[0] - else: - assert energy > min(energies_states)[0] - @pytest.mark.parametrize("g", [nx.complete_graph(3), rx.generators.mesh_graph(3, [0, 1, 2])]) def test_net_flow_constraint_wrong_graph_type_raises_error(self, g): """Test `net_flow_constraint` raises ValueError if input graph is not @@ -2422,70 +2229,3 @@ def find_simple_cycle(list_of_edges): assert energy == min(energies_bitstrings)[0] elif len(edges) > 0 and not find_simple_cycle(edges): assert energy > min(energies_bitstrings)[0] - - @pytest.mark.parametrize( - "g", [nx.complete_graph(3).to_directed(), rx.generators.directed_mesh_graph(3, [0, 1, 2])] - ) - @pytest.mark.usefixtures("use_legacy_opmath") - def test_net_flow_and_out_flow_constraint_legacy_opmath(self, g): - """Test the combined net-flow and out-flow constraint Hamiltonian is minimised by states that correspond to subgraphs - that qualify as simple_cycles - """ - g = nx.complete_graph(3).to_directed() - h = net_flow_constraint(g) + out_flow_constraint(g) - m = wires_to_edges(g) - wires = len(g.edge_list() if isinstance(g, rx.PyDiGraph) else g.edges) - - # Find the energies corresponding to each possible bitstring - dev = qml.device("default.qubit", wires=wires) - - # pylint: disable=unused-argument - def states(basis_state, **kwargs): - qml.BasisState(basis_state, wires=range(wires)) - - @qml.qnode(dev) - def cost(params): - states(params) - return qml.expval(h) - - # Calculate the set of all bitstrings - bitstrings = itertools.product([0, 1], repeat=wires) - - # Calculate the corresponding energies - energies_bitstrings = ((cost(np.array(bitstring)), bitstring) for bitstring in bitstrings) - - def find_simple_cycle(list_of_edges): - """Returns True if list_of_edges contains a permutation corresponding to a simple cycle""" - permutations = list(itertools.permutations(list_of_edges)) - - for edges in permutations: - if edges[0][0] != edges[-1][-1]: # check first node is equal to last node - continue - all_nodes = [] - for edge in edges: - for n in edge: - all_nodes.append(n) - inner_nodes = all_nodes[ - 1:-1 - ] # find all nodes in all edges excluding the first and last nodes - nodes_out = [ - inner_nodes[i] for i in range(len(inner_nodes)) if i % 2 == 0 - ] # find the nodes each edge is leaving - node_in = [ - inner_nodes[i] for i in range(len(inner_nodes)) if i % 2 != 0 - ] # find the nodes each edge is entering - if nodes_out == node_in and ( - len([all_nodes[0]] + nodes_out) == len(set([all_nodes[0]] + nodes_out)) - ): # check that each edge connect to the next via a common node and that no node is crossed more than once - return True - return False - - for energy, bs in energies_bitstrings: - # convert binary string to wires then wires to edges - wires_ = tuple(i for i, s in enumerate(bs) if s != 0) - edges = tuple(m[w] for w in wires_) - - if len(edges) > 0 and find_simple_cycle(edges): - assert energy == min(energies_bitstrings)[0] - elif len(edges) > 0 and not find_simple_cycle(edges): - assert energy > min(energies_bitstrings)[0] diff --git a/tests/test_queuing.py b/tests/test_queuing.py index ec4bf18efc0..111c5846df7 100644 --- a/tests/test_queuing.py +++ b/tests/test_queuing.py @@ -197,6 +197,7 @@ def test_append_qubit_observables(self): ] assert q.queue == ops + @pytest.mark.usefixtures("legacy_opmath_only") def test_append_tensor_ops(self): """Test that ops which are used as inputs to `Tensor` are successfully added to the queue, as well as the `Tensor` object.""" @@ -208,7 +209,7 @@ def test_append_tensor_ops(self): assert q.queue == [tensor_op] assert tensor_op.obs == [A, B] - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_append_tensor_ops_overloaded(self): """Test that Tensor ops created using `@` are successfully added to the queue, as well as the `Tensor` object.""" @@ -220,7 +221,7 @@ def test_append_tensor_ops_overloaded(self): assert q.queue == [tensor_op] assert tensor_op.obs == [A, B] - @pytest.mark.usefixtures("use_new_opmath") + @pytest.mark.usefixtures("new_opmath_only") def test_append_prod_ops_overloaded(self): """Test that Prod ops created using `@` are successfully added to the queue, as well as the `Prod` object.""" diff --git a/tests/test_vqe.py b/tests/test_vqe.py index 39548f5f7c5..f4d3da8c034 100644 --- a/tests/test_vqe.py +++ b/tests/test_vqe.py @@ -1003,27 +1003,6 @@ def circuit(w): dc = jax.grad(circuit)(w) assert np.allclose(dc, big_hamiltonian_grad, atol=tol) - @pytest.mark.usefixtures("legacy_opmath_only") - def test_specs_legacy(self): - """Test that the specs of a VQE circuit can be computed""" - dev = qml.device("default.qubit", wires=2) - H = qml.Hamiltonian([0.1, 0.2], [qml.PauliZ(0), qml.PauliZ(0) @ qml.PauliX(1)]) - - @qml.qnode(dev) - def circuit(): - qml.Hadamard(wires=0) - qml.CNOT(wires=[0, 1]) - return qml.expval(H) - - res = qml.specs(circuit)() - - assert res["num_observables"] == 1 - - # currently this returns 1 instead, because diagonalizing gates exist for H, - # but they aren't used in executing this qnode - # to be revisited in [sc-59117] - assert res["num_diagonalizing_gates"] == 0 - @pytest.mark.xfail( reason="diagonalizing gates defined but not used, should not be included in specs" ) diff --git a/tests/transforms/test_diagonalize_measurements.py b/tests/transforms/test_diagonalize_measurements.py index 9c6c1fa7929..35b9f763923 100644 --- a/tests/transforms/test_diagonalize_measurements.py +++ b/tests/transforms/test_diagonalize_measurements.py @@ -370,7 +370,7 @@ def test_diagonalize_all_measurements_composite_obs( assert fn == null_postprocessing - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_diagonalize_all_measurements_hamiltonian(self): """Test that the diagonalize_measurements transform diagonalizes a Hamiltonian with a pauli_rep when diagonalizing all measurements""" @@ -390,7 +390,7 @@ def test_diagonalize_all_measurements_hamiltonian(self): assert fn == null_postprocessing - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_diagonalize_all_measurements_tensor(self): """Test that the diagonalize_measurements transform diagonalizes a Tensor with a pauli_rep when diagonalizing all measurements""" diff --git a/tests/transforms/test_qcut.py b/tests/transforms/test_qcut.py index c84bfe90bfb..e5af9c16aff 100644 --- a/tests/transforms/test_qcut.py +++ b/tests/transforms/test_qcut.py @@ -21,7 +21,7 @@ import itertools import string import sys -from functools import partial +from functools import partial, reduce from itertools import product from os import environ from pathlib import Path @@ -5097,7 +5097,6 @@ def test_place_wire_cuts(self): range(len(graph.nodes) + len(cut_edges)) ) - @pytest.mark.usefixtures("use_legacy_opmath") @pytest.mark.parametrize("local_measurement", [False, True]) @pytest.mark.parametrize("with_manual_cut", [False, True]) @pytest.mark.parametrize( @@ -5110,7 +5109,7 @@ def test_place_wire_cuts(self): qcut.CutStrategy(max_free_wires=2, num_fragments_probed=5), # impossible to cut ], ) - def test_find_and_place_cuts(self, local_measurement, with_manual_cut, cut_strategy): + def test_find_and_place(self, local_measurement, with_manual_cut, cut_strategy): """Integration tests for auto cutting pipeline.""" pytest.importorskip("kahypar") @@ -5212,122 +5211,6 @@ def test_find_and_place_cuts(self, local_measurement, with_manual_cut, cut_strat in expected_num_cut_edges ) - @pytest.mark.parametrize("local_measurement", [False, True]) - @pytest.mark.parametrize("with_manual_cut", [False, True]) - @pytest.mark.parametrize( - "cut_strategy", - [ - None, - qcut.CutStrategy(qml.device("default.qubit", wires=3)), - qcut.CutStrategy(max_free_wires=4), - qcut.CutStrategy(max_free_wires=2), # extreme constraint forcing exhaustive probing. - qcut.CutStrategy(max_free_wires=2, num_fragments_probed=5), # impossible to cut - ], - ) - def test_find_and_place_cuts_opmath(self, local_measurement, with_manual_cut, cut_strategy): - """Integration tests for auto cutting pipeline with opmath enabled.""" - pytest.importorskip("kahypar") - - with qml.queuing.AnnotatedQueue() as q: - qml.RX(0.1, wires=0) - qml.RY(0.2, wires=1) - qml.RX(0.3, wires="a") - qml.RY(0.4, wires="b") - qml.CNOT(wires=[0, 1]) - if with_manual_cut: - qml.WireCut(wires=1) - qml.CNOT(wires=["a", "b"]) - qml.CNOT(wires=[1, "a"]) - qml.CNOT(wires=[0, 1]) - qml.CNOT(wires=["a", "b"]) - qml.RX(0.5, wires="a") - qml.RY(0.6, wires="b") - qml.expval( - qml.prod(qml.PauliX(wires=[0]), qml.PauliY(wires=["a"]), qml.PauliZ(wires=["b"])) - ) - - tape = qml.tape.QuantumScript.from_queue(q) - graph = qcut.tape_to_graph(tape) - - if cut_strategy is None: - expected_num_cut_edges = 2 - num_frags = 2 - cut_graph = qcut.find_and_place_cuts( - graph=graph, - num_fragments=num_frags, - imbalance=0.5, - replace_wire_cuts=True, - seed=self.seed, - local_measurement=local_measurement, - ) - - elif cut_strategy.num_fragments_probed: - with pytest.raises(ValueError): - cut_graph = qcut.find_and_place_cuts( - graph=graph, - cut_strategy=cut_strategy, - local_measurement=local_measurement, - ) - return - - else: - cut_graph = qcut.find_and_place_cuts( - graph=graph, - cut_strategy=cut_strategy, - replace_wire_cuts=True, - seed=self.seed, - local_measurement=local_measurement, - ) - - if cut_strategy.max_free_wires > 2: - expected_num_cut_edges = 2 - num_frags = 2 - else: - # There's some inherent randomness in Kahypar that's not fixable by seed. - # Need to make this condition a bit relaxed for the extreme case. - expected_num_cut_edges = [10, 11, 14, 15] - num_frags = [9, 10, 13, 14] - - frags, comm_graph = qcut.fragment_graph(cut_graph) - - if num_frags == 2: - assert len(frags) == num_frags - assert len(comm_graph.edges) == expected_num_cut_edges - - assert ( - len([n for n in cut_graph.nodes if isinstance(n.obj, qcut.MeasureNode)]) - == expected_num_cut_edges - ) - assert ( - len([n for n in cut_graph.nodes if isinstance(n.obj, qcut.PrepareNode)]) - == expected_num_cut_edges - ) - - # Cutting wire "a" is more balanced, thus will be cut if there's no manually placed cut on - # wire 1: - expected_cut_wire = 1 if with_manual_cut else "a" - assert all( - list(n.obj.wires) == [expected_cut_wire] - for n in cut_graph.nodes - if isinstance(n.obj, (qcut.MeasureNode, qcut.PrepareNode)) - ) - - expected_fragment_sizes = [7, 11] if with_manual_cut else [8, 10] - assert expected_fragment_sizes == [f.number_of_nodes() for f in frags] - - else: - assert len(frags) in num_frags - assert len(comm_graph.edges) in expected_num_cut_edges - - assert ( - len([n for n in cut_graph.nodes if isinstance(n.obj, qcut.MeasureNode)]) - in expected_num_cut_edges - ) - assert ( - len([n for n in cut_graph.nodes if isinstance(n.obj, qcut.PrepareNode)]) - in expected_num_cut_edges - ) - class TestAutoCutCircuit: """Integration tests for automatic-cutting-enabled `cut_circuit` transform. @@ -5507,7 +5390,6 @@ def circuit(x): assert cut_res_bs.shape == target.shape assert isinstance(cut_res_bs, type(target)) - @pytest.mark.usefixtures("use_legacy_opmath") @pytest.mark.parametrize("measure_all_wires", [False, True]) def test_cut_mps(self, measure_all_wires): """Test auto cut this circuit: @@ -5548,90 +5430,15 @@ def block(weights, wires): device_size = 2 cut_strategy = qml.qcut.CutStrategy(max_free_wires=device_size) - with qml.queuing.AnnotatedQueue() as q0: - qml.MPS(range(n_wires), n_block_wires, block, n_params_block, template_weights) - if measure_all_wires: - qml.expval(qml.pauli.string_to_pauli_word("Z" * n_wires)) - else: - qml.expval(qml.PauliZ(wires=n_wires - 1)) - - tape0 = qml.tape.QuantumScript.from_queue(q0) - tape = tape0.expand() - graph = qcut.tape_to_graph(tape) - cut_graph = qcut.find_and_place_cuts( - graph=graph, - cut_strategy=cut_strategy, - replace_wire_cuts=True, - ) - frags, _ = qcut.fragment_graph(cut_graph) - assert len(frags) == 7 - if measure_all_wires: - lower, upper = 5, 6 + obs = [qml.PauliZ(i) for i in range(n_wires)] + obs = reduce(lambda a, b: a @ b, obs) else: - lower, upper = 4, 5 - assert all(lower <= f.order() <= upper for f in frags) - - # each frag should have the device size constraint satisfied. - assert all(len(set(e[2] for e in f.edges.data("wire"))) <= device_size for f in frags) - - @pytest.mark.parametrize("measure_all_wires", [False, True]) - def test_cut_mps_opmath(self, measure_all_wires): - """Test auto cut this circuit with opmath enabled: - 0: ─╭C──RY──────────────────────────────────────────── β•­ - 1: ─╰X──RY─╭C──RY───────────────────────────────────── β”œ - 2: ────────╰X──RY─╭C──RY────────────────────────────── β”œ - 3: ───────────────╰X──RY─╭C──RY─────────────────────── β”œ - 4: ──────────────────────╰X──RY─╭C──RY──────────────── β”œ - 5: ─────────────────────────────╰X──RY─╭C──RY───────── β”œ - 6: ────────────────────────────────────╰X──RY─╭C──RY── β”œ - 7: ───────────────────────────────────────────╰X──RY── β•° - - into this: - - 0: ─╭C──RY──────────────────────────────────────────────────────────────────── β•­ - 1: ─╰X──RY──//─╭C──RY───────────────────────────────────────────────────────── β”œ - 2: ────────────╰X──RY──//─╭C──RY────────────────────────────────────────────── β”œ - 3: ───────────────────────╰X──RY──//─╭C──RY─────────────────────────────────── β”œ - 4: ──────────────────────────────────╰X──RY──//─╭C──RY──────────────────────── β”œ - 5: ─────────────────────────────────────────────╰X──RY──//─╭C──RY───────────── β”œ - 6: ────────────────────────────────────────────────────────╰X──RY──//─╭C──RY── β”œ - 7: ───────────────────────────────────────────────────────────────────╰X──RY── β•° - """ - - pytest.importorskip("kahypar") - - def block(weights, wires): - qml.CNOT(wires=[wires[0], wires[1]]) - qml.RY(weights[0], wires=wires[0]) - qml.RY(weights[1], wires=wires[1]) - - n_wires = 8 - n_block_wires = 2 - n_params_block = 2 - n_blocks = qml.MPS.get_n_blocks(range(n_wires), n_block_wires) - template_weights = [[0.1, -0.3]] * n_blocks - - device_size = 2 - cut_strategy = qml.qcut.CutStrategy(max_free_wires=device_size) + obs = qml.PauliZ(n_wires - 1) with qml.queuing.AnnotatedQueue() as q0: qml.MPS(range(n_wires), n_block_wires, block, n_params_block, template_weights) - if measure_all_wires: - qml.expval( - qml.prod( - qml.Z(0), - qml.Z(1), - qml.Z(2), - qml.Z(3), - qml.Z(4), - qml.Z(5), - qml.Z(6), - qml.Z(7), - ) - ) - else: - qml.expval(qml.PauliZ(wires=n_wires - 1)) + qml.expval(obs) tape0 = qml.tape.QuantumScript.from_queue(q0) tape = tape0.expand() @@ -5657,7 +5464,6 @@ def block(weights, wires): class TestCutCircuitWithHamiltonians: """Integration tests for `cut_circuit` transform with Hamiltonians.""" - @pytest.mark.usefixtures("use_legacy_opmath") def test_circuit_with_hamiltonian(self, mocker): """ Tests that the full automatic circuit cutting pipeline returns the correct value and @@ -5728,76 +5534,6 @@ def f(params): assert np.isclose(res, res_expected) assert np.allclose(grad, grad_expected) - def test_circuit_with_hamiltonian_opmath(self, mocker): - """ - Tests that the full automatic circuit cutting pipeline returns the correct value and - gradient for a complex circuit with multiple wire cut scenarios with opmath enabled. The circuit is the - uncut version of the circuit in ``TestCutCircuitTransform.test_complicated_circuit``. - - 0: ──BasisState(M0)─╭C───RX─╭C──╭C───────────────────── - 1: ─────────────────╰X──────╰X──╰Z────────────────╭RX── β•­ - 2: ──H──────────────╭C─────────────╭RY────────╭RY─│──── β”œ - 3: ─────────────────╰RY──H──╭C───H─╰C──╭RY──H─╰C──│──── β•° - 4: ─────────────────────────╰RY──H─────╰C─────────╰C─── - """ - - dev_original = qml.device("default.qubit", wires=5) - dev_cut = qml.device("default.qubit", wires=4) - - hamiltonian = qml.Hamiltonian( - [1.0, 1.0], - [qml.PauliZ(1) @ qml.PauliZ(2) @ qml.PauliZ(3), qml.PauliY(0) @ qml.PauliX(1)], - ) - - def two_qubit_unitary(param, wires): - qml.Hadamard(wires=[wires[0]]) - qml.CRY(param, wires=[wires[0], wires[1]]) - - def f(params): - qml.BasisState(np.array([1]), wires=[0]) - qml.WireCut(wires=0) - - qml.CNOT(wires=[0, 1]) - qml.WireCut(wires=0) - qml.RX(params[0], wires=0) - qml.CNOT(wires=[0, 1]) - - qml.WireCut(wires=0) - qml.WireCut(wires=1) - - qml.CZ(wires=[0, 1]) - qml.WireCut(wires=[0, 1]) - - two_qubit_unitary(params[1], wires=[2, 3]) - qml.WireCut(wires=3) - two_qubit_unitary(params[2] ** 2, wires=[3, 4]) - qml.WireCut(wires=3) - two_qubit_unitary(np.sin(params[3]), wires=[3, 2]) - qml.WireCut(wires=3) - two_qubit_unitary(np.sqrt(params[4]), wires=[4, 3]) - qml.WireCut(wires=3) - two_qubit_unitary(np.cos(params[1]), wires=[3, 2]) - qml.CRX(params[2], wires=[4, 1]) - - return qml.expval(hamiltonian) - - params = np.array([0.4, 0.5, 0.6, 0.7, 0.8], requires_grad=True) - - circuit = qml.QNode(f, dev_original) - cut_circuit = qcut.cut_circuit(qml.QNode(f, dev_cut)) - - res_expected = circuit(params) - grad_expected = qml.grad(circuit)(params) - - spy = mocker.spy(qcut.cutcircuit, "qcut_processing_fn") - res = cut_circuit(params) - assert spy.call_count == len(hamiltonian.ops) - - grad = qml.grad(cut_circuit)(params) - - assert np.isclose(res, res_expected) - assert np.allclose(grad, grad_expected) - def test_autoscale_and_grouped_with_hamiltonian(self, mocker): """ Tests that the full circuit cutting pipeline returns the correct value for a typical diff --git a/tests/transforms/test_transpile.py b/tests/transforms/test_transpile.py index aa2e7530ff8..b02ed2c15b6 100644 --- a/tests/transforms/test_transpile.py +++ b/tests/transforms/test_transpile.py @@ -82,7 +82,7 @@ def circuit(): with pytest.raises(NotImplementedError, match=err_msg): transpiled_qnode() - @pytest.mark.usefixtures("use_legacy_opmath") + @pytest.mark.usefixtures("legacy_opmath_only") def test_transpile_raise_not_implemented_tensorproduct_mmt(self): """test that error is raised when measurement is expectation of a Tensor product""" dev = qml.device("default.qubit", wires=[0, 1, 2, 3])