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

מעבר מ-Sampler ו-Estimator בצד השרת לצד הלקוח

מדריך זה מתאר כיצד לעבור מהמימושים בצד השרת של IBM Quantum® Sampler ו-Estimator למימושים החדשים שלהם בצד הלקוח ב- qiskit-ibm-runtime. הממשקים והאפשרויות נשארים ברובם ללא שינוי, כך שרוב הקוד רץ כמות שהוא, אך יש כמה הבדלים התנהגותיים שכדאי להבין.

רקע​

Sampler ו-Estimator הם ממשקי primitive שמוגדרים ב-Qiskit. IBM Quantum Compute Service (בעבר Qiskit Runtime) סיפק היסטורית את המימוש של primitives אלה בתוך סביבת זמן הריצה שלו. כאשר אתה קורא ל-sampler.run() או ל-estimator.run(), הבקשה נשלחת לשירות, וכל החישוב — כולל דיכוי שגיאות והפחתת שגיאות — מתבצע בצד השרת.

חוויית קופסה שחורה זו נוחה: אינך צריך לדאוג לפרטי המימוש. אבל היא גם מקשה על debug, התאמה אישית, או למידה מה-primitives, מכיוון שאינך יכול לראות מה קורה במהלך העיבוד.

מודל הביצוע המכוון שהוצג לאחרונה נוקט בגישה הפוכה ו מספק חוויית קופסה שקופה. כל כוונות העיצוב נלכדות בצד הלקוח, ו-primitive יחיד בצד השרת, Executor, מעבד את הקלטים האלה בדיוק כפי שהונחה — הוא אינו מקבל החלטות מרומזות מטעמך.

החל מ-qiskit-ibm-runtime v0.50.0, Sampler ו-Estimator ממומשים מחדש בצד הלקוח מעל Executor. הם מספקים את אותה נוחות והפשטה כמו קודם, ועכשיו אתה יכול לבדוק את פרטי המימוש כשאתה צריך זאת. מכיוון שהממשקים והאפשרויות נשארים ברובם זהים, המעבר אמור להיות חלק.

הערה: IBM Quantum תומך רק בגרסה 2 של ממשקי Sampler ו-Estimator (BaseSamplerV2 ו-BaseEstimatorV2). לכן, במדריך זה הם מכונים פשוט Sampler ו-Estimator.

עדכן את ה-imports​

כיום, עליך לייבא באופן מפורש את המימושים החדשים מהמודולים הייעודיים שלהם:

from qiskit_ibm_runtime.executor_sampler import Sampler
from qiskit_ibm_runtime.executor_estimator import Estimator

בעתיד הקרוב, ה-imports ברמה העליונה יפנו למימושים החדשים בצד הלקוח, ולא יידרש שינוי קוד:

# Coming soon — the following code will import the new client-side implementations.
from qiskit_ibm_runtime import Sampler, Estimator

באופן דומה, אם אתה בונה אובייקטי options עם טיפוסים (typed), עליך לייבא אותם מ- qiskit_ibm_runtime.options_models במקום זאת, או פשוט להעביר dict מקונן פשוט:

from qiskit_ibm_runtime.options_models import SamplerOptions, EstimatorOptions

מה נשאר זהה​

  • בניית primitive עם mode ו-options.

  • החתימה של run() ותבנית PUB.

  • עץ האפשרויות (options.twirling, options.resilience, options.default_shots, וכן הלאה).

  • מבנה נתוני התוצאה שמוחזר על ידי job.result().

שינויים לא תואמים ב-Sampler החדש​

