Repository: Qiskit/qiskit

Issue:
`QuantumCircuit.to_instruction` and `to_gate` fail with registerless bits, or bits with overlapping registers
### Environment

- **Qiskit Terra version**: 0.19.0
- **Python version**: any
- **Operating system**: any


### What is happening?

The circuit methods `QuantumCircuit.to_instruction` and `.to_gate`, via their converters `circuit_to_instruction` and `circuit_to_gate`, fail any bit is not in exactly one register.

### How can we reproduce the issue?

There are many possibilities for the exact error, depending on how many registers are defined and what is in them, but here's one example:
```python
In [1]: from qiskit.circuit import QuantumCircuit, Qubit, Clbit
   ...:
   ...: qc = QuantumCircuit([Qubit(), Clbit()])
   ...: qc.measure(0, 0)
   ...: qc.to_instruction()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-1-fa32ac1bdffe> in <module>
      3 qc = QuantumCircuit([Qubit(), Clbit()])
      4 qc.measure(0, 0)
----> 5 qc.to_instruction()

~/code/qiskit/terra/qiskit/circuit/quantumcircuit.py in to_instruction(self, parameter_map, label)
   1480         from qiskit.converters.circuit_to_instruction import circuit_to_instruction
   1481
-> 1482         return circuit_to_instruction(self, parameter_map, label=label)
   1483
   1484     def to_gate(

~/code/qiskit/terra/qiskit/converters/circuit_to_instruction.py in circuit_to_instruction(circuit, parameter_map, equivalence_library, label)
    101         regs.append(c)
    102
--> 103     qubit_map = {bit: q[idx] for idx, bit in enumerate(circuit.qubits)}
    104     clbit_map = {bit: c[idx] for idx, bit in enumerate(circuit.clbits)}
    105

~/code/qiskit/terra/qiskit/converters/circuit_to_instruction.py in <dictcomp>(.0)
    101         regs.append(c)
    102
--> 103     qubit_map = {bit: q[idx] for idx, bit in enumerate(circuit.qubits)}
    104     clbit_map = {bit: c[idx] for idx, bit in enumerate(circuit.clbits)}
    105

NameError: free variable 'q' referenced before assignment in enclosing scope
```

### What should happen?

The instruction should be created with 1 qubit and 1 clbit in this case.

### Any suggestions?

_No response_


Discussion:

Resolving patch:
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
@@ -18,7 +18,7 @@
 
 
 def circuit_to_gate(circuit, parameter_map=None, equivalence_library=None, label=None):
-    """Build a ``Gate`` object from a ``QuantumCircuit``.
+    """Build a :class:`.Gate` object from a :class:`.QuantumCircuit`.
 
     The gate is anonymous (not tied to a named quantum register),
     and so can be inserted into another circuit. The gate will
