Repository: Qiskit/qiskit-terra (python)

Issue:
Cannot compose circuits with different numbers of measurements
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

- **Qiskit Terra version**: 0.17.4
- **Python version**:
- **Operating system**:

### What is the current behavior?
Compose fails when combining circuits with no measurements together with those that do, e.g.

```python
ansatz = TwoLocal(2, rotation_blocks='ry',
                  entanglement_blocks='cx')  

qc = QuantumCircuit(2)
qc.measure_all()

ansatz.compose(qc)
```

gives:

CircuitError: "Trying to compose with another QuantumCircuit which has more 'in' edges."

while `+` works, but is deprecated in favor of `compose`.



### Steps to reproduce the problem



### What is the expected behavior?



### Suggested solutions




Pull request description:
Combine circuits with clbits onto those with none
fixes #6816

### Summary
Allows for the common situation where one combines circuits with measurements (clbits) to those without.  Currently you can do this by reversing the order of `combine` but that is not intuitive at all.  Given that many (if not the vast majority) workflows involve the situation described in #6816, it makes sense to allow it.  It also requires minimal modifications to do so.


### Details and comments




Patch:
diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py
index 6ad3b3d8f..e5297e11e 100644
--- a/qiskit/circuit/quantumcircuit.py
+++ b/qiskit/circuit/quantumcircuit.py
@@ -874,6 +874,14 @@ class QuantumCircuit:
         else:
             dest = self.copy()
 
+        # If self does not have any cregs but other does then we allow
+        # that and add the registers to the output dest
+        if isinstance(other, QuantumCircuit):
+            if not self.clbits and other.clbits:
+                dest.add_bits(other.clbits)
+                for reg in other.cregs:
+                    dest.add_register(reg)
+
         if wrap:
             try:
                 other = other.to_gate()
@@ -898,15 +906,14 @@ class QuantumCircuit:
 
         instrs = other.data
 
-        if other.num_qubits > self.num_qubits or other.num_clbits > self.num_clbits:
+        if other.num_qubits > dest.num_qubits or other.num_clbits > dest.num_clbits:
             raise CircuitError(
                 "Trying to compose with another QuantumCircuit which has more 'in' edges."
             )
 
         # number of qubits and clbits must match number in circuit or None
-        identity_qubit_map = dict(zip(other.qubits, self.qubits))
-        identity_clbit_map = dict(zip(other.clbits, self.clbits))
-
+        identity_qubit_map = dict(zip(other.qubits, dest.qubits))
+        identity_clbit_map = dict(zip(other.clbits, dest.clbits))
         if qubits is None:
             qubit_map = identity_qubit_map
         elif len(qubits) != len(other.qubits):
diff --git a/releasenotes/notes/compose-meas-no-meas-492ce91167d54154.yaml b/releasenotes/notes/compose-meas-no-meas-492ce91167d54154.yaml
new file mode 100644
index 000000000..e4fe28d32
--- /dev/null
+++ b/releasenotes/notes/compose-meas-no-meas-492ce91167d54154.yaml
@@ -0,0 +1,16 @@
+---
+features:
+  - |
+    :meth:`.QuantumCircuit.compose` will now accept an operand with classical
+    bits if the base circuit has none itself, as a convenience.  The pattern
+    of composing a circuit with measurements onto a quantum-only circuit is
+    now valid::
+
+      from qiskit import QuantumCircuit
+
+      base = QuantumCircuit(3)
+      terminus = QuantumCircuit(3, 3)
+      terminus.measure_all()
+
+      # This will now succeed, though it was previously a CircuitError.
+      base.compose(terminus)


Test patch:
diff --git a/test/python/circuit/test_compose.py b/test/python/circuit/test_compose.py
index eccd1c916..8a1c1a4c4 100644
--- a/test/python/circuit/test_compose.py
+++ b/test/python/circuit/test_compose.py
@@ -23,11 +23,12 @@ from qiskit.circuit import (
     ClassicalRegister,
     Clbit,
     QuantumCircuit,
+    Qubit,
     Parameter,
     Gate,
     Instruction,
 )
-from qiskit.circuit.library import HGate, RZGate, CXGate, CCXGate
+from qiskit.circuit.library import HGate, RZGate, CXGate, CCXGate, TwoLocal
 from qiskit.test import QiskitTestCase
 
 
@@ -651,6 +652,46 @@ class TestCircuitCompose(QiskitTestCase):
         self.assertIs(reg_instruction.condition[0], test_creg)
         self.assertEqual(reg_instruction.condition, (test_creg, 3))
 
+    def test_compose_no_clbits_in_one(self):
+        """Test combining a circuit with cregs to one without"""
+        ansatz = TwoLocal(2, rotation_blocks="ry", entanglement_blocks="cx")
+
+        qc = QuantumCircuit(2)
+        qc.measure_all()
+        out = ansatz.compose(qc)
+        self.assertEqual(out.clbits, qc.clbits)
+
+    def test_compose_no_clbits_in_one_inplace(self):
+        """Test combining a circuit with cregs to one without inplace"""
+        ansatz = TwoLocal(2, rotation_blocks="ry", entanglement_blocks="cx")
+
+        qc = QuantumCircuit(2)
+        qc.measure_all()
+        ansatz.compose(qc, inplace=True)
+        self.assertEqual(ansatz.clbits, qc.clbits)
+
+    def test_compose_no_clbits_in_one_multireg(self):
+        """Test combining a circuit with cregs to one without, multi cregs"""
+        ansatz = TwoLocal(2, rotation_blocks="ry", entanglement_blocks="cx")
+
+        qa = QuantumRegister(2, "q")
+        ca = ClassicalRegister(2, "a")
+        cb = ClassicalRegister(2, "b")
+        qc = QuantumCircuit(qa, ca, cb)
+        qc.measure(0, cb[1])
+        out = ansatz.compose(qc)
+        self.assertEqual(out.clbits, qc.clbits)
+        self.assertEqual(out.cregs, qc.cregs)
+
+    def test_compose_noclbits_registerless(self):
+        """Combining a circuit with cregs to one without, registerless case"""
+        inner = QuantumCircuit([Qubit(), Qubit()], [Clbit(), Clbit()])
+        inner.measure([0, 1], [0, 1])
+        outer = QuantumCircuit(2)
+        outer.compose(inner, inplace=True)
+        self.assertEqual(outer.clbits, inner.clbits)
+        self.assertEqual(outer.cregs, [])
+
 
 if __name__ == "__main__":
     unittest.main()