דלג לתוכן הראשי
פרויקט זה בשלב בטא. מצאת באג? פתח issue ב-GitHub. יש שאלה או רעיון? שאל ב-Discussions — נשמח לשמוע ממך!
מה איכות התרגום בדף זה?

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

גרסאות חבילה

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

qiskit[all]~=2.5.2
qiskit-ibm-runtime~=0.47.0
samplomatic~=0.21.0

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

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

הערה

NoiseLearner פועל רק עם EstimatorV2 בצד השרת ו-NoiseLearnerV3 פועל רק עם Executor ועם ה-Estimator החדש בצד הלקוח (שהוצג ב-qiskit-ibm-runtime v0.50.0).

NoiseLearner​

סקירה כללית​

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

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_ibm_runtime": "qiskit-ibm-runtime", "samplomatic": "samplomatic"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
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 המכילים את מודל הרעש עבור כל שכבת שזירה בודדת השייכת ל-Circuits המטרה. כל 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',
'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',
'IIIIIIIIIIIIIIIIIIIIIXIIIIX', 'IIIIIIIIIIIIIIIIIIIIIXIIIIY',
'IIIIIIIIIIIIIIIIIIIIIXIIIIZ', 'IIIIIIIIIIIIIIIIIIIIIYIIIII',
'IIIIIIIIIIIIIIIIIIIIIYIIIIX', 'IIIIIIIIIIIIIIIIIIIIIYIIIIY',
'IIIIIIIIIIIIIIIIIIIIIYIIIIZ', 'IIIIIIIIIIIIIIIIIIIIIZIIIII',
'IIIIIIIIIIIIIIIIIIIIIZIIIIX', 'IIIIIIIIIIIIIIIIIIIIIZIIIIY',
'IIIIIIIIIIIIIIIIIIIIIZIIIIZ', 'IIIIIIIIIIIIIIIIIIIIXIIIIII',
'IIIIIIIIIIIIIIIIIIIIXIXIIII', 'IIIIIIIIIIIIIIIIIIIIXIYIIII',
'IIIIIIIIIIIIIIIIIIIIXIZIIII', 'IIIIIIIIIIIIIIIIIIIIYIIIIII',
'IIIIIIIIIIIIIIIIIIIIYIXIIII', 'IIIIIIIIIIIIIIIIIIIIYIYIIII',
'IIIIIIIIIIIIIIIIIIIIYIZIIII', 'IIIIIIIIIIIIIIIIIIIIZIIIIII',
'IIIIIIIIIIIIIIIIIIIIZIXIIII', 'IIIIIIIIIIIIIIIIIIIIZIYIIII', ...]

Along with the error rates:
[0.00037 0.00043 0.00032 ... 0.00031 0.00035 0.00017]

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

Λ(ρ)=exp⁡∑jrj(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 של IBM Quantum. ניתן להעביר אותו ל-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='r223A', modifier_ref='', site='after')]

תוצאת העבודה היא רשימה של אובייקטים מסוג NoiseLearnerV3Result, אחד לכל קבוצת הוראות ארוזות בקופסאות שהוזנה. ל-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_5, Y_5, Z_5]>

Along with the error rates (truncated to 3):
[2.3e-04 9.0e-05 1.1e-04]

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

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

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

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

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

  • inject_noise_targets=”gates” מציין להוסיף את ההערות InjectNoise ל-boxes המכילים entanglers.
  • 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='r223A', modifier_ref='r223A', site='after')]

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

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)

שימוש ב-NoiseLearnerV3 עם Estimator בצד הלקוח​

הקוד הבא מראה כיצד להשתמש בתוכנית העזר עם ה-Estimator החדש בצד הלקוח

from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

from qiskit_ibm_runtime import QiskitRuntimeService, NoiseLearnerV3
from qiskit_ibm_runtime.executor_estimator import Estimator

# 1. Account + backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# 2. Circuit + observable
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)

observable = SparsePauliOp("ZZ")

# 3. Transpile to ISA
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
pubs = [(isa_circuit, isa_observable)]

# 4. Initialize Estimator with options
estimator = Estimator(backend)
estimator.options.resilience.pec_mitigation = True

# 5. Extract the unique boxed layers from PUBs
layers = estimator.find_unique_layers(pubs)

# 6. Learn the noise model for those layers.
learner = NoiseLearnerV3(backend)
learner_job = learner.run(layers)
learner_result = learner_job.result()

# 7. Convert the result to Pauli-Lindblad maps and pass them to Estimator.
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()
estimator.options.resilience.layer_noise_model = zip(
layers, pauli_lindblad_maps
)

# 8. Execute the target PUBs.
job = estimator.run(pubs)
result = job.result()

הצעדים הבאים​