Repository: PennyLaneAI/pennylane
Pull request #1200 (2021-04-09 05:17:17)

Adjoint upgrades
**Context:**
Now that operations are replacing templates, the assumption that adjoints can be defined as a single gate isn't always true. In these contexts, it may make the most sense to expand an operation first and then adjoint it.

Diego ran into this issue when trying to add `adjoint` to his Carry and Sum templates. 

I also went ahead and removed the `do_queue` argument on adjoint, making the signatures of transforms more aligned (see #1161)

**Description of the Change:**
Adjoint will now expand ops that don't have a native `adjoint` defined. 

**Benefits:**
Nearly all ops should now work out of the box with adjoint.

**Possible Drawbacks:**
Devices that can take advantage of certain non-decomposed ops may see significant overhead when forced to use the decomposed adjoint.

**Related GitHub Issues:**
#1161 

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/1200?src=pr&el=h1) Report
> Merging [#1200](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1200?src=pr&el=desc) (af0333e) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/779f9ecba2620a5ad7a2afce1757430c287d53ce?el=desc) (779f9ec) will **decrease** coverage by `0.00%`.
> The diff coverage is `98.78%`.

[![Impacted file tree graph](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1200/graphs/tree.svg?width=650&height=150&src=pr&token=9S3YzlUgGk)](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1200?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1200      +/-   ##
==========================================
- Coverage   98.11%   98.11%   -0.01%     
==========================================
  Files         145      145              
  Lines       10943    10941       -2     
==========================================
- Hits        10737    10735       -2     
  Misses        206      206              
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1200?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pennylane/operation.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1200/diff?src=pr&el=tree#diff-cGVubnlsYW5lL29wZXJhdGlvbi5weQ==) | `96.02% <ø> (+0.20%)` | :arrow_up: |
| [pennylane/transforms/adjoint.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1200/diff?src=pr&el=tree#diff-cGVubnlsYW5lL3RyYW5zZm9ybXMvYWRqb2ludC5weQ==) | `92.30% <66.66%> (-7.70%)` | :arrow_down: |
| [pennylane/ops/qubit.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1200/diff?src=pr&el=tree#diff-cGVubnlsYW5lL29wcy9xdWJpdC5weQ==) | `98.29% <100.00%> (-0.01%)` | :arrow_down: |
| [pennylane/transforms/control.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/1200/diff?src=pr&el=tree#diff-cGVubnlsYW5lL3RyYW5zZm9ybXMvY29udHJvbC5weQ==) | `100.00% <100.00%> (ø)` | |

------

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

Thenerdstation: @josh146 I would also like to add diego on this PR
Thenerdstation: Dont you hate it when coverage goes down because you removed things lol
josh146: > Dont you hate it when coverage goes down because you removed things lol

I really don't get why this happens
Thenerdstation: @DSGuala you shouldn't need to define adjoint for template-like ops after this is merged in.

Patch:
diff --git a/pennylane/ops/qubit.py b/pennylane/ops/qubit.py
index 49d26ad14..2af1f6f37 100644
--- a/pennylane/ops/qubit.py
+++ b/pennylane/ops/qubit.py
@@ -92,8 +92,8 @@ class Hadamard(Observable, Operation):
         ]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
-        return Hadamard(wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return Hadamard(wires=self.wires)
 
 
 class PauliX(Observable, Operation):
@@ -147,8 +147,8 @@ class PauliX(Observable, Operation):
         ]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
-        return PauliX(wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return PauliX(wires=self.wires)
 
     def _controlled(self, wire):
         CNOT(wires=Wires(wire) + self.wires)
@@ -207,8 +207,8 @@ class PauliY(Observable, Operation):
         ]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
-        return PauliY(wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return PauliY(wires=self.wires)
 
     def _controlled(self, wire):
         CY(wires=Wires(wire) + self.wires)
@@ -250,8 +250,8 @@ class PauliZ(Observable, DiagonalOperation):
         decomp_ops = [PhaseShift(np.pi, wires=wires)]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
-        return PauliZ(wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return PauliZ(wires=self.wires)
 
     def _controlled(self, wire):
         CZ(wires=Wires(wire) + self.wires)
@@ -291,8 +291,8 @@ class S(DiagonalOperation):
         decomp_ops = [PhaseShift(np.pi / 2, wires=wires)]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
-        return S(wires=self.wires, do_queue=do_queue).inv()
+    def adjoint(self):
+        return S(wires=self.wires).inv()
 
 
 class T(DiagonalOperation):
@@ -329,8 +329,8 @@ class T(DiagonalOperation):
         decomp_ops = [PhaseShift(np.pi / 4, wires=wires)]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
-        return T(wires=self.wires, do_queue=do_queue).inv()
+    def adjoint(self):
+        return T(wires=self.wires).inv()
 
 
 class SX(Operation):
@@ -372,8 +372,8 @@ class SX(Operation):
         ]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
-        return SX(wires=self.wires, do_queue=do_queue).inv()
+    def adjoint(self):
+        return SX(wires=self.wires).inv()
 
 
 class CNOT(Operation):
@@ -406,8 +406,8 @@ class CNOT(Operation):
     def _matrix(cls, *params):
         return CNOT.matrix
 
-    def adjoint(self, do_queue=False):
-        return CNOT(wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return CNOT(wires=self.wires)
 
     def _controlled(self, wire):
         Toffoli(wires=Wires(wire) + self.wires)
@@ -448,8 +448,8 @@ class CZ(DiagonalOperation):
     def _eigvals(cls, *params):
         return cls.eigvals
 
-    def adjoint(self, do_queue=False):
-        return CZ(wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return CZ(wires=self.wires)
 
 
 class CY(Operation):
@@ -494,8 +494,8 @@ class CY(Operation):
         decomp_ops = [CRY(np.pi, wires=wires), S(wires=wires[0])]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
-        return CY(wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return CY(wires=self.wires)
 
 
 class SWAP(Operation):
@@ -526,8 +526,8 @@ class SWAP(Operation):
     def _matrix(cls, *params):
         return cls.matrix
 
-    def adjoint(self, do_queue=False):
-        return SWAP(wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return SWAP(wires=self.wires)
 
     def _controlled(self, wire):
         CSWAP(wires=wire + self.wires)
@@ -578,8 +578,8 @@ class CSWAP(Operation):
     def _matrix(cls, *params):
         return cls.matrix
 
-    def adjoint(self, do_queue=False):
-        return CSWAP(wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return CSWAP(wires=self.wires)
 
 
 class Toffoli(Operation):
@@ -628,8 +628,8 @@ class Toffoli(Operation):
     def _matrix(cls, *params):
         return cls.matrix
 
-    def adjoint(self, do_queue=False):
-        return Toffoli(wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return Toffoli(wires=self.wires)
 
 
 class RX(Operation):
@@ -666,8 +666,8 @@ class RX(Operation):
 
         return np.array([[c, js], [js, c]])
 
-    def adjoint(self, do_queue=False):
-        return RX(-self.data[0], wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return RX(-self.data[0], wires=self.wires)
 
     def _controlled(self, wire):
         CRX(*self.parameters, wires=wire + self.wires)
@@ -707,8 +707,8 @@ class RY(Operation):
 
         return np.array([[c, -s], [s, c]])
 
-    def adjoint(self, do_queue=False):
-        return RY(-self.data[0], wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return RY(-self.data[0], wires=self.wires)
 
     def _controlled(self, wire):
         CRY(*self.parameters, wires=wire + self.wires)
@@ -754,8 +754,8 @@ class RZ(DiagonalOperation):
 
         return np.array([p, p.conjugate()])
 
-    def adjoint(self, do_queue=False):
-        return RZ(-self.data[0], wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return RZ(-self.data[0], wires=self.wires)
 
     def _controlled(self, wire):
         CRZ(*self.parameters, wires=wire + self.wires)
@@ -802,8 +802,8 @@ class PhaseShift(DiagonalOperation):
         decomp_ops = [RZ(phi, wires=wires)]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
-        return PhaseShift(-self.data[0], wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return PhaseShift(-self.data[0], wires=self.wires)
 
     def _controlled(self, wire):
         ControlledPhaseShift(*self.parameters, wires=wire + self.wires)
@@ -860,8 +860,8 @@ class ControlledPhaseShift(DiagonalOperation):
         ]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
-        return ControlledPhaseShift(-self.data[0], wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return ControlledPhaseShift(-self.data[0], wires=self.wires)
 
 
 class Rot(Operation):
@@ -917,9 +917,9 @@ class Rot(Operation):
         decomp_ops = [RZ(phi, wires=wires), RY(theta, wires=wires), RZ(omega, wires=wires)]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
+    def adjoint(self):
         phi, theta, omega = self.parameters
-        return Rot(-omega, -theta, -phi, wires=self.wires, do_queue=do_queue)
+        return Rot(-omega, -theta, -phi, wires=self.wires)
 
     def _controlled(self, wire):
         CRot(*self.parameters, wires=wire + self.wires)
@@ -1012,7 +1012,7 @@ class MultiRZ(DiagonalOperation):
         for i in range(len(wires) - 1):
             CNOT(wires=[wires[i + 1], wires[i]])
 
-    def adjoint(self, do_queue=False):
+    def adjoint(self):
         return MultiRZ(-self.parameters[0], wires=self.wires)
 
 
@@ -1198,10 +1198,8 @@ class PauliRot(Operation):
             elif gate == "Y":
                 RX(-np.pi / 2, wires=[wire])
 
-    def adjoint(self, do_queue=False):
-        return PauliRot(
-            -self.parameters[0], self.parameters[1], wires=self.wires, do_queue=do_queue
-        )
+    def adjoint(self):
+        return PauliRot(-self.parameters[0], self.parameters[1], wires=self.wires)
 
 
 # Four term gradient recipe for controlled rotations
@@ -1267,8 +1265,8 @@ class CRX(Operation):
         ]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
-        return CRX(-self.data[0], wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return CRX(-self.data[0], wires=self.wires)
 
 
 class CRY(Operation):
@@ -1324,8 +1322,8 @@ class CRY(Operation):
         ]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
-        return CRY(-self.data[0], wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return CRY(-self.data[0], wires=self.wires)
 
 
 class CRZ(DiagonalOperation):
@@ -1400,8 +1398,8 @@ class CRZ(DiagonalOperation):
         ]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
-        return CRZ(-self.data[0], wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return CRZ(-self.data[0], wires=self.wires)
 
 
 class CRot(Operation):
@@ -1465,9 +1463,9 @@ class CRot(Operation):
         ]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
+    def adjoint(self):
         phi, theta, omega = self.parameters
-        return CRot(-omega, -theta, -phi, wires=self.wires, do_queue=do_queue)
+        return CRot(-omega, -theta, -phi, wires=self.wires)
 
 
 class U1(Operation):
@@ -1509,8 +1507,8 @@ class U1(Operation):
     def decomposition(phi, wires):
         return [PhaseShift(phi, wires=wires)]
 
-    def adjoint(self, do_queue=False):
-        return U1(-self.data[0], wires=self.wires, do_queue=do_queue)
+    def adjoint(self):
+        return U1(-self.data[0], wires=self.wires)
 
 
 class U2(Operation):
@@ -1568,11 +1566,11 @@ class U2(Operation):
         ]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
+    def adjoint(self):
         phi, lam = self.parameters
         new_lam = (np.pi - phi) % (2 * np.pi)
         new_phi = (np.pi - lam) % (2 * np.pi)
-        return U2(new_phi, new_lam, wires=self.wires, do_queue=do_queue)
+        return U2(new_phi, new_lam, wires=self.wires)
 
 
 class U3(Operation):
@@ -1637,11 +1635,11 @@ class U3(Operation):
         ]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
+    def adjoint(self):
         theta, phi, lam = self.parameters
         new_lam = (np.pi - phi) % (2 * np.pi)
         new_phi = (np.pi - lam) % (2 * np.pi)
-        return U3(theta, new_phi, new_lam, wires=self.wires, do_queue=do_queue)
+        return U3(theta, new_phi, new_lam, wires=self.wires)
 
 
 # =============================================================================
@@ -1712,9 +1710,9 @@ class SingleExcitation(Operation):
         ]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
+    def adjoint(self):
         (phi,) = self.parameters
-        return SingleExcitation(-phi, wires=self.wires, do_queue=do_queue)
+        return SingleExcitation(-phi, wires=self.wires)
 
 
 class SingleExcitationMinus(Operation):
@@ -1755,9 +1753,9 @@ class SingleExcitationMinus(Operation):
 
         return np.array([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]])
 
-    def adjoint(self, do_queue=False):
+    def adjoint(self):
         (phi,) = self.parameters
-        return SingleExcitationMinus(-phi, wires=self.wires, do_queue=do_queue)
+        return SingleExcitationMinus(-phi, wires=self.wires)
 
 
 class SingleExcitationPlus(Operation):
@@ -1798,9 +1796,9 @@ class SingleExcitationPlus(Operation):
 
         return np.array([[e, 0, 0, 0], [0, c, -s, 0], [0, s, c, 0], [0, 0, 0, e]])
 
-    def adjoint(self, do_queue=False):
+    def adjoint(self):
         (phi,) = self.parameters
-        return SingleExcitationPlus(-phi, wires=self.wires, do_queue=do_queue)
+        return SingleExcitationPlus(-phi, wires=self.wires)
 
 
 # =============================================================================
@@ -1839,10 +1837,8 @@ class QubitUnitary(Operation):
 
         return U
 
-    def adjoint(self, do_queue=False):
-        return QubitUnitary(
-            qml.math.T(qml.math.conj(self.data[0])), wires=self.wires, do_queue=do_queue
-        )
+    def adjoint(self):
+        return QubitUnitary(qml.math.T(qml.math.conj(self.data[0])), wires=self.wires)
 
     def _controlled(self, wire):
         ControlledQubitUnitary(*self.parameters, control_wires=wire, wires=self.wires)
@@ -2043,10 +2039,8 @@ class DiagonalQubitUnitary(DiagonalOperation):
     def decomposition(D, wires):
         return [QubitUnitary(np.diag(D), wires=wires)]
 
-    def adjoint(self, do_queue=False):
-        return DiagonalQubitUnitary(
-            qml.math.conj(self.parameters[0]), wires=self.wires, do_queue=do_queue
-        )
+    def adjoint(self):
+        return DiagonalQubitUnitary(qml.math.conj(self.parameters[0]), wires=self.wires)
 
     def _controlled(self, control):
         DiagonalQubitUnitary(
@@ -2091,7 +2085,6 @@ class QFT(Operation):
             qml.QFT(wires=range(wires))
             return qml.state()
 
-    The inverse quantum Fourier transform is accessed using ``qml.QFT(wires).inv()``.
     """
     num_params = 0
     num_wires = AnyWires
@@ -2142,9 +2135,6 @@ class QFT(Operation):
 
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
-        return QFT(wires=self.wires, do_queue=do_queue).inv()
-
 
 # =============================================================================
 # Quantum chemistry
@@ -2226,9 +2216,9 @@ class DoubleExcitation(Operation):
         ]
         return decomp_ops
 
-    def adjoint(self, do_queue=False):
+    def adjoint(self):
         (theta,) = self.parameters
-        return DoubleExcitation(-theta, wires=self.wires, do_queue=do_queue)
+        return DoubleExcitation(-theta, wires=self.wires)
 
 
 class DoubleExcitationPlus(Operation):
@@ -2286,9 +2276,9 @@ class DoubleExcitationPlus(Operation):
 
         return U
 
-    def adjoint(self, do_queue=False):
+    def adjoint(self):
         (theta,) = self.parameters
-        return DoubleExcitationPlus(-theta, wires=self.wires, do_queue=do_queue)
+        return DoubleExcitationPlus(-theta, wires=self.wires)
 
 
 class DoubleExcitationMinus(Operation):
@@ -2346,9 +2336,9 @@ class DoubleExcitationMinus(Operation):
 
         return U
 
-    def adjoint(self, do_queue=False):
+    def adjoint(self):
         (theta,) = self.parameters
-        return DoubleExcitationMinus(-theta, wires=self.wires, do_queue=do_queue)
+        return DoubleExcitationMinus(-theta, wires=self.wires)
 
 
 # =============================================================================
@@ -2387,7 +2377,7 @@ class BasisState(Operation):
     def decomposition(n, wires):
         return BasisStatePreparation(n, wires)
 
-    def adjoint(self, do_queue=False):
+    def adjoint(self):
         raise AdjointError("No adjoint exists for BasisState operations.")
 
 
@@ -2421,7 +2411,7 @@ class QubitStateVector(Operation):
     def decomposition(state, wires):
         return MottonenStatePreparation(state, wires)
 
-    def adjoint(self, do_queue=False):
+    def adjoint(self):
         raise AdjointError("No adjoint exists for QubitStateVector operations.")
 
 
diff --git a/pennylane/transforms/adjoint.py b/pennylane/transforms/adjoint.py
index 00c9dac19..c135027ec 100644
--- a/pennylane/transforms/adjoint.py
+++ b/pennylane/transforms/adjoint.py
@@ -14,7 +14,7 @@
 """Code for the adjoint transform."""
 
 from functools import wraps
-from pennylane.tape import QuantumTape
+from pennylane.tape import QuantumTape, get_active_tape
 
 
 def adjoint(fn):
@@ -86,9 +86,15 @@ def adjoint(fn):
 
     @wraps(fn)
     def wrapper(*args, **kwargs):
-        with QuantumTape(do_queue=False) as tape:
+        with get_active_tape().stop_recording(), QuantumTape() as tape:
             fn(*args, **kwargs)
         for op in reversed(tape.queue):
-            op.adjoint(do_queue=True)
+            try:
+                op.adjoint()
+            except NotImplementedError:
+                # Decompose the operation and adjoint the result.
+                # We do not do anything with the output since
+                # decomposition will automatically queue the new operations.
+                adjoint(op.decomposition)(wires=op.wires)
 
     return wrapper
diff --git a/pennylane/transforms/control.py b/pennylane/transforms/control.py
index a651835e6..6540da9fe 100644
--- a/pennylane/transforms/control.py
+++ b/pennylane/transforms/control.py
@@ -15,7 +15,7 @@
 Contains the control transform.
 """
 from functools import wraps
-from pennylane.tape import QuantumTape
+from pennylane.tape import QuantumTape, get_active_tape
 from pennylane.operation import Operation, AnyWires
 from pennylane.wires import Wires
 from pennylane.transforms.adjoint import adjoint
@@ -90,11 +90,12 @@ class ControlledOperation(Operation):
             tape = expand_with_control(tape, wire)
         return tape
 
-    def adjoint(self, do_queue=False):
-        with QuantumTape(do_queue=False) as new_tape:
+    def adjoint(self):
+        """Returns a new ControlledOperation that is equal to the adjoint of `self`"""
+        with get_active_tape().stop_recording(), QuantumTape() as new_tape:
             # Execute all ops adjointed.
             adjoint(requeue_ops_in_tape)(self.subtape)
-        return ControlledOperation(new_tape, self.control_wires, do_queue=do_queue)
+        return ControlledOperation(new_tape, self.control_wires)
 
     def _controlled(self, wires):
         ControlledOperation(tape=self.subtape, control_wires=Wires(wires) + self.control_wires)


Test patch:
diff --git a/tests/ops/test_qubit_ops.py b/tests/ops/test_qubit_ops.py
index 24d244649..9b20e5dc1 100644
--- a/tests/ops/test_qubit_ops.py
+++ b/tests/ops/test_qubit_ops.py
@@ -366,7 +366,6 @@ class TestOperations:
             qml.CRot(0.123, 0.456, 0.789, wires=[0, 1]),
             qml.QubitUnitary(np.eye(2) * 1j, wires=0),
             qml.DiagonalQubitUnitary(np.array([1.0, 1.j]), wires=1),
-            qml.QFT(wires=[1, 2, 3]),
             qml.ControlledQubitUnitary(np.eye(2) * 1j, wires=[0], control_wires=[2]),
             qml.MultiControlledX(control_wires=[0, 1], wires=2, control_values='01'),
             qml.SingleExcitation(0.123, wires=[0, 3]),
@@ -384,6 +383,19 @@ class TestOperations:
         np.testing.assert_allclose(res2, np.eye(2 ** len(op.wires)), atol=tol)
         assert op.wires == op_d.wires
 
+    @pytest.mark.parametrize(
+        "op_builder", 
+        [
+            lambda: qml.QFT(wires=[1, 2, 3])
+        ])
+    def test_adjoint_with_decomposition(self, op_builder):
+        op = op_builder()
+        decomposed_ops = op.decomposition(wires=op.wires)
+        with qml.tape.QuantumTape() as adjoint_tape:
+            qml.adjoint(op_builder)()
+        for a, b in zip(decomposed_ops, reversed(adjoint_tape.operations)):
+            np.testing.assert_allclose(a.matrix, np.conj(b.matrix).T)
+
     @pytest.mark.parametrize(
         "op", 
         [