Breaking into error mitigation with Mitiq’s calibration module#

../_images/calibration.png

This tutorial helps answer the question: “What quantum error mitigation technique should I use for my problem?”. The newly introduced mitiq.calibration module helps answer that in an optimized way, thrhough Benchmarks and Strategies.

More specifically, this tutorial covers:

  • Getting started with Mitiq’s calibration module with ZNE

  • Use Qiskit noisy simulator with FakeJakarta as backend

  • Run calibration with some special settings, RBSettings, using the cal.run(log=True) option

Getting started with Mitiq#

from mitiq.benchmarks import generate_rb_circuits
from mitiq.zne import execute_with_zne
from mitiq import (
    Calibrator,
    Settings,
    execute_with_mitigation,
    MeasurementResult,
)

from qiskit.providers.fake_provider import FakeJakarta  # Fake (simulated) QPU

Define the circuit to study#

Global variables#

Define global variables for the quantum circuit of interest: number of qubits, depth of the quantum circuit and number of shots.

n_qubits = 2
depth_circuit = 20
shots = 10 ** 3

Quantum circuit: Randomized benchmarking (RB)#

We now use Mitiq’s built-in generate_rb_circuits from the mitiq.benchmarks module to define the quantum circuit.

circuit = generate_rb_circuits(n_qubits, depth_circuit,return_type="qiskit")[0]
circuit.measure_all()
print(len(circuit))
print(circuit)
211
        ┌───┐  ┌────┐    ┌─────────┐   ┌────┐   ┌───┐  ┌───────┐             »
   q_0: ┤ Y ├──┤ √X ├──■─┤ Ry(π/2) ├───┤ √X ├───┤ X ├──┤ Ry(0) ├───────────■─»
        ├───┤┌─┴────┴┐ │ └─┬──────┬┘┌──┴────┴──┐├───┴┐┌┴───────┴─┐┌──────┐ │ »
   q_1: ┤ Y ├┤ Rx(0) ├─■───┤ √Xdg ├─┤ Ry(-π/2) ├┤ √X ├┤ Ry(-π/2) ├┤ √Xdg ├─■─»
        └───┘└───────┘     └──────┘ └──────────┘└────┘└──────────┘└──────┘   »
meas: 2/═════════════════════════════════════════════════════════════════════»
                                                                             »
