Noise learning helper
The error mitigation techniques PEA and PEC both utilize a noise learning component based on a Pauli-Lindblad noise model, which is typically managed during execution after submitting one or more jobs through qiskit-ibm-runtime
without any local access to the fitted noise model. However, as of qiskit-ibm-runtime
0.27.1, a NoiseLearner
and associated NoiseLearnerOptions
class have been created to obtain the results of these noise learning experiments. These results can then be stored locally as a NoiseLearnerResult
and used as input in later experiments. This page provides an overview of its usage and the associated options available.
Overview
The NoiseLearner
class performs experiments that characterize noise processes based on a Pauli-Lindblad noise model for one (or more) circuits. It possesses a run()
method that executes the learning experiments and takes as input either a list of circuits or a PUB, and returns a NoiseLearnerResult
containing the learned noise channels and metadata about the job(s) submitted. Below is a code snippet demonstrating the usage of the helper program.
from qiskit import QuantumCircuit
from qiskit.transpiler import CouplingMap
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2
from qiskit_ibm_runtime.noise_learner import NoiseLearner
from qiskit_ibm_runtime.options import NoiseLearnerOptions, ResilienceOptionsV2, EstimatorOptions
# Build a circuit with two entangling layers
num_qubits = 27
edges = list(CouplingMap.from_line(num_qubits, bidirectional=False))
even_edges = edges[::2]
odd_edges = edges[1::2]
circuit = QuantumCircuit(num_qubits)
for pair in even_edges:
circuit.cx(pair[0], pair[1])
for pair in odd_edges:
circuit.cx(pair[0], pair[1])
# Choose a backend to run on
service = QiskitRuntimeService()
backend = service.least_busy()
# Transpile the circuit for execution
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
circuit_to_learn = pm.run(circuit)
# Instantiate a NoiseLearner object and execute the noise learning program
learner = NoiseLearner(mode=backend)
job = learner.run([circuit_to_learn])
noise_model = job.result()
The resulting NoiseLearnerResult.data
is a list of LayerError
objects containing the noise model for each individual entangling layer that belongs to the target circuit(s). Each LayerError
stores the layer information, in the form of a circuit and a set of qubit labels, alongside the PauliLindBladError
for the noise model that was learned for the given layer.
print(f'Noise learner result contains {len(noise_model.data)} entries'\
f' and has the following type:\n {type(noise_model)}\n')
print(f'Each element of `NoiseLearnerResult` then contains'\
f' an object of type:\n {type(noise_model.data[0])}\n')
print(f'And each of these `LayerError` objects possess the'\
f' following data: \n{noise_model.data[0].error}\n')
Output:
Noise learner result contains 2 entries and has the following type:
<class 'qiskit_ibm_runtime.utils.noise_learner_result.NoiseLearnerResult'>
Each element of `NoiseLearnerResult` then contains an object of type:
<class 'qiskit_ibm_runtime.utils.noise_learner_result.LayerError'>
And each of these `LayerError` objects possess the following data:
PauliLindbladError(generators=['IIIIIIIIIIIIIIIIIIIIIIIIIIX', 'IIIIIIIIIIIIIIIIIIIIIIIIIIY',
'IIIIIIIIIIIIIIIIIIIIIIIIIIZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIXI',
'IIIIIIIIIIIIIIIIIIIIIIIIIXX', 'IIIIIIIIIIIIIIIIIIIIIIIIIXY',
'IIIIIIIIIIIIIIIIIIIIIIIIIXZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIYI',
'IIIIIIIIIIIIIIIIIIIIIIIIIYX', 'IIIIIIIIIIIIIIIIIIIIIIIIIYY',
'IIIIIIIIIIIIIIIIIIIIIIIIIYZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIZI',
'IIIIIIIIIIIIIIIIIIIIIIIIIZX', 'IIIIIIIIIIIIIIIIIIIIIIIIIZY',
'IIIIIIIIIIIIIIIIIIIIIIIIIZZ', 'IIIIIIIIIIIIIIIIIIIIIIIIXII',
'IIIIIIIIIIIIIIIIIIIIIIIIXXI', 'IIIIIIIIIIIIIIIIIIIIIIIIXYI',
'IIIIIIIIIIIIIIIIIIIIIIIIXZI', 'IIIIIIIIIIIIIIIIIIIIIIIIYII',
'IIIIIIIIIIIIIIIIIIIIIIIIYXI', 'IIIIIIIIIIIIIIIIIIIIIIIIYYI',
'IIIIIIIIIIIIIIIIIIIIIIIIYZI', 'IIIIIIIIIIIIIIIIIIIIIIIIZII',
'IIIIIIIIIIIIIIIIIIIIIIIIZXI', 'IIIIIIIIIIIIIIIIIIIIIIIIZYI',
'IIIIIIIIIIIIIIIIIIIIIIIIZZI', 'IIIIIIIIIIIIIIIIIIIIIIIXIII',
'IIIIIIIIIIIIIIIIIIIIIIIXXII', 'IIIIIIIIIIIIIIIIIIIIIIIXYII',
'IIIIIIIIIIIIIIIIIIIIIIIXZII', 'IIIIIIIIIIIIIIIIIIIIIIIYIII',
'IIIIIIIIIIIIIIIIIIIIIIIYXII', 'IIIIIIIIIIIIIIIIIIIIIIIYYII',
'IIIIIIIIIIIIIIIIIIIIIIIYZII', 'IIIIIIIIIIIIIIIIIIIIIIIZIII',
'IIIIIIIIIIIIIIIIIIIIIIIZXII', 'IIIIIIIIIIIIIIIIIIIIIIIZYII',
'IIIIIIIIIIIIIIIIIIIIIIIZZII', 'IIIIIIIIIIIIIIIIIIIIIIXIIII',
'IIIIIIIIIIIIIIIIIIIIIIXXIII', 'IIIIIIIIIIIIIIIIIIIIIIXYIII',
'IIIIIIIIIIIIIIIIIIIIIIXZIII', 'IIIIIIIIIIIIIIIIIIIIIIYIIII',
'IIIIIIIIIIIIIIIIIIIIIIYXIII', 'IIIIIIIIIIIIIIIIIIIIIIYYIII',
'IIIIIIIIIIIIIIIIIIIIIIYZIII', 'IIIIIIIIIIIIIIIIIIIIIIZIIII',
'IIIIIIIIIIIIIIIIIIIIIIZXIII', 'IIIIIIIIIIIIIIIIIIIIIIZYIII',
'IIIIIIIIIIIIIIIIIIIIIIZZIII', 'IIIIIIIIIIIIIIIIIIIIIXIIIII',
'IIIIIIIIIIIIIIIIIIIIIXXIIII', 'IIIIIIIIIIIIIIIIIIIIIXYIIII',
'IIIIIIIIIIIIIIIIIIIIIXZIIII', 'IIIIIIIIIIIIIIIIIIIIIYIIIII',
'IIIIIIIIIIIIIIIIIIIIIYXIIII', 'IIIIIIIIIIIIIIIIIIIIIYYIIII',
'IIIIIIIIIIIIIIIIIIIIIYZIIII', 'IIIIIIIIIIIIIIIIIIIIIZIIIII',
'IIIIIIIIIIIIIIIIIIIIIZXIIII', 'IIIIIIIIIIIIIIIIIIIIIZYIIII',
'IIIIIIIIIIIIIIIIIIIIIZZIIII', 'IIIIIIIIIIIIIIIIIIIIXIIIIII',
'IIIIIIIIIIIIIIIIIIIIXXIIIII', 'IIIIIIIIIIIIIIIIIIIIXYIIIII',
'IIIIIIIIIIIIIIIIIIIIXZIIIII', 'IIIIIIIIIIIIIIIIIIIIYIIIIII',
'IIIIIIIIIIIIIIIIIIIIYXIIIII', 'IIIIIIIIIIIIIIIIIIIIYYIIIII',
'IIIIIIIIIIIIIIIIIIIIYZIIIII', 'IIIIIIIIIIIIIIIIIIIIZIIIIII',
'IIIIIIIIIIIIIIIIIIIIZXIIIII', 'IIIIIIIIIIIIIIIIIIIIZYIIIII', ...], rates=[0.00084, 0.00089, 0.00054, 0.00156, 0.00089, 0.00084, 0.00023, 0.0, 0.0, 0.0, 0.00102, 0.00015, 0.0, 3e-05, 0.00082, 0.0013, 0.0, 6e-05, 9e-05, 0.0, 5e-05, 0.00044, 0.00029, 0.00027, 0.00013, 0.00034, 0.00056, 0.00181, 0.00135, 0.0, 0.0, 0.00132, 0.00184, 0.0, 5e-05, 0.00096, 0.00013, 0.00119, 0.00078, 0.00069, 0.0, 3e-05, 0.0, 0.00047, 3e-05, 0.0, 0.0, 0.00061, 0.0, 0.0, 0.00481, 0.00074, 0.0005, 0.00072, 0.00022, 0.00025, 0.0, 0.00028, 0.00086, 0.00057, 0.0, 0.0, 0.00054, 0.00084, 0.0, 0.0, 0.00025, 0.00081, 0.0, 0.00025, 0.0, 0.00764, 0.0, 3e-05, 3e-05, 0.00133, 0.00106, 0.00109, 0.0, 0.00027, 3e-05, 0.0, 0.00132, 0.00061, 0.00036, 5e-05, 0.00098, 0.00124, 7e-05, 0.0, 0.0, 0.00062, 1e-05, 0.00059, 0.00012, 0.00272, 1e-05, 0.00012, 0.00059, 0.00032, 0.00173, 0.00029, 0.0, 0.00118, 0.00087, 0.00036, 0.00057, 0.00462, 0.00021, 0.00343, 0.00134, 0.00103, 9e-05, 0.00023, 0.0, 0.00127, 0.00023, 9e-05, 0.0, 0.00218, 0.00023, 0.00023, 0.00582, 0.0, 0.0, 0.00193, 0.00049, 0.00514, 0.00501, 0.0006, 0.0, 0.0, 0.0, 0.00149, 0.00045, 0.0005, 0.0, 0.00057, 0.00153, 0.0, 0.0, 0.0, 0.00017, 0.0, 0.00053, 0.00048, 0.0, 0.0, 0.00012, 0.00086, 0.00154, 0.00207, 0.00018, 0.0001, 0.00179, 0.00181, 0.00012, 0.0, 0.00106, 0.00047, 0.00098, 0.00117, 0.00171, 0.0, 0.0, 0.0, 3e-05, 0.0, 5e-05, 0.00035, 0.0002, 5e-05, 0.0, 0.00035, 0.00061, 0.00101, 0.0, 0.0, 0.00049, 0.00113, 0.0, 3e-05, 0.00036, 0.00022, 0.0006, 0.00044, 0.0, 0.00028, 0.00028, 0.00012, 0.0, 5e-05, 1e-05, 0.0004, 0.0, 0.00019, 0.00023, 0.00058, 0.00211, 0.00248, 0.00024, 0.0, 0.00248, 0.00211, 0.00068, 0.0, 0.0043, 0.00072, 0.00062, 0.00032, 0.00206, 0.0, 0.0, 0.0, 0.00117, 0.0, 0.0, 0.0, 0.00344, 0.0, 0.0, 0.00263, 0.00047, 0.00117, 0.00206, 0.00072, 0.0, 0.00055, 0.0, 0.00194, 0.00087, 0.00059, 0.0, 0.00095, 0.00153, 0.00158, 0.00135, 0.00043, 0.0, 0.0, 0.0, 0.0007, 4e-05, 6e-05, 0.00196, 0.00017, 0.00052, 0.00043, 0.00083, 0.0, 0.00035, 0.00183, 0.00017, 0.00033, 0.00062, 0.00201, 0.00042, 9e-05, 0.0, 5e-05, 1e-05, 0.0005, 0.0, 0.00274, 0.00509, 0.00517, 0.00163, 0.00261, 0.00056, 0.00252, 0.0, 0.00143, 0.00018, 7e-05, 0.00174, 0.00124, 0.0, 0.00038, 0.00194, 0.00027, 0.0, 0.0, 0.00043, 0.00028, 0.0, 0.00043, 0.0, 0.00056, 0.00014, 0.0, 0.0, 0.00197, 0.00071, 0.0007, 0.00037, 0.00011, 0.00031, 0.00018, 0.0006, 0.00028, 0.0, 0.0, 0.00044, 0.00142, 0.0, 6e-05, 6e-05, 0.00198, 0.0, 4e-05, 4e-05, 0.00851, 0.0, 0.00022, 0.00022])
The LayerError.error
attribute of the noise learning result contains the generators and error rates of the fitted Pauli Lindblad model, which has the form
where the are the LayerError.rates
and are the Pauli operators specified in LayerError.generators
.
Noise learning options
You can choose among several options to input when you instantiate a NoiseLearner
object. These options are encapsulated by the qiskit_ibm_runtime.options.NoiseLearnerOptions
class and include the ability to specify the maximum layers to learn, number of randomizations, and the twirling strategy, among others. Refer to the API documentation about NoiseLearnerOptions
for more detailed information.
Below is a simple example showing how to use the NoiseLearnerOptions
in a NoiseLeaner
experiment:
# Build a GHZ circuit
circuit = QuantumCircuit(10)
circuit.h(0)
circuit.cx(range(0, 9), range(1, 10))
# Choose a backend to run on
service = QiskitRuntimeService()
backend = service.least_busy()
# Transpile the circuit for execution
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
circuit_to_run = pm.run(circuit_to_learn)
# Instantiate a noise learner options object
learner_options = NoiseLearnerOptions(
max_layers_to_learn = 3,
num_randomizations = 32,
twirling_strategy = "all"
)
# Instantiate a NoiseLearner object and execute the noise learning program
learner = NoiseLearner(mode=backend, options=learner_options)
job = learner.run([circuit_to_run])
noise_model = job.result()
Input noise model to a primitive
The noise model learned on the circuit can also be used as an input to the EstimatorV2
primitive implemented in Qiskit IBM Runtime. This can be passed into the primitive a few different ways. The next three examples show how you can pass the noise model to the estimator.options
attribute directly, via a ResilienceOptionsV2
object before instantiating an Estimator primitive, and by passing in an appropriately formatted dictionary.
# pass the noise model to the `estimator.options` attribute directly
estimator = EstimatorV2(mode=backend)
estimator.options.resilience.layer_noise_model = noise_model
# Specify options via a ResilienceOptionsV2 object
resilience_options = ResilienceOptionsV2(layer_noise_model=noise_model)
estimator_options = EstimatorOptions(resilience=resilience_options)
estimator = EstimatorV2(mode=backend, options=estimator_options)
# Specify options via a dictionary
options_dict = {'resilience_level':2,
'resilience':{'layer_noise_model': noise_model}}
estimator = EstimatorV2(mode=backend, options=options_dict)
Once the noise model is passed into the EstimatorV2
object, it can be used to run workloads and perform error mitigation as normal.
Next steps
- Read more about configuring error mitigation.
- Review the EstimatorOptions API reference and ResilienceOptionsV2 API reference.
- Learn more about Error mitigation and suppression techniques that are available through Qiskit Runtime.
- Review how to Specify options for the Qiskit Runtime primitives.
- Read Migrate to V2 primitives.