Calibration#
The mitiq.Calibrator
class provides a workflow for users to run a set of experiments to automatically determine an error mitigation strategy.
This gives the user freedom to work on more important parts of their algorithm/quantum program, and allows them to spend less time tuning error mitigation parameters.
Workflow#
To begin, we will need to define an executor which tells Mitiq how to run circuits. In order to use the calibration capabilities of Mitiq, we will need to define an executor which returns all the measured bitstrings, rather than an expectation value. This allows the calibration experiment to extract more fine-grained data from each circuit experiment it will run
import cirq
import numpy as np
from mitiq import MeasurementResult
def execute(circuit, noise_level=0.001):
circuit = circuit.with_noise(cirq.amplitude_damp(noise_level))
result = cirq.DensityMatrixSimulator().run(circuit, repetitions=100)
bitstrings = np.column_stack(list(result.measurements.values()))
return MeasurementResult(bitstrings)
We can now import the required objects and functions required for calibration.
from mitiq import Calibrator
To instantiate a Calibrator
we need to pass it an executor (as defined above), and a Settings
object.
You are free to define your own Settings
, but as a simple starting point, we provide ZNE_SETTINGS
based on different zero-noise extrapolation strategies and PEC_SETTINGS
based on different quasiprobability representations of ideal gates.
Finally, the execute_with_mitigation
function allows us to pass the calibration results directly to Mitiq and have it pick the strategy that performed best of those supplied in the Settings
object.
Calibration Experiments#
Before running any experiments, we can call the get_cost
function to ensure the experiments will not be too costly.
Once instantiated, we call the run
method to run the set of experiments, and the results of such experiments are stored internal to the class in cal.results
.
cal = Calibrator(execute, frontend="cirq")
print(cal.get_cost())
cal.run()
{'noisy_executions': 100, 'ideal_executions': 0}
Verbose Log Output#
To print results from the experiments the calibrator performs, the log
parameter can be passed in to the run
method with either the value of flat
or cartesian
. By using the log
parameter, detailed information about each experiment is printed when the run
method completes.
The detailed information can also be generated from the results of the calibrator after run
is called by calling either log_results_flat()
or log_results_cartesian()
.
The two options display the information in different formats, though both use a cross (✘) or a check (✔) to signal whether the error mitigation expirement obtained an expectation value better than the non-mitigated one.
# cal.results.log_results_flat()
cal.results.log_results_cartesian()
┌────────────────────────────────────┬────────────────────────────┬───────────────────────────┬────────────────────────────┬────────────────────────────┐
│ strategy\benchmark │ Type: ghz │ Type: w │ Type: rb │ Type: mirror │
│ │ Num qubits: 2 │ Num qubits: 2 │ Num qubits: 2 │ Num qubits: 2 │
│ │ Circuit depth: 2 │ Circuit depth: 2 │ Circuit depth: 46 │ Circuit depth: 33 │
│ │ Two qubit gate count: 1 │ Two qubit gate count: 2 │ Two qubit gate count: 11 │ Two qubit gate count: 14 │
├────────────────────────────────────┼────────────────────────────┼───────────────────────────┼────────────────────────────┼────────────────────────────┤
│ Technique: ZNE │ ✘ │ ✘ │ ✘ │ ✘ │
│ Factory: Richardson │ Noisy error: 0.04 │ Noisy error: 0.5 │ Noisy error: 0.03 │ Noisy error: 0.01 │
│ Scale factors: 1.0, 2.0, 3.0 │ Mitigated error: 0.28 │ Mitigated error: 0.5 │ Mitigated error: 0.11 │ Mitigated error: 0.04 │
│ Scale method: fold_global │ Improvement factor: 0.1429 │ Improvement factor: 1.0 │ Improvement factor: 0.2727 │ Improvement factor: 0.25 │
├────────────────────────────────────┼────────────────────────────┼───────────────────────────┼────────────────────────────┼────────────────────────────┤
│ Technique: ZNE │ ✔ │ ✘ │ ✔ │ ✔ │
│ Factory: Richardson │ Noisy error: 0.04 │ Noisy error: 0.5 │ Noisy error: 0.03 │ Noisy error: 0.01 │
│ Scale factors: 1.0, 3.0, 5.0 │ Mitigated error: 0.0075 │ Mitigated error: 0.5 │ Mitigated error: 0.0138 │ Mitigated error: 0.0075 │
│ Scale method: fold_global │ Improvement factor: 5.3333 │ Improvement factor: 1.0 │ Improvement factor: 2.1818 │ Improvement factor: 1.3333 │
├────────────────────────────────────┼────────────────────────────┼───────────────────────────┼────────────────────────────┼────────────────────────────┤
│ Technique: ZNE │ ✘ │ ✘ │ ✔ │ ✔ │
│ Factory: Linear │ Noisy error: 0.04 │ Noisy error: 0.5 │ Noisy error: 0.03 │ Noisy error: 0.01 │
│ Scale factors: 1.0, 2.0, 3.0 │ Mitigated error: 0.0467 │ Mitigated error: 0.5 │ Mitigated error: 0.0033 │ Mitigated error: 0.0067 │
│ Scale method: fold_global │ Improvement factor: 0.8571 │ Improvement factor: 1.0 │ Improvement factor: 9.0 │ Improvement factor: 1.5 │
├────────────────────────────────────┼────────────────────────────┼───────────────────────────┼────────────────────────────┼────────────────────────────┤
│ Technique: ZNE │ ✔ │ ✘ │ ✔ │ ✘ │
│ Factory: Linear │ Noisy error: 0.04 │ Noisy error: 0.5 │ Noisy error: 0.03 │ Noisy error: 0.01 │
│ Scale factors: 1.0, 3.0, 5.0 │ Mitigated error: 0.0117 │ Mitigated error: 0.5 │ Mitigated error: 0.0125 │ Mitigated error: 0.0225 │
│ Scale method: fold_global │ Improvement factor: 3.4286 │ Improvement factor: 1.0 │ Improvement factor: 2.4 │ Improvement factor: 0.4444 │
├────────────────────────────────────┼────────────────────────────┼───────────────────────────┼────────────────────────────┼────────────────────────────┤
│ Technique: ZNE │ ✘ │ ✘ │ ✘ │ ✘ │
│ Factory: Richardson │ Noisy error: 0.04 │ Noisy error: 0.5 │ Noisy error: 0.03 │ Noisy error: 0.01 │
│ Scale factors: 1.0, 2.0, 3.0 │ Mitigated error: 0.15 │ Mitigated error: 0.5 │ Mitigated error: 0.12 │ Mitigated error: 0.06 │
│ Scale method: fold_gates_at_random │ Improvement factor: 0.2667 │ Improvement factor: 1.0 │ Improvement factor: 0.25 │ Improvement factor: 0.1667 │
├────────────────────────────────────┼────────────────────────────┼───────────────────────────┼────────────────────────────┼────────────────────────────┤
│ Technique: ZNE │ ✘ │ ✘ │ ✘ │ ✘ │
│ Factory: Richardson │ Noisy error: 0.04 │ Noisy error: 0.5 │ Noisy error: 0.03 │ Noisy error: 0.01 │
│ Scale factors: 1.0, 3.0, 5.0 │ Mitigated error: 0.2775 │ Mitigated error: 0.5 │ Mitigated error: 0.0337 │ Mitigated error: 0.0125 │
│ Scale method: fold_gates_at_random │ Improvement factor: 0.1441 │ Improvement factor: 1.0 │ Improvement factor: 0.8889 │ Improvement factor: 0.8 │
├────────────────────────────────────┼────────────────────────────┼───────────────────────────┼────────────────────────────┼────────────────────────────┤
│ Technique: ZNE │ ✔ │ ✘ │ ✔ │ ✘ │
│ Factory: Linear │ Noisy error: 0.04 │ Noisy error: 0.5 │ Noisy error: 0.03 │ Noisy error: 0.01 │
│ Scale factors: 1.0, 2.0, 3.0 │ Mitigated error: 0.0033 │ Mitigated error: 0.5 │ Mitigated error: 0.0233 │ Mitigated error: 0.0233 │
│ Scale method: fold_gates_at_random │ Improvement factor: 12.0 │ Improvement factor: 1.0 │ Improvement factor: 1.2857 │ Improvement factor: 0.4286 │
├────────────────────────────────────┼────────────────────────────┼───────────────────────────┼────────────────────────────┼────────────────────────────┤
│ Technique: ZNE │ ✔ │ ✘ │ ✔ │ ✘ │
│ Factory: Linear │ Noisy error: 0.04 │ Noisy error: 0.5 │ Noisy error: 0.03 │ Noisy error: 0.01 │
│ Scale factors: 1.0, 3.0, 5.0 │ Mitigated error: 0.0133 │ Mitigated error: 0.5 │ Mitigated error: 0.0042 │ Mitigated error: 0.0183 │
│ Scale method: fold_gates_at_random │ Improvement factor: 3.0 │ Improvement factor: 1.0 │ Improvement factor: 7.2 │ Improvement factor: 0.5455 │
└────────────────────────────────────┴────────────────────────────┴───────────────────────────┴────────────────────────────┴────────────────────────────┘
Applying the optimal error mitigation strategy#
We first define randomized benchmarking circuit to test the effect of error mitigation.
from mitiq.benchmarks import generate_rb_circuits
circuit = generate_rb_circuits(2, 10)[0]
# circuits passed to an executor returning bitstrings must contain measurements
circuit.append(cirq.measure(circuit.all_qubits()))
Instead of deciding what error mitigation technique and what options to use, we can ask Mitiq to determine the optimal error mitigation strategy based on the previously performed calibration.
We can obtain this by calling the execute_with_mitigation
function and passing the circuit
, Calibrator
object, and a new expectation value executor.
def execute(circuit, noise_level=0.001):
circuit = circuit.with_noise(cirq.amplitude_damp(noise_level))
rho = (
cirq.DensityMatrixSimulator()
.simulate(circuit)
.final_density_matrix
)
return rho[0, 0].real
cal.execute_with_mitigation(circuit, execute)
1.000000789761543
Tutorial#
You can find an example on quantum error mitigation calibration in the Examples section of the documentation. This example illustrates functionalities from the calibration module using ZNE on a simulated IBM Quantum backend using Qiskit, defining a new settings object.