אלכסון קוונטי קרילוב של המילטוניאנים במבנה רשת
הערכת שימוש: 20 דקות על Heron r2 (הערה: זוהי הערכה בלבד. זמן הריצה שלך עשוי להשתנות.)
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-ibm-runtime scipy sympy
# This cell is hidden from users – it disables some lint rules
# ruff: noqa: E402 E722 F601
רקע
מדריך זה מדגים כיצד ליישם את אלגוריתם האלכסון הקוונטי קרילוב (KQD) בהקשר של תבניות Qiskit. תחילה תלמד על התיאוריה מאחורי האלגוריתם ולאחר מכן תראה הדגמה של ביצועו על QPU.
בתחומים שונים, אנחנו מעוניינים ללמוד תכונות של מצב היסוד של מערכות קוונטיות. דוגמאות כוללות הבנת האופי הבסיסי של חלקיקים וכוחות, חיזוי והבנת התנהגות של חומרים מורכבים והבנת אינטראקציות וריאקציות ביוכימיות. בשל הצמיחה האקספוננציאלית של מרחב הילברט והקורלציה הנוצרת במערכות שזורות, אלגוריתמים קלאסיים מתקשים לפתור בעיה זו עבור מערכות קוונטיות בגודל הולך וגדל. בקצה אחד של הספקטרום נמצאת הגישה הקיימת שמנצלת את החומרה הקוונטית המתמקדת בשיטות קוונטיות ו ריאציוניות (לדוגמה, variational quantum eigensolver). טכניקות אלה מתמודדות עם אתגרים במכשירים הנוכחיים בגלל המספר הגבוה של קריאות פונקציה הנדרשות בתהליך האופטימיזציה, מה שמוסיף עומס משאבים גדול ברגע שטכניקות מתקדמות להפחתת שגיאות מוכנסות, ובכך מגביל את יעילותן למערכות קטנות. בקצה השני של הספקטרום, ישנן שיטות קוונטיות עמידות בתקלות עם ערבויות ביצועים (לדוגמה, quantum phase estimation), הדורשות מעגלים עמוקים שניתן לבצע רק על מכשיר עמיד בתקלות. מסיבות אלה, אנחנו מציגים כאן אלגוריתם קוונטי המבוסס על שיטות תת-מרחב (כפי שמתואר במאמר סקירה זה), אלגוריתם האלכסון הקוונטי קרילוב (KQD). אלגוריתם זה פועל היטב בקנה מידה גדול [1] על חומרה קוונטית קיימת, חולק ערבויות ביצועים דומות להערכת פאזה, תואם לטכניקות מתקדמות להפחתת שגיאות, ויכול לספק תוצאות שאינן נגישות באופן קלאסי.
דרישות
לפני התחלת מדריך זה, וודא שיש לך את הדברי ם הבאים מותקנים:
- Qiskit SDK v2.0 או מאוחר יותר, עם תמיכה בvisualization
- Qiskit Runtime v0.22 או מאוחר יותר (
pip install qiskit-ibm-runtime)
הגדרה
import numpy as np
import scipy as sp
import matplotlib.pylab as plt
from typing import Union, List
import itertools as it
import copy
from sympy import Matrix
import warnings
warnings.filterwarnings("ignore")
from qiskit.quantum_info import SparsePauliOp, Pauli, StabilizerState
from qiskit.circuit import Parameter, IfElseOp
from qiskit import QuantumCircuit, QuantumRegister
from qiskit.circuit.library import PauliEvolutionGate
from qiskit.synthesis import LieTrotter
from qiskit.transpiler import Target, CouplingMap
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import (
QiskitRuntimeService,
EstimatorV2 as Estimator,
)
def solve_regularized_gen_eig(
h: np.ndarray,
s: np.ndarray,
threshold: float,
k: int = 1,
return_dimn: bool = False,
) -> Union[float, List[float]]:
"""
Method for solving the generalized eigenvalue problem with regularization
Args:
h (numpy.ndarray):
The effective representation of the matrix in the Krylov subspace
s (numpy.ndarray):
The matrix of overlaps between vectors of the Krylov subspace
threshold (float):
Cut-off value for the eigenvalue of s
k (int):
Number of eigenvalues to return
return_dimn (bool):
Whether to return the size of the regularized subspace
Returns:
lowest k-eigenvalue(s) that are the solution of the regularized generalized eigenvalue problem
"""
s_vals, s_vecs = sp.linalg.eigh(s)
s_vecs = s_vecs.T
good_vecs = np.array(
[vec for val, vec in zip(s_vals, s_vecs) if val > threshold]
)
h_reg = good_vecs.conj() @ h @ good_vecs.T
s_reg = good_vecs.conj() @ s @ good_vecs.T
if k == 1:
if return_dimn:
return sp.linalg.eigh(h_reg, s_reg)[0][0], len(good_vecs)
else:
return sp.linalg.eigh(h_reg, s_reg)[0][0]
else:
if return_dimn:
return sp.linalg.eigh(h_reg, s_reg)[0][:k], len(good_vecs)
else:
return sp.linalg.eigh(h_reg, s_reg)[0][:k]
def single_particle_gs(H_op, n_qubits):
"""
Find the ground state of the single particle(excitation) sector
"""
H_x = []
for p, coeff in H_op.to_list():
H_x.append(set([i for i, v in enumerate(Pauli(p).x) if v]))
H_z = []
for p, coeff in H_op.to_list():
H_z.append(set([i for i, v in enumerate(Pauli(p).z) if v]))
H_c = H_op.coeffs
print("n_sys_qubits", n_qubits)
n_exc = 1
sub_dimn = int(sp.special.comb(n_qubits + 1, n_exc))
print("n_exc", n_exc, ", subspace dimension", sub_dimn)
few_particle_H = np.zeros((sub_dimn, sub_dimn), dtype=complex)
sparse_vecs = [
set(vec) for vec in it.combinations(range(n_qubits + 1), r=n_exc)
] # list all of the possible sets of n_exc indices of 1s in n_exc-particle states
m = 0
for i, i_set in enumerate(sparse_vecs):
for j, j_set in enumerate(sparse_vecs):
m += 1
if len(i_set.symmetric_difference(j_set)) <= 2:
for p_x, p_z, coeff in zip(H_x, H_z, H_c):
if i_set.symmetric_difference(j_set) == p_x:
sgn = ((-1j) ** len(p_x.intersection(p_z))) * (
(-1) ** len(i_set.intersection(p_z))
)
else:
sgn = 0
few_particle_H[i, j] += sgn * coeff
gs_en = min(np.linalg.eigvalsh(few_particle_H))
print("single particle ground state energy: ", gs_en)
return gs_en
שלב 1: מיפוי קלטים קלאסיים לבעיה קוונטית
מרחב קרילוב
מרחב קרילוב מסדר הוא המרחב הנפרש על ידי וקטורים המתקבלים על ידי הכפלת חזקות גבוהות יותר של מטריצה , עד , עם וקטור ייחוס .
אם המטריצה היא ההמילטוניאן , נתייחס למרחב המתאים כמרחב קרילוב החזקה . במקרה שבו הוא אופרטור האבולוציה בזמן שנוצר על ידי ההמילטוניאן , נתייחס למרחב כמרחב קרילוב יוניטרי . תת-המרחב קרילוב החזקה שבו אנחנו משתמשים באופן קלאסי אינו יכול להיווצר ישירות על מחשב קוונטי מכיוון ש- אינו אופרטור יוניטרי. במקום זאת, אנחנו יכולים להשתמש באופרטור האבולוציה בזמן שניתן להראות שהוא נותן ערבויות התכנסות דומות לשיטת החזקה. חזקות של הופכות לצעדי זמן שונים .
ראה את הנספח עבור גזירה מפורטת של איך מרחב קרילוב היוניטרי מאפשר לייצג מצבים עצמיים באנרגיה נמוכה בצורה מדויקת.
אלגוריתם האלכסון הקוונטי קרילוב
בהינתן המילטוניאן שאנו רוצים לאלכסן, תחילה נשקול את מרחב קרילוב היוניטרי המתאים . המטרה היא למצוא ייצוג קומפקטי של ההמילטוניאן ב-, שאליו נתייחס כ-. האלמנטים המטריציאליים של , ההקרנה של ההמילטוניאן במרחב קרילוב, ניתן לחשב על ידי חישוב ערכי התצפית הבאים
כאשר הם הוקטורים של מרחב קרילוב היוניטרי ו- הם הכפולות של צעד הזמן שנבחר. על מחשב קוונטי, החישוב של כל אלמנט מטריציאלי ניתן לבצע עם כל אלגוריתם המאפשר לקבל חפיפה בין מצבים קוונטיים. מדריך זה מתמקד במבחן הדאמאר. בהינתן ש- בעל ממד , ההמילטוניאן המוקרן לתוך תת-המרחב יהיה בעל ממדים . עם מספיק קטן (בדרך כלל מספיק כדי להשיג התכנסות של הערכות של אנרגיות עצמיות) אנחנו יכולים לאחר מכן בקלות לאלכסן את ההמילטוניאן המוקרן . עם זאת, אנחנו לא יכולים לאלכסן ישירות את בגלל אי-האורתוגונליות של וקטורי מרחב קרילוב. נצטרך למדוד את החפיפות שלהם ולבנות מטריצה
זה מאפשר לנו לפתור את בעיית הערך העצמי במרחב לא אורתוגונלי (שנקראת גם בעיית ערך עצמי מוכללת)
לאחר מכן ניתן להשיג הערכות של הערכים העצמיים והמצבים העצמיים של על ידי התבוננות באלה של . לדוגמה, ההערכה של אנרגיית מצב היסוד מתקבלת על ידי לקיחת הערך העצמי הקטן ביותר ומצב היסוד מהווקטור העצמי המתאים . המקדמים ב- קובעים את התרומה של הוקטורים השונים הפורשים את .