@@ -73,7 +73,7 @@ def circuit_to_gate(circuit, parameter_map=None, equivalence_library=None, label
 
     gate = Gate(
         name=circuit.name,
-        num_qubits=sum(qreg.size for qreg in circuit.qregs),
+        num_qubits=circuit.num_qubits,
         params=[*parameter_dict.values()],
         label=label,
     )
diff --git a/qiskit/converters/circuit_to_instruction.py b/qiskit/converters/circuit_to_instruction.py
--- a/qiskit/converters/circuit_to_instruction.py
+++ b/qiskit/converters/circuit_to_instruction.py
@@ -19,7 +19,7 @@
 
 
 def circuit_to_instruction(circuit, parameter_map=None, equivalence_library=None, label=None):
-    """Build an ``Instruction`` object from a ``QuantumCircuit``.
+    """Build an :class:`~.circuit.Instruction` object from a :class:`.QuantumCircuit`.
 
     The instruction is anonymous (not tied to a named quantum register),
     and so can be inserted into another circuit. The instruction will
@@ -48,7 +48,6 @@ def circuit_to_instruction(circuit, parameter_map=None, equivalence_library=None
 
             from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
             from qiskit.converters import circuit_to_instruction
-            %matplotlib inline
 
             q = QuantumRegister(3, 'q')
             c = ClassicalRegister(3, 'c')
@@ -77,8 +76,8 @@ def circuit_to_instruction(circuit, parameter_map=None, equivalence_library=None
 
     instruction = Instruction(
         name=circuit.name,
-        num_qubits=sum(qreg.size for qreg in circuit.qregs),
-        num_clbits=sum(creg.size for creg in circuit.cregs),
+        num_qubits=circuit.num_qubits,
+        num_clbits=circuit.num_clbits,
         params=[*parameter_dict.values()],
         label=label,
     )


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
@@ -13,7 +13,7 @@
 """Tests for the converters."""
 
 from qiskit import QuantumRegister, QuantumCircuit
-from qiskit.circuit import Gate
+from qiskit.circuit import Gate, Qubit
 from qiskit.test import QiskitTestCase
 from qiskit.exceptions import QiskitError
 
@@ -35,6 +35,39 @@ def test_simple_circuit(self):
         self.assertIsInstance(gate, Gate)
         self.assertEqual(gate.definition[0][1], [q[1], q[6]])
 
+    def test_circuit_with_registerless_bits(self):
+        """Test a circuit with registerless bits can be converted to a gate."""
+        qr1 = QuantumRegister(2)
+        qubits = [Qubit(), Qubit(), Qubit()]
+        qr2 = QuantumRegister(3)
+        circ = QuantumCircuit(qr1, qubits, qr2)
+        circ.cx(3, 5)
+
+        gate = circ.to_gate()
+        self.assertIsInstance(gate, Gate)
+        self.assertEqual(gate.num_qubits, len(qr1) + len(qubits) + len(qr2))
+        gate_definition = gate.definition
+        _, cx_qargs, cx_cargs = gate_definition.data[0]
+        self.assertEqual(cx_qargs, [gate_definition.qubits[3], gate_definition.qubits[5]])
+        self.assertEqual(cx_cargs, [])
+
+    def test_circuit_with_overlapping_registers(self):
+        """Test that the conversion works when the given circuit has bits that are contained in more
+        than one register."""
+        qubits = [Qubit() for _ in [None] * 10]
+        qr1 = QuantumRegister(bits=qubits[:6])
+        qr2 = QuantumRegister(bits=qubits[4:])
+        circ = QuantumCircuit(qubits, qr1, qr2)
+        circ.cx(3, 5)
+
+        gate = circ.to_gate()
+        self.assertIsInstance(gate, Gate)
+        self.assertEqual(gate.num_qubits, len(qubits))
+        gate_definition = gate.definition
+        _, cx_qargs, cx_cargs = gate_definition.data[0]
+        self.assertEqual(cx_qargs, [gate_definition.qubits[3], gate_definition.qubits[5]])
+        self.assertEqual(cx_cargs, [])
+
     def test_raises(self):
         """test circuit which can't be converted raises"""
         circ1 = QuantumCircuit(3)
diff --git a/test/python/converters/test_circuit_to_instruction.py b/test/python/converters/test_circuit_to_instruction.py
--- a/test/python/converters/test_circuit_to_instruction.py
+++ b/test/python/converters/test_circuit_to_instruction.py
@@ -16,6 +16,7 @@
 
 from qiskit.converters import circuit_to_instruction
 from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
+from qiskit.circuit import Qubit, Clbit
 from qiskit.circuit import Parameter
 from qiskit.test import QiskitTestCase
 from qiskit.exceptions import QiskitError
@@ -67,6 +68,54 @@ def test_flatten_registers_of_circuit_single_bit_cond(self):
         self.assertEqual(inst.definition[1][0].condition, (c[3], False))
         self.assertEqual(inst.definition[2][0].condition, (c[5], True))
 
+    def test_flatten_circuit_registerless(self):
+        """Test that the conversion works when the given circuit has bits that are not contained in
+        any register."""
+        qr1 = QuantumRegister(2)
+        qubits = [Qubit(), Qubit(), Qubit()]
+        qr2 = QuantumRegister(3)
+        cr1 = ClassicalRegister(2)
+        clbits = [Clbit(), Clbit(), Clbit()]
+        cr2 = ClassicalRegister(3)
+        circ = QuantumCircuit(qr1, qubits, qr2, cr1, clbits, cr2)
+        circ.cx(3, 5)
+        circ.measure(4, 4)
+
+        inst = circuit_to_instruction(circ)
+        self.assertEqual(inst.num_qubits, len(qr1) + len(qubits) + len(qr2))
+        self.assertEqual(inst.num_clbits, len(cr1) + len(clbits) + len(cr2))
+        inst_definition = inst.definition
+        _, cx_qargs, cx_cargs = inst_definition.data[0]
+        _, measure_qargs, measure_cargs = inst_definition.data[1]
+        self.assertEqual(cx_qargs, [inst_definition.qubits[3], inst_definition.qubits[5]])
+        self.assertEqual(cx_cargs, [])
+        self.assertEqual(measure_qargs, [inst_definition.qubits[4]])
+        self.assertEqual(measure_cargs, [inst_definition.clbits[4]])
+
+    def test_flatten_circuit_overlapping_registers(self):
+        """Test that the conversion works when the given circuit has bits that are contained in more
+        than one register."""
+        qubits = [Qubit() for _ in [None] * 10]
+        qr1 = QuantumRegister(bits=qubits[:6])
+        qr2 = QuantumRegister(bits=qubits[4:])
+        clbits = [Clbit() for _ in [None] * 10]
+        cr1 = ClassicalRegister(bits=clbits[:6])
+        cr2 = ClassicalRegister(bits=clbits[4:])
+        circ = QuantumCircuit(qubits, clbits, qr1, qr2, cr1, cr2)
+        circ.cx(3, 5)
+        circ.measure(4, 4)
+
+        inst = circuit_to_instruction(circ)
+        self.assertEqual(inst.num_qubits, len(qubits))
+        self.assertEqual(inst.num_clbits, len(clbits))
+        inst_definition = inst.definition
+        _, cx_qargs, cx_cargs = inst_definition.data[0]
+        _, measure_qargs, measure_cargs = inst_definition.data[1]
+        self.assertEqual(cx_qargs, [inst_definition.qubits[3], inst_definition.qubits[5]])
+        self.assertEqual(cx_cargs, [])
+        self.assertEqual(measure_qargs, [inst_definition.qubits[4]])
+        self.assertEqual(measure_cargs, [inst_definition.clbits[4]])
+
     def test_flatten_parameters(self):
         """Verify parameters from circuit are moved to instruction.params"""
         qr = QuantumRegister(3, "qr")