«        ┌──────┐┌──────────┐┌──────┐┌───────┐              ┌─────────┐┌────┐»
«   q_0: ┤ √Xdg ├┤ Ry(-π/2) ├┤ √Xdg ├┤ Ry(0) ├────────────■─┤ Ry(π/2) ├┤ √X ├»
«        ├──────┤├──────────┤└┬────┬┘└┬──────┤┌─────────┐ │ └──┬───┬──┘├────┤»
«   q_1: ┤ √Xdg ├┤ Ry(-π/2) ├─┤ √X ├──┤ √Xdg ├┤ Ry(π/2) ├─■────┤ Y ├───┤ √X ├»
«        └──────┘└──────────┘ └────┘  └──────┘└─────────┘      └───┘   └────┘»
«meas: 2/════════════════════════════════════════════════════════════════════»
«                                                                            »
«        ┌───┐   ┌───┐      ┌──────┐┌──────────┐   ┌───┐    ┌──────┐   »
«   q_0: ┤ Y ├───┤ X ├────■─┤ √Xdg ├┤ Ry(-π/2) ├───┤ Y ├────┤ √Xdg ├─■─»
«        ├───┤┌──┴───┴──┐ │ └┬───┬─┘└──┬────┬──┘┌──┴───┴──┐┌┴──────┤ │ »
«   q_1: ┤ X ├┤ Ry(π/2) ├─■──┤ Y ├─────┤ √X ├───┤ Ry(π/2) ├┤ Rx(0) ├─■─»
«        └───┘└─────────┘    └───┘     └────┘   └─────────┘└───────┘   »
«meas: 2/══════════════════════════════════════════════════════════════»
«                                                                      »
«        ┌─────────┐   ┌────┐  ┌──────────┐┌────┐   ┌─────────┐      ┌───┐    »
«   q_0: ┤ Ry(π/2) ├───┤ √X ├──┤ Ry(-π/2) ├┤ √X ├─■─┤ Ry(π/2) ├─■────┤ Y ├────»
«        ├─────────┤┌──┴────┴─┐└──┬────┬──┘└────┘ │ └─┬──────┬┘ │ ┌──┴───┴───┐»
«   q_1: ┤ Ry(π/2) ├┤ Ry(π/2) ├───┤ √X ├──────────■───┤ √Xdg ├──■─┤ Ry(-π/2) ├»
«        └─────────┘└─────────┘   └────┘              └──────┘    └──────────┘»
«meas: 2/═════════════════════════════════════════════════════════════════════»
«                                                                             »
«           ┌────┐  ┌───┐ ┌──────┐   ┌─────────┐   ┌───┐ ┌────┐┌──────────┐»
«   q_0: ───┤ √X ├──┤ Y ├─┤ √Xdg ├─■─┤ Ry(π/2) ├─■─┤ Y ├─┤ √X ├┤ Ry(-π/2) ├»
«        ┌──┴────┴─┐├───┴┐└──────┘ │ └─┬──────┬┘ │ ├───┴┐├───┬┘└┬───────┬─┘»
«   q_1: ┤ Ry(π/2) ├┤ √X ├─────────■───┤ √Xdg ├──■─┤ √X ├┤ X ├──┤ Ry(0) ├──»
«        └─────────┘└────┘             └──────┘    └────┘└───┘  └───────┘  »
«meas: 2/══════════════════════════════════════════════════════════════════»
«                                                                          »
«        ┌──────┐   ┌─────────┐      ┌───┐      ┌────┐ ┌────┐┌─────────┐   »
«   q_0: ┤ √Xdg ├─■─┤ Ry(π/2) ├─■────┤ Y ├──────┤ √X ├─┤ √X ├┤ Ry(π/2) ├─■─»
«        └──────┘ │ └─┬──────┬┘ │ ┌──┴───┴───┐┌─┴────┴┐└────┘└─────────┘ │ »
«   q_1: ─────────■───┤ √Xdg ├──■─┤ Ry(-π/2) ├┤ Rx(0) ├──────────────────■─»
«                     └──────┘    └──────────┘└───────┘                    »
«meas: 2/══════════════════════════════════════════════════════════════════»
«                                                                          »
«        ┌──────┐┌──────────┐┌──────┐┌──────────┐   ┌─────────┐   ┌─────────┐ »
«   q_0: ┤ √Xdg ├┤ Ry(-π/2) ├┤ √Xdg ├┤ Ry(-π/2) ├─■─┤ Ry(π/2) ├─■─┤ Ry(π/2) ├─»
«        └┬───┬─┘└──┬────┬──┘└┬───┬─┘├──────────┤ │ └─┬──────┬┘ │ ├─────────┴┐»
«   q_1: ─┤ Y ├─────┤ √X ├────┤ X ├──┤ Ry(-π/2) ├─■───┤ √Xdg ├──■─┤ Ry(-π/2) ├»
«         └───┘     └────┘    └───┘  └──────────┘     └──────┘    └──────────┘»
«meas: 2/═════════════════════════════════════════════════════════════════════»
«                                                                             »
«           ┌────┐   ┌───────┐                ┌──────┐ ┌──────────┐┌────┐»
«   q_0: ───┤ √X ├───┤ Ry(0) ├────────────■───┤ √Xdg ├─┤ Ry(-π/2) ├┤ √X ├»
«        ┌──┴────┴──┐└─┬────┬┘┌─────────┐ │ ┌─┴──────┴┐├──────────┤├────┤»
«   q_1: ┤ Ry(-π/2) ├──┤ √X ├─┤ Ry(π/2) ├─■─┤ Ry(π/2) ├┤ Ry(-π/2) ├┤ √X ├»
«        └──────────┘  └────┘ └─────────┘   └─────────┘└──────────┘└────┘»
«meas: 2/════════════════════════════════════════════════════════════════»
«                                                                        »
«        ┌──────────┐   ┌──────────┐   ┌─────────┐    ┌─────────┐┌───────┐»
«   q_0: ┤ Ry(-π/2) ├─■─┤ Ry(-π/2) ├─■─┤ Ry(π/2) ├──■─┤ Ry(π/2) ├┤ Rx(0) ├»
«        ├─────────┬┘ │ ├─────────┬┘ │ ├─────────┴┐ │ ├─────────┤└─┬───┬─┘»
«   q_1: ┤ Ry(π/2) ├──■─┤ Ry(π/2) ├──■─┤ Ry(-π/2) ├─■─┤ Ry(π/2) ├──┤ Y ├──»
«        └─────────┘    └─────────┘    └──────────┘   └─────────┘  └───┘  »
«meas: 2/═════════════════════════════════════════════════════════════════»
«                                                                         »
«                 ┌──────────┐   ┌─────────┐      ┌──────┐ ┌──────────┐      »
«   q_0: ───────■─┤ Ry(-π/2) ├─■─┤ Ry(π/2) ├──■───┤ √Xdg ├─┤ Ry(-π/2) ├──────»
«        ┌────┐ │ ├─────────┬┘ │ ├─────────┴┐ │ ┌─┴──────┴┐└──┬───┬───┘┌────┐»
«   q_1: ┤ √X ├─■─┤ Ry(π/2) ├──■─┤ Ry(-π/2) ├─■─┤ Ry(π/2) ├───┤ Y ├────┤ √X ├»
«        └────┘   └─────────┘    └──────────┘   └─────────┘   └───┘    └────┘»
«meas: 2/════════════════════════════════════════════════════════════════════»
«                                                                            »
«           ┌─────────┐   ┌────┐   ┌─────────┐  ┌──────┐             »
«   q_0: ─■─┤ Ry(π/2) ├───┤ √X ├───┤ Ry(π/2) ├──┤ √Xdg ├───────────■─»
«         │ └─┬──────┬┘┌──┴────┴──┐└──┬────┬─┘┌─┴──────┴┐┌───────┐ │ »
«   q_1: ─■───┤ √Xdg ├─┤ Ry(-π/2) ├───┤ √X ├──┤ Ry(π/2) ├┤ Rx(0) ├─■─»
«             └──────┘ └──────────┘   └────┘  └─────────┘└───────┘   »
«meas: 2/════════════════════════════════════════════════════════════»
«                                                                    »
«          ┌──────┐ ┌──────────┐┌──────┐┌──────────┐   ┌─────────┐   »
«   q_0: ──┤ √Xdg ├─┤ Ry(-π/2) ├┤ √Xdg ├┤ Ry(-π/2) ├─■─┤ Ry(π/2) ├─■─»
«        ┌─┴──────┴┐├──────────┤└┬────┬┘└──────────┘ │ └─┬──────┬┘ │ »
«   q_1: ┤ Ry(π/2) ├┤ Ry(-π/2) ├─┤ √X ├──────────────■───┤ √Xdg ├──■─»
«        └─────────┘└──────────┘ └────┘                  └──────┘    »
«meas: 2/════════════════════════════════════════════════════════════»
«                                                                    »
«        ┌─────────┐┌──────────┐┌──────┐┌───┐┌───────┐            ┌─────────┐»
«   q_0: ┤ Ry(π/2) ├┤ Ry(-π/2) ├┤ √Xdg ├┤ Y ├┤ Rx(0) ├──────────■─┤ Ry(π/2) ├»
«        └──┬────┬─┘├─────────┬┘└┬────┬┘├───┤└┬──────┤┌───────┐ │ └─┬──────┬┘»
«   q_1: ───┤ √X ├──┤ Ry(π/2) ├──┤ √X ├─┤ Y ├─┤ √Xdg ├┤ Rx(0) ├─■───┤ √Xdg ├─»
«           └────┘  └─────────┘  └────┘ └───┘ └──────┘└───────┘     └──────┘ »
«meas: 2/════════════════════════════════════════════════════════════════════»
«                                                                            »
«              ┌───┐    ┌────┐┌──────────┐┌────┐   ┌─────────┐   ┌──────┐»
«   q_0: ─■────┤ Y ├────┤ √X ├┤ Ry(-π/2) ├┤ √X ├─■─┤ Ry(π/2) ├─■─┤ √Xdg ├»
«         │ ┌──┴───┴───┐├───┬┘├──────────┤└────┘ │ └─┬──────┬┘ │ └┬────┬┘»
«   q_1: ─■─┤ Ry(-π/2) ├┤ X ├─┤ Ry(-π/2) ├───────■───┤ √Xdg ├──■──┤ √X ├─»
«           └──────────┘└───┘ └──────────┘           └──────┘     └────┘ »
«meas: 2/════════════════════════════════════════════════════════════════»
«                                                                        »
«        ┌──────────┐┌────┐   ┌────┐  ┌─────────┐   ┌──────┐┌──────────┐»
«   q_0: ┤ Ry(-π/2) ├┤ √X ├───┤ √X ├──┤ Ry(π/2) ├─■─┤ √Xdg ├┤ Ry(-π/2) ├»
«        ├─────────┬┘├────┤┌──┴────┴─┐└─┬──────┬┘ │ └┬───┬─┘└──┬────┬──┘»
«   q_1: ┤ Ry(π/2) ├─┤ √X ├┤ Ry(π/2) ├──┤ √Xdg ├──■──┤ Y ├─────┤ √X ├───»
«        └─────────┘ └────┘└─────────┘  └──────┘     └───┘     └────┘   »
«meas: 2/═══════════════════════════════════════════════════════════════»
«                                                                       »
«        ┌─────────┐  ┌────┐    ┌─────────┐   ┌────┐          ░ ┌─┐   
«   q_0: ┤ Ry(π/2) ├──┤ √X ├──■─┤ Ry(π/2) ├───┤ √X ├──────────░─┤M├───
«        └──┬───┬──┘┌─┴────┴┐ │ └─┬──────┬┘┌──┴────┴──┐┌────┐ ░ └╥┘┌─┐
«   q_1: ───┤ X ├───┤ Ry(0) ├─■───┤ √Xdg ├─┤ Ry(-π/2) ├┤ √X ├─░──╫─┤M├
«           └───┘   └───────┘     └──────┘ └──────────┘└────┘ ░  ║ └╥┘
«meas: 2/════════════════════════════════════════════════════════╩══╩═
«                                                                0  1 

