Repository: PennyLaneAI/pennylane
Pull request #2133 (2022-01-27 18:20:40)

Updated non_parametric ops adjoint method and added test
**Context:**
The current `adjoint()` method implementation for non-parametric operations would lead to a bug in which taking multiple adjoints of the operation do not return the correct result ([#1968](https://github.com/PennyLaneAI/pennylane/issues/1968)). 

**Description of the Change:**
Updated the `adjoint()` method such that instead of initializing a new operation (of the same kind) and applying `inv()`, it would access the internal boolean attribute `op.inverse` and flip it. 

```python
>>> op = qml.S(wires=0)
>>>
>>>
>>> # OLD: 
>>> print(op)
S(wires=[0])
>>>
>>> print(op.adjoint())
S.inv(wires=[0])
>>>
>>> print(op.adjoint().adjoint())
S.inv(wires=[0])
>>>
>>> 
>>> # New: 
>>> print(op)
S(wires=[0])
>>>
>>> print(op.adjoint())
S.inv(wires=[0])
>>>
>>> print(op.adjoint().adjoint())
S(wires=[0])
```



github-actions[bot]: Hello. You may have forgotten to update the changelog!
Please edit [`doc/releases/changelog-dev.md`](/PennyLaneAI/pennylane/blob/master/doc/releases/changelog-dev.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/2133?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#2133](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2133?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (b305ae3) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/f9bffe4566a93f52f732723f9d29f98340898eff?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (f9bffe4) will **decrease** coverage by `2.80%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2133/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/2133?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)

```diff
@@            Coverage Diff             @@
##           master    #2133      +/-   ##
==========================================
- Coverage   99.20%   96.39%   -2.81%     
==========================================
  Files         230      230              
  Lines       17717    17727      +10     
==========================================
- Hits        17576    17088     -488     
- Misses        141      639     +498     
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2133?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Coverage Δ | |
|---|---|---|
| [pennylane/ops/qubit/non\_parametric\_ops.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2133/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL29wcy9xdWJpdC9ub25fcGFyYW1ldHJpY19vcHMucHk=) | `97.30% <100.00%> (+0.06%)` | :arrow_up: |
| [pennylane/interfaces/batch/jax.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2133/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2ludGVyZmFjZXMvYmF0Y2gvamF4LnB5) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [pennylane/interfaces/batch/jax\_jit.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2133/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2ludGVyZmFjZXMvYmF0Y2gvamF4X2ppdC5weQ==) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [pennylane/interfaces/jax.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2133/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2ludGVyZmFjZXMvamF4LnB5) | `0.00% <0.00%> (-97.44%)` | :arrow_down: |
| [pennylane/devices/default\_qubit\_jax.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2133/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvZGVmYXVsdF9xdWJpdF9qYXgucHk=) | `0.00% <0.00%> (-96.63%)` | :arrow_down: |
| [pennylane/qnn/cost.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2133/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3Fubi9jb3N0LnB5) | `42.85% <0.00%> (-57.15%)` | :arrow_down: |
| [pennylane/transforms/classical\_jacobian.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2133/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3RyYW5zZm9ybXMvY2xhc3NpY2FsX2phY29iaWFuLnB5) | `77.08% <0.00%> (-22.92%)` | :arrow_down: |
| [pennylane/math/utils.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2133/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL21hdGgvdXRpbHMucHk=) | `84.41% <0.00%> (-15.59%)` | :arrow_down: |
| [pennylane/math/multi\_dispatch.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2133/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL21hdGgvbXVsdGlfZGlzcGF0Y2gucHk=) | `87.73% <0.00%> (-12.27%)` | :arrow_down: |
| [pennylane/math/is\_independent.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2133/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL21hdGgvaXNfaW5kZXBlbmRlbnQucHk=) | `88.50% <0.00%> (-11.50%)` | :arrow_down: |
| ... and [15 more](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2133/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2133?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/2133?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Last update [f9bffe4...b305ae3](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2133?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).

Jaybsoni: [sc-11983]

Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index e0f49be30..06e6332ee 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -585,7 +585,12 @@
   vector.
   [(#2028)](https://github.com/PennyLaneAI/pennylane/pull/2028)
 
-<h3>Documentation</h3>
+* Updated the `adjoint()` method for non-parametric qubit operations to 
+  solve a bug where repeated `adjoint()` calls don't return the correct 
+  operator.
+  [(#2133)](https://github.com/PennyLaneAI/pennylane/pull/2133)
+
+* <h3>Documentation</h3>
 
 * Fixes an error in the signs of equations in the `DoubleExcitation` page.
   [(#2072)](https://github.com/PennyLaneAI/pennylane/pull/2072)
diff --git a/pennylane/ops/qubit/non_parametric_ops.py b/pennylane/ops/qubit/non_parametric_ops.py
index 4a5374b44..385b7eba9 100644
--- a/pennylane/ops/qubit/non_parametric_ops.py
+++ b/pennylane/ops/qubit/non_parametric_ops.py
@@ -314,7 +314,9 @@ class S(Operation):
         return decomp_ops
 
     def adjoint(self):
-        return S(wires=self.wires).inv()
+        op = S(wires=self.wires)
+        op.inverse = not self.inverse
+        return op
 
     def single_qubit_rot_angles(self):
         # S = RZ(\pi/2) RY(0) RZ(0)
@@ -358,7 +360,9 @@ class T(Operation):
         return decomp_ops
 
     def adjoint(self):
-        return T(wires=self.wires).inv()
+        op = T(wires=self.wires)
+        op.inverse = not self.inverse
+        return op
 
     def single_qubit_rot_angles(self):
         # T = RZ(\pi/4) RY(0) RZ(0)
@@ -407,7 +411,9 @@ class SX(Operation):
         return decomp_ops
 
     def adjoint(self):
-        return SX(wires=self.wires).inv()
+        op = SX(wires=self.wires)
+        op.inverse = not self.inverse
+        return op
 
     def single_qubit_rot_angles(self):
         # SX = RZ(-\pi/2) RY(\pi/2) RZ(\pi/2)
@@ -646,7 +652,9 @@ class ISWAP(Operation):
         return decomp_ops
 
     def adjoint(self):
-        return ISWAP(wires=self.wires).inv()
+        op = ISWAP(wires=self.wires)
+        op.inverse = not self.inverse
+        return op
 
 
 class SISWAP(Operation):
@@ -707,7 +715,9 @@ class SISWAP(Operation):
         return decomp_ops
 
     def adjoint(self):
-        return SISWAP(wires=self.wires).inv()
+        op = SISWAP(wires=self.wires)
+        op.inverse = not self.inverse
+        return op
 
 
 SQISW = SISWAP


Test patch:
diff --git a/tests/ops/qubit/test_non_parametric_ops.py b/tests/ops/qubit/test_non_parametric_ops.py
index 392697939..ebcbdc7f2 100644
--- a/tests/ops/qubit/test_non_parametric_ops.py
+++ b/tests/ops/qubit/test_non_parametric_ops.py
@@ -915,3 +915,72 @@ def test_control_wires(op, control_wires):
     """Test ``control_wires`` attribute for non-parametrized operations."""
 
     assert op.control_wires == control_wires
+
+
+all_ops = [
+    qml.Identity(0),
+    qml.Hadamard(0),
+    qml.PauliX(0),
+    qml.PauliY(0),
+    qml.PauliZ(0),
+    qml.S(wires=0),
+    qml.T(wires=0),
+    qml.SX(wires=0),
+    qml.CNOT(wires=(0, 1)),
+    qml.CZ(wires=(0, 1)),
+    qml.CY(wires=(0, 1)),
+    qml.SWAP(wires=(0, 1)),
+    qml.ISWAP(wires=(0, 1)),
+    qml.SISWAP(wires=(0, 1)),
+    qml.SQISW(wires=(0, 1)),
+    qml.CSWAP(wires=(0, 1, 2)),
+    qml.Toffoli(wires=(0, 1, 2)),
+    qml.MultiControlledX(control_wires=(0, 1, 2), wires=(3)),
+    qml.Barrier(0),
+    qml.WireCut(wires=0),
+]
+
+involution_ops = [  # ops who are their own inverses
+    qml.Identity,
+    qml.Hadamard,
+    qml.PauliX,
+    qml.PauliY,
+    qml.PauliZ,
+    qml.CNOT,
+    qml.CZ,
+    qml.CY,
+    qml.SWAP,
+    qml.CSWAP,
+    qml.Toffoli,
+    qml.MultiControlledX,
+    qml.Barrier,
+    qml.WireCut,
+]
+
+
+@pytest.mark.parametrize("op", all_ops)
+def test_adjoint_method(op, tol):
+    for num_adjoint_calls in range(1, 4):
+
+        adj_op = copy.copy(op)
+        for i in range(num_adjoint_calls):
+            adj_op = adj_op.adjoint()
+
+        if (type(op) in involution_ops) or (num_adjoint_calls % 2 == 0):
+            expected_adj_op = copy.copy(op)
+
+        else:
+            expected_adj_op = copy.copy(op)
+            expected_adj_op.inverse = not expected_adj_op.inverse
+
+        assert adj_op.name == expected_adj_op.name
+        assert (
+            adj_op.label() == expected_adj_op.label()
+        )  # check that the name and labels are the same
+
+        try:
+            np.testing.assert_allclose(
+                adj_op.matrix, expected_adj_op.matrix, atol=tol
+            )  # compare matrix if its defined
+        except NotImplementedError:
+            pass