סימולציה מדויקת עם primitives של Qiskit SDK
גרסאות חבילות
הקוד בדף זה פותח תוך שימוש בדרישות הבאות. אנחנו ממליצים להשתמש בגרסאות אלו או בחדשות יותר.
qiskit[all]~=2.4.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")
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]]
זרימת העבודה של 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
)
לדוגמה מלאה, ראה את הדף דוגמאות Estimator.
שימוש ב-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")
כל Circuit קוונטי שמועבר ל-Sampler חייב לכלול מדידות.
זרימת העבודה של 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': 495, '00': 529}
שינוי אפשרויות הרצה
כברירת מחדל, ה-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 0x7feb2d393d90>
לדוגמה מלאה, ראה את הדף דוגמאות Sampler.
הצעדים הבאים
- לסימולציה בעלת ביצועים גבוהים יותר שיכולה להתמודד עם Circuit גדולים יותר, או כדי לשלב מודלי רעש בסימולציה שלך, ראה סימולציה מדויקת ורועשת עם Qiskit Aer primitives.
- כדי ללמוד כיצד להשתמש ב-Quantum Composer לסימולציה, ראה את המדריך IBM Quantum Composer.
- קרא את המדריך Qiskit Estimator API.
- קרא את המדריך Qiskit Sampler API.
- קרא מעבר ל-V2 primitives.