We define a function that executes the quantum circuits and returns the expectation value. This is consumed by Mitiq’s execute_with_zne. In this example, the expectation value is the probability of measuring the ground state, which is what one would expect from an ideal randomized benchmarking circuit.

def execute_circuit(circuit):
    """Execute the input circuit and return the expectation value of |00..0><00..0|"""
    noisy_backend = FakeJakarta()
    noisy_result = noisy_backend.run(circuit, shots=shots).result()
    noisy_counts = noisy_result.get_counts(circuit)
    noisy_expectation_value = noisy_counts[n_qubits * "0"] / shots
    return noisy_expectation_value
mitigated = execute_with_zne(circuit, execute_circuit)
unmitigated = execute_circuit(circuit)
ideal = 1 #property of RB circuits

print("ideal = \t \t",ideal)
print("unmitigated = \t \t",unmitigated)
print("mitigated = \t \t",mitigated)
ideal = 	 	 1
unmitigated = 	 	 0.939
mitigated = 	 	 1.0199999999999982

Using calibration to improve the results#

Let’s consider a noisy backend using the Qiskit noisy simulator, FakeJakarta. Note that the executor passed to the Calibrator object must return counts, as opposed to expectation values.

def execute_calibration(qiskit_circuit):
    """Execute the input circuits and return the measurement results."""
    noisy_backend = FakeJakarta()
    noisy_result = noisy_backend.run(qiskit_circuit, shots=shots).result()
    noisy_counts = noisy_result.get_counts(qiskit_circuit)
    noisy_counts = { k.replace(" ",""):v for k, v in noisy_counts.items()}
    measurements = MeasurementResult.from_counts(noisy_counts)
    return measurements

