סימולציה מדויקת עם primitives של Qiskit SDK
גרסאות חבילות
הקוד בדף זה פותח תוך שימוש בדרישות הבאות. אנחנו ממליצים להשתמש בגרסאות אלו או בחדשות יותר.
qiskit[all]~=2.5.2
ה-primitives הייחוסיים ב-Qiskit SDK מבצעים סימולציות statevector מקומיות. סימולציות אלו אינן תומכות במידול רעש של המכשיר, אך הן שימושיות ליצירת אב-טיפוס מהיר של אלגוריתמים, לפני שפונים לטכניקות סימולציה מתקדמות יותר (שימוש ב-Qiskit Aer) או להרצה על מכשירים אמיתיים (IBM Quantum primitives).
ה-Estimator Primitive יכול לחשב ערכי תוחלת של Circuit, וה-Sampler Primitive יכול לדגום מהתפלגויות הפלט של Circuit.
הסעיפים הבאים מראים כיצד להשתמש ב-primitives הייחוסיים להרצת זרימת העבודה שלך באופן מקומי.
שימוש ב-Estimator הייחוסי
המימוש הייחוסי של EstimatorV2 ב-qiskit.primitives שרץ על סימולטורי statevector מקומיים
הוא המחלקה StatevectorEstimator. היא יכולה לקבל Circuit, observables ופרמטרים כקלטים ומחזירה את ערכי התוחלת המחושבים באופן מקומי.
הקוד הבא מכין את הקלטים שישמשו בדוגמאות שיבואו. סוג הקלט הצפוי עבור
ה-observables הוא qiskit.quantum_info.SparsePauliOp. שים לב
ש-Circuit בדוגמה הוא פרמטרי, אך ניתן גם להריץ את Estimator על Circuit לא-פרמטריים.
אסור שמעגל שמועבר ל-Estimator יכלול מדידות כלשהן.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"numpy": "numpy", "qiskit": "qiskit"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
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]]
זרימת העבודה של IBM Quantum primitives דורשת שמעגלים ו-observables יומרו לשימוש רק בהוראות הנתמכות על ידי ה-QPU (המכונים מעגלי instruction set architecture (ISA) ו-observables). ה-primitives הייחוסיים עדיין מקבלים הוראות מופשטות, מכיוון שהם מסתמכים על סימולציות statevector מקומיות, אך Transpile של המעגל עשוי עדיין להיות מועיל מבחינת אופטימיזציית המעגל.
# 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 פרמטרי בודד, אך ניתן גם להריץ את ה-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 חייב לכלול מדידות.
זרימת העבודה של IBM Quantum primitives דורשת שמעגלים יומרו לשימוש רק בהוראות הנתמכות על ידי ה-QPU (המכונים מעגלי ISA). ה-primitives הייחוסיים עדיין מקבלים הוראות מופשטות, מכיוון שהם מסתמכים על סימולציות statevector מקומיות, אך Transpile של המעגל עשוי עדיין להיות מועיל מבחינת אופטימיזציית המעגל.
# 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 מציגים את תוצאות המדידה, תוך שמירה על סדר ה-shots שבהם נמדדו. אובייקטי תוצאת ה-Sampler מארגנים את הנתונים לפי שמות הרגיסטרים הקלאסיים של מעגלי הקלט שלהם, לצורך תאימות עם מעגלים דינמיים.
שם הרגיסטר הקלאסי מוגדר כברירת מחדל ל-"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: {'00': 513, '11': 511}
שינוי אפשרויות הרצה
כברירת מחדל, ה-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 0x1297391d0>
לדוגמה מלאה, ראה את הדף דוגמאות Sampler.
הצעדים הבאים
- לסימולציה בעלת ביצועים גבוהים יותר שיכולה להתמודד עם מעגלים גדולים יותר, או כדי לשלב מודלי רעש בסימולציה שלך, ראה סימולציה מדויקת ורועשת עם Qiskit Aer primitives.
- כדי ללמוד כיצד להשתמש ב-Quantum Composer לסימולציה, ראה את המדריך IBM Quantum Composer.
- עיין בתיעוד ה-API של Qiskit Estimator API.
- עיין בתיעוד ה-API של Qiskit Sampler API.