Repository: PennyLaneAI/pennylane

Issue:
Have control state of qubits visible in the circuit drawing
### Feature details

When using a controlled gate with custom control values, the drawing of the circuit does not represent this. The control values correctly show up in the mpl figure.
Example:
```
dev = qml.device('default.qubit',wires=4, shots=100)
@qml.qnode(dev)
def circ():
    for i in range(4):
        qml.Hadamard(wires=i)
    qml.MultiControlledX([0,1,2],3,'010')
    qml.MultiControlledX([0, 1, 2], 3, '111')
    qml.MultiControlledX([0, 1, 2], 3, '000')
    return qml.probs(0)

print(qml.draw(circ)())
fig, ax = qml.draw_mpl(circ)()
fig.show()
```
```
0: ──H─╭C─╭C─╭C─┤  Probs
1: ──H─├C─├C─├C─┤       
2: ──H─├C─├C─├C─┤       
3: ──H─╰X─╰X─╰X─┤
```
![image](https://user-images.githubusercontent.com/51011137/171986384-d868d98f-2e5f-4e8c-b89a-ca43a0b7daa5.png)



### Implementation

Will have to fix different symbols for 0 and 1 control state. Maybe 0 state can be represented by an 'O' and keep 'C' for state 1.

### How important would you say this feature is?

1: Not important. Would be nice to have.

### Additional information

_No response_


Discussion:
Hi @ankit27kh, this looks like a great issue to fix! Would you like to work on this for UnitaryHACK? You wouldn't get a bounty but you would get swag. Let us know if you would be interested!
Hey @CatalinaAlbornoz, sure I can take a stab at it.

Resolving patch:
diff --git a/pennylane/drawer/tape_mpl.py b/pennylane/drawer/tape_mpl.py
--- a/pennylane/drawer/tape_mpl.py
+++ b/pennylane/drawer/tape_mpl.py
@@ -86,7 +86,7 @@ def _add_wirecut(drawer, layer, mapped_wires, op):
 }
 """Dictionary mapping special case classes to functions for drawing them."""
 
