דלג לתוכן הראשי
פרויקט זה בשלב בטא. מצאת באג? פתח issue ב-GitHub. יש שאלה או רעיון? שאל ב-Discussions — נשמח לשמוע ממך!
מה איכות התרגום בדף זה?

מאחדים את הכול עם IBM Quantum Compute Service

סיכום​

ויקטוריה ליפינסקה מציגה סיכום סופי של מה שלמדנו עד כה.

מקורות​

המאמרים הבאים מוזכרים בסרטון לעיל.

VQE עם דפוסי Qiskit​

יש לנו את כל המרכיבים הנחוצים לחישוב VQE:

  • Hamiltonian

  • Ansatz

  • אופטימייזר קלאסי

כעת נצטרך רק לשלב אותם במסגרת דפוסי Qiskit.

שלב 1: ממפים קלטים קלאסיים לבעיה קוונטית​

כפי שצוין קודם, נניח כאן שכבר נוצר Hamiltonian מתאים בפורמט הנכון. אם יש לך שאלות בנושא, ראה את השיעור על Hamiltonians להנחיה. בלוק הקוד שלהלן מגדיר את הרכיבים שהוסברו בשיעורים הקודמים. כאן בחרנו לדגם את H2 מכיוון שה-Hamiltonian שלו קומפקטי מספיק לכתיבה מלאה.

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_aer": "qiskit-aer", "qiskit_ibm_runtime": "qiskit-ibm-runtime", "scipy": "scipy"}
_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")
# General imports
import numpy as np
from qiskit.quantum_info import SparsePauliOp

# Hamiltonian obtained from a previous lesson

H = SparsePauliOp(
[
"IIII",
"IIIZ",
"IZII",
"IIZI",
"ZIII",
"IZIZ",
"IIZZ",
"ZIIZ",
"IZZI",
"ZZII",
"ZIZI",
"YYYY",
"XXYY",
"YYXX",
"XXXX",
],
coeffs=[
-0.09820182 + 0.0j,
-0.1740751 + 0.0j,
-0.1740751 + 0.0j,
0.2242933 + 0.0j,
0.2242933 + 0.0j,
0.16891402 + 0.0j,
0.1210099 + 0.0j,
0.16631441 + 0.0j,
0.16631441 + 0.0j,
0.1210099 + 0.0j,
0.17504456 + 0.0j,
0.04530451 + 0.0j,
0.04530451 + 0.0j,
0.04530451 + 0.0j,
0.04530451 + 0.0j,
],
)

nuclear_repulsion = 0.7199689944489797

אנחנו בוחרים Circuit מסוג efficient_su2 ואת האופטימייזר COBYLA כנקודת התחלה.

# Pre-defined ansatz circuit
from qiskit.circuit.library import efficient_su2

# SciPy minimizer routine
from scipy.optimize import minimize

# Plotting functions

# Random initial state and efficient_su2 ansatz
ansatz = efficient_su2(H.num_qubits, su2_gates=["rx"], entanglement="linear", reps=1)
x0 = 2 * np.pi * np.random.random(ansatz.num_parameters)
print(ansatz.decompose().depth())
ansatz.decompose().draw("mpl")
5

Output of the previous code cell

עכשיו אנחנו בונים את פונקציית העלות שלנו. היא כמובן קשורה ל-Hamiltonian, אך שונה ממנו בכך שה-Hamiltonian הוא אופרטור, ואנחנו רוצים פונקציה שמחזירה את ערך הציפייה של אותו אופרטור, תוך שימוש ב-Estimator. כמובן, היא משיגה זאת באמצעות ה-ansatz והפרמטרים הווריאציוניים, כך שכל אלה מופיעים כארגומנטים. להלן, אנחנו מגדירים גרסאות מעט שונות לשימוש על חומרה אמיתית או סימולטורים.

def cost_func(params, ansatz, H, estimator):
pub = (ansatz, [H], [params])
result = estimator.run(pubs=[pub]).result()
energy = result[0].data.evs[0]
return energy

# def cost_func_sim(params, ansatz, H, estimator):
# energy = estimator.run(ansatz, H, parameter_values=params).result().values[0]
# return energy

שלב 2: אופטימיזציה של הבעיה להרצה קוונטית​

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

# To run on hardware, select the backend with the fewest number of jobs in the queue
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService(channel="ibm_quantum_platform")
backend = service.least_busy(operational=True, simulator=False)
backend.name

