Skip to main contentIBM Quantum Documentation Preview
This is a preview build of IBM Quantum™ documentation. Refer to docs.quantum.ibm.com for the official documentation.

Noise learning helper

Package versions

The code on this page was developed using the following requirements. We recommend using these versions or newer.

qiskit[all]~=1.3.1
qiskit-ibm-runtime~=0.34.0
qiskit-aer~=0.15.1
qiskit-serverless~=0.18.0
qiskit-ibm-catalog~=0.2
qiskit-addon-sqd~=0.8.1
qiskit-addon-utils~=0.1.0
qiskit-addon-mpf~=0.2.0
scipy~=1.14.1
qiskit-addon-aqc-tensor~=0.1.2
qiskit-addon-obp~=0.1.0
scipy~=1.14.1
pyscf~=2.7.0

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.00041, 0.00027, 0.00046, 0.00021, 0.0, 1e-05, 8e-05, 0.00046, 0.0, 8e-05, 1e-05, 0.00129, 0.0, 1e-05, 1e-05, 0.00023, 0.00058, 0.00033, 0.00031, 7e-05, 5e-05, 0.00012, 0.00042, 0.00022, 0.0, 6e-05, 0.00026, 0.00132, 0.00017, 0.0001, 0.0001, 0.00061, 0.00017, 0.0001, 0.0001, 0.00176, 0.0, 0.0, 0.0, 0.00147, 0.00098, 0.00169, 0.00024, 0.00033, 0.0, 0.0, 0.00163, 0.0, 0.0, 0.0, 0.00216, 0.00075, 0.00035, 0.00075, 0.00063, 0.0, 0.0, 0.0012, 0.0, 0.0, 0.0, 4e-05, 0.001, 0.00258, 0.00365, 6e-05, 1e-05, 0.00355, 0.00266, 0.00134, 0.00138, 0.00126, 0.00146, 3e-05, 0.00013, 0.00138, 0.0, 0.0, 0.0004, 0.0, 4e-05, 0.0, 0.00113, 0.00024, 0.0, 0.00014, 0.00124, 0.00075, 0.00104, 0.0009, 0.0, 0.00059, 0.0012, 0.0, 0.00051, 0.0029, 0.00106, 0.00154, 0.00115, 0.00034, 0.00055, 0.00011, 0.0, 0.0005, 0.0004, 0.00017, 1e-05, 0.00191, 0.00012, 0.00055, 0.00037, 0.0009, 0.0, 0.0, 0.0, 0.00046, 3e-05, 3e-05, 0.0, 0.00712, 0.0, 0.0, 0.0, 0.00027, 0.0, 0.0, 3e-05, 0.00084, 0.0, 5e-05, 0.0, 0.00107, 5e-05, 0.0, 0.0, 0.00097, 0.00049, 9e-05, 1e-05, 0.00049, 0.00097, 5e-05, 0.0, 0.00075, 0.00027, 0.00112, 0.0009, 0.00093, 0.0, 0.0, 0.0, 0.0006, 0.0, 0.0, 0.0, 0.00041, 0.0, 0.0, 0.0, 0.0008, 0.00162, 0.00018, 1e-05, 0.00156, 0.00088, 0.0, 0.0, 0.00103, 0.00023, 0.00041, 0.0006, 0.00056, 0.0, 0.0, 0.0, 0.0, 0.0, 3e-05, 0.0, 0.00072, 0.00011, 0.0, 0.0, 0.00098, 0.0017, 3e-05, 6e-05, 0.00141, 0.00127, 0.00017, 0.0, 0.00717, 0.0001, 0.00079, 0.0, 0.00067, 6e-05, 6e-05, 0.00023, 0.0, 5e-05, 0.00018, 0.00015, 9e-05, 0.00018, 5e-05, 0.00016, 4e-05, 0.0008, 0.0, 0.00042, 0.00017, 0.00067, 0.0, 0.00021, 0.00244, 0.0002, 0.00048, 0.00039, 0.0008, 0.00047, 0.00017, 0.00046, 0.00047, 0.0008, 0.00252, 0.0054, 1e-05, 0.0, 0.00648, 0.00617, 0.00071, 0.00033, 0.00572, 0.00046, 0.0, 0.00031, 0.0, 0.00038, 0.0, 0.0, 0.00031, 0.00307, 0.0, 0.0, 0.0, 0.0007, 0.00069, 0.00077, 0.0006, 0.00045, 0.0, 0.00033, 0.00045, 0.00033, 0.0, 0.00021, 0.00057, 0.00048, 0.0, 0.0, 0.00011, 0.00041, 0.0, 0.00011, 0.0, 0.00118, 0.0, 1e-05, 1e-05, 0.00113, 0.00053, 0.00059, 0.00015, 0.00082, 7e-05, 0.0002, 0.00115, 0.00089, 0.0, 0.00018, 0.00108, 0.00067, 0.0, 0.00015, 0.0, 0.00068, 0.0, 0.0, 0.00015, 0.00089, 0.0, 0.00011, 0.00011, 0.00043, 0.00021, 0.00021, 0.0, 0.00084, 0.00083, 7e-05, 9e-05, 0.00018, 0.00022, 0.0001, 0.00013, 0.0, 0.0005, 0.0, 0.00022, 0.00018, 0.0001, 5e-05, 0.00018, 0.00059])

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

Λ(ρ)=expjrj(PjρPjρ),\Lambda(\rho) = \exp{\sum_j r_j \left(P_j \rho P_j^\dagger - \rho\right)},

where the rjr_j are the LayerError.rates and PjP_j 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

Recommendations