تقطير كريلوف الكمي لهاميلتونيات الشبكة
تقدير الاستخدام: 20 دقيقة على Heron r2 (ملاحظة: هذا تقدير فحسب. قد يختلف وقت التشغيل الفعلي لديك.)
# This cell is hidden from users – it disables some lint rules
# ruff: noqa: E402 E722 F601
الخلفية النظرية
يوضّح هذا البرنامج التعليمي كيفية تنفيذ خوارزمية تقطير كريلوف الكمي (KQD) في سياق أنماط Qiskit. ستتعرّف أولاً على النظرية التي تقوم عليها الخوارزمية، ثم ترى عرضاً توضيحياً لتنفيذها على وحدة معالجة كمية (QPU).
في شتّى التخصصات، نسعى إلى التعرّف على خصائص الحالة الأساسية للأنظمة الكمية. ومن الأمثلة على ذلك: فهم الطبيعة الجوهرية للجسيمات والقوى، والتنبؤ بسلوك المواد المعقدة وفهمه، وفهم التفاعلات والتفاعلات الكيميائية الحيوية. ونظراً للنمو الأسي لفضاء هيلبرت والترابط الناشئ في الأنظمة المتشابكة، تعجز الخوارزميات الكلاسيكية عن حل هذه المسألة في الأنظمة الكمية ذات الأحجام المتزايدة. في أحد طرفي الطيف، ثمة النهج القائم على الاستفادة من العتاد الكمي من خلال الأساليب الكمية التبايُنية (مثل محاكي القيم الذاتية الكمي التبايُني). وتواجه هذه التقنيات تحديات مع الأجهزة الحالية بسبب الحاجة إلى عدد كبير من استدعاءات الدوال في عملية التحسين، مما يُضيف عبئاً كبيراً في الموارد عند إدخال تقنيات متقدمة لتخفيف الأخطاء، وبذلك تتقلص فعاليتها لتقتصر على الأنظمة الصغيرة. وفي الطرف الآخر، توجد الأساليب الكمية المتسامحة مع الأخطاء ذات ضمانات الأداء (مثل تقدير الطور الكمي)، والتي تستلزم دوائر عميقة لا يمكن تنفيذها إلا على جهاز متسامح مع الأخطاء. لهذه الأسباب، نقدّم هنا خوارزمية كمية تستند إلى أساليب الفضاء الجزئي (كما هو موصوف في ورقة المراجعة هذه)، وهي خوارزمية تقطير كريلوف الكمي (KQD). تُحقق هذه الخوارزمية أداءً جيداً على نطاق واسع [1] على العتاد الكمي الحالي، وتشترك في ضمانات أداء مماثلة لتقدير الطور، وهي متوافقة مع تقنيات تخفيف الأخطاء المتقدمة، ويمكنها تقديم نتائج يتعذّر الحصول عليها كلاسيكياً.
المتطلبات
قبل البدء بهذا البرنامج التعليمي، تأكد من تثبيت ما يلي:
- Qiskit SDK الإصدار 2.0 أو أحدث، مع دعم التصوير البياني
- Qiskit Runtime الإصدار 0.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: تعيين المدخلات الكلاسيكية إلى مسألة كمية
فضاء كريلوف
فضاء كريلوف من الرتبة هو الفضاء الممتد بالمتجهات الناتجة عن ضرب القوى المتصاعدة لمصفوفة ، حتى ، في متجه مرجعي .
إذا كانت المصفوفة هي هاميلتوني ، فسنشير إلى الفضاء المقابل باسم فضاء كريلوف القوّي . أما في حالة كون هي عامل التطور الزمني المولَّد بواسطة الهاميلتوني ، فسنشير إلى الفضاء باسم فضاء كريلوف الأحادي . لا يمكن توليد الفضاء الجزئي لكريلوف القوّي الذي نستخدمه كلاسيكياً بصورة مباشرة على حاسوب كمي، إذ إن ليست عاملاً أحادياً. غير أنه يمكننا استخدام عامل التطور الزمني الذي يمكن إثبات أنه يُعطي ضمانات تقارب مماثلة للطريقة القوّية. وتصبح قوى حينئذٍ خطوات زمنية مختلفة .
راجع الملحق للاطلاع على اشتقاق تفصيلي لكيفية تمكين فضاء كريلوف الأحادي من تمثيل الحالات الذاتية منخفضة الطاقة بدقة.
خوارزمية تقطير كريلوف الكمي
بالنظر إلى هاميلتوني المراد قطره، نبدأ بالنظر في فضاء كريلوف الأحادي المقابل . الهدف هو إيجاد تمثيل مضغوط للهاميلتوني في ، وهو ما سنشير إليه بـ . يمكن حساب عناصر المصفوفة لـ ، أي إسقاط الهاميلتوني في فضاء كريلوف، عن طريق حساب القيم المتوقعة التالية
حيث هي متجهات فضاء كريلوف الأحادي و هي مضاعفات خطوة الزمن المختارة. على الحاسوب الكمي، يمكن حساب كل عنصر من عناصر المصفوفة بأي خوارزمية تتيح الحصول على التداخل بين الحالات الكمية. يركز هذا البرنامج التعليمي على اختبار Hadamard. وبالنظر إلى أن ذات بُعد ، سيكون للهاميلتوني المُسقَط في الفضاء الجزئي أبعاد . مع صغر بما فيه الكفاية (يكفي عموماً للحصول على تقارب في تقديرات طاقات القيم الذاتية)، يمكننا حينئذٍ قطر الهاميلتوني المُسقَط بسهولة. غير أننا لا نستطيع قطر مباشرةً بسبب عدم تعامد متجهات فضاء كريلوف. سيتوجب علينا قياس تداخلاتها وبناء مصفوفة
يتيح لنا ذلك حل مسألة القيمة الذاتية في فضاء غير متعامد (تُعرف أيضاً بمسألة القيمة الذاتية المعممة)
يمكن حينئذٍ الحصول على تقديرات للقيم الذاتية والحالات الذاتية لـ بالنظر في مقابلاتها في . على سبيل المثال، يُحصَل على تقدير طاقة الحالة الأساسية بأخذ أصغر قيمة ذاتية والحالة الأساسية من المتجه الذاتي المقابل . تحدد المعاملات في مساهمة المتجهات المختلفة التي تمتد إلى .

