Krylov Quante-Diagonalisierung vun Gitter-Hamiltoniane
Nutzungsschätzung: 20 Minute op nem Heron r2 (HENGER: Dat es nor en Schätzung. Ding Laufzick kann anders sin.)
# 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
Hintergrund
Dat Tutorial zeich dir, wie mer dä Krylov Quantum Diagonalization Algorithm (KQD) em Kontext vun Qiskit-Mustere ömsetze kann. Do liers zeez övver de Theorie hinger däm Algorithmus un dann kriss de en Vörführung vun singer Usführung op enem QPU.
Övver all Diszipline hann mir Intress, Grundzustandseigenschafte vun Quantesysteme ze liere. Beispiele enthalde et Verstonn vun dä fundamentale Natur vun Partikel un Kräfte, et Vörhersare un Verstonn vum Verhalte vun komplexe Materiale un et Verstonn vun biochemische Interaktione un Reaktione. Wääje däm exponentielle Wachstum vum Hilbert-Ruum un dä Korrelation, die en verschränkte Systeme optrette, han klassische Algorithme Schwierigkeite, dat Problem för Quantesysteme vun wachsender Gröss ze löse. An enem Eng vum Spektrum es dä bestehende Ansatz, dä vun dä Quantehardware profitiert un sisch op variationelle Quantemethode konzentriert (zom Beispill variational quantum eigensolver). Die Technike stonn vör Herausforderunge met aktuelle Geräte wääje dä huhe Zahl vun Funktioneufrufe, die em Optimierungsprozess nötig sen, wat ne große Overhead an Ressource bringt, wann moderne Fehlerminderungstechnike engeföhrt wäde, un domet ehr Wirksamkeit op kleine Systeme beschränk. Am andere Eng vum Spektrum git et fehlertolerante Quantemethode met Leistungsgarantie (zom Beispill quantum phase estimation), die deef Schaltkreise bruche, die nor op enem fehlertolerante Gerät usgeföhrt wäde künne. Uss dänne Gründe stelle mir hee ne Quantealgorithmus vör, dä op Teilruummethode basiert (wie beschrevve en däm Övversechtspapier), dä Krylov Quante-Diagonalisierungsalgorithmus (KQD). Dä Algorithmus funktioneert gut bei große Maßstav [1] op bestehender Quantehardware, deilt ähnliche Leistungsgarantie wie Phase Estimation, es kompatibel met moderne Fehlerminderungstechnike un künnt Ergebnisse levere, die klassisch nit zogänglich sen.
Vöraussetzunge
Bevör de met däm Tutorial aafängs, sorg doför, dat de Folgendes installiert häs:
- Qiskit SDK v2.0 oder nöer, met Visualisierung
- Qiskit Runtime v0.22 oder nöer (
pip install qiskit-ibm-runtime)
Opbau
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
Schritt 1: Kartier klassische Ingänge op e Quanteproblem
Dä Krylov-Ruum
Dä Krylov-Ruum vun Ordnung es dä Ruum, dä vun Vektore opgespannt weed, die mer kritt, endemm mir höhere Potenze vun ener Matrix , bes zo , met enem Referenzvekter multipliziere.
Wann de Matrix dä Hamiltonian es, wäde mir dä entsprechende Ruum als Potenz-Krylov-Ruum bezeichne. Em Fall, wann dä Zickentwicklungsoperator es, dä vum Hamiltonian erzeugt weed, wäde mir dä Ruum als unitäre Krylov-Ruum bezeichne. Dä Potenz-Krylov-Teilruum, dä mir klassisch bruche, kann nit direkt op enem Quantecomputer erzeugt wäde, weil keine unitäre Operator es. Stattdesse künne mir dä Zickentwicklungsoperator bruche, vun däm mer zeige kann, dat hä ähnliche Konvergenzgarantie wäi de Potenzmethode gitt. Potenze vun wäde dann verschiedene Zickschritte .
Luur em Aanhang för en detaillierte Herleitung, wie dä unitäre Krylov-Ruum et erlaubt, niederenergetische Eigenzuständ genau darzestelle.
Krylov Quante-Diagonalisierungsalgorithmus
Gevve ene Hamiltonian , dä mir diagonalisiere welle, betrachte mir zeez dä entsprechende unitäre Krylov-Ruum . Et Ziel es et, en kompakte Darstellung vum Hamiltonian en ze finge, die mir als bezeichne. De Matrixelemente vun , de Projektion vum Hamiltonian em Krylov-Ruum, künne berechnet wäde, endemm mir de folgend Erwartungswerte berechne
Woh de Vektore vum unitäre Krylov-Ruum sen un de Vielfache vum Zickschritt sin, dä gewählt wood. Op enem Quantecomputer kann de Berechnung vun jedem Matrixelement met jedem Algorithmus gemaat wäde, dä et erlaubt, Övverlappunge zwesche Quantezuständ ze erhalde. Dat Tutorial konzentriert sisch op dä Hadamard-Test. Gevve dat de Dimension hät, hätt dä Hamiltonian, projiziert en dä Teilruum, Dimensione . Met klein genuch (normalerwies langt för de Konvergenz vun Schätzunge vun Eigenenergien) künne mir dann einfach dä projizierte Hamiltonian diagonalisiere. Ävver mir künne nit direkt diagonalisiere wääje dä Nit-Orthogonalität vun dä Krylov-Ruum-Vektore. Mir müsse ehr Övverlappunge messe un en Matrix konstruiere
Dat erlaubt oss, et Eigenwäätproblem en enem nit-orthogonale Ruum ze löse (och verallgemeinert Eigenwäätproblem genannt)
Mer kann dann Schätzunge vun dä Eigenwääte un Eigenzuständ vun erhalde, endemm mer op die vun luurt. Zom Beispill kreijt mer de Schätzung vun dä Grundzustandsenergie, endemm mer dä kleenste Eigenwäät nimmp un dä Grundzustand us däm entsprechende Eigenvekter . De Koeffiziente en bestimme dä Bidrag vun dä verschiedene Vektore, die opspanne.