אופטימיזציה של Circuit להרצה על Backend אמיתי היא נושא עשיר וקריטי. אך הוא אינו ייחודי ל-VQE. לעת עתה, נזכיר בקצרה שני מונחים חשובים:

  • optimization_level: זה מתאר עד כמה ה-Circuit מותאם לפריסה של ה-Backend שנבחר. רמת האופטימיזציה הנמוכה ביותר עושה רק את המינימום הנדרש כדי להריץ את ה-Circuit על המכשיר; היא ממפה את ה-Qubit של ה-Circuit ל-Qubit של המכשיר ומוסיפה שערי החלפה כדי לאפשר את כל פעולות שני ה-Qubit. רמת האופטימיזציה הגבוהה ביותר הרבה יותר חכמה ומשתמשת בהרבה טריקים כדי להפחית את ספירת ה-Gate הכוללת. מכיוון שלשערים מרובי qubit יש שיעורי שגיאה גבוהים ו-Qubit מאבדים קוהרנטיות עם הזמן, מעגלים קצרים יותר אמורים לתת תוצאות טובות יותר.

  • Dynamical Decoupling: אנחנו יכולים להחיל רצף של Gates על qubit בלתי פעילים. זה מבטל חלק מהאינטראקציות הלא רצויות עם הסביבה. ראה בתיעוד המקושר לקבלת מידע נוסף על אופטימיזציה של Circuits. הקוד שלהלן מייצר מנהל pass manager תוך שימוש ב-preset pass managers מ-qiskit.transpiler.

from qiskit.transpiler import PassManager
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.transpiler.passes import (
ALAPScheduleAnalysis,
PadDynamicalDecoupling,
ConstrainedReschedule,
)
from qiskit.circuit.library import XGate

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
pm.scheduling = PassManager(
[
ALAPScheduleAnalysis(target=target),
ConstrainedReschedule(
acquire_alignment=target.acquire_alignment,
pulse_alignment=target.pulse_alignment,
target=target,
),
PadDynamicalDecoupling(
target=target,
dd_sequence=[XGate(), XGate()],
pulse_alignment=target.pulse_alignment,
),
]
)

# Use the pass manager and draw the resulting circuit
ansatz_isa = pm.run(ansatz)
ansatz_isa.draw(output="mpl", idle_wires=False, style="iqp")

Output of the previous code cell

עלינו גם להחיל את מאפייני פריסת המכשיר על ה-Hamiltonian.

hamiltonian_isa = H.apply_layout(ansatz_isa.layout)
hamiltonian_isa
SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIZII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIZII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYYYII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYYXXII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXYYII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIXXXXII'],
coeffs=[-0.09820182+0.j, -0.1740751 +0.j, -0.1740751 +0.j, 0.2242933 +0.j,
0.2242933 +0.j, 0.16891402+0.j, 0.1210099 +0.j, 0.16631441+0.j,
0.16631441+0.j, 0.1210099 +0.j, 0.17504456+0.j, 0.04530451+0.j,
0.04530451+0.j, 0.04530451+0.j, 0.04530451+0.j])

שלב 3: הרצה באמצעות primitives של IBM Quantum.​

לפני שאנו מריצים על החומרה שנבחרה, כדאי להשתמש בסימולטור לניפוי שגיאות ראשוני, ולעיתים לאמדן שגיאות. מסיבות אלו, נדגים בקצרה כיצד להריץ VQE על סימולטור. אך חשוב לציין כי שום מחשב קלאסי, סימולטור או GPU אינו יכול לסמלץ במדויק את הפונקציונליות המלאה של מחשב קוונטי בעל 127 qubit עם שזירה גבוהה. בעידן הנוכחי של שימושיות קוונטית, לסימולטורים יהיה שימוש מוגבל.

זכור שעבור כל בחירת פרמטרים ב-Circuit הווריאציוני, יש לחשב ערך ציפייה (שכן זה הערך שיש למזער). כפי שאולי כבר ניחשת, הדרך היעילה ביותר לעשות זאת היא שימוש ב-Primitive של Qiskit SDK, Estimator. נתחיל עם סימולטור מקומי, אשר ידרוש מאיתנו להשתמש בגרסה המקומית של Estimator הנקראת BackendEstimator.

בשמירת ה-Backend האמיתי שבו השתמשנו לאופטימיזציה, נוכל לייבא מודל של התנהגות הרעש של אותו מכשיר ולהשתמש בו עם הסימולטור המקומי שנבחר. כאן נשתמש ב-aer_simulator_statevector.

# We will start by using a local simulator
from qiskit_aer import AerSimulator

