Repository: PennyLaneAI/pennylane
Pull request #5149 (2024-02-02 20:30:58)

BugFix: draw_mpl fails with adjointed CNOT
**Context:**
Error raised when calling `draw_mpl` on a circuit containing an `Adjoint(CNOT)`. This is due to `unwrap_control` incorrectly considering `Adjoint` as a controlled operation because it has the `control_wires` property, and double counting the same control wires when generating default control values.

**Description of the Change:**
A check is added to only append more control values if the operation in focus is indeed a controlled operation.

**Related GitHub Issues:**
https://github.com/PennyLaneAI/pennylane/issues/5064

**Related Shortcut Story**
[sc-53947]

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://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5149?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
Attention: `1 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`35af47f`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/35af47f7ea71b172e30345a77990aed7d424b82f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) 99.69% compared to head [(`29abd45`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5149?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) 99.68%.
> Report is 3 commits behind head on master.

| [Files](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5149?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Patch % | Lines |
|---|---|---|
| [pennylane/drawer/utils.py](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5149?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RyYXdlci91dGlscy5weQ==) | 92.30% | [1 Missing :warning: ](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5149?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #5149      +/-   ##
==========================================
- Coverage   99.69%   99.68%   -0.01%     
==========================================
  Files         394      394              
  Lines       36073    35856     -217     
==========================================
- Hits        35962    35743     -219     
- Misses        111      113       +2     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5149?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).


Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index a9cdbc935..c2b6e436d 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -354,6 +354,9 @@
   [(#5125)](https://github.com/PennyLaneAI/pennylane/pull/5125/) 
   [(#5148)](https://github.com/PennyLaneAI/pennylane/pull/5148)
 
+* `draw_mpl` no longer raises an error when drawing a circuit containing an adjoint of a controlled operation.
+  [(#5149)](https://github.com/PennyLaneAI/pennylane/pull/5149)
+
 <h3>Contributors ✍️</h3>
 
 This release contains contributions from (in alphabetical order):
diff --git a/pennylane/drawer/utils.py b/pennylane/drawer/utils.py
index 9d474b94f..90c05ec08 100644
--- a/pennylane/drawer/utils.py
+++ b/pennylane/drawer/utils.py
@@ -14,6 +14,7 @@
 """
 This module contains some useful utility functions for circuit drawing.
 """
+import pennylane as qml
 from pennylane.ops import Controlled, Conditional
 from pennylane.measurements import MeasurementProcess, MidMeasureMP, MeasurementValue
 
@@ -95,6 +96,12 @@ def convert_wire_order(tape, wire_order=None, show_all_wires=False):
     return {wire: ind for ind, wire in enumerate(wire_order)}
 
 
+def _is_controlled(op):
+    """Checks whether an operation is a controlled operation."""
+    # TODO: remove the other ones once they all inherit from Controlled [sc-37951]
+    return isinstance(op, (Controlled, qml.CCZ, qml.CSWAP, qml.CH, qml.CNOT, qml.Toffoli))
+
+
 def unwrap_controls(op):
     """Unwraps nested controlled operations for drawing.
 
@@ -121,15 +128,17 @@ def unwrap_controls(op):
         next_ctrl = op
 
         while hasattr(next_ctrl, "base"):
-            base_control_wires = getattr(next_ctrl.base, "control_wires", [])
-            control_wires += base_control_wires
 
-            base_control_values = next_ctrl.base.hyperparameters.get(
-                "control_values", [True] * len(base_control_wires)
-            )
+            if _is_controlled(next_ctrl.base):
+                base_control_wires = getattr(next_ctrl.base, "control_wires", [])
+                control_wires += base_control_wires
+
+                base_control_values = next_ctrl.base.hyperparameters.get(
+                    "control_values", [True] * len(base_control_wires)
+                )
 
-            if control_values is not None:
-                control_values.extend(base_control_values)
+                if control_values is not None:
+                    control_values.extend(base_control_values)
 
             next_ctrl = next_ctrl.base
 


Test patch:
diff --git a/tests/drawer/test_draw_mpl.py b/tests/drawer/test_draw_mpl.py
index f53e1511f..6308a2b5f 100644
--- a/tests/drawer/test_draw_mpl.py
+++ b/tests/drawer/test_draw_mpl.py
@@ -383,3 +383,18 @@ def test_qnode_transform_program(mocker):
 
     _ = draw_qnode()
     spy.assert_called_once()
+
+
+def test_draw_mpl_with_control_in_adjoint():
+    def U(wires):
+        qml.adjoint(qml.CNOT)(wires=wires)
+
+    @qml.qnode(dev)
+    def circuit():
+        qml.ctrl(U, control=0)(wires=["a", 1.23])
+        return qml.state()
+
+    _, ax = qml.draw_mpl(circuit)()
+    assert len(ax.lines) == 4  # three wires, one control
+    assert len(ax.texts) == 4  # three wire labels, one gate label
+    assert ax.texts[-1].get_text() == "X†"