דלג לתוכן הראשי

עוזר לימוד הרעש

Package versions

הקוד בדף זה פותח תוך שימוש בדרישות הבאות. אנחנו ממליצים להשתמש בגרסאות אלה או בגרסאות חדשות יותר.

qiskit[all]~=2.4.1
qiskit-ibm-runtime~=0.47.0
samplomatic~=0.18.0

טכניקות הפחתת השגיאות PEA ו-PEC משתמשות שתיהן ברכיב לימוד רעש המבוסס על מודל רעש Pauli-Lindblad, שמנוהל בדרך כלל במהלך הביצוע לאחר הגשת עבודה אחת או יותר דרך qiskit-ibm-runtime, ללא גישה מקומית למודל הרעש המותאם. אולם, החל מגרסה v0.27.1 של qiskit-ibm-runtime, נוצרו מחלקות NoiseLearner ו-NoiseLearnerOptions כדי לקבל את תוצאות ניסויי לימוד הרעש הללו. ניתן לאחסן תוצאות אלה מקומית כ-NoiseLearnerResult ולהשתמש בהן כקלט בניסויים עתידיים. דף זה מספק סקירה של אופן השימוש בו ואפשרויות ה-API הזמינות.

בנוסף, החל מגרסה v0.47.0 של qiskit-ibm-runtime, קיימת מחלקה חדשה NoiseLearnerV3 התואמת ל-Executor primitive. גרסה חדשה זו, שהיא גם חלק מ-מודל הביצוע הישיר, מעניקה לך את היכולת לציין במפורש את השכבות שברצונך ללמוד.

הערה

NoiseLearner פועל רק עם EstimatorV2 ו-NoiseLearnerV3 פועל רק עם Executor.

NoiseLearner

סקירה כללית

מחלקת NoiseLearner מבצעת ניסויים המאפיינים תהליכי רעש המבוססים על מודל רעש Pauli-Lindblad עבור Circuit אחד או יותר. היא מכילה מתודת run() המבצעת את ניסויי הלמידה ומקבלת כקלט רשימת Circuits או PUB, ומחזירה NoiseLearnerResult המכיל את ערוצי הרעש שנלמדו ומטא-נתונים על העבודה/ות שהוגשו. להלן קטע קוד המדגים את השימוש בתוכנית העוזר.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime samplomatic
from qiskit import QuantumCircuit
from qiskit.transpiler import CouplingMap
from qiskit.transpiler 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()

הנתונים NoiseLearnerResult.data המתקבלים הם רשימה של אובייקטים מסוג LayerError המכילים את מודל הרעש עבור כל שכבת שזירה בודדת השייכת ל-Circuit/ים המטרה. כל LayerError שומר את פרטי השכבה בצורת Circuit וסט תוויות qubit, לצד ה-PauliLindbladError של מודל הרעש שנלמד עבור השכבה הנתונה.

