Repository: Qiskit/qiskit

Issue:
Display custom gate labels in circuit drawer
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues to confirm this idea does not exist. -->

### What is the expected enhancement?

In the most recent version of Terra gate objects now support a custom label string (default None). If this string is set it would be good to display this in the circuit drawers.

For example:

```python
from qiskit import QuantumRegister, QuantumCircuit
from qiskit.extensions import XGate, UnitaryGate

qr = QuantumRegister(2)
circ = QuantumCircuit(qr)
circ.append(XGate(), [qr[0]])
circ.append(XGate(label='alt-X'), [qr[0]])
circ.append(UnitaryGate([[1, 0, 0, 0],
                         [0, 0, 1j, 0],
                         [0, 1j, 1, 0],
                         [0, 0, 0, 1]], label="iswap"), [qr[0], qr[1]])
print(circ)
```

currently shows:
```
         ┌───┐┌───┐┌──────────┐
q1_0: |0>┤ X ├┤ X ├┤0         ├
         └───┘└───┘│  unitary │
q1_1: |0>──────────┤1         ├
                   └──────────┘
```

But with this change would ideally show:

```
         ┌───┐┌───────┐┌────────┐
q1_0: |0>┤ X ├┤ alt-X ├┤0       ├
         └───┘└───────┘│  iswap │
q1_1: |0>──────────────┤1       ├
                       └────────┘
```


Discussion:
This might just be an issue for unitary gates, which probably set the name parameter to `unitary`. I know for non-unitary custom gates this definitely works as you're describing.
Oh, I missed the custom `XGate` there too. I expect that's the same issue those subclasses of `Gate` set the name parameter and assigning a label doesn't override that.
What's the expected output if there are parameters? Also `Instruction` has no `label`...
And what about gates that dont have a place for label, like `swap`?
```
               
q1_0: |0>─X────
          │    
q1_1: |0>─┼──X─
          │  │ 
q2_0: |0>─X──┼─
             │ 
q2_1: |0>────X─
               
```
I see two options.

1. dont allow it on anything that is not unitary.
2. if we override then the swap becomes a box with the label with 0 and 1 as Chris gives in the example above


On the use of the label as an identifier https://github.com/Qiskit/qiskit-aer/issues/207

Resolving patch:
diff --git a/qiskit/visualization/text.py b/qiskit/visualization/text.py
--- a/qiskit/visualization/text.py
+++ b/qiskit/visualization/text.py
@@ -692,6 +692,8 @@ def params_for_label(instruction):
     @staticmethod
     def label_for_box(instruction, controlled=False):
         """ Creates the label for a box."""
+        if getattr(instruction.op, 'label', None) is not None:
+            return instruction.op.label
         if controlled:
             label = instruction.op.base_gate_name
         else:
@@ -789,7 +791,14 @@ def add_connected_gate(instruction, gates, layer, current_cons):
                 actual_index = self.qregs.index(instruction.qargs[i])
                 current_cons.append((actual_index, gate))
 
-        if instruction.name == 'measure':
+        if len(instruction.qargs) >= 2 and \
+                not instruction.cargs and \
+                getattr(instruction.op, 'label', None) is not None:
+            # If a multi qubit instruction has a label, it is a box
+            layer._set_multibox(instruction.op.label, qubits=instruction.qargs,
+                                conditional=conditional)
+
+        elif instruction.name == 'measure':
             gate = MeasureFrom()
             layer.set_qubit(instruction.qargs[0], gate)
             layer.set_clbit(instruction.cargs[0], MeasureTo())
@@ -879,18 +888,12 @@ def add_connected_gate(instruction, gates, layer, current_cons):
 
         elif len(instruction.qargs) >= 2 and not instruction.cargs:
             # multiple qubit gate
-            label = instruction.name
-            params = TextDrawing.params_for_label(instruction)
-            if params:
-                label += "(%s)" % ','.join(params)
+            label = TextDrawing.label_for_box(instruction)
             layer.set_qu_multibox(instruction.qargs, label, conditional=conditional)
 
         elif instruction.qargs and instruction.cargs:
             # multiple gate, involving both qargs AND cargs
-            label = instruction.name
-            params = TextDrawing.params_for_label(instruction)
-            if params:
-                label += "(%s)" % ','.join(params)
+            label = TextDrawing.label_for_box(instruction)
             layer._set_multibox(label, qubits=instruction.qargs, clbits=instruction.cargs,
                                 conditional=conditional)
         else:


Test patch:
diff --git a/test/python/visualization/test_circuit_text_drawer.py b/test/python/visualization/test_circuit_text_drawer.py
--- a/test/python/visualization/test_circuit_text_drawer.py
+++ b/test/python/visualization/test_circuit_text_drawer.py
@@ -29,7 +29,7 @@
 from qiskit.transpiler import Layout
 from qiskit.visualization import text as elements
 from qiskit.visualization.circuit_visualization import _text_circuit_drawer
