Execute dynamic circuits
The code on this page was developed using the following requirements. We recommend using these versions or newer.
qiskit[all]~=2.3.0 qiskit-ibm-runtime~=0.43.1
The new version of dynamic circuits is now available to all users on all backends. You can now run dynamic circuits at utility scale. See the announcement for more details.
Dynamic circuits are powerful tools with which you can measure qubits in the middle of a quantum circuit execution and then perform classical logic operations within the circuit, based on the outcome of those mid-circuit measurements. This process is also known as classical feedforward. While these are early days of understanding how best to take advantage of dynamic circuits, the quantum research community has already identified a number of use cases, such as the following:
- Efficient quantum state preparation, such as GHZ state, W-state (for more information about W-state, also refer to "State preparation by shallow circuits using feed forward"), and a broad class of matrix product states
- Efficient long-range entanglement between qubits on the same chip by using shallow circuits
- Efficient sampling of IQP-like circuits
These improvements brought by dynamic circuits, however, come with trade-offs. Mid-circuit measurements and classical operations typically have longer execution time than two-qubit gates, and this increase in time might negate the benefits of reduced circuit depth. Therefore, reducing the length of mid-circuit measurement is a focus area of improvement as IBM Quantum® releases the new version of dynamic circuits.
The OpenQASM 3 specification defines a number of control-flow structures, but Qiskit Runtime currently only supports the conditional if statement. In Qiskit SDK, this corresponds to the if_test method on QuantumCircuit. This method returns a context manager and is typically used in a with statement. This guide describes how to use this conditional statement.
The code examples in this guide use the standard measure instruction for mid-circuit measurements. However, it is recommended that you use the MidCircuitMeasure instruction instead, if the backend supports it. See the Mid-circuit measurements section for details.
Find backends that support dynamic circuits
To find all backends that your account can access and support dynamic circuits, run code like the following. This example assumes that you have saved your login credentials. You could also explicitly specify credentials when initializing your Qiskit Runtime service account. This would let you view backends available on a specific instance or plan type, for example.
- The backends that are available to the account depend on the instance specified in the credentials.
- The new version of dynamic circuits is now available to all users on all backends. See the announcement for more details.
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
dc_backends = service.backends(dynamic_circuits=True)
print(dc_backends)Output:
[<IBMBackend('ibm_fez')>, <IBMBackend('ibm_pittsburgh')>, <IBMBackend('ibm_boston')>, <IBMBackend('ibm_miami')>, <IBMBackend('ibm_marrakesh')>, <IBMBackend('ibm_torino')>, <IBMBackend('ibm_kingston')>]
Mid-circuit measurements
Prior to qiskit-ibm-runtime v0.43.0, measure was the only measurement instruction in Qiskit. Mid-circuit measurements, however, have different tuning requirements than terminal measurements (measurements that happen at the end of a circuit). For example, you need to consider the instruction duration when tuning a mid-circuit measurement because longer instructions cause noisier circuits. You don't need to consider instruction duration for terminal measurements because there are no instructions after terminal measurements.
The MidCircuitMeasure instruction maps to the measure_2 instruction reported in the backend's supported_instructions. However, measure_2 is not supported on all backends. Use service.backends(filters=lambda b: "measure_2" in b.supported_instructions) to find backends that support it. New measurements might be added in the future, but this is not guarenteed.
MidCircuitMeasure method
In qiskit-ibm-runtime v0.43.0, the MidCircuitMeasure instruction was introduced. As the name suggests, it is a new measurement instruction that is optimized for mid-circuit on IBM® QPUs. While you can use QuantumCircuit.measure for a mid-circuit measurement, because of its design, MidCircuitMeasure is typically a better choice. For example, it adds less overhead to your circuit than when using QuantumCircuit.measure.
from qiskit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime.circuit import MidCircuitMeasure
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
circ = QuantumCircuit(2, 2)
circ.x(0)
circ.append(MidCircuitMeasure(), [0], [0])
# circ.measure([0], [0])
# circ.measure_all()
print(circ.draw(cregbundle=False))- There must be at least one classical register in order to use measurements.
- The Sampler primitive requires circuit measurements. You can add circuit measurements with the Estimator primitive, but they are ignored.
Qiskit Runtime limitations
Be aware of the following constraints when running dynamic circuits in Qiskit Runtime.
-
Due to the limited physical memory on control electronics, there is also a limit on the number of
ifstatements and size of their operands. This limit is a function of the number of broadcasts and the number of broadcasted bits in a job (not a circuit).When processing an
ifcondition, measurement data needs to be transferred to the control logic to make that evaluation. A broadcast is a transfer of unique classical data, and broadcasted bits is the number of classical bits being transferred. Consider the following:c0 = ClassicalRegister(3) c1 = ClassicalRegister(5) ... with circuit.if_test((c0, 1)) ... with circuit.if_test((c0, 3)) ... with circuit.if_test((c1[2], 1)) ...In the previous code example, the first two
if_testobjects onc0are considered one broadcast because the content ofc0did not change, and thus does not need to be re-broadcasted. Theif_testonc1is a second broadcast. The first one broadcasts all three bits inc0and the second broadcasts just one bit, making a total of four broadcasted bits.Currently, if you broadcast 60 bits each time, then the job can have approximately 300 broadcasts. If you broadcast just one bit each time, however, then the job can have 2400 broadcasts.
-
The operand used in an
if_teststatement must be 32 or fewer bits. Thus, if you are comparing an entireClassicalRegister, the size of thatClassicalRegistermust be 32 or fewer bits. If you are comparing just a single bit from aClassicalRegister, however, thatClassicalRegistercan be of any size (since the operand is only one bit).For example, the "Not valid" code block does not work because
cris more than 32 bits. You can, however, use a classical register wider than 32 bits if you are testing only one bit, as shown in the "Valid" code block.cr = ClassicalRegister(50) qr = QuantumRegister(50) circuit = QuantumCircuit(qr, cr) ... circ.measure(qr, cr) with circ.if_test((cr, 15)): ...cr = ClassicalRegister(50) qr = QuantumRegister(50) circuit = QuantumCircuit(qr, cr) ... circ.measure(qr, cr) with circ.if_test((cr[5], 1)): ... -
Nested conditionals are not allowed. For example, the following code block will not work because it has an
if_testinside anotherif_test:c1 = ClassicalRegister(1, "c1") c2 = ClassicalRegister(2, "c2") ... with circ.if_test((c1, 1)): with circ.if_test(c2, 1)): ...cr = ClassicalRegister(2) ... with circuit.if_test((cr, 0b11)): ... -
Having
resetor measurements inside conditionals is not supported. -
Arithmetic operations are not supported.
-
See the OpenQASM 3 feature table to determine which OpenQASM 3 features are supported on Qiskit and Qiskit Runtime.
-
When OpenQASM 3 (instead of
QuantumCircuit) is used as the input format to pass circuits to Qiskit Runtime primitives, only instructions that can be loaded into Qiskit are supported. Classical operations, for example, are not supported because they cannot be loaded into Qiskit. See Import an OpenQASM 3 program into Qiskit for more information. -
The
for,while, andswitchinstructions are not supported.
Use dynamic circuits with Estimator
Since Estimator does not support dynamic circuits, you can use Sampler and build your own measurement circuits instead. Alternatively, you can use the Executor primitive, which supports dynamic circuits.
To replicate Estimator's behavior, follow this process:
- Group the terms of all observables into a partition. This can be done by using the
PauliListAPI, for example.NoteYou can use the
BitArrayprimitive attribute to compute the expectation values of the provided observables. - Execute one basis change circuit per partition (whichever basis change needs to be done for each partition). See the Measurement bases addon utility
measurement_basesmodule for more information. For more information, see the documentation for the Qiskit addon utilities package. - Add back together the results for each partition.
Next steps
- Learn how to implement accurate dynamic decoupling by using stretch.
- Review the classical feedforward and control flow guide.
- Use circuit schedule visualization to debug and optimize your dynamic circuits.