Repository: Qiskit/qiskit

Issue:
`random_circuit` does not include mid-circuit measurements
### What should we add?

`qiskit.circuit.utils.random_circuit` has a keyword argument `measure=False` that allows opt-in inclusion of an end-of-circuit measurement of all qubits: https://github.com/Qiskit/qiskit-terra/blob/ad7e609d10b02fd98090b0fac132113f329825b6/qiskit/circuit/random/utils.py#L50

It would be good to also be able to generalize this to be able to generate circuits which can additionally (and optionally) make of use mid-circuit measurement.


Discussion:
The "make use of mid-circuit measurement" bit should have some degree of automatic handling, if mid-circuit measurements go in - there's already a `conditional` flag that would implicitly use a register that's measured into.
> The "make use of mid-circuit measurement" bit should have some degree of automatic handling, if mid-circuit measurements go in - there's already a `conditional` flag that would implicitly use a register that's measured into.

Good catch, the `conditional` flag states that if `True` it will insert mid-circuit measurements, but as far as I can tell it presently won't.
Oh, I hadn't actually even looked at the docstring.  I was just thinking that it behaves _as if_ there were useful mid-circuit measurements that it was reading.  Perhaps a sensible course of action is to have `conditional=True` add `Measure` to the allowed operations?  After #8983, we'd need to add an extra field to the array to count up `num_clbits` as well if so, but that's doable.

Resolving patch:
diff --git a/qiskit/circuit/random/utils.py b/qiskit/circuit/random/utils.py
--- a/qiskit/circuit/random/utils.py
+++ b/qiskit/circuit/random/utils.py
@@ -144,7 +144,7 @@ def random_circuit(
     qubits = np.array(qc.qubits, dtype=object, copy=True)
 
     # Apply arbitrary random operations in layers across all qubits.
-    for _ in range(depth):
+    for layer_number in range(depth):
         # We generate all the randomness for the layer in one go, to avoid many separate calls to
         # the randomisation routines, which can be fairly slow.
 
@@ -175,7 +175,7 @@ def random_circuit(
         # We've now generated everything we're going to need.  Now just to add everything.  The
         # conditional check is outside the two loops to make the more common case of no conditionals
         # faster, since in Python we don't have a compiler to do this for us.
-        if conditional:
+        if conditional and layer_number != 0:
             is_conditional = rng.random(size=len(gate_specs)) < 0.1
             condition_values = rng.integers(
                 0, 1 << min(num_qubits, 63), size=np.count_nonzero(is_conditional)
@@ -191,6 +191,7 @@ def random_circuit(
             ):
                 operation = gate(*parameters[p_start:p_end])
                 if is_cond:
+                    qc.measure(qc.qubits, cr)
                     # The condition values are required to be bigints, not Numpy's fixed-width type.
                     operation.condition = (cr, int(condition_values[c_ptr]))
                     c_ptr += 1


Test patch:
diff --git a/test/python/circuit/test_random_circuit.py b/test/python/circuit/test_random_circuit.py
--- a/test/python/circuit/test_random_circuit.py
+++ b/test/python/circuit/test_random_circuit.py
@@ -68,3 +68,17 @@ def test_large_conditional(self):
             # Condition values always have to be Python bigints (of which `bool` is a subclass), not
             # any of Numpy's fixed-width types, for example.
             self.assertIsInstance(value, int)
+
+    def test_random_mid_circuit_measure_conditional(self):
+        """Test random circuit with mid-circuit measurements for conditionals."""
+        num_qubits = depth = 2
+        circ = random_circuit(num_qubits, depth, conditional=True, seed=4)
+        self.assertEqual(circ.width(), 2 * num_qubits)
+        op_names = [instruction.operation.name for instruction in circ]
+        # Before a condition, there needs to be measurement in all the qubits.
+        self.assertEqual(4, len(op_names))
+        self.assertEqual(["measure"] * num_qubits, op_names[1 : 1 + num_qubits])
+        conditions = [
+            bool(getattr(instruction.operation, "condition", None)) for instruction in circ
+        ]
+        self.assertEqual([False, False, False, True], conditions)