-from qiskit.extensions.standard import HGate, U2Gate
+from qiskit.extensions import HGate, U2Gate, XGate, UnitaryGate, CzGate
 
 
 class TestTextDrawerElement(QiskitTestCase):
@@ -800,7 +800,7 @@ def test_2Qgate(self):
         qr = QuantumRegister(2, 'q')
         circuit = QuantumCircuit(qr)
 
-        my_gate2 = Gate(name='twoQ', num_qubits=2, params=[])
+        my_gate2 = Gate(name='twoQ', num_qubits=2, params=[], label='twoQ')
         circuit.append(my_gate2, [qr[0], qr[1]])
 
         self.assertEqual(str(_text_circuit_drawer(circuit, reverse_bits=True)), expected)
@@ -816,7 +816,7 @@ def test_2Qgate_cross_wires(self):
         qr = QuantumRegister(2, 'q')
         circuit = QuantumCircuit(qr)
 
-        my_gate2 = Gate(name='twoQ', num_qubits=2, params=[])
+        my_gate2 = Gate(name='twoQ', num_qubits=2, params=[], label='twoQ')
         circuit.append(my_gate2, [qr[1], qr[0]])
 
         self.assertEqual(str(_text_circuit_drawer(circuit, reverse_bits=True)), expected)
@@ -834,7 +834,7 @@ def test_3Qgate_cross_wires(self):
         qr = QuantumRegister(3, 'q')
         circuit = QuantumCircuit(qr)
 
-        my_gate3 = Gate(name='threeQ', num_qubits=3, params=[])
+        my_gate3 = Gate(name='threeQ', num_qubits=3, params=[], label='threeQ')
         circuit.append(my_gate3, [qr[1], qr[2], qr[0]])
 
         self.assertEqual(str(_text_circuit_drawer(circuit, reverse_bits=True)), expected)
@@ -851,7 +851,7 @@ def test_2Qgate_nottogether(self):
         qr = QuantumRegister(3, 'q')
         circuit = QuantumCircuit(qr)
 
-        my_gate2 = Gate(name='twoQ', num_qubits=2, params=[])
+        my_gate2 = Gate(name='twoQ', num_qubits=2, params=[], label='twoQ')
         circuit.append(my_gate2, [qr[0], qr[2]])
 
         self.assertEqual(str(_text_circuit_drawer(circuit, reverse_bits=True)), expected)
@@ -871,7 +871,7 @@ def test_2Qgate_nottogether_across_4(self):
         qr = QuantumRegister(4, 'q')
         circuit = QuantumCircuit(qr)
 
-        my_gate2 = Gate(name='twoQ', num_qubits=2, params=[])
+        my_gate2 = Gate(name='twoQ', num_qubits=2, params=[], label='twoQ')
         circuit.append(my_gate2, [qr[0], qr[3]])
 
         self.assertEqual(str(_text_circuit_drawer(circuit, reverse_bits=True)), expected)
@@ -882,7 +882,7 @@ def test_unitary_nottogether_across_4(self):
                               "q_0: |0>┤0         ├",
                               "        │          │",
                               "q_1: |0>┤          ├",
-                              "        │  unitary │",
+                              "        │  Unitary │",
                               "q_2: |0>┤          ├",
                               "        │          │",
                               "q_3: |0>┤1         ├",
@@ -914,7 +914,7 @@ def test_multiplexer(self):
         See https://github.com/Qiskit/qiskit-terra/pull/2238#issuecomment-487630014"""
         expected = '\n'.join(["        ┌──────────────┐",
                               "q_0: |0>┤0             ├",
-                              "        │  multiplexer │",
+                              "        │  Multiplexer │",
                               "q_1: |0>┤1             ├",
                               "        └──────────────┘"])
 
@@ -926,6 +926,37 @@ def test_multiplexer(self):
 
         self.assertEqual(str(_text_circuit_drawer(qc)), expected)
 