שינויפעולת מעבר
ה-primitive הבסיסי הוא כעת Executor. גם ממשק המשתמש של IBM Quantum Platform וגם job.primitive_id יציגו executor במקום sampler.עדכן כל קוד שמפנה ל-job.primitive_id.
המימוש החדש ממפה קלטי Sampler לקלטי Executor, כך ש-job.inputs מחזיר קלטי Executor.עדכן כל קוד שמפנה ל-job.inputs. ראה קלטי job.
כעת מתבצע יותר עיבוד מקדים ועיבוד משלים בצד הלקוח, כך ש-sampler.run() ו-job.result() עשויים לקחת יותר זמן מבעבר.הפעל רישום ברמת INFO כדי לעקוב אחר ההתקדמות של העיבוד בצד הלקוח. ראה הפעלת רישום INFO.
מטא-נתוני מעגל מועתקים למטא-נתוני התוצאה. סוגי הנתונים המותרים במטא-נתוני התוצאה מוגבלים כעת ל-str, float, int, bool, ורשימות או מילונים של סוגים אלה.אם אתה זקוק לסוגי נתונים אחרים, קודד אותם כמחרוזת תחילה (לדוגמה, עם base64).
מחלקות אפשרויות (options_models.SamplerOptions וכן הלאה) הן כעת מודלים של Pydantic במקום dataclasses, כך שכבר לא ניתן להמיר אותן למילוני Python באמצעות asdict().השתמש ב-options.model_dump() במקום זאת.
מחלקות אפשרויות שבעבר היה להן הסיומת V2 (ExecutionOptionsV2 וכן הלאה) כבר לא, מכיוון ש-primitives מגרסה V1 כבר לא נתמכים.הסר את הסיומת V2 ממחלקות אפשרויות אלה: החלף את ExecutionOptionsV2 ב-ExecutionOptions, את ResilienceOptionsV2 ב-ResilienceOptions, ואת SamplerExecutionOptionsV2 ב-SamplerExecutionOptions.
אם twirling מופעל ו-shots (ב-PUBs או ב-run()), shots_per_randomization, ו-num_randomizations כולם מוגדרים, אז num_randomizations * shots_per_randomization גובר על shots.השמט את num_randomizations ו-shots_per_randomization אם ברצונך שערך shots ישמש.
חלק מאימות הקלט עבר לצד השרת וכעת מעלה RuntimeError במקום IBMInputValueError.עדכן את סוגי החריגות שהקוד שלך תופס.
ערכי shot מעורבים ב-job בודד כבר לא נתמכים.הגש job נפרד עבור כל ערך shot. ראה פיצול job לשיקולים.
טווחי ביצוע (result.metadata["execution"]["execution_spans"]) הוחלפו בתזמון ה-chunks של job ה-Executor הבסיסי, ב-result.metadata["executor"].chunk_timing. ל-ExecutionSpan.mask() אין מקבילה.קרא את התזמון מ-result.metadata["executor"].chunk_timing והצג אותו באמצעות draw_chunk_timings במקום draw_execution_spans. ראה טווחי ביצוע.

שינויים לא תואמים ב-Estimator החדש​

שינויפעולת מעבר
ה-primitive הבסיסי הוא כעת Executor. גם ממשק המשתמש של IBM Quantum Platform וגם job.primitive_id יציגו executor במקום estimator.עדכן כל קוד שמפנה ל-job.primitive_id.
המימוש החדש ממפה קלטי Estimator לקלטי Executor, כך ש-job.inputs מחזיר קלטי Executor.עדכן כל קוד שמפנה ל-job.inputs. ראה קלטי job.
כעת מתבצע יותר עיבוד מקדים ועיבוד משלים בצד הלקוח, כך ש-estimator.run() ו-job.result() עשויים לקחת יותר זמן מבעבר.הפעל רישום ברמת INFO כדי לעקוב אחר ההתקדמות של העיבוד בצד הלקוח. ראה הפעלת רישום INFO.
מטא-נתוני מעגל מועתקים למטא-נתוני התוצאה. סוגי הנתונים המותרים במטא-נתוני התוצאה מוגבלים כעת ל-str, float, int, bool, ורשימות או מילונים של סוגים אלה.אם אתה זקוק לסוגי נתונים אחרים, קודד אותם כמחרוזת תחילה (לדוגמה, עם base64).
מחלקות אפשרויות (options_models.EstimatorOptions וכן הלאה) הן כעת מודלים של Pydantic במקום dataclasses, כך שכבר לא ניתן להמיר אותן למילוני Python באמצעות asdict().השתמש ב-options.model_dump() במקום זאת.
מחלקות אפשרויות שבעבר היה להן הסיומת V2 (ExecutionOptionsV2 וכן הלאה) כבר לא, מכיוון ש-primitives מגרסה V1 כבר לא נתמכים.הסר את הסיומת V2 ממחלקות אפשרויות אלה: החלף את ExecutionOptionsV2 ב-ExecutionOptions ואת ResilienceOptionsV2 ב-ResilienceOptions.
כל אפשרויות הקלט מוחזרות במטא-נתוני התוצאה, במקום תת-קבוצה נבחרת.אין — זהו מידע בלבד.
חלק מאימות הקלט עבר לצד השרת וכעת מעלה RuntimeError במקום IBMInputValueError.עדכן את סוגי החריגות שהקוד שלך תופס.
אין יותר למידת רעש מרומזת עבור PEA ו-PEC. למידת רעשי מדידה עבור TREX עדיין נתמכת.למד את מודלי הרעש בנפרד והעבר אותם ל-Estimator. ראה ביצוע למידת רעש מפורשת עבור PEA ו-PEC.
סוג הקלט של ResilienceOptions.layer_noise_model שונה וניתן לבנות אותו מתוצאות NoiseLearnerV3.ראה ביצוע למידת רעש מפורשת עבור PEA ו-PEC כיצד ללמוד את מודלי הרעש באמצעות NoiseLearnerV3 ולהעביר אותם ל-Estimator.
MeasureNoiseLearningOptions.shots_per_randomization כבר לא נתמך.ערך shot יחיד משמש עבור כל המעגלים ב-job, כולל מעגלי למידת רעשי מדידה. אם עליך להשתמש בערך shot שונה, החל TREX עם qiskit-mitigation מחוץ ל-Estimator.
ערכי דיוק מעורבים ב-job בודד כבר לא נתמכים.הגש job נפרד עבור כל דיוק רצוי. ראה פיצול job לשיקולים.
האפשרות seed_estimator כבר לא נתמכת.הסר כל הקצאה של options.seed_estimator (הגדרתה מעלה ValidationError). אין מקבילה בצד הלקוח, כך שהתוצאות כבר לא ניתנות לשחזור באמצעות seed זה.

