From c1b9aa239ad6317a5b3175852189f93bce0b8d33 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Fri, 28 Jul 2023 16:35:28 -0400 Subject: [PATCH] Raise warning in Operator `__eq__` and `__hash__` about change in behaviour (#4144) * Updated Operator * Added tests * Updated warning * black * Updated warning text * black * Update doc/development/deprecations.rst * Updated docs, tests * Updated deprecations.rst * Fixing tests * Updated tests and warnings * Pylint * Testing warnings * Testing changes * Updated tests * testing what breaks * Checking tests * Testing changes * Testing changes to operator __eq__ * Updated until `nodes_between` * Updated metric tensor, circuit graph * Updating `qml.equal` * Linters * Updated tape tests * Added coverage * Updates changelog * Removed changes to __eq__ and __hash__ * Update pennylane/operation.py Co-authored-by: Matthew Silverman * Testing changes to operator __eq__ * Updated until `nodes_between` * Updated metric tensor, circuit graph * Updating `qml.equal` * Linters * Fixes for new autoray version (#4396) * Fixes for new autoray version * unused import * Use if instead of try-except * Fix case of two torch tensors * Updated tape tests * Add ability to apply `TransformProgram` to batch of tapes (#4364) * Draft structure * draf exec * Simple execute * Update * More tests * Update * Update exec * Pylint and black * Update tests * Update more tests * More tests * changelog * Coverage * Cover fix * pylint * Pylint * Pylint tests * proposed changes to transform program integration * oops * add to legacy, remove cotransform support * just transform program call component * just transform program call component * no longer support cotransforms, fix _batch_postprocessing * some more testing * test null postprocessing function * docstring, rename batch_slices to slices, black * Apply suggestions from code review Co-authored-by: Matthew Silverman --------- Co-authored-by: rmoyard Co-authored-by: Matthew Silverman * torch qnode integration with DefaultQubit2 (#4392) * copy-paste torch file; replace device in most places * make tests work; little changes in pennylane * pylint fix * changelog * update test name and docstring for hamiltonian_expand * minimal test for single-shot bugfix * Added coverage * Updates changelog * Removed changes to __eq__ and __hash__ * Update pennylane/operation.py Co-authored-by: Matthew Silverman * Updated deprecations to 0.32 * Added warnings * Trigger CI * Removed docstrings * Added correctness test * Trigger CI --------- Co-authored-by: Tom Bromley <49409390+trbromley@users.noreply.github.com> Co-authored-by: Matthew Silverman Co-authored-by: Edward Jiang <34989448+eddddddy@users.noreply.github.com> Co-authored-by: Christina Lee Co-authored-by: rmoyard --- doc/development/deprecations.rst | 22 ++++++++++++++ doc/releases/changelog-dev.md | 4 +++ pennylane/operation.py | 25 +++++++++++++++ tests/pytest.ini | 3 +- tests/test_operation.py | 52 ++++++++++++++++++++++++++++++++ 5 files changed, 105 insertions(+), 1 deletion(-) diff --git a/doc/development/deprecations.rst b/doc/development/deprecations.rst index 70ce217dbfb..7d2c59061cd 100644 --- a/doc/development/deprecations.rst +++ b/doc/development/deprecations.rst @@ -46,6 +46,28 @@ Pending deprecations some_qfunc(params) return qml.expval(Hamiltonian) +* The behaviour of ``Operator.__eq__`` and ``Operator.__hash__`` will be updated soon. Their documentation + has been updated to reflect the incoming changes. + + The upcoming changes to operator equality will allow users to use operator equality the same way as + with ``qml.equal``. With the changes to hashing, unique operators that are equal will have the same + hash. These changes will allow behaviour such as the following: + + >>> qml.RX(0.1, wires=0) == qml.RX(0.1, wires=0) + True + >>> {qml.PauliZ(0), qml.PauliZ(0)} + {PauliZ(wires=[0])} + + Meanwhile, the current behaviour is shown below: + + >>> qml.RX(0.1, wires=0) == qml.RX(0.1, wires=0) + False + >>> {qml.PauliZ(0), qml.PauliZ(0)} + {PauliZ(wires=[0]), PauliZ(wires=[0])} + + - Added in v0.32 + - Behaviour will change in v0.33 + * The public methods of ``DefaultQubit`` are pending changes to follow the new device API, as used in ``DefaultQubit2``. diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 93358b94b61..7b3f990b1e7 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -165,6 +165,10 @@ been deprecated. Please use `QuantumScript.bind_new_parameters` instead. [(#4346)](https://github.com/PennyLaneAI/pennylane/pull/4346) +* `Operator.__eq__` and `Operator.__hash__` will now raise warnings to reflect upcoming + changes to operator equality and hashing. + [(#4144)](https://github.com/PennyLaneAI/pennylane/pull/4144) +

Documentation 📝

* The `qml.pulse.transmon_interaction` and `qml.pulse.transmon_drive` documentation has been updated. diff --git a/pennylane/operation.py b/pennylane/operation.py index 5f359d3e4e0..16920b5213c 100644 --- a/pennylane/operation.py +++ b/pennylane/operation.py @@ -715,6 +715,31 @@ def hash(self): ) ) + # pylint: disable=useless-super-delegation + def __eq__(self, other): + warnings.warn( + "The behaviour of operator equality will be updated soon. Currently, op1 == op2 is " + "True if op1 and op2 are the same object. Soon, op1 == op2 will be equivalent to " + "qml.equal(op1, op2). To continue using operator equality in its current state, " + "use 'op1 is op2'.", + UserWarning, + ) + + return super().__eq__(other) + + # pylint: disable=useless-super-delegation + def __hash__(self): + warnings.warn( + "The behaviour of operator hashing will be updated soon. Currently, each operator " + "instance has a unique hash. Soon, an operator's hash will be determined by the " + "combined hash of the name, wires, parameters and hyperparameters of the operator. " + "To continue using operator hashing in its current state, wrap the operator inside " + "a qml.queuing.WrappedObj instance.", + UserWarning, + ) + + return super().__hash__() + @staticmethod def compute_matrix(*params, **hyperparams): # pylint:disable=unused-argument r"""Representation of the operator as a canonical matrix in the computational basis (static method). diff --git a/tests/pytest.ini b/tests/pytest.ini index 58dc726a507..61de94d5928 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -19,4 +19,5 @@ filterwarnings = ignore:Casting complex values to real::autograd.numpy.numpy_wrapper ignore:Casting complex values to real discards the imaginary part:UserWarning:torch.autograd ignore:Call to deprecated create function:DeprecationWarning - ignore:the imp module is deprecated:DeprecationWarning \ No newline at end of file + ignore:the imp module is deprecated:DeprecationWarning + error:The behaviour of operator:UserWarning \ No newline at end of file diff --git a/tests/test_operation.py b/tests/test_operation.py index 185314404b8..38941929c8c 100644 --- a/tests/test_operation.py +++ b/tests/test_operation.py @@ -272,6 +272,58 @@ def __init__(self, wires, basis_state=None): # pylint:disable=super-init-not-ca state = [0, 1, 0] assert MyOp(wires=1, basis_state=state).hyperparameters["basis_state"] == state + def test_eq_warning(self): + """Test that a warning is raised when two operators are compared for equality + using `==`.""" + + class DummyOp(qml.operation.Operator): + num_wires = 1 + + op1 = DummyOp(0) + op2 = DummyOp(0) + + with pytest.warns(UserWarning, match="The behaviour of operator equality"): + _ = op1 == op2 + + def test_eq_correctness(self): + """Test that using `==` on two equivalent operators is True when both operators + are the same object and False otherwise.""" + + class DummyOp(qml.operation.Operator): + num_wires = 1 + + op1 = DummyOp(0) + op2 = DummyOp(0) + + with pytest.warns(UserWarning, match="The behaviour of operator equality"): + assert op1 == op1 # pylint: disable=comparison-with-itself + assert op1 != op2 + + def test_hash_warning(self): + """Test that a warning is raised when an operator's hash is used.""" + + class DummyOp(qml.operation.Operator): + num_wires = 1 + + op = DummyOp(0) + + with pytest.warns(UserWarning, match="The behaviour of operator hashing"): + _ = hash(op) + + def test_hash_correctness(self): + """Test that the hash of two equivalent operators is the same when both operators + are the same object and different otherwise.""" + + class DummyOp(qml.operation.Operator): + num_wires = 1 + + op1 = DummyOp(0) + op2 = DummyOp(0) + + with pytest.warns(UserWarning, match="The behaviour of operator hash"): + assert len({op1, op1}) == 1 + assert len({op1, op2}) == 2 + class TestPytreeMethods: def test_pytree_defaults(self):