Breaking into error mitigation with Mitiq’s calibration module#

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 backendRun calibration with some special settings,
RBSettings
, using thecal.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
andmitiq.zne.scaling.folding.fold_gates_at_random
), and ZNE factories for extrapolation (such asmitiq.zne.inference.RichardsonFactory
andmitiq.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