diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 18fa801074c..32a30d2bedb 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -111,6 +111,8 @@ * `BasisEmbedding` can accept an int as argument instead of a list of bits (optionally). Example: `qml.BasisEmbedding(4, wires = range(4))` is now equivalent to `qml.BasisEmbedding([0,1,0,0], wires = range(4))` (because 4=0b100). [(#2601)](https://github.com/PennyLaneAI/pennylane/pull/2601) +* Introduced a new `is_hermitian` property to determine if an operator can be used in a measurement process. + [(#2629)](https://github.com/PennyLaneAI/pennylane/pull/2629)

Breaking changes

* The `qml.queuing.Queue` class is now removed. diff --git a/pennylane/measurements.py b/pennylane/measurements.py index dfffe51f06a..b11b387f081 100644 --- a/pennylane/measurements.py +++ b/pennylane/measurements.py @@ -463,7 +463,7 @@ def circuit(x): Raises: QuantumFunctionError: `op` is not an instance of :class:`~.Observable` """ - if not isinstance(op, (qml.operation.Observable, qml.Hamiltonian)): + if not op.is_hermitian: raise qml.QuantumFunctionError( f"{op.name} is not an observable: cannot be used with expval" ) @@ -498,7 +498,7 @@ def circuit(x): Raises: QuantumFunctionError: `op` is not an instance of :class:`~.Observable` """ - if not isinstance(op, qml.operation.Observable): + if not op.is_hermitian: raise qml.QuantumFunctionError(f"{op.name} is not an observable: cannot be used with var") return MeasurementProcess(Variance, obs=op, shape=(1,), numeric_type=float) @@ -577,7 +577,7 @@ def circuit(x): observable ``obs``. """ if ( - not isinstance(op, qml.operation.Observable) and op is not None + op is not None and not op.is_hermitian ): # None type is also allowed for op raise qml.QuantumFunctionError( f"{op.name} is not an observable: cannot be used with sample" diff --git a/pennylane/operation.py b/pennylane/operation.py index 880946ed30a..862f10a407e 100644 --- a/pennylane/operation.py +++ b/pennylane/operation.py @@ -1000,6 +1000,11 @@ def hyperparameters(self): self._hyperparameters = {} return self._hyperparameters + @property + def is_hermitian(self): + """This property determines if an operator is hermitian.""" + return False + def decomposition(self): r"""Representation of the operator as a product of other operators. @@ -1552,6 +1557,11 @@ def _queue_category(self): """ return "_ops" if isinstance(self, Operation) else None + @property + def is_hermitian(self): + """All observables must be hermitian""" + return True + # pylint: disable=abstract-method return_type = None """None or ObservableReturnTypes: Measurement type that this observable is called with.""" diff --git a/tests/test_operation.py b/tests/test_operation.py index 0fca28ac9a3..1ec39e1fed8 100644 --- a/tests/test_operation.py +++ b/tests/test_operation.py @@ -600,6 +600,17 @@ class DummyOp(qml.operation.Operation): op = DummyOp(1.0, wires=0, id="test") assert op.control_wires == qml.wires.Wires([]) + def test_is_hermitian(self): + """Test that is_hermitian defaults to False for an Operator""" + + class DummyOp(qml.operation.Operation): + r"""Dummy custom operation""" + num_wires = 1 + grad_method = None + + op = DummyOp(wires=0) + assert op.is_hermitian is False + class TestObservableConstruction: """Test custom observables construction.""" @@ -711,6 +722,17 @@ class DummyObservable(qml.operation.Observable): with pytest.raises(Exception, match="Must specify the wires *"): DummyObservable() + def test_is_hermitian(self): + """Test that the id attribute of an observable can be set.""" + + class DummyObserv(qml.operation.Observable): + r"""Dummy custom observable""" + num_wires = 1 + grad_method = None + + op = DummyObserv(wires=0) + assert op.is_hermitian is True + class TestOperatorIntegration: """Integration tests for the Operator class"""