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"""