-
+# pylint: disable=too-many-branches
 def tape_mpl(tape, wire_order=None, show_all_wires=False, decimals=None, **kwargs):
     """Produces a matplotlib graphic from a tape.
 
@@ -282,9 +282,19 @@ def tape_mpl(tape, wire_order=None, show_all_wires=False, decimals=None, **kwarg
             else:
                 control_wires = [wire_map[w] for w in op.control_wires]
                 target_wires = [wire_map[w] for w in op.wires if w not in op.control_wires]
+                control_values = op.hyperparameters.get("control_values", None)
+
+                if control_values is None:
+                    control_values = "1" * len(control_wires)
+                control_values = [(i == "1") for i in control_values]
 
                 if len(control_wires) != 0:
-                    drawer.ctrl(layer, control_wires, wires_target=target_wires)
+                    drawer.ctrl(
+                        layer,
+                        control_wires,
+                        wires_target=target_wires,
+                        control_values=control_values,
+                    )
                 drawer.box_gate(
                     layer,
                     target_wires,
diff --git a/pennylane/drawer/tape_text.py b/pennylane/drawer/tape_text.py
--- a/pennylane/drawer/tape_text.py
+++ b/pennylane/drawer/tape_text.py
@@ -41,8 +41,13 @@ def _add_op(op, layer_str, wire_map, decimals, cache):
     layer_str = _add_grouping_symbols(op, layer_str, wire_map)
 
     control_wires = op.control_wires
-    for w in control_wires:
-        layer_str[wire_map[w]] += "C"
+    control_values = op.hyperparameters.get("control_values", None)
+    if control_values:
+        for w, val in zip(control_wires, control_values):
+            layer_str[wire_map[w]] += "●" if val == "1" else "○"
+    else:
+        for w in control_wires:
+            layer_str[wire_map[w]] += "●"
 
     label = op.label(decimals=decimals, cache=cache).replace("\n", "")
     if len(op.wires) == 0:  # operation (e.g. barrier, snapshot) across all wires


Test patch:
diff --git a/tests/drawer/test_tape_mpl.py b/tests/drawer/test_tape_mpl.py
--- a/tests/drawer/test_tape_mpl.py
+++ b/tests/drawer/test_tape_mpl.py
@@ -412,6 +412,40 @@ def test_CRX_decimals(self):
         assert ax.texts[2].get_text() == "RX\n(1.23)"
         plt.close()
 
+    def test_control_values(self):
+        """Test control values get displayed correctly"""
+
+        with QuantumTape() as tape:
+            qml.ControlledQubitUnitary(
+                qml.matrix(qml.RX)(0, 0),
+                control_wires=[0, 1, 2, 3],
+                wires=[4],
+                control_values="1010",
+            )
+
+        _, ax = tape_mpl(tape)
+        layer = 0
+
+        # 5 wires -> 4 control, 1 target
+        assert len(ax.patches) == 5
+        # Circle for control values are positioned correctly
+        # Circle color matched according to the control value
+        assert ax.patches[0].center == (layer, 0)
+        assert ax.patches[0].get_facecolor() == mpl.colors.to_rgba(plt.rcParams["lines.color"])
+        assert ax.patches[1].center == (layer, 1)
+        assert ax.patches[1].get_facecolor() == mpl.colors.to_rgba(plt.rcParams["axes.facecolor"])
+        assert ax.patches[2].center == (layer, 2)
+        assert ax.patches[2].get_facecolor() == mpl.colors.to_rgba(plt.rcParams["lines.color"])
+        assert ax.patches[3].center == (layer, 3)
+        assert ax.patches[3].get_facecolor() == mpl.colors.to_rgba(plt.rcParams["axes.facecolor"])
+
+        # five wires, one control line
+        assert len(ax.lines) == 6
+        control_line = ax.lines[5]
+        assert control_line.get_data() == ((layer, layer), (0, 4))
+
+        plt.close()
+
 
 general_op_data = [
     qml.RX(1.234, wires=0),
diff --git a/tests/drawer/test_tape_text.py b/tests/drawer/test_tape_text.py
--- a/tests/drawer/test_tape_text.py
+++ b/tests/drawer/test_tape_text.py
@@ -73,8 +73,8 @@ def test_add_measurements_cache(self):
         "op, out",
         [
             (qml.PauliX(0), ["─X", "─", "─", "─"]),
-            (qml.CNOT(wires=(0, 2)), ["╭C", "│", "╰X", "─"]),
-            (qml.Toffoli(wires=(0, 1, 3)), ["╭C", "├C", "│", "╰X"]),
+            (qml.CNOT(wires=(0, 2)), ["╭●", "│", "╰X", "─"]),
+            (qml.Toffoli(wires=(0, 1, 3)), ["╭●", "├●", "│", "╰X"]),
             (qml.IsingXX(1.23, wires=(0, 2)), ["╭IsingXX", "│", "╰IsingXX", "─"]),
             (qml.Snapshot(), ["─|S|", "─|S|", "─|S|", "─|S|"]),
             (qml.Barrier(), ["─||", "─||", "─||", "─||"]),
@@ -88,8 +88,8 @@ def test_add_op(self, op, out):
         "op, out",
         [
             (qml.PauliY(1), ["─X", "─Y", "─", "─"]),
-            (qml.CNOT(wires=(1, 2)), ["─X", "╭C", "╰X", "─"]),
-            (qml.CRX(1.23, wires=(2, 3)), ["─X", "─", "╭C", "╰RX"]),
+            (qml.CNOT(wires=(1, 2)), ["─X", "╭●", "╰X", "─"]),
+            (qml.CRX(1.23, wires=(2, 3)), ["─X", "─", "╭●", "╰RX"]),
         ],
     )
     def test_add_second_op(self, op, out):
@@ -242,10 +242,11 @@ def test_setting_max_length(self, ml):
 
 
 single_op_tests_data = [
-    (qml.CNOT(wires=(0, 1)), "0: ─╭C─┤  \n1: ─╰X─┤  "),
-    (qml.Toffoli(wires=(0, 1, 2)), "0: ─╭C─┤  \n1: ─├C─┤  \n2: ─╰X─┤  "),
+    (qml.MultiControlledX([0, 1, 2], 3, "010"), "0: ─╭○─┤  \n1: ─├●─┤  \n2: ─├○─┤  \n3: ─╰X─┤  "),
+    (qml.CNOT(wires=(0, 1)), "0: ─╭●─┤  \n1: ─╰X─┤  "),
+    (qml.Toffoli(wires=(0, 1, 2)), "0: ─╭●─┤  \n1: ─├●─┤  \n2: ─╰X─┤  "),
     (qml.Barrier(wires=(0, 1, 2)), "0: ─╭||─┤  \n1: ─├||─┤  \n2: ─╰||─┤  "),
-    (qml.CSWAP(wires=(0, 1, 2)), "0: ─╭C────┤  \n1: ─├SWAP─┤  \n2: ─╰SWAP─┤  "),
+    (qml.CSWAP(wires=(0, 1, 2)), "0: ─╭●────┤  \n1: ─├SWAP─┤  \n2: ─╰SWAP─┤  "),
     (
         qml.DoubleExcitationPlus(1.23, wires=(0, 1, 2, 3)),
         "0: ─╭G²₊(1.23)─┤  \n1: ─├G²₊(1.23)─┤  \n2: ─├G²₊(1.23)─┤  \n3: ─╰G²₊(1.23)─┤  ",
diff --git a/tests/interfaces/test_tensorflow_qnode.py b/tests/interfaces/test_tensorflow_qnode.py
--- a/tests/interfaces/test_tensorflow_qnode.py
+++ b/tests/interfaces/test_tensorflow_qnode.py
@@ -126,7 +126,7 @@ def circuit(p1, p2=y, **kwargs):
             return qml.state()
 
         result = qml.draw(circuit)(p1=x, p3=z)
-        expected = "0: ──RX(0.10)──RX(0.40)─╭C─┤  State\n" "1: ──RY(0.06)───────────╰X─┤  State"
+        expected = "0: ──RX(0.10)──RX(0.40)─╭●─┤  State\n" "1: ──RY(0.06)───────────╰X─┤  State"
         assert result == expected
 
     def test_jacobian(self, dev_name, diff_method, mode, mocker, tol):
diff --git a/tests/interfaces/test_torch_qnode.py b/tests/interfaces/test_torch_qnode.py
--- a/tests/interfaces/test_torch_qnode.py
+++ b/tests/interfaces/test_torch_qnode.py
@@ -120,7 +120,7 @@ def circuit(p1, p2=y, **kwargs):
         circuit(p1=x, p3=z)
 
         result = qml.draw(circuit)(p1=x, p3=z)
-        expected = "0: ──RX(0.10)──RX(0.40)─╭C─┤  <Z>\n" "1: ──RY(0.06)───────────╰X─┤  <Z>"
+        expected = "0: ──RX(0.10)──RX(0.40)─╭●─┤  <Z>\n" "1: ──RY(0.06)───────────╰X─┤  <Z>"
 
         assert result == expected
 
diff --git a/tests/transforms/test_defer_measurements.py b/tests/transforms/test_defer_measurements.py
--- a/tests/transforms/test_defer_measurements.py
+++ b/tests/transforms/test_defer_measurements.py
@@ -804,8 +804,8 @@ def qfunc():
         transformed_qnode = qml.QNode(transformed_qfunc, dev)
 
         expected = (
-            "0: ─╭C────────────────────────────────────────────────────┤     \n"
+            "0: ─╭●────────────────────────────────────────────────────┤     \n"
             "1: ─╰ControlledOperation(0.31)─╭ControlledOperation(0.31)─┤  <Z>\n"
-            "2: ────────────────────────────╰C─────────────────────────┤     "
+            "2: ────────────────────────────╰●─────────────────────────┤     "
         )
         assert qml.draw(transformed_qnode)() == expected