import numpy

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"
)
# Results are truncated
with numpy.printoptions(threshold=200):
print(
f"And each of these `LayerError` objects possess"
f" data on the generators for the error channel: \n"
f"{noise_model.data[0].error.generators}\n"
)
# Results are truncated
with numpy.printoptions(threshold=200):
print(
f"Along with the error rates: \n{noise_model.data[0].error.rates}\n"
)
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 data on the generators for the error channel:
['IIIIIIIIIIIIIIIIIIIIIIIIIIX', 'IIIIIIIIIIIIIIIIIIIIIIIIIIY',
'IIIIIIIIIIIIIIIIIIIIIIIIIIZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIXI',
'IIIIIIIIIIIIIIIIIIIIIIIIIXX', 'IIIIIIIIIIIIIIIIIIIIIIIIIXY',
'IIIIIIIIIIIIIIIIIIIIIIIIIXZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIYI',
'IIIIIIIIIIIIIIIIIIIIIIIIIYX', 'IIIIIIIIIIIIIIIIIIIIIIIIIYY',
'IIIIIIIIIIIIIIIIIIIIIIIIIYZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIZI',
'IIIIIIIIIIIIIIIIIIIIIIIIIZX', 'IIIIIIIIIIIIIIIIIIIIIIIIIZY',
'IIIIIIIIIIIIIIIIIIIIIIIIIZZ', 'IIIIIIIIIIIIIIIIIIIIIIIIXII',
'IIIIIIIIIIIIIIIIIIIIIIIIXIX', 'IIIIIIIIIIIIIIIIIIIIIIIIXIY',
'IIIIIIIIIIIIIIIIIIIIIIIIXIZ', 'IIIIIIIIIIIIIIIIIIIIIIIIYII',
'IIIIIIIIIIIIIIIIIIIIIIIIYIX', 'IIIIIIIIIIIIIIIIIIIIIIIIYIY',
'IIIIIIIIIIIIIIIIIIIIIIIIYIZ', 'IIIIIIIIIIIIIIIIIIIIIIIIZII',
'IIIIIIIIIIIIIIIIIIIIIIIIZIX', 'IIIIIIIIIIIIIIIIIIIIIIIIZIY',
'IIIIIIIIIIIIIIIIIIIIIIIIZIZ', 'IIIIIIIIIIIIIIIIIIIIIIIXIII',
'IIIIIIIIIIIIIIIIIIIIIIIYIII', 'IIIIIIIIIIIIIIIIIIIIIIIZIII',
'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', 'IIIIIIIIIIIIIIIIIIIIZZIIIII',
'IIIIIIIIIIIIIIIIIIIXIIIIIII', 'IIIIIIIIIIIIIIIIIIIXXIIIIII',
'IIIIIIIIIIIIIIIIIIIXYIIIIII', 'IIIIIIIIIIIIIIIIIIIXZIIIIII',
'IIIIIIIIIIIIIIIIIIIYIIIIIII', 'IIIIIIIIIIIIIIIIIIIYXIIIIII',
'IIIIIIIIIIIIIIIIIIIYYIIIIII', 'IIIIIIIIIIIIIIIIIIIYZIIIIII', ...]

Along with the error rates:
[5.9e-04 5.3e-04 5.7e-04 ... 0.0e+00 1.0e-05 0.0e+00]

המאפיין LayerError.error של תוצאת לימוד הרעש מכיל את המחוללים ושיעורי השגיאה של מודל Pauli Lindblad המותאם, שצורתו היא

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

כאשר rjr_j הם LayerError.rates ו-PjP_j הם אופרטורי Pauli המוגדרים ב-LayerError.generators.

אפשרויות לימוד הרעש

ניתן לבחור מבין מספר אפשרויות בעת יצירת מופע של אובייקט NoiseLearner. אפשרויות אלה מוכלות במחלקה qiskit_ibm_runtime.options.NoiseLearnerOptions וכוללות את היכולת לציין את מספר השכבות המקסימלי ללמידה, מספר האקראיזציות ואסטרטגיית ה-twirling, בין היתר. ראה בתיעוד ה-API של NoiseLearnerOptions לקבלת מידע מפורט.

להלן דוגמה פשוטה המראה כיצד להשתמש ב-NoiseLearnerOptions בניסוי NoiseLearner:

# 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 NoiseLearnerOptions 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()

הזנת מודל רעש ל-primitive

מודל הרעש שנלמד על ה-Circuit יכול לשמש גם כקלט ל-primitive מסוג EstimatorV2 המיושם ב-Qiskit Runtime. ניתן להעביר אותו ל-primitive בכמה דרכים שונות. שלוש הדוגמאות הבאות מראות כיצד ניתן להעביר את מודל הרעש ישירות למאפיין estimator.options, על ידי שימוש באובייקט ResilienceOptionsV2 לפני יצירת מופע Estimator primitive, ועל ידי העברת מילון בפורמט מתאים.

