Repository: PennyLaneAI/pennylane
Pull request #1377 (2021-05-31 17:50:56)

Add an ID attribute to the Operator class
**Context:**

In several current additions to PennyLane, it would be useful if a user can "mark" operations in a circuit, which can then be identified and addressed (i.e. replaced) by a tape transform.

**Description of the Change:**

Adds an `id` attribute to the `Operator` class.

**Benefits:**

Easy way to give ops a custom name.

**Possible Drawbacks:**

It would be nice to follow tf's `name` as the expression, but this is already taken for the kind of op. 



github-actions[bot]: Hello. You may have forgotten to update the changelog!
Please edit [.github/CHANGELOG.md](/PennyLaneAI/pennylane/blob/master/.github/CHANGELOG.md) with:
* A one-to-two sentence description of the change. You may include a small working example for new features.
* A link back to this PR.
* Your name (or GitHub username) in the contributors section.
codecov[bot]: # [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1377?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#1377](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1377?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (c260161) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/a4d7abdc047869a2d378e54c7b1cc0b036ae7625?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (a4d7abd) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1377/graphs/tree.svg?width=650&height=150&src=pr&token=9S3YzlUgGk&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1377?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)

```diff
@@           Coverage Diff           @@
##           master    #1377   +/-   ##
=======================================
  Coverage   98.16%   98.16%           
=======================================
  Files         154      154           
  Lines       11548    11552    +4     
=======================================
+ Hits        11336    11340    +4     
  Misses        212      212           
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1377?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Coverage Δ | |
|---|---|---|
| [pennylane/operation.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1377/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL29wZXJhdGlvbi5weQ==) | `95.86% <100.00%> (+0.03%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1377?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1377?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Last update [a4d7abd...c260161](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1377?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).


Patch:
diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md
index 3fd197299..884d572ba 100644
--- a/.github/CHANGELOG.md
+++ b/.github/CHANGELOG.md
@@ -243,6 +243,12 @@ random_mat2 = rng.standard_normal(3, requires_grad=False)
 
 <h3>Improvements</h3>
 
+* The `Operator` (and by inheritance, the `Operation` and `Observable` class and their children) 
+  now have an `id` attribute, which can mark an operator in a circuit, for example to
+  identify it on the tape by a tape transform.
+  [(#1377)](https://github.com/PennyLaneAI/pennylane/pull/1377)
+
+  
 * Implement special handling for measuring the variance of Projector observables to improve memory usage.
   [(#1368)](https://github.com/PennyLaneAI/pennylane/pull/1368)
 
diff --git a/pennylane/operation.py b/pennylane/operation.py
index 422ce461d..1142c28cb 100644
--- a/pennylane/operation.py
+++ b/pennylane/operation.py
@@ -400,13 +400,19 @@ class Operator(abc.ABC):
         """String for the name of the operator."""
         return self._name
 
+    @property
+    def id(self):
+        """String for the ID of the operator."""
+        return self._id
+
     @name.setter
     def name(self, value):
         self._name = value
 
-    def __init__(self, *params, wires=None, do_queue=True):
+    def __init__(self, *params, wires=None, do_queue=True, id=None):
         # pylint: disable=too-many-branches
         self._name = self.__class__.__name__  #: str: name of the operator
+        self._id = id
         self.queue_idx = None  #: int, None: index of the Operator in the circuit queue, or None if not in a queue
 
         if wires is None:
@@ -685,7 +691,7 @@ class Operation(Operator):
         """Get and set the name of the operator."""
         return self._name + Operation.string_for_inverse if self.inverse else self._name
 
-    def __init__(self, *params, wires=None, do_queue=True):
+    def __init__(self, *params, wires=None, do_queue=True, id=None):
 
         self._inverse = False
 
@@ -712,7 +718,7 @@ class Operation(Operator):
         else:
             assert self.grad_recipe is None, "Gradient recipe is only used by the A method!"
 
-        super().__init__(*params, wires=wires, do_queue=do_queue)
+        super().__init__(*params, wires=wires, do_queue=do_queue, id=id)
 
 
 class DiagonalOperation(Operation):
@@ -960,13 +966,13 @@ class Observable(Operator):
         """
         return super().eigvals
 
-    def __init__(self, *params, wires=None, do_queue=True):
+    def __init__(self, *params, wires=None, do_queue=True, id=None):
         # extract the arguments
         if wires is None:
             wires = params[-1]
             params = params[:-1]
 
-        super().__init__(*params, wires=wires, do_queue=do_queue)
+        super().__init__(*params, wires=wires, do_queue=do_queue, id=id)
 
     def __repr__(self):
         """Constructor-call-like representation."""


Test patch:
diff --git a/tests/test_operation.py b/tests/test_operation.py
index a80545992..9d5ed3424 100644
--- a/tests/test_operation.py
+++ b/tests/test_operation.py
@@ -378,6 +378,19 @@ class TestOperationConstruction:
         with pytest.raises(ValueError, match="Must specify the wires"):
             DummyOp(0.54, 0)
 
+    def test_id(self):
+        """Test that the id attribute of an operator can be set."""
+
+        class DummyOp(qml.operation.Operation):
+            r"""Dummy custom operation"""
+            num_wires = 1
+            num_params = 1
+            par_domain = "N"
+            grad_method = None
+
+        op = DummyOp(1.0, wires=0, id="test")
+        assert op.id == "test"
+
 
 class TestObservableConstruction:
     """Test custom observables construction."""
@@ -460,6 +473,19 @@ class TestObservableConstruction:
         expected = "PauliZ(wires=['a'])"
         assert str(m) == expected
 
+    def test_id(self):
+        """Test that the id attribute of an observable can be set."""
+
+        class DummyObserv(qml.operation.Observable):
+            r"""Dummy custom observable"""
+            num_wires = 1
+            num_params = 1
+            par_domain = "N"
+            grad_method = None
+
+        op = DummyObserv(1.0, wires=0, id="test")
+        assert op.id == "test"
+
 
 class TestOperatorIntegration:
     """Integration tests for the Operator class"""