We import from the calibration module the key ingredients to use mitiq.calibration: the Calibrator class, the mitiq.calibration.settings.Settings class and the execute_with_mitigation function.

Currently mitiq.calibration supports ZNE as a technique to calibrate from, tuning different scale factors, extrapolation methods and circuit scaling methods.

Let’s run the calibration using an ad-hoc RBSettings and using the log=True option in order to print the list of experiments run.

  • benchmarks: Circuit type: “rb”

  • strategies: use various “zne” strategies, testing various “scale_noise” methods (such as mitiq.zne.scaling.folding.fold_global and mitiq.zne.scaling.folding.fold_gates_at_random), and ZNE factories for extrapolation (such as mitiq.zne.inference.RichardsonFactory and mitiq.zne.inference.LinearFactory)

from mitiq.zne.inference import LinearFactory, RichardsonFactory
from mitiq.zne.scaling import (
    fold_gates_at_random,
    fold_global,
)

RBSettings = Settings(
    benchmarks=[
        {
            "circuit_type": "rb",
            "num_qubits": n_qubits,
            "circuit_depth": depth_circuit,
        },
    ],
    strategies=[
        {
            "technique": "zne",
            "scale_noise": fold_global,
            "factory": RichardsonFactory([1.0, 2.0, 3.0]),
        },
        {
            "technique": "zne",
            "scale_noise": fold_global,
            "factory": RichardsonFactory([1.0, 3.0, 5.0]),
        },
        {
            "technique": "zne",
            "scale_noise": fold_global,
            "factory": LinearFactory([1.0, 2.0, 3.0]),
        },
        {
            "technique": "zne",
            "scale_noise": fold_global,
            "factory": LinearFactory([1.0, 3.0, 5.0]),
        },
        {
            "technique": "zne",
            "scale_noise": fold_gates_at_random,
            "factory": RichardsonFactory([1.0, 2.0, 3.0]),
        },
        {
            "technique": "zne",
            "scale_noise": fold_gates_at_random,
            "factory": RichardsonFactory([1.0, 3.0, 5.0]),
        },
        {
            "technique": "zne",
            "scale_noise": fold_gates_at_random,
            "factory": LinearFactory([1.0, 2.0, 3.0]),
        },
        {
            "technique": "zne",
            "scale_noise": fold_gates_at_random,
            "factory": LinearFactory([1.0, 3.0, 5.0]),
        },
    ],
)
cal = Calibrator(execute_calibration, frontend="qiskit", settings=RBSettings)
cal.run(log=True)
| performance | circuit | method | extrapolation | scale factors | scale_method         |
| ----------- | ------- | ------ | ------------- | ------------- | -------------------- |
|     ❌      |   rb    |  ZNE   | Richardson    | 1.0, 2.0, 3.0 | fold_global          |
|     ✅      |   rb    |  ZNE   | Richardson    | 1.0, 3.0, 5.0 | fold_global          |
|     ❌      |   rb    |  ZNE   | Linear        | 1.0, 2.0, 3.0 | fold_global          |
|     ✅      |   rb    |  ZNE   | Linear        | 1.0, 3.0, 5.0 | fold_global          |
|     ✅      |   rb    |  ZNE   | Richardson    | 1.0, 2.0, 3.0 | fold_gates_at_random |
|     ✅      |   rb    |  ZNE   | Richardson    | 1.0, 3.0, 5.0 | fold_gates_at_random |
|     ✅      |   rb    |  ZNE   | Linear        | 1.0, 2.0, 3.0 | fold_gates_at_random |
|     ✅      |   rb    |  ZNE   | Linear        | 1.0, 3.0, 5.0 | fold_gates_at_random |

As you can see above, several experiments were run, and each one has either a red cross (❌) or a green check (✅) to signal whether the error mitigation experiment obtained an expectation value that is better than the non-mitigated one.

calibrated_mitigated=execute_with_mitigation(circuit, execute_circuit, calibrator=cal)
mitigated=execute_with_zne(circuit, execute_circuit)
unmitigated=execute_circuit(circuit)

print("ideal = \t \t",ideal)
print("unmitigated = \t \t",unmitigated)
print("mitigated = \t \t",mitigated)
print("calibrated_mitigated = \t",calibrated_mitigated)
ideal = 	 	 1
unmitigated = 	 	 0.938
mitigated = 	 	 0.9569999999999975
calibrated_mitigated = 	 0.9709999999999996