+    def test_label_over_name_2286(self):
+        """ If there is a label, it should be used instead of the name
+        See https://github.com/Qiskit/qiskit-terra/issues/2286 """
+        expected = '\n'.join(["        ┌───┐┌───────┐┌────────┐",
+                              "q_0: |0>┤ X ├┤ alt-X ├┤0       ├",
+                              "        └───┘└───────┘│  iswap │",
+                              "q_1: |0>──────────────┤1       ├",
+                              "                      └────────┘"])
+        qr = QuantumRegister(2, 'q')
+        circ = QuantumCircuit(qr)
+        circ.append(XGate(), [qr[0]])
+        circ.append(XGate(label='alt-X'), [qr[0]])
+        circ.append(UnitaryGate(numpy.eye(4), label="iswap"), [qr[0], qr[1]])
+
+        self.assertEqual(str(_text_circuit_drawer(circ)), expected)
+
+    def test_label_turns_to_box_2286(self):
+        """ If there is a label, non-boxes turn into boxes
+        See https://github.com/Qiskit/qiskit-terra/issues/2286 """
+        expected = '\n'.join(["           ┌───────────┐",
+                              "q_0: |0>─■─┤0          ├",
+                              "         │ │  cz label │",
+                              "q_1: |0>─■─┤1          ├",
+                              "           └───────────┘"])
+        qr = QuantumRegister(2, 'q')
+        circ = QuantumCircuit(qr)
+        circ.append(CzGate(), [qr[0], qr[1]])
+        circ.append(CzGate(label='cz label'), [qr[0], qr[1]])
+
+        self.assertEqual(str(_text_circuit_drawer(circ)), expected)
+
 
 class TestTextDrawerParams(QiskitTestCase):
     """Test drawing parameters."""
@@ -1426,7 +1457,7 @@ def test_conditional_multiplexer(self):
 
         expected = '\n'.join(["         ┌──────────────┐",
                               "qr_0: |0>┤0             ├",
-                              "         │  multiplexer │",
+                              "         │  Multiplexer │",
                               "qr_1: |0>┤1             ├",
                               "         └──────┬───────┘",
                               "qr_2: |0>───────┼────────",
@@ -1495,7 +1526,7 @@ def test_text_complex(self):
         See https://github.com/Qiskit/qiskit-terra/issues/3640 """
         expected = '\n'.join(["        ┌────────────────────────────────────┐",
                               "q_0: |0>┤0                                   ├",
-                              "        │  initialize(0.5+0.1j,0,0,0.86023j) │",
+                              "        │  Initialize(0.5+0.1j,0,0,0.86023j) │",
                               "q_1: |0>┤1                                   ├",
                               "        └────────────────────────────────────┘"
                               ])
@@ -1509,11 +1540,11 @@ def test_text_complex_pireal(self):
         See https://github.com/Qiskit/qiskit-terra/issues/3640 """
         expected = '\n'.join(["        ┌─────────────────────────────────┐",
                               "q_0: |0>┤0                                ├",
-                              "        │  initialize(pi/10,0,0,0.94937j) │",
+                              "        │  Initialize(pi/10,0,0,0.94937j) │",
                               "q_1: |0>┤1                                ├",
                               "        └─────────────────────────────────┘"
                               ])
-        ket = numpy.array([0.1*numpy.pi, 0, 0, 0.9493702944526474*1j])
+        ket = numpy.array([0.1 * numpy.pi, 0, 0, 0.9493702944526474 * 1j])
         circuit = QuantumCircuit(2)
         circuit.initialize(ket, [0, 1])
         self.assertEqual(circuit.draw(output='text').single_string(), expected)
@@ -1523,11 +1554,11 @@ def test_text_complex_piimaginary(self):
         See https://github.com/Qiskit/qiskit-terra/issues/3640 """
         expected = '\n'.join(["        ┌─────────────────────────────────┐",
                               "q_0: |0>┤0                                ├",
-                              "        │  initialize(0.94937,0,0,pi/10j) │",
+                              "        │  Initialize(0.94937,0,0,pi/10j) │",
                               "q_1: |0>┤1                                ├",
                               "        └─────────────────────────────────┘"
                               ])
-        ket = numpy.array([0.9493702944526474, 0, 0, 0.1*numpy.pi*1j])
+        ket = numpy.array([0.9493702944526474, 0, 0, 0.1 * numpy.pi * 1j])
         circuit = QuantumCircuit(2)
         circuit.initialize(ket, [0, 1])
         self.assertEqual(circuit.draw(output='text').single_string(), expected)
@@ -1541,7 +1572,7 @@ def test_text_all_1q_1c(self):
         """ Test q0-c0 in q0-c0"""
         expected = '\n'.join(["         ┌───────┐",
                               "qr_0: |0>┤0      ├",
-                              "         │  name │",
+                              "         │  Name │",
                               " cr_0: 0 ╡0      ╞",
                               "         └───────┘"])
 
@@ -1559,7 +1590,7 @@ def test_text_all_2q_2c(self):
                               "qr_0: |0>┤0      ├",
                               "         │       │",
                               "qr_1: |0>┤1      ├",
-                              "         │  name │",
+                              "         │  Name │",
                               " cr_0: 0 ╡0      ╞",
                               "         │       │",
                               " cr_1: 0 ╡1      ╞",
@@ -1585,7 +1616,7 @@ def test_text_4q_2c(self):
                               "q_3: |0>┤2      ├",
                               "        │       │",
                               "q_4: |0>┤3      ├",
-                              "        │  name │",
+                              "        │  Name │",
                               "q_5: |0>┤       ├",
                               "        │       │",
                               " c_0: 0 ╡       ╞",