# Import Estimator, this time from Qiskit (we will import from IBM Quantum for real QPUs)
from qiskit.primitives import BackendEstimatorV2

# generate a simulator that mimics the real quantum system
backend_sim = AerSimulator.from_backend(backend)
estimator = BackendEstimatorV2(backend=backend_sim)

הגיע הזמן סוף סוף לממש VQE, תוך מזעור פונקציית העלות באמצעות ה-Hamiltonian שנבחר, ה-ansatz, האופטימייזר הקלאסי וה-BackendEstimator שלנו, המבוסס על ה-Backend האמיתי שבחרנו לשימוש הבא. שים לב שכאן בחרנו מספר קטן יחסית לאיטרציות המקסימליות. הסיבה לכך היא שאנו פשוט משתמשים בסימולטור לניפוי שגיאות. שלבי אופטימיזציה של VQE לרוב דורשים מאות איטרציות להתכנסות.

res = minimize(
cost_func,
x0,
args=(ansatz_isa, hamiltonian_isa, estimator),
method="cobyla",
options={"maxiter": 10, "disp": True},
)

print(getattr(res, "fun") - nuclear_repulsion)
print(res)
Return from COBYLA because the objective function has been evaluated MAXFUN times.
Number of function values = 10 Least value of F = -0.11556938907226563
The corresponding X is:
[4.11796514 4.52126324 0.69570423 4.12781503 6.55507846 1.80713073
0.9645473 6.23812214]

-0.8355383835212453
message: Return from COBYLA because the objective function has been evaluated MAXFUN times.
success: False
status: 3
fun: -0.11556938907226563
x: [ 4.118e+00 4.521e+00 6.957e-01 4.128e+00 6.555e+00
1.807e+00 9.645e-01 6.238e+00]
nfev: 10
maxcv: 0.0

הקוד הורץ כהלכה, אם כי לא התכנס — כפי שציפינו. נמשיך להריץ את החישוב על חומרה אמיתית, ואז נדון בפלטים. עבור Backends אמיתיים, נשתמש ב-IBM Quantum Estimator. נרצה להריץ זאת בתוך Session ובדרך כלל נרצה לציין אפשרויות עבור אותה Session.

from qiskit_ibm_runtime import QiskitRuntimeService, Session
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime.options import EstimatorOptions

בין היתר, שימוש ב-Session אומר שהמשימה שלנו תמתין בתור רק פעם אחת, בתחילה. האיטרציות הבאות של האופטימייזר הקלאסי לא ימתינו שוב בתור. ב-Session, נוכל להגדיר רמות עמידות ואופטימיזציה. כלים אלו חשובים מספיק כדי שנכלול סקירה קצרה של כל אחד ואת חשיבותו ב-VQE, עם קישורים ללמידה נוספת:

  • Sessions: VQE הוא מטבעו איטרטיבי, כאשר האופטימייזר הקלאסי בוחר פרמטרים ווריאציוניים חדשים, ובכך Gates חדשים נמצאים בשימוש, בכל ניסיון עוקב. ללא שימוש ב-sessions, הדבר עלול לגרום לזמן תור נוסף בין כל Circuit של ניסיון. עטיפת חישוב ה-VQE בתוך Session מביאה לתור ראשוני אחד בלבד לפני תחילת העבודה, ללא זמן תור נוסף בין שלבים ווריאציוניים. אסטרטגיה זו כבר שימשה בדוגמת השיעור הקודם, אך עשויה למלא תפקיד חשוב אף יותר בעת שינוי גיאומטריה. למידע נוסף על sessions, ראה בתיעוד מצבי הביצוע.

  • האופטימיזציה המובנית של Estimator: ב-Estimator ישנן אפשרויות מובנות לאופטימיזציה של חישוב. בהקשרים רבים (כולל Estimator), ההגדרות מוגבלות ל-0 ו-1, כאשר 0 מציין אין אופטימיזציה, ו-1 (ברירת המחדל) מציין אופטימיזציה מסוימת של המעגל שלך לחומרה שנבחרה. הקשרים אחרים מאפשרים הגדרות של 0, 1, 2, או 3. למידע נוסף על השיטות הספציפיות בהן נעשה שימוש בהגדרות שונות, ראה את התיעוד. כאן, למעשה נגדיר את האופטימיזציה ל-0, ונשתמש ב-'skip_transpilation = true', מכיוון שכבר ביצענו transpile למעגל שלנו באמצעות ה-pass manager שלמעלה, בחלק האופטימיזציה.

  • עמידות מובנית של Estimator: כמו באופטימיזציה, ל-Estimator יש הגדרות מובנות לעמידות מפני שגיאות, בהתאם לגישות שונות למיתון שגיאות. כדי ללמוד עוד על הגדרות רמת העמידות, עיין בתיעוד.