הפעלת רישום INFO​

מכיוון שיותר עבודה מתבצעת כעת בצד הלקוח, שימושי לראות את ההתקדמות של עיבוד זה. הפעל רישום ברמת INFO עבור ה-logger qiskit_ibm_runtime:

import logging

logger = logging.getLogger("qiskit_ibm_runtime")
logger.setLevel(logging.INFO)

ביצוע למידת רעש מפורשת עבור PEA ו-PEC​

ה-Estimator החדש כבר לא מבצע למידת רעש מרומזת כאשר שיטת הפחתת השגיאות PEA או PEC נבחרת. עליך ללמוד את מודלי הרעש באופן מפורש ולהעביר אותם פנימה. השתמש ב-NoiseLearnerV3 החדש כדי לשלוט כיצד מעגלים מחולקים לשכבות. הוא מקבל רשימה של הוראות מעגל ממוסגרות (לדוגמה, השכבות הייחודיות) כקלט.

חשוב

PEA ו-PEC כעת דורשים את התבנית המפורשת הזו. אל תדלג על שלב למידת הרעש אחרת הקוד שלך ייכשל. למידת רעש מדידה עבור TREX אינה מושפעת וממשיכה לעבוד כרגיל.

באופן דומה, אם הקוד שלך משתמש ב-NoiseLearner ומעביר את מודל הרעש המתקבל ל-Estimator בצד השרת, עליך לעבור ל-NoiseLearnerV3. אל תשתמש ב-NoiseLearner הישן יותר, שאינו תואם ל-Estimator החדש.

כל אפשרויות למידת הרעש ב-Estimator בצד השרת (LayerNoiseLearningOptions) ממופות ישירות לאפשרות NoiseLearnerV3 (NoiseLearnerV3Options), למעט max_layers_to_learn. מספר השכבות שיש ללמוד מבוסס במקום זאת על מספר השכבות שהועברו ל-NoiseLearnerV3.

לדוגמה:

Estimator בצד השרת (עם PEC מופעל):

from qiskit_ibm_runtime import Estimator

pubs = [...] # Your PUBs
estimator = Estimator(mode, options)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier
estimator.options.resilience.layer_noise_learning.num_randomizations = 64

job = estimator.run(pubs)

Estimator בצד הלקוח (עם PEC מופעל):

from qiskit_ibm_runtime.executor_estimator import Estimator
from qiskit_ibm_runtime import NoiseLearnerV3

pubs = [...] # Your PUBs
estimator = Estimator(mode, options)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier

# Identify the unique layers to learn.
layers = estimator.find_unique_layers(pubs)

# Learn the noise model for those layers (runs as a separate job).
learner = NoiseLearnerV3(mode)
learner.options.num_randomizations = 64 # Same as layer_noise_learning.num_randomizations
learner_job = learner.run(layers)
learner_result = learner_job.result()

# Convert results to Pauli-Lindblad noise maps.
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()

# Assign the learned noise maps so PEA/PEC uses them.
estimator.options.resilience.layer_noise_model = zip(layers, pauli_lindblad_maps)

# Now execute the target PUBs.
job = estimator.run(pubs)

מעבר מ-NoiseLearner ל-NoiseLearnerV3​

NoiseLearner עובד רק עם המימוש בצד השרת של Estimator. לכן, אם הקוד שלך משתמש ב-NoiseLearner כדי ללמוד את מודל הרעש ולהעביר אותו ל-Estimator, עליך לעדכן את הקוד שלך כדי להשתמש ב-NoiseLearnerV3.