# Pass the noise model to the `estimator.options` attribute directly
estimator = EstimatorV2(mode=backend)
estimator.options.resilience.layer_noise_model = noise_model
# Specify options through 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 by using a dictionary
options_dict = {
"resilience_level": 2,
"resilience": {"layer_noise_model": noise_model},
}

estimator = EstimatorV2(mode=backend, options=options_dict)

לאחר שמודל הרעש מועבר לאובייקט EstimatorV2, ניתן להשתמש בו להרצת עומסי עבודה ולביצוע הפחתת שגיאות כרגיל.

NoiseLearnerV3

סקירה כללית

בדומה ל-NoiseLearner, מחלקת NoiseLearnerV3 מבצעת ניסויים המאפיינים תהליכי רעש המבוססים על מודל רעש Pauli-Lindblad עבור Circuit אחד או יותר. מתודת ה-run() שלה מקבלת רשימת הוראות, שכל אחת מהן חייבת להיות BoxOp עם הערות twirling המכיל פעולות ISA.

תוצאת עבודת NoiseLearnerV3 מכילה רשימה של אובייקטים מסוג NoiseLearnerV3Result, אחד לכל הוראת קלט. הקוד הבא מראה כיצד להשתמש בתוכנית העוזר.

from qiskit import QuantumCircuit
from qiskit.transpiler import CouplingMap
from qiskit.transpiler import generate_preset_pass_manager

from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic.utils import find_unique_box_instructions

# 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)
isa_circuit = pm.run(circuit)

# Run the boxing pass manager to group instructions into annotated boxes
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=False,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)
boxed_circuit = boxing_pm.run(isa_circuit)

# Find unique boxed instructions
unique_box_instructions = find_unique_box_instructions(boxed_circuit.data)
print(f"Found {len(unique_box_instructions)} unique layers")
print(
f"Each instruction is of type {type(unique_box_instructions[0].operation)}"
)
print(
f"And has annotations: {unique_box_instructions[0].operation.annotations}"
)

# Instantiate a NoiseLearnerV3 object and execute the noise learning program
learner = NoiseLearnerV3(backend)
learner.options.shots_per_randomization = 128
learner.options.num_randomizations = 32
learner_job = learner.run(unique_box_instructions)
learner_result = learner_job.result()
Found 3 unique layers
Each instruction is of type <class 'qiskit.circuit.controlflow.box.BoxOp'>
And has annotations: [Twirl(group='pauli', dressing='left', decomposition='rzsx'), InjectNoise(ref='r789B', modifier_ref='', site='before')]

תוצאת העבודה היא רשימה של אובייקטים מסוג NoiseLearnerV3Result, אחד לכל קבוצת הוראות boxed שהוזנה. ל-NoiseLearnerV3Result יש מתודת to_pauli_lindblad_map() המחזירה אובייקט מסוג PauliLindbladMap, שיש לו מתודות לחילוץ המחוללים, שיעורי השגיאה ועוד.

print(
f"The Noise learner V3 result contains {len(learner_result)} entries"
f" and each has the following type:\n {type(learner_result[0])}\n"
)
noise_map = learner_result[0].to_pauli_lindblad_map()
print(
f"After converting to PauliLindbladMap, you can extract data "
f" on the generators for the error channel "
f"(truncated to 3): \n{noise_map.generators()[:3]}\n"
)
with numpy.printoptions(threshold=20):
print(
f"Along with the error rates "
f"(truncated to 3): \n{noise_map.rates[:3]}\n"
)
The Noise learner V3 result contains 3 entries and each has the following type:
<class 'qiskit_ibm_runtime.results.noise_learner_v3.NoiseLearnerV3Result'>

After converting to PauliLindbladMap, you can extract data on the generators for the error channel (truncated to 3):
<QubitSparsePauliList with 3 elements on 27 qubits: [X_0, Y_0, Z_0]>