האיור מציג ייצוג מעגל של מבחן הדאמאר המותאם, שיטה המשמשת לחישוב החפיפה בין מצבים קוונטיים שונים. עבור כל אלמנט מטריציאלי , מתבצע מבחן הדאמאר בין המצב , . זה מודגש באיור על ידי ערכת הצבעים עבור האלמנטים המטריציאליים והפעולות המתאימות , . לפיכך, נדרש סט של מבחני הדאמאר עבור כל השילובים האפשריים של וקטורי מרחב קרילוב כדי לחשב את כל האלמנטים המטריציאליים של ההמילטוניאן המוקרן . החוט העליון במעגל מבחן הדאמאר הוא qubit עזר שנמדד בבסיס X או Y, ערך התצפית שלו קובע את הערך של החפיפה בין המצבים. החוט התחתון מייצג את כל ה-qubits של ההמילטוניאן של המערכת. הפעולה מכינה את qubit המערכת במצב מותנה במצב של qubit העזר (באופן דומה עבור ) והפעולה מייצגת פירוק פאולי של ההמילטוניאן של המערכת . גזירה מפורטת יותר של הפעולות המחושבות על ידי מבחן הדאמאר ניתנת להלן.
הגדרת המילטוניאן
בואו נשקול את המילטוניאן הייזנברג עבור qubits על שרשרת לינארית:
# Define problem Hamiltonian.
n_qubits = 30
J = 1 # coupling strength for ZZ interaction
# Define the Hamiltonian:
H_int = [["I"] * n_qubits for _ in range(3 * (n_qubits - 1))]
for i in range(n_qubits - 1):
H_int[i][i] = "Z"
H_int[i][i + 1] = "Z"
for i in range(n_qubits - 1):
H_int[n_qubits - 1 + i][i] = "X"
H_int[n_qubits - 1 + i][i + 1] = "X"
for i in range(n_qubits - 1):
H_int[2 * (n_qubits - 1) + i][i] = "Y"
H_int[2 * (n_qubits - 1) + i][i + 1] = "Y"
H_int = ["".join(term) for term in H_int]
H_tot = [(term, J) if term.count("Z") == 2 else (term, 1) for term in H_int]
# Get operator
H_op = SparsePauliOp.from_list(H_tot)
print(H_tot)
[('ZZIIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IZZIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIZZIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIZZIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIZZIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIZZIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIZZIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIZZIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIZZIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIZZIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIZZIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIZZIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIZZIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIZZIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIZZIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIZZIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIZZIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIZZIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIZZIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIZZIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIZZIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIZZIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIZZIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIZZIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIZZIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIZZIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIZZII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIZZI', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ', 1), ('XXIIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IXXIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIXXIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIXXIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIXXIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIXXIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIXXIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIXXIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIXXIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIXXIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIXXIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIXXIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIXXIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIXXIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIXXIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIXXIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIXXIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIXXIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIXXIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIXXIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIXXIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIXXIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIXXIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIXXIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIXXIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIXXIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIXXII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIXXI', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIIXX', 1), ('YYIIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IYYIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIYYIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIYYIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIYYIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIYYIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIYYIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIYYIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIYYIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIYYIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIYYIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIYYIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIYYIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIYYIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIYYIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIYYIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIYYIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIYYIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIYYIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIYYIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIYYIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIYYIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIYYIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIYYIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIYYIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIYYIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIYYII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIYYI', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIIYY', 1)]
הגדרת פרמטרים לאלגוריתם
אנחנו בוחרים בצורה היוריסטית ערך עבור צעד הזמן dt (בהתבסס על גבולות עליונים על הנורמה של ההמילטוניאן). הפניה [2] הראתה שצעד זמן קטן מספיק הוא , ושעדיף עד נקודה מסוימת להעריך נמוך את הערך הזה מאשר להעריך גבוה, מכיוון שהערכת יתר יכולה לאפשר תרומות ממצבי אנרגיה גבוהה להשחית אפילו את המצב האופטימלי במרחב קרילוב. מצד שני, בחירת קטן מדי מובילה להתניה גרועה יותר של תת-מרחב קרילוב, מכיוון שוקטורי הבסיס קרילוב שונים פחות מצעד זמן לצעד זמן.
# Get Hamiltonian restricted to single-particle states
single_particle_H = np.zeros((n_qubits, n_qubits))
for i in range(n_qubits):
for j in range(i + 1):
for p, coeff in H_op.to_list():
p_x = Pauli(p).x
p_z = Pauli(p).z
if all(
p_x[k] == ((i == k) + (j == k)) % 2 for k in range(n_qubits)
):
sgn = (
(-1j) ** sum(p_z[k] and p_x[k] for k in range(n_qubits))
) * ((-1) ** p_z[i])
else:
sgn = 0
single_particle_H[i, j] += sgn * coeff
for i in range(n_qubits):
for j in range(i + 1, n_qubits):
single_particle_H[i, j] = np.conj(single_particle_H[j, i])
# Set dt according to spectral norm
dt = np.pi / np.linalg.norm(single_particle_H, ord=2)
dt
np.float64(0.10833078115826875)
והגדר פרמטרים אחרים של האלגוריתם. לצורך מדריך זה, נגביל את עצמנו לשימוש במרחב קרילוב עם חמישה ממדים בלבד, שהוא די מגביל.
# Set parameters for quantum Krylov algorithm
krylov_dim = 5 # size of Krylov subspace
num_trotter_steps = 6
dt_circ = dt / num_trotter_steps