ראה את המדריך מעבר מ-NoiseLearner ל-NoiseLearnerV3 לפרטים.

פיצול job​

כאשר עליך לפצל job אחד למספר jobs מכיוון שערכי shot או דיוק מעורבים ב-job בודד כבר לא נתמכים, שקול את הדברים הבאים:

  • קבץ את ה-PUBs לפי ערך היעד שלהם — job אחד לכל ערך ייחודי, לא job אחד לכל PUB. פיצול הוא קיבוץ מחדש, כך שמספר ה-PUBs הכולל שאתה מגיש לא משתנה. לדוגמה, בהינתן [A@0.01, B@0.05, C@0.01], הגש שני jobs: [A, C] ב-precision=0.01 ו-[B] ב-precision=0.05. הגשת A ו-C כ-jobs נפרדים פחות יעילה, מכיוון שכל job מגיע עם עלות תקורה קבועה.

  • למד פעם אחת והשתמש במודלי הרעש בכל ה-jobs המפוצלים. יעיל יותר להריץ job אחד של NoiseLearnerV3 על איחוד כל השכבות. התוצאה של job למידת רעש מכילה רשימה של אובייקטי NoiseLearnerV3Result, אחד עבור כל הוראת קלט, ובאותו סדר כמו רשימת הקלט. אתה יכול להשתמש בפלט של job למידת הרעש הזה בכל ה-jobs המפוצלים (Estimator), ומודלי רעש של שכבות שאינן מופיעות ב-PUBs של job מפוצל פשוט לא ייכנסו לשימוש.

  • הגש קודם את כל ה-jobs המפוצלים ב-Batch, ורק אז אסוף את תוצאותיהם. מצב הביצוע Batch מספק ביצוע מקבילי יעיל כאשר יש מספר jobs. עם זאת, job.result() חוסם, כך שקריאה לו בתוך לולאת ההגשה הופכת את ה-jobs לסדרתיים ומבטלת את היתרונות של שימוש ב-Batch. ודא שאתה משתמש בתבנית 'הגש הכל ואז אסוף' (מוצגת למטה).

בדוגמה הבאה, pub1 ו-pub2 דורשים precision=0.5, בעוד ש-pub3 דורש precision=0.1:

group1_pubs = [pub1, pub2]
group2_pubs = [pub3]

with Batch(backend=backend) as batch:
estimator = Estimator(mode=batch)
estimator.options.resilience.pec_mitigation = True

# Learn once, over the union of every job's layers.
all_layers = estimator.find_unique_layers(group1_pubs + group2_pubs)
learner_job = NoiseLearnerV3(mode=batch).run(all_layers)
learner_result = learner_job.result()
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()

# Assign the learned noise maps. Any layers not found in the input PUBs are ignored.
estimator.options.resilience.layer_noise_model = zip(all_layers, pauli_lindblad_maps)

# Submit every split job with different precision values.
jobs = []
jobs.append(estimator.run(group1_pubs, precision=0.5))
jobs.append(estimator.run(group2_pubs, precision=0.1))

# Block once, at the end — the jobs run in parallel.
results = [job.result() for job in jobs]

טווחי ביצוע​

ה-Sampler בצד השרת החזיר מידע על תזמון הביצוע כאובייקט ExecutionSpans ב-result.metadata["execution"]["execution_spans"]. ה-Sampler וה-Estimator בצד הלקוח מחזירים במקום זאת את התזמון של job ה-Executor הבסיסי, תחת המפתח "executor" של מטא-נתוני התוצאה.

job ה-Executor מריץ את העבודה שלו על ה-QPU ב-chunks. result.metadata["executor"].chunk_timing היא רשימה שמכילה ChunkSpan אחד לכל chunk, עם ההבדלים הבאים מטווחי הביצוע הישנים:

  • חותמות הזמן מדויקות. start ו-stop הם הזמנים בפועל שבהם ה-chunk התחיל ופסק לרוץ על ה-QPU, ולא גבולות עליונים ותחתונים.

  • העבודה מתוארת לפי פריט, לא לפי shot. במקום מתודת mask() שמחזירה מסכה בוליאנית על פני shots, לכל ChunkSpan יש רשימה של parts. כל part נותן את האינדקס של פריט בתוכנית ה-Executor (idx_item) ואת מספר התצורות מאותו פריט (size) שרצו ב-chunk. עבור Sampler, idx_item הוא אינדקס ה-PUB. עבור Estimator, הוא לא בהכרח תואם לאינדקס של PUB, כי Estimator יכול להוסיף פריטי תוכנית, כמו מעגלי כיול של שגיאות מדידה עבור TREX.

  • השתמש בפונקציית הצגה אחרת. החלף את draw_execution_spans ב-draw_chunk_timings, שמקבלת ישירות את רשימת chunk_timing.

