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:
codecov[bot]: # [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2668?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#2668](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2668?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (de76f5b) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/1ea40e7e7c41e9b48b3349e678b4f140226f567d?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (1ea40e7) will **decrease** coverage by `40.09%`.
> The diff coverage is `50.00%`.

```diff
@@             Coverage Diff             @@
##           master    #2668       +/-   ##
===========================================
- Coverage   99.58%   59.49%   -40.10%     
===========================================
  Files         249      249               
  Lines       20135    20141        +6     
===========================================
- Hits        20052    11982     -8070     
- Misses         83     8159     +8076     
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2668?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Coverage Δ | |
|---|---|---|
| [pennylane/drawer/tape\_text.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2668/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RyYXdlci90YXBlX3RleHQucHk=) | `72.44% <50.00%> (-27.56%)` | :arrow_down: |
| [pennylane/interfaces/jax.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2668/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%> (-100.00%)` | :arrow_down: |
| [pennylane/interfaces/jax\_jit.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2668/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2ludGVyZmFjZXMvamF4X2ppdC5weQ==) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [pennylane/interfaces/tensorflow.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2668/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2ludGVyZmFjZXMvdGVuc29yZmxvdy5weQ==) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [pennylane/interfaces/tensorflow\_autograph.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2668/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2ludGVyZmFjZXMvdGVuc29yZmxvd19hdXRvZ3JhcGgucHk=) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [pennylane/devices/default\_qubit\_jax.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2668/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.67%)` | :arrow_down: |
| [pennylane/devices/default\_qubit\_tf.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2668/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvZGVmYXVsdF9xdWJpdF90Zi5weQ==) | `0.00% <0.00%> (-92.76%)` | :arrow_down: |
| [pennylane/qchem/openfermion\_obs.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2668/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3FjaGVtL29wZW5mZXJtaW9uX29icy5weQ==) | `7.26% <0.00%> (-92.74%)` | :arrow_down: |
| [pennylane/hf/hamiltonian.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2668/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2hmL2hhbWlsdG9uaWFuLnB5) | `8.13% <0.00%> (-91.87%)` | :arrow_down: |
| [pennylane/fourier/visualize.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2668/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2ZvdXJpZXIvdmlzdWFsaXplLnB5) | `9.21% <0.00%> (-90.79%)` | :arrow_down: |
| ... and [189 more](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2668/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/2668?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/2668?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Last update [1ea40e7...de76f5b](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2668?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).

ankit27kh: Hey @albi3ro, I found another discrepancy with the mpl drawer. While out of the scope of this PR, it is still something you may want to look at when generalising control operations.
I have made bug report #2677.

Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 30ebfd728..8600b0912 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -309,6 +309,9 @@
 * The performance of building sparse Hamiltonians has been improved by accumulating the sparse representation of coefficient-operator pairs in a temporary storage and by eliminating unnecessary `kron` operations on identity matrices. 
   [(#2630)](https://github.com/PennyLaneAI/pennylane/pull/2630)
 
+* Control values are now displayed distinctly in text and mpl drawings of circuits.
+  [(#2668)](https://github.com/PennyLaneAI/pennylane/pull/2668)
+
 <h3>Breaking changes</h3>
 
 * The `qml.queuing.Queue` class is now removed.
diff --git a/pennylane/drawer/tape_mpl.py b/pennylane/drawer/tape_mpl.py
index 670e2f01d..937da2e34 100644
--- a/pennylane/drawer/tape_mpl.py
+++ b/pennylane/drawer/tape_mpl.py
@@ -86,7 +86,7 @@ special_cases = {
 }
 """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
index 508e351c6..c3e71e9c1 100644
--- 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
index ef98cf981..144383c2c 100644
--- a/tests/drawer/test_tape_mpl.py
+++ b/tests/drawer/test_tape_mpl.py
@@ -412,6 +412,40 @@ class TestControlledGates:
         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
index 44f1a46d3..00512e8bc 100644
--- a/tests/drawer/test_tape_text.py
+++ b/tests/drawer/test_tape_text.py
@@ -73,8 +73,8 @@ class TestHelperFunctions:
         "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 @@ class TestHelperFunctions:
         "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 @@ class TestMaxLength:
 
 
 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
index b956ee808..ea9d34786 100644
--- a/tests/interfaces/test_tensorflow_qnode.py
+++ b/tests/interfaces/test_tensorflow_qnode.py
@@ -126,7 +126,7 @@ class TestQNode:
             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
index 520496106..b3c7a1ecf 100644
--- a/tests/interfaces/test_torch_qnode.py
+++ b/tests/interfaces/test_torch_qnode.py
@@ -120,7 +120,7 @@ class TestQNode:
         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
index cc76b7609..570cd4934 100644
--- a/tests/transforms/test_defer_measurements.py
+++ b/tests/transforms/test_defer_measurements.py
@@ -804,8 +804,8 @@ class TestDrawing:
         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