From 79075a6d6f1e21acc02729f6fd061d793a1f8201 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 27 May 2022 12:07:02 -0400 Subject: [PATCH 1/7] initial commit for is_hermitian attribute --- pennylane/measurements.py | 9 ++++++--- pennylane/operation.py | 10 ++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/pennylane/measurements.py b/pennylane/measurements.py index 528f1dea9aa..fdbe86da318 100644 --- a/pennylane/measurements.py +++ b/pennylane/measurements.py @@ -468,7 +468,8 @@ 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: + # if not isinstance(op, (qml.operation.Observable, qml.Hamiltonian)): raise qml.QuantumFunctionError( f"{op.name} is not an observable: cannot be used with expval" ) @@ -503,7 +504,8 @@ 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: + # if not isinstance(op, qml.operation.Observable): 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) @@ -582,7 +584,8 @@ def circuit(x): observable ``obs``. """ if ( - not isinstance(op, qml.operation.Observable) and op is not None + not op.is_hermitian and op is not None + # not isinstance(op, qml.operation.Observable) and op is not None ): # 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 116e4f5e44e..432068b1c60 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.""" From 53b0e007c331630a20c849363790c61f6822599c Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 27 May 2022 14:57:36 -0400 Subject: [PATCH 2/7] pin protobuf temporarily for CI --- requirements-ci.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements-ci.txt b/requirements-ci.txt index 123122b9058..d9cf838d963 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -12,4 +12,5 @@ semantic_version==2.6 dask[delayed] autoray matplotlib -opt_einsum \ No newline at end of file +opt_einsum +protobuf<4.21.0 \ No newline at end of file From 4ac7c7deb90556fdd3c75697a34e54b3e8292724 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Fri, 27 May 2022 15:50:14 -0400 Subject: [PATCH 3/7] swap order of if to check none type first --- pennylane/measurements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane/measurements.py b/pennylane/measurements.py index fdbe86da318..0d9d0faea76 100644 --- a/pennylane/measurements.py +++ b/pennylane/measurements.py @@ -584,7 +584,7 @@ def circuit(x): observable ``obs``. """ if ( - not op.is_hermitian and op is not None + op is not None and not op.is_hermitian # not isinstance(op, qml.operation.Observable) and op is not None ): # None type is also allowed for op raise qml.QuantumFunctionError( From 20f64a8930bba799ebec02fcb713d2d94e8384fe Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 30 May 2022 16:11:59 -0400 Subject: [PATCH 4/7] added tests --- tests/test_operation.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_operation.py b/tests/test_operation.py index d9e1df1471d..0c8bb0ac398 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""" From ffb623ca4e7392569385a6e57892c3ccdba8bac4 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 30 May 2022 16:20:52 -0400 Subject: [PATCH 5/7] undo temp change for CI checks --- requirements-ci.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements-ci.txt b/requirements-ci.txt index d9cf838d963..123122b9058 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -12,5 +12,4 @@ semantic_version==2.6 dask[delayed] autoray matplotlib -opt_einsum -protobuf<4.21.0 \ No newline at end of file +opt_einsum \ No newline at end of file From ab2cf5edfec97909b2c68fd4801f6e9e940fe96a Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 30 May 2022 16:23:36 -0400 Subject: [PATCH 6/7] changelog --- doc/releases/changelog-dev.md | 2 ++ 1 file changed, 2 insertions(+) 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. From 3bf72baa35a16c389b77d6218e7ffef85f91f129 Mon Sep 17 00:00:00 2001 From: Jay Soni Date: Mon, 30 May 2022 16:36:16 -0400 Subject: [PATCH 7/7] Remove commented lines --- pennylane/measurements.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pennylane/measurements.py b/pennylane/measurements.py index 693ebeba6bd..b11b387f081 100644 --- a/pennylane/measurements.py +++ b/pennylane/measurements.py @@ -464,7 +464,6 @@ def circuit(x): QuantumFunctionError: `op` is not an instance of :class:`~.Observable` """ if not op.is_hermitian: - # if not isinstance(op, (qml.operation.Observable, qml.Hamiltonian)): raise qml.QuantumFunctionError( f"{op.name} is not an observable: cannot be used with expval" ) @@ -500,7 +499,6 @@ def circuit(x): QuantumFunctionError: `op` is not an instance of :class:`~.Observable` """ if not op.is_hermitian: - # if not isinstance(op, qml.operation.Observable): 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) @@ -580,7 +578,6 @@ def circuit(x): """ if ( op is not None and not op.is_hermitian - # not isinstance(op, qml.operation.Observable) and op is not None ): # None type is also allowed for op raise qml.QuantumFunctionError( f"{op.name} is not an observable: cannot be used with sample"