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

מעבר ל-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 המצוין.

from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime import SamplerV2 as 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 (קלט ופלט)

# 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

דוגמאות Sampler (קלט ופלט)

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

דוגמה שמשתמשת ברגיסטרי פלט שונים

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 הוא מטיפוס Python dataclass. אפשר להשתמש במתודה המובנית asdict כדי להמיר אותו למילון.

ראו את API reference לרשימת האפשרויות הזמינות.

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 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))

צמצום ודיכוי שגיאות

  • מכיוון ש-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_level 0-2, כפי שמתואר בטבלה הבאה. אפשרויות אלה מתקדמות יותר מעמיתותיהן ב-V1. ניתן גם להפעיל או לכבות באופן מפורש שיטות בודדות של error mitigation / suppression.

    רמה 1רמה 2
    Measurement twirlingMeasurement twirling
    Readout error mitigationReadout error mitigation
    ZNE
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 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}")

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 לפרטים.

job = estimator.run(...)

# check if a job is still running
print(f"Job {job.job_id()} is still running: {job.status() == "RUNNING"}")

שלבים למעבר ל-Estimator V2

  1. החלף את from qiskit_ibm_runtime import Estimator ב-from qiskit_ibm_runtime import EstimatorV2 as Estimator.

  2. הסר כל הצהרות 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
  3. עיין בכל האפשרויות הנתמכות ועדכן בהתאם.

  4. קבץ כל Circuit שברצונך להריץ יחד עם ה-observables וערכי הפרמטרים שרוצים להחיל עליו ב-tuple (PUB). לדוגמה, השתמש ב-(circuit1, observable1, parameter_set1) אם רוצה להריץ את circuit1 עם observable1 ו-parameter_set1.

  5. ייתכן שתצטרך לשנות את צורת מערכי ה-observables או ערכי הפרמטרים אם רוצה להחיל את המכפלה החיצונית שלהם. לדוגמה, מערך observables בצורה (4, 1) ומערך ערכי פרמטרים בצורה (1, 6) יניב תוצאה של (4, 6) ערכי ציפייה. ראה את כללי ה-broadcasting של NumPy לפרטים נוספים.

  6. אפשר גם לציין את הדיוק הרצוי עבור אותו PUB ספציפי.

  7. עדכן את מתודת run() של ה-Estimator כדי להעביר את רשימת ה-PUBs. לדוגמה, run([(circuit1, observable1, parameter_set1)]). אפשר גם לציין כאן precision, שיחול על כל ה-PUBs.

  8. תוצאות עבודה של 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 בודד.

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}")

הרצת מספר ניסויים בעבודה אחת

השתמש ב-Estimator כדי לקבוע את ערכי הציפייה של מספר זוגות Circuit-observable.

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}")

הרצת Circuits עם פרמטרים

השתמש ב-Estimator כדי להריץ מספר ניסויים בעבודה אחת, תוך ניצול ערכי פרמטרים להגדלת שימוש חוזר ב-Circuit. בדוגמה הבאה, שים לב ששלבים 1 ו-2 זהים ל-V1 וב-V2.

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}")

שימוש ב-Sessions ואפשרויות מתקדמות

חקור Sessions ואפשרויות מתקדמות לאופטימיזציה של ביצועי Circuit על QPUs.

זהירות

בלוק הקוד הבא יחזיר שגיאה עבור משתמשי תוכנית Open Plan, מכיוון שהוא משתמש ב-Sessions. עבודות ב-Open Plan יכולות לרוץ רק במצב עבודה או מצב אצווה.

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}")

שלבים למעבר ל-Sampler V2

  1. החלף את from qiskit_ibm_runtime import Sampler ב-from qiskit_ibm_runtime import SamplerV2 as Sampler.
  2. הסר כל הצהרות 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
  3. עיין בכל האפשרויות הנתמכות ועדכן בהתאם.
  4. קבץ כל Circuit שאתה רוצה להריץ יחד עם ה-observables וערכי הפרמטרים שברצונך להחיל על ה-Circuit ב-tuple (כלומר PUB). לדוגמה, השתמש ב-(circuit1, parameter_set1) אם ברצונך להריץ את circuit1 עם parameter_set1. אפשר גם לציין את מספר ה-shots הרצוי עבור אותו PUB ספציפי.
  5. עדכן את מתודת run() של ה-Sampler כך שתעביר את רשימת ה-PUBs. לדוגמה, run([(circuit1, parameter_set1)]). אפשר גם לציין shots כאן, מה שיחול על כל ה-PUBs.
  6. תוצאות עבודת 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 בודד.

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

הרצת ניסויים מרובים במשרה אחת

השתמש ב-Sampler כדי לקבוע את הספירות או התפלגויות הקוואזי-הסתברות של מספר Circuits במשרה אחת.

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()}")

הרצת Circuits עם פרמטרים

הרץ מספר ניסויים במשרה אחת, תוך ניצול ערכי פרמטרים להגברת שימוש חוזר ב-Circuit.

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()}")

שימוש ב-Sessions ואפשרויות מתקדמות

חקור Sessions ואפשרויות מתקדמות לאופטימיזציה של ביצועי Circuit על QPUs.

זהירות

בלוק הקוד הבא יחזיר שגיאה למשתמשים בתוכנית הפתוחה (Open Plan) כי הוא משתמש ב-Sessions. עומסי עבודה בתוכנית הפתוחה יכולים לרוץ רק במצב משרה או מצב אצווה.

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()}")

הצעדים הבאים

המלצות