Repository: Qiskit/qiskit

Issue:
QuantumCircuit.append(custom_gate, label='foo') overwrites earlier labels
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->


### Steps to reproduce the problem

```
>>> import qiskit as qk
>>> hadamard = qk.QuantumCircuit(1)
>>> hadamard.h(0)
>>> h_gate = hadamard.to_gate()
>>>
>>> circ = qk.QuantumCircuit(1)
>>> circ.append(h_gate, [0], label='Q')
>>> circ.append(h_gate, [0], label='P')
>>> print(circ)
     ┌───┐┌───┐
q_0: ┤ P ├┤ P ├
     └───┘└───┘
```

### What is the expected behavior?



### Suggested solutions





Discussion:
Rather than adding label to the circuit methods, can we add a method to `Instruction` or `Gate` that can return a shallow copy of the objet with a label set.

One suggestion would be to do this in the call method (along with adding qargs/cargs to the call method). The qi operator classes use call for `qargs`.

Eg in Instruction:
```python
def __call__(self, qargs=None, cargs=None, label=None):
    ret = copy.copy(self)  # make a shallow copy
    ret._qargs = tuple(qargs)
    ret._cargs = tuple(cargs)
    ret._label = str(label)
    return ret
```

Then you could use this as:
```python
import qiskit as qk
hadamard = qk.QuantumCircuit(1)
hadamard.h(0)
h_gate = hadamard.to_gate()

circ = qk.QuantumCircuit(1)
circ.append(h_gate([0], label='Q'))
circ.append(h_gate([0], label='P'))

# or equivalently without using qargs call
circ = qk.QuantumCircuit(1)
circ.append(h_gate(label='Q'), [0])
circ.append(h_gate(label='P'), [0])
```


Currently, something like this works:
```python
hadamard = qk.QuantumCircuit(1)
hadamard.h(0)
h_gate_q = hadamard.to_gate()
h_gate_q.label = 'Q'
h_gate_p = hadamard.to_gate()
h_gate_p.label = 'P'

circ = qk.QuantumCircuit(1)
circ.append(h_gate_q, [0])
circ.append(h_gate_p, [0])
circ.draw()
```
```
     ┌───┐┌───┐
q_0: ┤ Q ├┤ P ├
     └───┘└───┘
```

Maybe add a `label` parameter to `to_gate` would add the desired convenience? @ajavadia @kdk 

That would look like this :
```python
hadamard = qk.QuantumCircuit(1)
hadamard.h(0)

circ = qk.QuantumCircuit(1)
circ.append(hadamard.to_gate(label='Q'), [0])
circ.append(hadamard.to_gate(label='P'), [0])
```

Resolving patch:
diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py
--- a/qiskit/circuit/quantumcircuit.py
+++ b/qiskit/circuit/quantumcircuit.py
@@ -787,7 +787,7 @@ def to_instruction(self, parameter_map=None):
         from qiskit.converters.circuit_to_instruction import circuit_to_instruction
         return circuit_to_instruction(self, parameter_map)
 
-    def to_gate(self, parameter_map=None):
+    def to_gate(self, parameter_map=None, label=None):
         """Create a Gate out of this circuit.
 
         Args:
@@ -795,13 +795,14 @@ def to_gate(self, parameter_map=None):
                parameters in the circuit to parameters to be used in the
                gate. If None, existing circuit parameters will also
                parameterize the gate.
+            label (str): Optional gate label.
 
         Returns:
             Gate: a composite gate encapsulating this circuit
             (can be decomposed back)
         """
         from qiskit.converters.circuit_to_gate import circuit_to_gate
-        return circuit_to_gate(self, parameter_map)
+        return circuit_to_gate(self, parameter_map, label=label)
 
     def decompose(self):
         """Call a decomposition pass on this circuit,
diff --git a/qiskit/converters/circuit_to_gate.py b/qiskit/converters/circuit_to_gate.py
--- a/qiskit/converters/circuit_to_gate.py
+++ b/qiskit/converters/circuit_to_gate.py
@@ -20,7 +20,7 @@
 from qiskit.exceptions import QiskitError
 
 
-def circuit_to_gate(circuit, parameter_map=None, equivalence_library=None):
+def circuit_to_gate(circuit, parameter_map=None, equivalence_library=None, label=None):
     """Build a ``Gate`` object from a ``QuantumCircuit``.
 
     The gate is anonymous (not tied to a named quantum register),
@@ -35,6 +35,7 @@ def circuit_to_gate(circuit, parameter_map=None, equivalence_library=None):
            Gate.
         equivalence_library (EquivalenceLibrary): Optional equivalence library
            where the converted gate will be registered.
+        label (str): Optional gate label.
 
     Raises:
         QiskitError: if circuit is non-unitary or if
@@ -67,7 +68,8 @@ def circuit_to_gate(circuit, parameter_map=None, equivalence_library=None):
 
     gate = Gate(name=circuit.name,
                 num_qubits=sum([qreg.size for qreg in circuit.qregs]),
-                params=sorted(parameter_dict.values(), key=lambda p: p.name))
+                params=sorted(parameter_dict.values(), key=lambda p: p.name),
+                label=label)
     gate.condition = None
 
     def find_bit_position(bit):


Test patch:
diff --git a/test/python/converters/test_circuit_to_gate.py b/test/python/converters/test_circuit_to_gate.py
--- a/test/python/converters/test_circuit_to_gate.py
+++ b/test/python/converters/test_circuit_to_gate.py
@@ -66,3 +66,12 @@ def test_generated_gate_inverse(self):
         gate = circ.to_gate()
         out_gate = gate.inverse()
         self.assertIsInstance(out_gate, Gate)
+
+    def test_to_gate_label(self):
+        """Test label setting."""
+        qr1 = QuantumRegister(2, 'qr1')
+        circ = QuantumCircuit(qr1, name='a circuit name')
+        circ.cx(qr1[1], qr1[0])
+        gate = circ.to_gate(label='a label')
+
+        self.assertEqual(gate.label, 'a label')