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

סימולציה מדויקת עם primitives של Qiskit SDK

גרסאות חבילות

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

qiskit[all]~=2.3.0

ה-primitives הייחוסיים ב-Qiskit SDK מבצעים סימולציות statevector מקומיות. סימולציות אלו אינן תומכות במידול רעש של המכשיר, אך הן שימושיות לאב-טיפוס מהיר של אלגוריתמים לפני שמתבוננים בטכניקות סימולציה מתקדמות יותר (שימוש ב-Qiskit Aer) או בהרצה על מכשירים אמיתיים (Qiskit Runtime primitives).

ה-Estimator Primitive יכול לחשב ערכי ציפייה של Circuit, וה-Sampler Primitive יכול לדגום מהתפלגויות הפלט של Circuit.

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

שימוש ב-Estimator הייחוסי

המימוש הייחוסי של EstimatorV2 ב-qiskit.primitives שרץ על סימולטורי statevector מקומיים הוא המחלקה StatevectorEstimator. היא יכולה לקבל Circuit, observables ופרמטרים כקלטים ומחזירה את ערכי הציפייה המחושבים באופן מקומי.

הקוד הבא מכין את הקלטים שישמשו בדוגמאות שיבואו. סוג הקלט הצפוי עבור ה-observables הוא qiskit.quantum_info.SparsePauliOp. שים לב ש-Circuit בדוגמה הוא פרמטרי, אך ניתן גם להריץ את ה-Estimator על Circuit לא-פרמטריים.

הערה

כל Circuit שמועבר ל-Estimator חייב שלא לכלול מדידות.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
from qiskit import QuantumCircuit
from qiskit.circuit import Parameter

# circuit for which you want to obtain the expected value
circuit = QuantumCircuit(2)
circuit.ry(Parameter("theta"), 0)
circuit.h(0)
circuit.cx(0, 1)
circuit.draw("mpl", style="iqp")

Output of the previous code cell

from qiskit.quantum_info import SparsePauliOp
import numpy as np

# observable(s) whose expected values you want to compute

observable = SparsePauliOp(["II", "XX", "YY", "ZZ"], coeffs=[1, 1, -1, 1])

# value(s) for the circuit parameter(s)
parameter_values = [[0], [np.pi / 6], [np.pi / 2]]
Transpile ל-ISA circuits ו-observables

זרימת העבודה של Qiskit Runtime primitives דורשת ש-Circuit וה-observables יומרו לשימוש רק בהוראות הנתמכות על ידי ה-QPU (המכונות מעגלי instruction set architecture (ISA) ו-observables). ה-primitives הייחוסיים עדיין מקבלים הוראות מופשטות, מכיוון שהם מסתמכים על סימולציות statevector מקומיות, אך Transpile של ה-Circuit עשוי עדיין להיות מועיל מבחינת אופטימיזציית ה-Circuit.

# Generate a pass manager without providing a backend
from qiskit.transpiler import generate_preset_pass_manager

pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)

אתחול Estimator

צור מופע של qiskit.primitives.StatevectorEstimator.

from qiskit.primitives import StatevectorEstimator

estimator = StatevectorEstimator()

הרצה וקבלת תוצאות

דוגמה זו משתמשת רק ב-Circuit אחד (מסוג QuantumCircuit) וב-observable אחד.

הרץ את ההערכה על ידי קריאה למתודה StatevectorEstimator.run, שמחזירה מופע של אובייקט PrimitiveJob. ניתן לקבל את התוצאות מהמשימה (כאובייקט qiskit.primitives.PrimitiveResult) עם המתודה qiskit.primitives.PrimitiveJob.result.

job = estimator.run([(circuit, observable, parameter_values)])
result = job.result()
print(f" > Result class: {type(result)}")
> Result class: <class 'qiskit.primitives.containers.primitive_result.PrimitiveResult'>

קבלת ערך הציפייה מהתוצאה

תוצאות ה-primitives מפיקות מערך של אובייקטי PubResult, כאשר כל פריט במערך הוא אובייקט PubResult שמכיל בנתוניו את מערך ההערכות המתאים לכל שילוב Circuit-observable ב-PUB.

כדי לאחזר את ערכי הציפייה והמטא-נתונים עבור הערכת ה-Circuit הראשונה (ובמקרה זה, היחידה), עלינו לגשת ל-data של ההערכה עבור PUB 0:

print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")
> Expectation value: [4.         3.73205081 2.        ]
> Metadata: {'target_precision': 0.0, 'circuit_metadata': {}}

הגדרת אפשרויות הרצה של Estimator

כברירת מחדל, ה-Estimator הייחוסי מבצע חישוב statevector מדויק המבוסס על המחלקה quantum_info.Statevector. אולם, ניתן לשנות זאת כדי להכניס את ההשפעה של תקורת הדגימה (המכונה גם "shot noise").

ה-Estimator מקבל ארגומנט precision שמבטא את פסי השגיאה שמימוש ה-primitive אמור לשאוף אליהם עבור הערכות ערכי ציפייה. זוהי תקורת הדגימה ומוגדרת אך ורק במתודה .run(). זה מאפשר לכוון את האפשרות עד לרמת ה-PUB.