De Figur zeich en Schaltkreisdarstellung vum modifizierte Hadamard-Test, en Methode, die benutzt weed, öm de Övverlappung zwesche verschiedene Quantezuständ ze berechne. För jedes Matrixelement weed ne Hadamard-Test zwesche däm Zustand , dörchgeföhrt. Dat weed en dä Figur dörch et Farvschema för de Matrixelemente un de entsprechende , Operatione hervorgehove. Also es en Menge vun Hadamard-Tests för all mögliche Kombinatione vun Krylov-Ruum-Vektore nötig, öm all Matrixelemente vum projizierte Hamiltonian ze berechne. De bovverste Leitung em Hadamard-Test-Schaltkreis es e Ancilla-Qubit, dat entweder en dä X- oder Y-Basis gemesse weed, singer Erwartungswäät bestimmp dä Wäät vun dä Övverlappung zwesche dä Zuständ. De ungerste Leitung stellt all Qubits vum System-Hamiltonian dar. De Operation bereidt et System-Qubit em Zustand vör, gesteuert dörch dä Zustand vum Ancilla-Qubit (ähnlich för ) un de Operation stellt de Pauli-Zerlegung vum System-Hamiltonian dar. En detaillierter Herleitung vun dä Operatione, die vum Hadamard-Test berechnet wäde, es onge gevve.
Hamiltonian definiere
Loss oss dä Heisenberg-Hamiltonian för Qubits op ener lineare Kette betrachte:
# 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)]
Parameter för dä Algorithmus festlääje
Mir wähle heuristisch ne Wäät för dä Zickschritt dt (basierend op bovverste Grenze vun dä Hamiltonian-Norm). Ref [2] hät gezeich, dat ne genuch kleine Zickschritt es, un dat et bes zo enem Punkt besser es, dä Wäät z'ungerschätze statt övverschätze, weil Övverschätze Bidräch vun huhenergetische Zuständ zolööt, die söjar dä optimale Zustand em Krylov-Ruum verdarve künne. Anderersits föhrt de Wahl vun ze klein dazu, dat de Konditionierung vum Krylov-Teilruum schlächter weed, weil de Krylov-Basisvektore sisch weniger vun Zickschritt ze Zickschritt ungerscheide.
# 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)
Un setz ander Parameter vum Algorithmus. För et Tutorial beschränke mir oss op de Nutzung vun enem Krylov-Ruum met nor fünf Dimensione, wat ziemlisch beschränkend es.
# Set parameters for quantum Krylov algorithm
krylov_dim = 5 # size of Krylov subspace
num_trotter_steps = 6
dt_circ = dt / num_trotter_steps
Zustandsvörbereitung
Wähl ne Referenzzustand , dä irgendwie Övverlappung met däm Grundzustand hät. För dä Hamiltonian bruche mir dä Zustand met ener Anregung em mittlere Qubit als unse Referenzzustand.
qc_state_prep = QuantumCircuit(n_qubits)
qc_state_prep.x(int(n_qubits / 2) + 1)
qc_state_prep.draw("mpl", scale=0.5)
Zickentwicklung
Mir künne dä Zickentwicklungsoperator, dä vun enem gevvene Hamiltonian erzeugt weed, realisiere: övver de Lie-Trotter-Approximation.
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-Test

Woh eine vun dä Terme en dä Zerlegung vum Hamiltonian es un , gesteuerte Operatione sen, die , Vektore vum unitäre Krylov-Ruum vörbereide, met . Öm ze messe, wende zeez aan...
... dann mess:
Us dä Identität . Ähnlich levvt de Messung vun
## 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
Dä Hadamard-Test-Schaltkreis kann ne deefe Schaltkreis sin, sobald mer en native Gates zerläje (wat noch mieh zunimmp, wann mer de Topologie vum Gerät berücksichtige)
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