Repository: PennyLaneAI/pennylane

Issue:
[BUG] draw_mpl misses controls for multi-target-wire ops with interspersed wires
### Expected behavior

All control and target wires are indicated in some way, or the draw function fails.

### Actual behavior

The control wire sandwiched between target wires is hidden behind a box drawing, For example, the attached image is hiding a control on wire 2.

![image](https://github.com/PennyLaneAI/pennylane/assets/23283972/8ddcac21-1fe9-46e8-94f0-731d931be315)


### Additional information

_No response_

### Source code

```shell
def c():
    qml.ctrl(qml.IsingXX(1.1, [1, 3]), [0, 2])

_ = qml.draw_mpl(c, wire_order=range(4))()
```


### Tracebacks

_No response_

### System information

```shell
Name: PennyLane
Version: 0.31.0.dev0
Summary: PennyLane is a Python quantum machine learning library by Xanadu Inc.
Home-page: https://github.com/XanaduAI/pennylane
Author: 
Author-email: 
License: Apache License 2.0
Location: /Users/matthews/.pyenv/versions/3.9.13/envs/pl/lib/python3.9/site-packages
Editable project location: /Users/matthews/src/github.com/PennyLaneAI/pennylane
Requires: appdirs, autograd, autoray, cachetools, networkx, numpy, pennylane-lightning, requests, rustworkx, scipy, semantic-version, toml
Required-by: PennyLane-Lightning

Platform info:           macOS-13.4-arm64-arm-64bit
Python version:          3.9.13
Numpy version:           1.23.5
Scipy version:           1.10.0
Installed devices:
- default.gaussian (PennyLane-0.31.0.dev0)
- default.mixed (PennyLane-0.31.0.dev0)
- default.qubit (PennyLane-0.31.0.dev0)
- default.qubit.autograd (PennyLane-0.31.0.dev0)
- default.qubit.jax (PennyLane-0.31.0.dev0)
- default.qubit.tf (PennyLane-0.31.0.dev0)
- default.qubit.torch (PennyLane-0.31.0.dev0)
- default.qutrit (PennyLane-0.31.0.dev0)
- null.qubit (PennyLane-0.31.0.dev0)
- lightning.qubit (PennyLane-Lightning-0.31.0.dev3)
```


### Existing GitHub issues

- [X] I have searched existing GitHub issues to make sure the issue does not already exist.

Discussion:
timmysilv: [sc-39671]
timmysilv: hehe you both realized the same thing. was trying to be smart and save some checks but i'll just check all wires.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4295?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#4295](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4295?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (66ed461) into [master](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/b5613afdbdc0673b76835f428f2dcdd47389a880?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (b5613af) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##           master    #4295   +/-   ##
=======================================
  Coverage   99.79%   99.79%           
=======================================
  Files         352      352           
  Lines       32151    32162   +11     
=======================================
+ Hits        32084    32095   +11     
  Misses         67       67           
```


| [Impacted Files](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4295?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Coverage Δ | |
|---|---|---|
| [pennylane/gradients/\_\_init\_\_.py](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4295?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2dyYWRpZW50cy9fX2luaXRfXy5weQ==) | `100.00% <ø> (ø)` | |
| [pennylane/gradients/pulse\_generator\_gradient.py](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4295?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2dyYWRpZW50cy9wdWxzZV9nZW5lcmF0b3JfZ3JhZGllbnQucHk=) | `100.00% <ø> (ø)` | |
| [pennylane/gradients/pulse\_gradient.py](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4295?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2dyYWRpZW50cy9wdWxzZV9ncmFkaWVudC5weQ==) | `100.00% <ø> (ø)` | |
| [pennylane/math/\_\_init\_\_.py](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4295?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL21hdGgvX19pbml0X18ucHk=) | `100.00% <ø> (ø)` | |
| [pennylane/math/quantum.py](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4295?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL21hdGgvcXVhbnR1bS5weQ==) | `100.00% <ø> (ø)` | |
| [pennylane/operation.py](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4295?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL29wZXJhdGlvbi5weQ==) | `97.33% <ø> (ø)` | |
| [pennylane/pulse/convenience\_functions.py](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4295?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3B1bHNlL2NvbnZlbmllbmNlX2Z1bmN0aW9ucy5weQ==) | `100.00% <ø> (ø)` | |
| [pennylane/qchem/convert.py](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4295?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3FjaGVtL2NvbnZlcnQucHk=) | `100.00% <ø> (ø)` | |
| [pennylane/qchem/observable\_hf.py](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4295?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3FjaGVtL29ic2VydmFibGVfaGYucHk=) | `100.00% <ø> (ø)` | |
| [pennylane/qchem/tapering.py](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4295?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3FjaGVtL3RhcGVyaW5nLnB5) | `99.52% <ø> (ø)` | |
| ... and [14 more](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4295?src=pr&el=tree-more&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 7667e77f5..5c4a4b0d9 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -14,6 +14,11 @@
 
 <h3>Bug fixes 🐛</h3>
 
+* Raise a warning if control indicators are hidden when calling `qml.draw_mpl`
+  [(#4295)](https://github.com/PennyLaneAI/pennylane/pull/4295)
+
 <h3>Contributors ✍️</h3>
 
 This release contains contributions from (in alphabetical order):
+
+Matthew Silverman
diff --git a/pennylane/drawer/mpldrawer.py b/pennylane/drawer/mpldrawer.py
index 29c260d2b..c0d1a6cce 100644
--- a/pennylane/drawer/mpldrawer.py
+++ b/pennylane/drawer/mpldrawer.py
@@ -15,6 +15,7 @@
 This module contains the MPLDrawer class for creating circuit diagrams with matplotlib
 """
 from collections.abc import Iterable
+import warnings
 
 has_mpl = True
 try:
@@ -591,6 +592,15 @@ class MPLDrawer:
         min_wire = min(wires_all)
         max_wire = max(wires_all)
 
+        if len(wires_target) > 1:
+            min_target, max_target = min(wires_target), max(wires_target)
+            if any(min_target < w < max_target for w in wires_ctrl):
+                warnings.warn(
+                    "Some control indicators are hidden behind an operator. Consider re-ordering "
+                    "your circuit wires to ensure all control indicators are visible.",
+                    UserWarning,
+                )
+
         line = plt.Line2D((layer, layer), (min_wire, max_wire), **options)
         self._ax.add_line(line)
 


Test patch:
diff --git a/tests/drawer/test_mpldrawer.py b/tests/drawer/test_mpldrawer.py
index 28cd5a94c..26bb683ff 100644
--- a/tests/drawer/test_mpldrawer.py
+++ b/tests/drawer/test_mpldrawer.py
@@ -19,6 +19,7 @@ page in the developement guide.
 """
 # pylint: disable=protected-access,wrong-import-position
 
+import warnings
 import pytest
 
 plt = pytest.importorskip("matplotlib.pyplot")
@@ -429,6 +430,28 @@ class TestCTRL:
         assert circle.center == (0, 0)
         plt.close()
 
+    @pytest.mark.parametrize(
+        "control_wires,target_wires",
+        [
+            ((1,), (0, 2)),
+            ((0, 2), (1, 3)),
+            ((1, 3), (0, 2)),
+            ((0, 2, 4), (1, 3)),
+        ],
+    )
+    def test_ctrl_raises_warning_with_overlap(self, control_wires, target_wires):
+        """Tests that a warning is raised if some control indicators are not visible."""
+        drawer = MPLDrawer(1, 4)
+        with pytest.warns(UserWarning, match="control indicators are hidden behind an operator"):
+            drawer.ctrl(0, control_wires, target_wires)
+
+    @pytest.mark.parametrize("control_wires,target_wires", [((0,), (1, 2)), ((2,), (0, 1))])
+    def test_ctrl_no_warning_without_overlap(self, control_wires, target_wires):
+        drawer = MPLDrawer(1, 3)
+        with warnings.catch_warnings(record=True) as w:
+            drawer.ctrl(0, control_wires, target_wires)
+        assert len(w) == 0
+
     def test_target_x(self):
         """Tests hidden target_x drawing method"""