How to run a circuit with mid-circuit measurements

Creating a circuit with mid-circuit measurements and executing it with Fire Opal

Mid-circuit measurement is a device feature offered by some hardware providers, including IBM. This powerful feature allows a single qubit to be measured at real-time during the execution of the circuit, as opposed to measuring all qubits only after execution is completed.

Mid-circuit measurement is crucial to implementing some algorithms, such as Quantum Error Correction (QEC). Most implementations of QEC require repeated measurements—stabilizer checks—of ancilla qubits which encode information about errors that occurred during the execution of the circuit. These checks allow for the real-time identification and correction of errors. The same philosophy can be applied to other algorithms by using mid-circuit measurement to identify errors and filtering out results based on a method called post-selection.

Mid-circuit measurement, in combination with qubit reset, also provides the opportunity to leverage devices more efficiently by reusing qubits. Today’s quantum hardware is limited by the number of qubits, so the potential to use mid-circuit measurement with qubit reset is key to condensing larger circuits to work on smaller devices. Qubit reuse is a key way to maximize the potential of the NISQ-era devices available today.

At the most basic level, mid-circuit measurement provides a way to influence the state of one or more qubits - similar to performing a gate operation. Since measurements are projective operations, measurement of one qubit will affect the others if a qubit is entangled with other qubits.

In this notebook, a simple circuit is created using the Qiskit QuantumCircuit object, and a measurement is performed between two gates. In this example, the measurement is used to demonstrate the collapse of a Hadamard state.

Here we demonstrate that measurement collapses a $(|0\rangle + |1\rangle)/\sqrt{2}$ state to either $|0\rangle$ or $|1\rangle$. Once the qubit has been measured, the quantum state has been destroyed, and it is available to be reset and used to perform further operations.

1. Import libraries and provide credentials

In this example, IBM is used as a backend provider. You will need to sign up for an IBM account and provide your token.

import fireopal
import qiskit
from qiskit_ibm_provider import IBMProvider
import os
# These are the properties for the publicly available provider for IBM backends.
# If you have access to a private provider and wish to use it, replace these values.
hub = "ibm-q"
group = "open"
project = "main"
token = "YOUR_IBM_TOKEN"
credentials = fireopal.credentials.make_credentials_for_ibmq(
    token=token, hub=hub, group=group, project=project
)

IBMProvider.save_account(
    token=token, overwrite=True, instance=hub + "/" + group + "/" + project
)

2. Choose a backend and validate that it supports mid-circuit measurements

Be sure to choose a backend to which you have access through the specified provider, and then check the boolean flag multi_meas_enabled to determine if mid-circuit measurements are supported.

# Run fireopal.show_supported_devices() with your credentials to get a list of suppported backend names
# Replace "desired_backend" with a device name
backend_name = "desired_backend"
if not qiskit.IBMQ.active_account():
    qiskit.IBMQ.enable_account(token=credentials["token"])

backend = provider.get_backend(backend_name)
config = backend.configuration()
config.multi_meas_enabled

3. Define the circuit

The simple, single-qubit circuit is defined below. A Hadamard gate is performed on the qubit to bring it into a superposition state where the probability of measuring 0 and 1 are equal. Then, a measurement is performed, which collapses the qubit's state. An X-gate is performed next, switching the state of the collapsed qubit, and then a final measurement is performed.

circuit = qiskit.QuantumCircuit(1, 2)
circuit.h(0)
circuit.measure(0, 0)
circuit.x(0)
circuit.measure(0, 1)
print(circuit)
     ┌───┐┌─┐┌───┐┌─┐
  q: ┤ H ├┤M├┤ X ├┤M├
     └───┘└╥┘└───┘└╥┘
c: 2/══════╩═══════╩═
           0       1 

4. Execute the circuit on real hardware using Fire Opal

Fire Opal suppresses noise across all sorts of circuits, including those with mid-circuit measurements. Mid-circuit measurements increase the depth of a circuit, and they are often used in complex algorithms. As circuits get deeper and more complex, there's greater opportunity for noise to get introduced and affect the results. Fire Opal can help suppress noise and enable you to consistently get the correct answer, even with more complex circuit operations.

result = fireopal.execute(
    circuits=[circuit.qasm()],
    shot_count=1024,
    credentials=credentials,
    backend_name=backend_name,
)

If the hadamard gets collapsed, we expect the two measurements to be opposite.

print(result["results"][0])
{'00': 0.0048828125, '01': 0.44921875, '10': 0.5166015625, '11': 0.029296875}

Because the qubit was placed in superposition, then the state was collapsed and subsequently switched, the expected outcome is a fifty-fifty distribution of probabilites across two opposite values. This is largely what is observed, taking into account a bit of remaining noise. Fire Opal can make the correct result abundantly clear, but it is not able to completely eliminate noise.

5. Organize the measurements into different classical registers

For clarity in circuit design, it can be helpful to separate mid-circuit measurements from end-of-circuit measurements by storing them in different classical registers. In the example below, the same circuit is implemented using a single quantum register and two classical registers—one for mid-circuit measurements and one for final.

quantum_register = qiskit.QuantumRegister(1, "q")
mcm_register = qiskit.ClassicalRegister(1, "mcm")
end_register = qiskit.ClassicalRegister(1, "end")
circuit2 = qiskit.QuantumCircuit(quantum_register, mcm_register, end_register)
circuit2.h(0)
circuit2.measure(quantum_register, mcm_register)
circuit2.x(0)
circuit2.measure(quantum_register, end_register)
print(circuit2)
       ┌───┐┌─┐┌───┐┌─┐
    q: ┤ H ├┤M├┤ X ├┤M├
       └───┘└╥┘└───┘└╥┘
mcm: 1/══════╩═══════╬═
             0       ║ 
                     ║ 
end: 1/══════════════╩═
                     0 
result = fireopal.execute(
    circuits=[circuit2.qasm()],
    shot_count=1024,
    credentials=credentials,
    backend_name=backend_name,
)
print(result["results"][0])
{'0 0': 0.0048828125, '0 1': 0.4521484375, '1 0': 0.5244140625, '1 1': 0.0185546875}

Upon executing the results once again using Fire Opal, the results are fairly similar, and the space in the bitstrings denotes the separation of the two registers.

Now that you've seen a simple example of running a circuit with mid-circuit measurements using Fire Opal, run through a tutorial on Performing parity checks with mid-circuit measurements.

The package versions below were used to produce this notebook.

from fireopal import print_package_versions

print_package_versions()
| Package      | Version |
| ------------ | ------- |
| Python       | 3.11.3  |
| networkx     | 2.8.8   |
| numpy        | 1.23.5  |
| qiskit-terra | 0.24.1  |
| sympy        | 1.12    |
| fire-opal    | 5.3.1   |

Was this useful?

cta background

New to Fire Opal?

Get access to everything you need to automate and optimize quantum hardware performance at scale.