לדוגמה:

Sampler בצד השרת:

from qiskit_ibm_runtime.visualization import draw_execution_spans

spans = job.result().metadata["execution"]["execution_spans"]
print("Total duration (s):", spans.duration)

fig = draw_execution_spans(spans)

Sampler או Estimator בצד הלקוח:

from qiskit_ibm_runtime.visualization import draw_chunk_timings

chunk_timing = job.result().metadata["executor"].chunk_timing

for span in chunk_timing:
print(span.start, span.stop, [(p.idx_item, p.size) for p in span.parts])

fig = draw_chunk_timings(chunk_timing)

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

מבנה קלטי job​

המימוש החדש ממפה קלטי Sampler או Estimator לקלטי Executor, כך ש-job.inputs מחזיר מילון שמכיל קלטי Executor. למילון זה יש את המפתחות הבאים:

  • options: קלט ה-ExecutorOption.

  • quantum_program: קלט ה-QuantumProgram

  • schema_version: גרסת הסכימה בצד השרת שבה נעשה שימוש.

אם הקוד שלך השתמש ב-job.inputs['options'] כדי למצוא אפשרויות שצוינו עבור ה-job, אתה יכול כעת להשתמש ב-job.result().metadata['options'] במקום זאת.

בדיקה מקומית עם backend מדומה​

לפני הגשה לחומרה, אתה יכול לאמת את הקוד המעודכן (לאחר המעבר) מול backend מסוג Fake* כדי לתפוס שגיאות תחביר מוקדם. שים לב לפרטים הבאים לגבי מצב בדיקה מקומי:

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

  • ל-NoiseLearnerV3 אין מצב בדיקה מקומי: ה-mode שלו מקבל רק Backend אמיתי, Session, או Batch, כך שאתה לא יכול להפעיל את שלב למידת הרעש מול backend מדומה. אמת את החלק הזה של הקוד שלך מול מסמכי ה-API של NoiseLearnerV3 במקום זאת. ודא שהבנאי, צורת הקלט של run(instructions), וכל helper (כגון ה-helper של שכבה ייחודית) משמשים כפי שמתועד.

הפוך את המעגל ל-Clifford לסימולציה מקומית יעילה​

backend מדומה משתמש בסימולטור statevector (רועש), שהעלות שלו גדלה באופן אקספוננציאלי עם מספר הקיובטים והעומק. לכן, מעגל עומס עבודה ריאליסטי עלול להיתקע או לגמור זיכרון. מכיוון שבדיקה מקומית צריכה רק להפעיל את נתיבי האפשרויות (לא לשחזר תוצאות פיזיקליות), צמצם את המעגל למעגל Clifford תחילה עם ConvertISAToClifford, שמעגל כל זווית RZ/RZZ/RX לכפולה הקרובה ביותר של π/2. ניתן לבצע סימולציה של מעגלי Clifford ביעילות (סימולציית stabilizer) ללא קשר לגודלם.

from qiskit.transpiler import PassManager
from qiskit_ibm_runtime.transpiler.passes import ConvertISAToClifford

clifford = PassManager([ConvertISAToClifford()]).run(isa_circuit)
# run `clifford` (not the original) through the fake-backend primitive

ConvertISAToClifford דורש מעגל ISA כקלט (הפלט של generate_preset_pass_manager(...).run(...) שמכוון ל-backend). עליך לקחת בחשבון את ההשלכות הבאות כאשר אתה בונה את ה-PUB המקומי:

  • התכונה .layout נשמטת. המעגל שהפך ל-Clifford שומר על אותו מספר קיובטים, אך clifford.layout הוא None, כך ש-observable.apply_layout(clifford.layout) נכשל. במקום זאת, החל על ה-observable את ה-layout של מעגל ה-ISA שלפני ההמרה ל-Clifford: isa_obs = observable.apply_layout(isa_circuit.layout), ואז הרץ (clifford, isa_obs).

  • פרמטרים נקשרים ומוסרים. עיגול זוויות הסיבוב הופך מעגל ISA פרמטרי למעגל Clifford קונקרטי, כך ש-clifford.num_parameters הופך ל-0. PUB שעדיין נושא מערך ערכי פרמטרים נכשל בהמרה. עבור ההרצה המקומית, הסר את מערך הפרמטרים מה-PUB; ההרצה בחומרה שומרת על המעגל הפרמטרי המקורי וערכיו.

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