# Estimate expectation values for two PUBs, both with 0.05 precision.
precise_job = estimator.run(
[(circuit, observable, parameter_values)], precision=0.05
)

לדוגמה מלאה, ראה את הדף דוגמאות Primitives.

שימוש ב-Sampler הייחוסי

המימוש הייחוסי של SamplerV2 ב-qiskit.primitives הוא המחלקה StatevectorSampler. היא מקבלת Circuit ופרמטרים כקלטים ומחזירה את התוצאות מדגימה מהתפלגויות ההסתברות של מצבי הפלט כהתפלגות קוואזי-הסתברות של מצבי פלט.

הקוד הבא מכין את הקלטים שישמשו בדוגמאות שיבואו. שים לב שדוגמאות אלו מריצות Circuit פרמטרי בודד, אך ניתן גם להריץ את ה-Sampler על Circuit לא-פרמטריים.

from qiskit import QuantumCircuit

circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
circuit.draw("mpl", style="iqp")

Output of the previous code cell

הערה

כל Circuit קוונטי שמועבר ל-Sampler חייב לכלול מדידות.

Transpile ל-ISA circuits ו-observables

זרימת העבודה של Qiskit Runtime primitives דורשת ש-Circuit יומרו לשימוש רק בהוראות הנתמכות על ידי ה-QPU (המכונות ISA circuits). ה-primitives הייחוסיים עדיין מקבלים הוראות מופשטות, מכיוון שהם מסתמכים על סימולציות statevector מקומיות, אך Transpile של ה-Circuit עשוי עדיין להיות מועיל מבחינת אופטימיזציית ה-Circuit.

# Generate a pass manager without providing a backend
from qiskit.transpiler import generate_preset_pass_manager

pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(qc)

אתחול SamplerV2

צור מופע של qiskit.primitives.StatevectorSampler:

from qiskit.primitives import StatevectorSampler

sampler = StatevectorSampler()

הרצה וקבלת תוצאות

# execute 1 circuit with Sampler
job = sampler.run([circuit])
pub_result = job.result()[0]
print(f" > Result class: {type(pub_result)}")
> Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>

Primitives מקבלים מספר PUBs כקלטים, וכל PUB מקבל תוצאה משלו. לפיכך, ניתן להריץ Circuit שונים עם שילובי פרמטר/observable שונים, ולאחזר את תוצאות ה-PUB:

from qiskit.transpiler import generate_preset_pass_manager

# create two circuits
circuit1 = circuit.copy()
circuit2 = circuit.copy()

# transpile circuits
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit1 = pm.run(circuit1)
isa_circuit2 = pm.run(circuit2)
# execute 2 circuits using Sampler
job = sampler.run([(isa_circuit1), (isa_circuit2)])
pub_result_1 = job.result()[0]
pub_result_2 = job.result()[1]
print(f" > Result class: {type(pub_result)}")
> Result class: <class 'qiskit.primitives.containers.sampler_pub_result.SamplerPubResult'>

קבלת התפלגות ההסתברות או תוצאת המדידה

דגימות תוצאות המדידה מוחזרות כ-bitstrings או counts. ה-bitstrings מציגים את תוצאות המדידה, תוך שמירה על סדר הירות שבו נמדדו. אובייקטי תוצאת ה-Sampler מארגנים נתונים במונחים של שמות הרגיסטרים הקלאסיים של Circuit הקלט שלהם, לצורך תאימות עם Circuit דינמיים.

הערה

שם הרגיסטר הקלאסי מוגדר כברירת מחדל ל-"meas". שם זה ישמש מאוחר יותר לגישה ל-bitstrings של המדידה.

# Define 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
# Transpile circuit
pm = generate_preset_pass_manager(optimization_level=1)
isa_circuit = pm.run(circuit)
# Run using sampler
result = sampler.run([circuit]).result()
# Access result data for PUB 0
data_pub = result[0].data
# Access bitstring for the classical register "meas"
bitstrings = data_pub.meas.get_bitstrings()
print(f"The number of bitstrings is: {len(bitstrings)}")
# Get counts for the classical register "meas"
counts = data_pub.meas.get_counts()
print(f"The counts are: {counts}")
The number of bitstrings is: 1024
The counts are: {'11': 515, '00': 509}

שינוי אפשרויות הרצה

כברירת מחדל, ה-Sampler הייחוסי מבצע חישוב statevector מדויק המבוסס על המחלקה quantum_info.Statevector. אולם, ניתן לשנות זאת כדי להכניס את ההשפעה של תקורת הדגימה (המכונה גם "shot noise"). כדי לעזור לנהל תקורה זו, ממשק ה-Sampler מקבל ארגומנט shots שניתן להגדיר ברמת ה-PUB.

דוגמה זו מניחה שהגדרת שני Circuit.

# Sample two circuits at 128 shots each.
sampler.run([isa_circuit1, isa_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([(isa_circuit1, None, 123), (isa_circuit2, None, 456)])
<qiskit.primitives.primitive_job.PrimitiveJob at 0x7fa430e39dd0>

לדוגמה מלאה, ראה את הדף דוגמאות Primitives.

הצעדים הבאים

המלצות