כדאי לציין שמיתון שגיאות ממלא תפקיד מורכב בהתכנסות של חישוב VQE. האופטימייזר הקלאסי מחפש במרחב הפרמטרים את הפרמטרים שממזערים את האנרגיה. כשאתה רחוק מאוד מהפרמטרים האופטימליים, שיפוע תלול עשוי להיות גלוי לאופטימייזר הקלאסי אפילו בנוכחות שגיאות. אך ככל שהחישוב מתכנס ואתה מתקרב לערכים האופטימליים, השיפוע הופך קטן יותר, ומוצף בקלות רבה יותר על ידי שגיאות. כמה מיתון שגיאות אתה רוצה להשתמש בו? באיזה שלבים בהתכנסות? אלו בחירות שעליך לעשות עבור מקרה השימוש הספציפי שלך.

עבור הרצת החומרה הראשונה הזו, הגדרנו עמידות ל-0 כדי לאפשר הרצה מהירה יחסית. לכל יישום רציני, תרצה להשתמש במיתון שגיאות. שים לב שבתא שלהלן יש שתי ערכות אפשרויות: (1) אפשרויות ל-Session, שכינינו "session_options", ו-(2) אפשרויות לאופטימייזר הקלאסי, שנקראות כאן פשוט "options".

estimator_options = EstimatorOptions(resilience_level=0, default_shots=2000)
with Session(backend=backend) as session:
estimator = Estimator(mode=session, options=estimator_options)

res = minimize(
cost_func,
x0,
args=(ansatz_isa, hamiltonian_isa, estimator),
method="cobyla",
options={"maxiter": 10, "disp": True},
)
Return from COBYLA because the objective function has been evaluated MAXFUN times.
Number of function values = 10 Least value of F = -0.11691688904
The corresponding X is:
[5.11796514 5.52126324 0.69570423 5.12781503 6.55507846 1.80713073
1.9645473 6.23812214]

תוכל לצפות בהתקדמות המשימה שלך בפלטפורמת IBM Quantum® תחת Workloads.

print(getattr(res, "fun") - nuclear_repulsion)
print(res)
-0.8368858834889796
message: Return from COBYLA because the objective function has been evaluated MAXFUN times.
success: False
status: 3
fun: -0.11691688904
x: [ 5.118e+00 5.521e+00 6.957e-01 5.128e+00 6.555e+00
1.807e+00 1.965e+00 6.238e+00]
nfev: 10
maxcv: 0.0

שלב 4: עיבוד לאחר ביצוע, החזרת תוצאה בפורמט קלאסי​

בואו נקדיש רגע לוודא שאנחנו מבינים את הפלטים האלה. פלט "fun" הוא הערך המינימלי שקיבלנו עבור פונקציית העלות (לא בהכרח הערך האחרון שחושב). זוהי האנרגיה הכוללת, כולל דחיית גרעין חיובית, ולכן הגדרנו גם את electron_energy.

במקרה לעיל, יש לנו הודעה שמספר הערכות הפונקציה המרבי חרג, ושמספר הערכות הפונקציה (nfev) היה 10. משמעות הדבר היא פשוט שקריטריונים אחרים להתכנסות האופטימיזציה לא התקיימו; במילים אחרות, אין סיבה לחשוב שמצאנו את אנרגיית מצב היסוד. זוהי גם משמעות success שהוא "False".

לבסוף, יש לנו x. זהו וקטור הפרמטרים הווריאציוניים. אלו הפרמטרים ששימשו בחישוב שהניב את פונקציית העלות המינימלית (ערך הציפייה של האנרגיה). שמונת הערכים הללו מתאימים לשמונת זוויות הסיבוב באותן Gates ב-ansatz שמקבלות זוויות סיבוב משתנות.

כל הכבוד! הרצת חישוב VQE על QPU של IBM Quantum!

בשיעור הבא, נראה כיצד לכוון את workstream הזה כך שיכלול משתנים ב-Hamiltonian שלך. בהקשר של בעיות כימיה קוונטית, הדבר עשוי להיות כרוך בשינוי גיאומטריה כדי לקבוע צורות של מולקולות או אתרי קשירה.

import qiskit
import qiskit_ibm_runtime

print(qiskit.version.get_version_info())
print(qiskit_ibm_runtime.version.get_version_info())
2.1.0
0.40.1