يُظهر الشكل تمثيلاً دائرياً لاختبار Hadamard المعدَّل، وهو أسلوب يُستخدم لحساب التداخل بين الحالات الكمية المختلفة. لكل عنصر من عناصر المصفوفة ، يُجرى اختبار Hadamard بين الحالتين و . يُبرز ذلك مخطط الألوان لعناصر المصفوفة والعمليتين و المقابلتين. وبذلك، يلزم إجراء مجموعة من اختبارات Hadamard لجميع التوليفات الممكنة لمتجهات فضاء كريلوف لحساب جميع عناصر مصفوفة الهاميلتوني المُسقَطة . السلك العلوي في دائرة اختبار Hadamard هو كيوبت مساعد (ancilla) يُقاس إما في الأساس X أو الأساس Y، وتحدد قيمته المتوقعة قيمة التداخل بين الحالتين. يمثل السلك السفلي جميع كيوبتات هاميلتوني النظام. تُعدّ عملية كيوبت النظام في الحالة بشكل مشروط بحالة الكيوبت المساعد (وبالمثل لعملية )، فيما تمثل العملية تحليل باولي لهاميلتوني النظام . يُعطى اشتقاق أكثر تفصيلاً للعمليات التي يحسبها اختبار Hadamard أدناه.
تعريف الهاميلتوني
لنأخذ هاميلتوني هايزنبرغ لـ كيوبتاً على سلسلة خطية:
# 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
تحضير الحالة
اختر حالة مرجعية تمتلك بعض التداخل مع الحالة الأساسية. لهذا الهاميلتوني، نستخدم حالة ذات إثارة في الكيوبت الأوسط كحالتنا المرجعية.
qc_state_prep = QuantumCircuit(n_qubits)
qc_state_prep.x(int(n_qubits / 2) + 1)
qc_state_prep.draw("mpl", scale=0.5)
التطور الزمني
يمكننا تحقيق عامل التطور الزمني المولَّد بواسطة هاميلتوني معطى: عبر تقريب Lie-Trotter.
t = Parameter("t")
## Create the time-evo op circuit
evol_gate = PauliEvolutionGate(
H_op, time=t, synthesis=LieTrotter(reps=num_trotter_steps)
)
qr = QuantumRegister(n_qubits)
qc_evol = QuantumCircuit(qr)
qc_evol.append(evol_gate, qargs=qr)
<qiskit.circuit.instructionset.InstructionSet at 0x11eef9be0>
اختبار Hadamard

حيث هو أحد حدود تحليل الهاميلتوني و و هي عمليات مشروطة تُعدّ متجهات و من فضاء كريلوف الأحادي، مع . لقياس ، نطبّق أولاً ...
... ثم القياس:
من المتطابقة . وبالمثل، يُعطي قياس
## Create the time-evo op circuit
evol_gate = PauliEvolutionGate(
H_op, time=dt, synthesis=LieTrotter(reps=num_trotter_steps)
)
## Create the time-evo op dagger circuit
evol_gate_d = PauliEvolutionGate(
H_op, time=dt, synthesis=LieTrotter(reps=num_trotter_steps)
)
evol_gate_d = evol_gate_d.inverse()
# Put pieces together
qc_reg = QuantumRegister(n_qubits)
qc_temp = QuantumCircuit(qc_reg)
qc_temp.compose(qc_state_prep, inplace=True)
for _ in range(num_trotter_steps):
qc_temp.append(evol_gate, qargs=qc_reg)
for _ in range(num_trotter_steps):
qc_temp.append(evol_gate_d, qargs=qc_reg)
qc_temp.compose(qc_state_prep.inverse(), inplace=True)
# Create controlled version of the circuit
controlled_U = qc_temp.to_gate().control(1)
# Create hadamard test circuit for real part
qr = QuantumRegister(n_qubits + 1)
qc_real = QuantumCircuit(qr)
qc_real.h(0)
qc_real.append(controlled_U, list(range(n_qubits + 1)))
qc_real.h(0)
print(
"Circuit for calculating the real part of the overlap in S via Hadamard test"
)
qc_real.draw("mpl", fold=-1, scale=0.5)
Circuit for calculating the real part of the overlap in S via Hadamard test
يمكن أن تكون دائرة اختبار Hadamard دائرة عميقة بمجرد تحليلها إلى البوابات الأصلية (وهو ما سيزداد أكثر عند مراعاة طولوجيا الجهاز)
print(
"Number of layers of 2Q operations",
qc_real.decompose(reps=2).depth(lambda x: x[0].num_qubits == 2),
)
Number of layers of 2Q operations 112753