Along with the error rates (truncated to 3):
[0.00026 0.00032 0.00023]

אפשרויות לימוד הרעש

NoiseLearnerV3 תומך במספר אפשרויות, כולל מספר האקראיזציות ועומק זוגות השכבות, בין היתר. בדומה ל-primitives, ניתן לציין את האפשרויות במהלך יצירת מופע של אובייקט NoiseLearnerV3 או לאחריה. דוגמת הקוד הקודמת הדגימה כיצד לקבוע את אפשרויות shots_per_randomization ו-num_randomizations. ראה בתיעוד ה-API של NoiseLearnerV3Options לקבלת מידע מפורט.

הזנת מודל רעש ל-Executor

Executor פועל לפי כוונות העיצוב המצוינות בהערות ה-Circuit (בצורת samplex) ואפשרויות. InjectNoise היא ההערה לציון היכן להזריק רעש, וארגומנט ה-samplex pauli_lindblad_maps מציין איזה מפת רעש להשתמש בה.

ה-Circuit בדוגמה הקודמת עובר דרך ה-boxing pass manager, אשר מקבץ הוראות לתוך boxes עם הערות. הקוד הרלוונטי מוצג כאן לנוחות ההבנה.

  • inject_noise_targets="gates" מציין להוסיף הערות InjectNoise ל-boxes המכילים שערים מסובכים.
  • inject_noise_strategy="uniform_modification" מציין להקצות את אותו ref ו-modifier_ref לכל ה-boxes השקולים עם הערות InjectNoise.
    • InjectNoise.ref הוא מזהה ייחודי המשמש להקצאת מודל רעש ל-box.
    • InjectNoise.modifier_ref מאפשר לשנות את גודל מודל הרעש המוקצה ל-box בגורמים מכפילים.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=False,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)

ה-Circuit מהדוגמה הקודמת מכיל שלושה boxes, שניים מהם מכילים הערות InjectNoise עם מאפייני ref שונים (מכיוון שאינם שקולים).

# box_circuit comes from the example above
for idx, instruction in enumerate(boxed_circuit):
# The `InjectNoise` annotation defines which boxes to inject noise.
print(f"Annotations of box #{idx}: {instruction.operation.annotations}\n")
Annotations of box #0: [Twirl(group='pauli', dressing='left', decomposition='rzsx'), InjectNoise(ref='r789B', modifier_ref='r789B', site='before')]

Annotations of box #1: [Twirl(group='pauli', dressing='left', decomposition='rzsx'), InjectNoise(ref='r054B', modifier_ref='r054B', site='before')]

Annotations of box #2: [Twirl(group='pauli', dressing='right', decomposition='rzsx')]

יש להמיר את תוצאת עבודת NoiseLearnerV3 למילון לפני העברתה ל-Executor. המפתחות של מילון זה הם מאפייני InjectNoise.ref והערכים הם מפות הרעש המתאימות. מיפוי זה מיידע את Executor אילו מודלי רעש להזריק ובאיזה מקום.

הקוד הבא מראה כיצד לקחת את ה-Circuit ותוצאת NoiseLearnerV3 מהדוגמה הקודמת ולהעבירם ל-Executor, אשר ייצור את גרסאות ה-Circuit עם מודלי הרעש המוזרקים ויריץ אותן על חומרה.

from qiskit_ibm_runtime.quantum_program import QuantumProgram
from samplomatic import build

# Generate a quantum program
program = QuantumProgram(shots=1000)

# Build the template circuit and samplex pair
template_circuit, samplex = build(boxed_circuit)

# Convert the NoiseLearnerV3 result to a dictionary
noise_maps = learner_result.to_dict(
instructions=unique_box_instructions, require_refs=False
)

# Append the samplex item and execute
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
"pauli_lindblad_maps": noise_maps,
},
)

executor = Executor(backend)
executor_job = executor.run(program)

הצעדים הבאים

המלצות