מעבר ל-Qiskit Runtime V2 primitives
ה-primitives המקוריים (המכונים V1 primitives), V1 Sampler ו-V1 Estimator, הוצאו משימוש ב-qiskit-ibm-runtime 0.23.
התמיכה בהם הוסרה ב-15 באוגוסט 2024.
עם ביטול ה-V1 primitives, יש להעביר את כל הקוד לשימוש בממשקי V2. המדריך הזה מתאר מה השתנה ב-Qiskit Runtime V2 primitives (זמינים עם qiskit-ibm-runtime 0.21.0) ומדוע, מתאר כל primitive חדש בפירוט, ומספק דוגמאות שיעזרו לך להעביר קוד מה-primitives הישנים ל-V2 primitives. הדוגמאות במדריך כולן משתמשות ב-Qiskit Runtime primitives, אבל באופן כללי, אותם שינויים חלים גם על יישומי primitives אחרים. הפונקציות הייחודיות ל-Qiskit Runtime כמו error mitigation נשארות ייחודיות ל-Qiskit Runtime.
למידע על השינויים ב-Qiskit reference primitives (הנקראים כעת statevector primitives), ראו את קטע qiskit.primitives בדף שינויי תכונות Qiskit 1.0. ראו StatevectorSampler ו-StatevectorEstimator לגבי יישומי ייחוס של V2 primitive.
סקירה כללית
גרסה 2 של ה-primitives מוצגת עם מחלקת בסיס חדשה עבור Sampler ו-Estimator (BaseSamplerV2 ו-BaseEstimatorV2), יחד עם סוגים חדשים לקלט ולפלט שלהם.
הממשק החדש מאפשר לך לציין Circuit יחיד ו-observables מרובים (אם משתמשים ב-Estimator) וסטים של ערכי פרמטרים עבור אותו Circuit, כך שניתן לציין בצורה יעילה סריקות על סטי ערכי פרמטרים ו-observables. בעבר, היית צריך לציין את אותו Circuit מספר פעמים כדי להתאים לגודל הנתונים שיש לשלב. כמו כן, אמנם עדיין ניתן להשתמש ב-resilience_level (אם משתמשים ב-Estimator) כמנוף פשוט, אבל V2 primitives מעניקים לך את הגמישות להפעיל או לכבות שיטות בודדות של error mitigation / suppression כדי להתאים אותן לצרכיך.
כדי לצמצם את זמן הרצת העבודה הכולל, V2 primitives מקבלים רק Circuits ו-observables שמשתמשים בהוראות הנתמכות על ידי ה-QPU (quantum processing unit) היעד. Circuits ו-observables כאלה מכונים instruction set architecture (ISA) circuits ו-observables. V2 primitives לא מבצעים פעולות layout, routing ותרגום. ראו את תיעוד הTranspiler להוראות לשינוי Circuits.
Sampler V2 פושט ומתמקד במשימת הליבה שלו — דגימת רגיסטר הפלט מביצוע מעגלים קוונטיים. הוא מחזיר את הדגימות, שסוגן מוגדר על ידי התוכנית, ללא משקלים. נתוני הפלט מופרדים גם לפי שמות רגיסטר הפלט שמוגדרים על ידי התוכנית. שינוי זה מאפשר תמיכה עתידית ב-Circuits עם בקרת זרימה קלאסית.
ראו את API reference של EstimatorV2 ואת API reference של SamplerV2 לפרטים מלאים.
שינויים עיקריים
ייבוא
לצורך תאימות לאחור, עליך לייבא את V2 primitives באופן מפורש. ציון import <primitive>V2 as <primitive> אינו חובה, אבל מקל על המעבר של קוד ל-V2.
לאחר שה-V1 primitives לא יהיו נתמכים עוד, import <primitive> ייבא את גרסת V2 של ה-primitive המצוין.
- Estimator V2
- Estimator (V1)
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime import Estimator
- Sampler V2
- Sampler (V1)
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit_ibm_runtime import Sampler
קלט ופלט
קלט
גם SamplerV2 וגם EstimatorV2 מקבלים כקלט PUB אחד או יותר — primitive unified blocs (PUBs). כל PUB הוא tuple שמכיל Circuit אחד ואת הנתונים שמופצים לאותו Circuit, שיכולים להיות observables ופרמטרים מרובים. כל PUB מחזיר תוצאה.
- פורמט PUB של Sampler V2: (
<circuit>,<parameter values>,<shots>), כאשר<parameter values>ו-<shots>הם אופציונליים. - פורמט PUB של Estimator V2: (
<circuit>,<observables>,<parameter values>,<precision>), כאשר<parameter values>ו-<precision>הם אופציונליים. כללי broadcasting של Numpy משמשים בשילוב observables וערכי פרמטרים.
בנוסף, בוצעו השינויים הבאים:
- Estimator V2 קיבל ארגומנט
precisionבמתודהrun()שמציין את הדיוק המטורגט של אומדני ערך הציפייה. - Sampler V2 כולל את ארגומנט
shotsבמתודתrun()שלו.
דוגמאות
דוגמת Estimator V2 שמשתמשת ב-precision ב-run():
# Estimate expectation values for two PUBs, both with 0.05 precision.
estimator.run([(circuit1, obs_array1), (circuit2, obs_array_2)], precision=0.05)
דוגמת Sampler V2 שמשתמשת ב-shots ב-run():
# Sample two circuits at 128 shots each.
sampler.run([circuit1, circuit2], shots=128)
# Sample two circuits at different amounts of shots.
# The "None"s are necessary as placeholders
# for the lack of parameter values in this example.
sampler.run([
(circuit1, None, 123),
(circuit2, None, 456),
])
פלט
הפלט כעת בפורמט PubResult. PubResult הוא הנתונים והמטאדאטה הנובעים מביצוע PUB יחיד.
-
Estimator V2 ממשיך להחזיר ערכי ציפייה.
-
חלק ה-
dataשל PubResult של Estimator V2 מכיל גם ערכי ציפייה וגם שגיאות סטנדרטיות (stds). V1 החזיר variance במטאדאטה. -
Sampler V2 מחזיר מדידות per-shot בצורת bitstrings, במקום התפלגויות quasi-probability מממשק V1. ה-bitstrings מציגות את תוצאות המדידה, תוך שמירה על סדר ה-shots שבו נמדדו.
-
Sampler V2 כולל מתודות עזר כמו
get_counts()שמסייעות במעבר. -
אובייקטי תוצאות Sampler V2 מארגנים נתונים לפי שמות הרגיסטרים הקלאסיים של מעגלי הקלט, לצורך תאימות עם dynamic circuits. כברירת מחדל, שם הרגיסטר הקלאסי הוא
meas, כפי שמוצג בדוגמה הבאה. בעת הגדרת ה-Circuit שלך, אם יצרת רגיסטר קלאסי אחד או יותר עם שם שאינו ברירת המחדל, השתמש באותו שם כדי לקבל את התוצאות. אפשר למצוא את שם הרגיסטר הקלאסי על ידי הרצת<circuit_name>.cregs. לדוגמה,qc.cregs.# Define a quantum circuit with 2 qubits
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw()┌───┐ ░ ┌─┐
q_0: ┤ H ├──■───░─┤M├───
└───┘┌─┴─┐ ░ └╥┘┌─┐
q_1: ─────┤ X ├─░──╫─┤M├
└───┘ ░ ║ └╥┘
meas: 2/══════════════╩══╩═
0 1
דוגמאות Estimator (קלט ופלט)
- Circuit אחד, 4 observables
- Circuit אחד, 4 observables, 2 סטי פרמטרים
- 2 Circuits, 2 observables
# Estimator V1: Execute 1 circuit with 4 observables
job = estimator_v1.run([circuit] * 4, [obs1, obs2, obs3, obs4])
evs = job.result().values
# Estimator V2: Execute 1 circuit with 4 observables
job = estimator_v2.run([(circuit, [obs1, obs2, obs3, obs4])])
evs = job.result()[0].data.evs
# Estimator V1: Execute 1 circuit with 4 observables and 2 parameter sets
job = estimator_v1.run([circuit] * 8, [obs1, obs2, obs3, obs4] * 2, [vals1, vals2] * 4)
evs = job.result().values
# Estimator V2: Execute 1 circuit with 4 observables and 2 parameter sets
job = estimator_v2.run([(circuit, [[obs1], [obs2], [obs3], [obs4]], [[vals1], [vals2]])])
evs = job.result()[0].data.evs
# Estimator V1: Cannot execute 2 circuits with different observables
# Estimator V2: Execute 2 circuits with 2 different observables. There are
# two PUBs because each PUB can have only one circuit.
job = estimator_v2.run([(circuit1, obs1), (circuit2, obs2)])
evs1 = job.result()[0].data.evs # result for pub 1 (circuit 1)
evs2 = job.result()[1].data.evs # result for pub 2 (circuit 2)
דוגמאות Sampler (קלט ופלט)
- Circuit אחד, 3 סטי פרמטרים
- 2 Circuits, סט פרמטרים אחד
- המרת פלט V2 לפורמט V1
# Sampler V1: Execute 1 circuit with 3 parameter sets
job = sampler_v1.run([circuit] * 3, [vals1, vals2, vals3])
dists = job.result().quasi_dists
# Sampler V2: Executing 1 circuit with 3 parameter sets
job = sampler_v2.run([(circuit, [vals1, vals2, vals3])])
counts = job.result()[0].data.meas.get_counts()
# Sampler V1: Execute 2 circuits with 1 parameter set
job = sampler_v1.run([circuit1, circuit2], [vals1] * 2)
dists = job.result().quasi_dists
# Sampler V2: Execute 2 circuits with 1 parameter set
job = sampler_v2.run([(circuit1, vals1), (circuit2, vals1)])
counts1 = job.result()[0].data.meas.get_counts() # result for pub 1 (circuit 1)
counts2 = job.result()[1].data.meas.get_counts() # result for pub 2 (circuit 2)
פורמט הפלט של V1 היה מילון של bitstrings (כמספר שלם) כמפתח ו-quasi-probabilities כערך עבור כל Circuit. פורמט V2 כולל את אותו מפתח (אבל כמחרוזת) ו-counts כערך. כדי להמיר את פורמט V2 ל-V1, מחלקים את ה-counts במספר ה-shots, כאשר מספר ה-shots שנבחר מתואר במדריך ציון אפשרויות.
v2_result = sampler_v2_job.result()
v1_format = []
for pub_result in v2_result:
counts = pub_result.data.meas.get_counts()
v1_format.append( {int(key, 2): val/shots for key, val in counts.items()} )
דוגמה שמשתמשת ברגיסטרי פלט שונים
from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
alpha = ClassicalRegister(5, "alpha")
beta = ClassicalRegister(7, "beta")
qreg = QuantumRegister(12)
circuit = QuantumCircuit(qreg, alpha, beta)
circuit.h(0)
circuit.measure(qreg[:5], alpha)
circuit.measure(qreg[5:], beta)
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=12)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
sampler = Sampler(backend)
job = sampler.run([isa_circuit])
result = job.result()
# Get results for the first (and only) PUB
pub_result = result[0]
print(f" >> Counts for the alpha output register: {pub_result.data.alpha.get_counts()}")
print(f" >> Counts for the beta output register: {pub_result.data.beta.get_counts()}")
אפשרויות
האפשרויות מצוינות באופן שונה ב-V2 primitives בדרכים הבאות:
- ל-
SamplerV2ול-EstimatorV2יש כעת מחלקות אפשרויות נפרדות. אפשר לראות את האפשרויות הזמינות ולעדכן ערכי אפשרויות במהלך אתחול ה-primitive או לאחריו. - במקום מתודת
set_options(), לאפשרויות V2 primitive יש מתודתupdate()שמחילה שינויים על המאפייןoptions. - אם לא מציינים ערך לאפשרות, היא מקבלת ערך מיוחד של
Unsetוברירות המחדל של השרת משמשות. - עבור V2 primitives, המאפיין
optionsהוא מטיפוס Pythondataclass. אפשר להשתמש במתודה המובניתasdictכדי להמיר אותו למילון.
ראו את API reference לרשימת האפשרויות הזמינות.
- Estimator V2
- Estimator (V1)
from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Setting options during primitive initialization
estimator = Estimator(backend, options={"resilience_level": 2})
# Setting options after primitive initialization
# This uses auto complete.
estimator.options.default_shots = 4000
# This does bulk update.
estimator.options.update(default_shots=4000, resilience_level=2)
# Print the dictionary format.
# Server defaults are used for unset options.
print(asdict(estimator.options))
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Options
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Setting options during primitive initialization
options = Options()
# This uses auto complete.
options.resilience_level = 2
estimator = Estimator(backend=backend, options=options)
# Setting options after primitive initialization.
# This does bulk update.
estimator.set_options(shots=4000)
- Sampler V2
- Sampler (V1)
from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Setting options during primitive initialization
sampler = Sampler(backend, options={"default_shots": 4096})
# Setting options after primitive initialization
# This uses auto complete.
sampler.options.dynamical_decoupling.enable = True
# Turn on gate twirling. Requires qiskit_ibm_runtime 0.23.0 or later.
sampler.options.twirling.enable_gates = True
# This does bulk update. The value for default_shots is overridden if you specify shots with run() or in the PUB.
sampler.options.update(default_shots=1024, dynamical_decoupling={"sequence_type": "XpXm"})
# Print the dictionary format.
# Server defaults are used for unset options.
print(asdict(sampler.options))
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Options
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Setting options during primitive initialization
options = Options()
# This uses auto complete.
options.resilience_level = 2
sampler = Sampler(backend=backend, options=options)
# Setting options after primitive initialization.
# This does bulk update.
sampler.set_options(shots=2000)
צמצום ודיכוי שגיאות
-
מכיוון ש-Sampler V2 מחזיר דגימות ללא עיבוד לאחר, הוא לא תומך ברמות resilience.
-
Sampler V2 לא תומך ב-
optimization_level. -
Estimator V2 יפסיק לתמוך ב-
optimization_levelביום 30 בספטמבר 2024 או סביבו. -
Estimator V2 לא תומך ברמת resilience 3. הסיבה לכך היא שרמת resilience 3 ב-V1 Estimator משתמשת ב-Probabilistic Error Cancellation (PEC), שהוכחה לתת תוצאות חסרות הטיה במחיר של זמן עיבוד אקספוננציאלי. רמה 3 הוסרה כדי להבליט את אותו פשרה. עם זאת, עדיין ניתן להשתמש ב-PEC כשיטת error mitigation על ידי ציון האפשרות
pec_mitigation. -
Estimator V2 תומך ב-
resilience_level0-2, כפי שמתואר בטבלה הבאה. אפשרויות אלה מתקדמות יותר מעמיתותיהן ב-V1. ניתן גם להפעיל או לכבות באופן מפורש שיטות בודדות של error mitigation / suppression.רמה 1 רמה 2 Measurement twirling Measurement twirling Readout error mitigation Readout error mitigation ZNE
- Estimator V2
- Estimator (V1)
from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Setting options during primitive initialization
estimator = Estimator(backend)
# Set resilience_level to 0
estimator.options.resilience_level = 0
# Turn on measurement error mitigation
estimator.options.resilience.measure_mitigation = True
from qiskit_ibm_runtime import Estimator, Options
estimator = Estimator(backend, options=options)
options = Options()
options.resilience_level = 2
- Sampler V2
- Sampler (V1)
from qiskit_ibm_runtime import SamplerV2 as Sampler
sampler = Sampler(backend)
# Turn on dynamical decoupling with sequence XpXm.
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XpXm"
print(f">> dynamical decoupling sequence to use: {sampler.options.dynamical_decoupling.sequence_type}")
from qiskit_ibm_runtime import Sampler, Options
sampler = Sampler(backend, options=options)
options = Options()
options.resilience_level = 2
Transpilation
V2 primitives תומכים רק ב-Circuits שעומדים ב-Instruction Set Architecture (ISA) של Backend מסוים. מכיוון שה-primitives לא מבצעים פעולות layout, routing ותרגום, אפשרויות ה-transpilation המתאימות מ-V1 אינן נתמכות.
סטטוס עבודה
ל-V2 primitives יש מחלקה חדשה RuntimeJobV2, שיורשת מ-BasePrimitiveJob. מתודת status() של מחלקה חדשה זו מחזירה מחרוזת במקום enum מסוג JobStatus מ-Qiskit. ראו את API reference של RuntimeJobV2 לפרטים.
- V2 primitives
- V1 primitives
job = estimator.run(...)
# check if a job is still running
print(f"Job {job.job_id()} is still running: {job.status() == "RUNNING"}")
from qiskit.providers.jobstatus import JobStatus
job = estimator.run(...)
#check if a job is still running
print(f"Job {job.job_id()} is still running: {job.status() is JobStatus.RUNNING}")
שלבים למעבר ל-Estimator V2
-
החלף את
from qiskit_ibm_runtime import Estimatorב-from qiskit_ibm_runtime import EstimatorV2 as Estimator. -
הסר כל הצהרות
from qiskit_ibm_runtime import Options, מכיוון שמחלקתOptionsאינה בשימוש ב-primitives מגרסה V2. במקומה, אפשר להעביר אפשרויות כמילון בעת אתחול מחלקתEstimatorV2(לדוגמהestimator = Estimator(backend, options={"dynamical_decoupling": {"enable": True}})), או להגדיר אותן לאחר האתחול:estimator = Estimator(backend)
estimator.options.dynamical_decoupling.enable = True -
עיין בכל האפשרויות הנתמכות ועדכן בהתאם.
-
קבץ כל Circuit שברצונך להריץ יחד עם ה-observables וערכי הפרמטרים שרוצים להחיל עליו ב-tuple (PUB). לדוגמה, השתמש ב-
(circuit1, observable1, parameter_set1)אם רוצה להריץ אתcircuit1עםobservable1ו-parameter_set1. -
ייתכן שתצטרך לשנות את צורת מערכי ה-observables או ערכי הפרמטרים אם רוצה להחיל את המכפלה החיצונית שלהם. לדוגמה, מערך observables בצורה (4, 1) ומערך ערכי פרמטרים בצורה (1, 6) יניב תוצאה של (4, 6) ערכי ציפייה. ראה את כללי ה-broadcasting של NumPy לפרטים נוספים.
-
אפשר גם לציין את הדיוק הרצוי עבור אותו PUB ספציפי.
-
עדכן את מתודת
run()של ה-Estimator כדי להעביר את רשימת ה-PUBs. לדוגמה,run([(circuit1, observable1, parameter_set1)]). אפשר גם לציין כאןprecision, שיחול על כל ה-PUBs. -
תוצאות עבודה של Estimator V2 מקובצות לפי PUBs. אפשר לראות את ערך הציפייה והשגיאה הסטנדרטית עבור כל PUB על ידי אינדוקס אליו. לדוגמה:
pub_result = job.result()[0]
print(f">>> Expectation values: {pub_result.data.evs}")
print(f">>> Standard errors: {pub_result.data.stds}")
דוגמאות מלאות ל-Estimator
הרצת ניסוי בודד
השתמש ב-Estimator כדי לקבוע את ערך הציפייה של זוג Circuit-observable בודד.
- Estimator V2
- Estimator (V1)
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import EstimatorV2 as Estimator, QiskitRuntimeService
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
estimator = Estimator(backend)
n_qubits = 127
mat = np.real(random_hermitian(n_qubits, seed=1234))
circuit = IQP(mat)
observable = SparsePauliOp("Z" * n_qubits)
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
job = estimator.run([(isa_circuit, isa_observable)])
result = job.result()
print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Estimator
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
n_qubits = 127
mat = np.real(random_hermitian(n_qubits, seed=1234))
circuit = IQP(mat)
observable = SparsePauliOp("Z" * n_qubits)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
estimator = Estimator(backend)
job = estimator.run(isa_circuit, isa_observable)
result = job.result()
print(f" > Observable: {observable.paulis}")
print(f" > Expectation value: {result.values}")
print(f" > Metadata: {result.metadata}")
הרצת מספר ניסויים בעבודה אחת
השתמש ב-Estimator כדי לקבוע את ערכי הציפייה של מספר זוגות Circuit-observable.
- Estimator V2
- Estimator (V1)
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
n_qubits = 3
rng = np.random.default_rng()
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
circuits = [IQP(mat) for mat in mats]
observables = [
SparsePauliOp("X" * n_qubits),
SparsePauliOp("Y" * n_qubits),
SparsePauliOp("Z" * n_qubits),
]
isa_circuits = pm.run(circuits)
isa_observables = [ob.apply_layout(isa_circuits[0].layout) for ob in observables]
estimator = Estimator(backend)
job = estimator.run([(isa_circuits[0], isa_observables[0]),(isa_circuits[1], isa_observables[1]),(isa_circuits[2], isa_observables[2])])
job_result = job.result()
for idx in range(len(job_result)):
pub_result = job_result[idx]
print(f">>> Expectation values for PUB {idx}: {pub_result.data.evs}")
print(f">>> Standard errors for PUB {idx}: {pub_result.data.stds}")
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Estimator
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
n_qubits = 127
rng = np.random.default_rng()
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
circuits = [IQP(mat) for mat in mats]
observables = [
SparsePauliOp("X" * n_qubits),
SparsePauliOp("Y" * n_qubits),
SparsePauliOp("Z" * n_qubits),
]
# Get ISA circuits
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuits = pm.run(circuits)
isa_observables = [ob.apply_layout(isa_circuits[0].layout) for ob in observables]
estimator = Estimator(backend)
job = estimator.run(isa_circuits, isa_observables)
result = job.result()
print(f" > Expectation values: {result.values}")
הרצת Circuits עם פרמטרים
השתמש ב-Estimator כדי להריץ מספר ניסויים בעבודה אחת, תוך ניצול ערכי פרמטרים להגדלת שימוש חוזר ב-Circuit. בדוגמה הבאה, שים לב ששלבים 1 ו-2 זהים ל-V1 וב-V2.
- Estimator V2
- Estimator (V1)
import numpy as np
from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
# Step 1: Map classical inputs to a quantum problem
theta = Parameter("θ")
chsh_circuit = QuantumCircuit(2)
chsh_circuit.h(0)
chsh_circuit.cx(0, 1)
chsh_circuit.ry(theta, 0)
number_of_phases = 21
phases = np.linspace(0, 2 * np.pi, number_of_phases)
individual_phases = [[ph] for ph in phases]
ZZ = SparsePauliOp.from_list([("ZZ", 1)])
ZX = SparsePauliOp.from_list([("ZX", 1)])
XZ = SparsePauliOp.from_list([("XZ", 1)])
XX = SparsePauliOp.from_list([("XX", 1)])
ops = [ZZ, ZX, XZ, XX]
# Step 2: Optimize problem for quantum execution.
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
chsh_isa_circuit = pm.run(chsh_circuit)
isa_observables = [operator.apply_layout(chsh_isa_circuit.layout) for operator in ops]
from qiskit_ibm_runtime import EstimatorV2 as Estimator
# Step 3: Execute using Qiskit primitives.
# Reshape observable array for broadcasting
reshaped_ops = np.fromiter(isa_observables, dtype=object)
reshaped_ops = reshaped_ops.reshape((4, 1))
estimator = Estimator(backend, options={"default_shots": int(1e4)})
job = estimator.run([(chsh_isa_circuit, reshaped_ops, individual_phases)])
# Get results for the first (and only) PUB
pub_result = job.result()[0]
print(f">>> Expectation values: {pub_result.data.evs}")
print(f">>> Standard errors: {pub_result.data.stds}")
print(f">>> Metadata: {pub_result.metadata}")
import numpy as np
from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
# Step 1: Map classical inputs to a quantum problem
theta = Parameter("θ")
chsh_circuit = QuantumCircuit(2)
chsh_circuit.h(0)
chsh_circuit.cx(0, 1)
chsh_circuit.ry(theta, 0)
number_of_phases = 21
phases = np.linspace(0, 2 * np.pi, number_of_phases)
individual_phases = [[ph] for ph in phases]
ZZ = SparsePauliOp.from_list([("ZZ", 1)])
ZX = SparsePauliOp.from_list([("ZX", 1)])
XZ = SparsePauliOp.from_list([("XZ", 1)])
XX = SparsePauliOp.from_list([("XX", 1)])
ops = [ZZ, ZX, XZ, XX]
# Step 2: Optimize problem for quantum execution.
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
chsh_isa_circuit = pm.run(chsh_circuit)
isa_observables = [operator.apply_layout(chsh_isa_circuit.layout) for operator in ops]
from qiskit_ibm_runtime import Estimator
# Step 3: Execute using Qiskit Primitives.
num_ops = len(isa_observables)
batch_circuits = [chsh_isa_circuit] * number_of_phases * num_ops
batch_ops = [op for op in isa_observables for _ in individual_phases]
batch_phases = individual_phases * num_ops
estimator = Estimator(backend, options={"shots": int(1e4)})
job = estimator.run(batch_circuits, batch_ops, batch_phases)
expvals = job.result().values
שימוש ב-Sessions ואפשרויות מתקדמות
חקור Sessions ואפשרויות מתקדמות לאופטימיזציה של ביצועי Circuit ע ל QPUs.
- Estimator V2
- Estimator (V1)
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Session, EstimatorV2 as Estimator
n_qubits = 127
rng = np.random.default_rng(1234)
mat = np.real(random_hermitian(n_qubits, seed=rng))
circuit = IQP(mat)
mat = np.real(random_hermitian(n_qubits, seed=rng))
another_circuit = IQP(mat)
observable = SparsePauliOp("X" * n_qubits)
another_observable = SparsePauliOp("Y" * n_qubits)
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
another_isa_circuit = pm.run(another_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
another_isa_observable = another_observable.apply_layout(another_isa_circuit.layout)
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
with Session(backend=backend) as session:
estimator = Estimator()
estimator.options.resilience_level = 1
job = estimator.run([(isa_circuit, isa_observable)])
another_job = estimator.run([(another_isa_circuit, another_isa_observable)])
result = job.result()
another_result = another_job.result()
# first job
print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")
# second job
print(f" > Another Expectation value: {another_result[0].data.evs}")
print(f" > More Metadata: {another_result[0].metadata}")
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Session, Estimator, Options
n_qubits = 127
rng = np.random.default_rng(1234)
mat = np.real(random_hermitian(n_qubits, seed=rng))
circuit = IQP(mat)
mat = np.real(random_hermitian(n_qubits, seed=rng))
another_circuit = IQP(mat)
observable = SparsePauliOp("X" * n_qubits)
another_observable = SparsePauliOp("Y" * n_qubits)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
another_isa_circuit = pm.run(another_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
another_isa_observable = another_observable.apply_layout(another_isa_circuit.layout)
options = Options()
options.optimization_level = 2
options.resilience_level = 2
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
with Session(backend=backend) as session:
estimator = Estimator(options=options)
job = estimator.run(isa_circuit, isa_observable)
another_job = estimator.run(another_isa_circuit, another_isa_observable)
result = job.result()
another_result = another_job.result()
# first job
print(f" > Expectation values job 1: {result.values}")
# second job
print(f" > Expectation values job 2: {another_result.values}")
שלבים למעבר ל-Sampler V2
- החלף את
from qiskit_ibm_runtime import Samplerב-from qiskit_ibm_runtime import SamplerV2 as Sampler. - הסר כל הצהרות
from qiskit_ibm_runtime import Options, כיוון שמחלקתOptionsאינה נמצאת בשימוש על ידי primitives מ גרסה V2. במקום זאת, אפשר להעביר אפשרויות כמילון בעת אתחול מחלקתSamplerV2(לדוגמהsampler = Sampler(backend, options={"default_shots": 1024})), או להגדיר אותן לאחר האתחול:sampler = Sampler(backend)
sampler.options.default_shots = 1024 - עיין בכל האפשרויות הנתמכות ועדכן בהתאם.
- קבץ כל Circuit שאתה רוצה להריץ יחד עם ה-observables וערכי הפרמטרים שברצונך להחיל על ה-Circuit ב-tuple (כלומר PUB). לדוגמה, השתמש ב-
(circuit1, parameter_set1)אם ברצונך להריץ אתcircuit1עםparameter_set1. אפשר גם לציין את מספר ה-shots הרצוי עבור אותו PUB ספציפי. - עדכן את מתודת
run()של ה-Sampler כך שתעביר את רשימת ה-PUBs. לדוגמה,run([(circuit1, parameter_set1)]). אפשר גם לצייןshotsכאן, מה שיחול על כל ה-PUBs. - תוצאות עבודת Sampler V2 מקובצות לפי PUBs. ניתן לראות את נתוני הפלט של כל PUB על ידי גישה באינדקס. בעוד ש-Sampler V2 מחזיר דגימות לא-ממושקלות, למחלקת התוצאה יש מתודת עזר לקבלת ספירות במקום. לדוגמה:
pub_result = job.result()[0]
print(f">>> Counts: {pub_result.data.meas.get_counts()}")
print(f">>> Per-shot measurement: {pub_result.data.meas.get_counts()}")
כדי לקבל את התוצאות, צריך את שם הרג'יסטר הקלאסי. כברירת מחדל, הוא נקרא meas כשמשתמשים ב-measure_all(). כשמגדירים את ה-Circuit, אם יוצרים רג'יסטר קלאסי אחד או יותר עם שם שאינו ברירת המחדל, יש להשתמש בשם הזה לקבלת התוצאות. ניתן למצוא את שם הרג'יסטר הקלאסי על ידי הרצת <circuit_name>.cregs. לדוגמה, qc.cregs.
דוגמאות מלאות ל-Sampler
הרצת ניסוי בודד
השתמש ב-Sampler כדי לקבוע את הספירות או התפלגות הקוואזי-הסתברות של Circuit בודד.
- Sampler V2
- Sampler (V1)
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
n_qubits = 127
mat = np.real(random_hermitian(n_qubits, seed=1234))
circuit = IQP(mat)
circuit.measure_all()
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
sampler = Sampler(backend)
job = sampler.run([isa_circuit])
result = job.result()
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
n_qubits = 127
mat = np.real(random_hermitian(n_qubits, seed=1234))
circuit = IQP(mat)
circuit.measure_all()
sampler = Sampler(backend)
job = sampler.run(circuit)
result = job.result()
print(f" > Quasi-probability distribution: {result.quasi_dists}")
print(f" > Metadata: {result.metadata}")
הרצת ניסויים מרובים במשרה אחת
השתמש ב-Sampler כדי לקבוע את הספירות או התפלגויות הקוואזי-הסתברות של מספר Circuits במשרה אחת.
- Sampler V2
- Sampler (V1)
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
n_qubits = 127
rng = np.random.default_rng()
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
circuits = [IQP(mat) for mat in mats]
for circuit in circuits:
circuit.measure_all()
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuits = pm.run(circuits)
sampler = Sampler(backend)
job = sampler.run(isa_circuits)
result = job.result()
for idx, pub_result in enumerate(result):
print(f" > Counts for pub {idx}: {pub_result.data.meas.get_counts()}")
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
n_qubits = 127
rng = np.random.default_rng()
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
circuits = [IQP(mat) for mat in mats]
for circuit in circuits:
circuit.measure_all()
sampler = Sampler(backend)
job = sampler.run(circuits)
result = job.result()
print(f" > Quasi-probability distribution: {result.quasi_dists}")
הרצת Circuits עם פרמטרים
הרץ מספר ניסויים במשרה אחת, תוך ניצול ערכי פרמטרים להגברת שימוש חוזר ב-Circuit.
- Sampler V2
- Sampler (V1)
import numpy as np
from qiskit.circuit.library import RealAmplitudes
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
# Step 1: Map classical inputs to a quantum problem
num_qubits = 127
circuit = RealAmplitudes(num_qubits=num_qubits, reps=2)
circuit.measure_all()
# Define three sets of parameters for the circuit
rng = np.random.default_rng(1234)
parameter_values = [
rng.uniform(-np.pi, np.pi, size=circuit.num_parameters) for _ in range(3)
]
# Step 2: Optimize problem for quantum execution.
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=num_qubits)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
# Step 3: Execute using Qiskit primitives.
from qiskit_ibm_runtime import SamplerV2 as Sampler
sampler = Sampler(backend)
job = sampler.run([(isa_circuit, parameter_values)])
result = job.result()
# Get results for the first (and only) PUB
pub_result = result[0]
# Get counts from the classical register "meas".
print(f" >> Counts for the meas output register: {pub_result.data.meas.get_counts()}")
import numpy as np
from qiskit.circuit.library import RealAmplitudes
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
# Step 1: Map classical inputs to a quantum problem
num_qubits = 5
circuit = RealAmplitudes(num_qubits=num_qubits, reps=2)
circuit.measure_all()
# Define three sets of parameters for the circuit
rng = np.random.default_rng(1234)
parameter_values = [
rng.uniform(-np.pi, np.pi, size=circuit.num_parameters) for _ in range(3)
]
# Step 2: Optimize problem for quantum execution.
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=num_qubits)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
# Step 3: Execute using Qiskit primitives.
from qiskit_ibm_runtime import Sampler
sampler = Sampler(backend)
job = sampler.run([isa_circuit] * 3, parameter_values)
result = job.result()
print(f" > Quasi-probability distribution: {result.quasi_dists}")
print(f" > Metadata: {result.metadata}")
שימוש ב-Sessions ואפשרויות מתקדמות
חקור Sessions ואפשרויות מתקדמות לאופטימיזציה של ביצועי Circuit על QPUs.
- Sampler V2
- Sampler (V1)
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler, Session
n_qubits = 127
rng = np.random.default_rng(1234)
mat = np.real(random_hermitian(n_qubits, seed=rng))
circuit = IQP(mat)
circuit.measure_all()
mat = np.real(random_hermitian(n_qubits, seed=rng))
another_circuit = IQP(mat)
another_circuit.measure_all()
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
another_isa_circuit = pm.run(another_circuit)
service = QiskitRuntimeService()
# Turn on dynamical decoupling with sequence XpXm.
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XpXm"
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
with Session(backend=backend) as session:
sampler = Sampler()
job = sampler.run([isa_circuit])
another_job = sampler.run([another_isa_circuit])
result = job.result()
another_result = another_job.result()
# first job
print(f" > Counts for job 1: {result[0].data.meas.get_counts()}")
# second job
print(f" > Counts for job 2: {another_result[0].data.meas.get_counts()}")
import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Session, Options
n_qubits = 127
rng = np.random.default_rng(1234)
mat = np.real(random_hermitian(n_qubits, seed=rng))
circuit = IQP(mat)
circuit.measure_all()
mat = np.real(random_hermitian(n_qubits, seed=rng))
another_circuit = IQP(mat)
another_circuit.measure_all()
options = Options()
options.optimization_level = 2
options.resilience_level = 0
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
with Session(backend=backend) as session:
sampler = Sampler(options=options)
job = sampler.run(circuit)
another_job = sampler.run(another_circuit)
result = job.result()
another_result = another_job.result()
# first job
print(f" > Quasi-probability distribution job 1: {result.quasi_dists}")
# second job
print(f" > Quasi-probability distribution job 2: {another_result.quasi_dists}")
הצעדים הבאים
- למד עוד על הגדרת אפשרויות במדריך ציון אפשרויות.
- למד פרטים נוספים על קלטים ופלטים של Primitive.
- התנסה עם המדריך אי-שוויון CHSH.