Pepenjuruan kuantum Krylov berasaskan sampel bagi model kekisi fermionik
Anggaran penggunaan: Sembilan saat pada pemproses Heron r2 (NOTA: Ini hanyalah anggaran. Masa jalan anda mungkin berbeza.)
Hasil pembelajaranβ
-
Cara menggunakan SQD Qiskit addon untuk menganggarkan tenaga keadaan dasar model kekisi menggunakan rentetan bit yang disampling daripada unit pemprosesan kuantum (QPU).
-
Cara menggunakan ffsim untuk membina litar evolusi masa bagi simulasi fermionic.
-
Cara menggabungkan sampel daripada pelbagai litar untuk pasca-pemprosesan dengan algoritma pepenjuruan Krylov berasaskan sampel (SKQD).
Prasyaratβ
Latar belakangβ
Tutorial ini menunjukkan cara menggunakan pepenjuruan kuantum berasaskan sampel (SQD) untuk menganggarkan tenaga keadaan dasar bagi model kekisi fermionic. Khususnya, kami mengkaji model Anderson impuriti tunggal satu dimensi (SIAM), yang digunakan untuk menerangkan impuriti magnetik yang tertanam dalam logam.
Tutorial ini mengikuti aliran kerja yang serupa dengan tutorial berkaitan Pepenjuruan kuantum berasaskan sampel bagi Hamiltonian kimia. Namun, perbezaan utama terletak pada cara litar kuantum dibina. Tutorial yang lain menggunakan ansatz variasi heuristik, yang menarik untuk Hamiltonian kimia dengan berpotensi jutaan sebutan interaksi. Sebaliknya, tutorial ini menggunakan litar yang menghampiri evolusi masa oleh Hamiltonian. Litar sedemikian boleh menjadi sangat dalam, menjadikan pendekatan ini lebih sesuai untuk aplikasi pada model kekisi. Vektor keadaan yang disediakan oleh litar-litar ini membentuk asas bagi subruang Krylov, dan hasilnya, algoritma terbukti dan cekap menumpu ke keadaan dasar, di bawah andaian yang sesuai.
Pendekatan yang digunakan dalam tutorial ini boleh dilihat sebagai gabungan teknik yang digunakan dalam SQD dan pepenjuruan kuantum Krylov (KQD). Pendekatan gabungan ini kadang-kadang dirujuk sebagai pepenjuruan kuantum Krylov berasaskan sampel (SQKD). Lihat Pepenjuruan kuantum Krylov bagi Hamiltonian kekisi untuk tutorial tentang kaedah KQD.
Tutorial ini berdasarkan kerja "Quantum-Centric Algorithm for Sample-Based Krylov Diagonalization", yang boleh dirujuk untuk maklumat lanjut.
Model Anderson impuriti tunggal (SIAM)β
Hamiltonian SIAM satu dimensi ialah jumlah tiga sebutan:
di mana
Di sini, ialah operator penciptaan/pemusnahan fermionic bagi tapak bath/takungan ke- dengan spin , ialah operator penciptaan/pemusnahan bagi mod impuriti, dan . , , dan ialah nombor nyata yang menerangkan interaksi lompatan, interaksi pada tapak (on-site), hibridasi, dan ialah nombor nyata yang menentukan potensi kimia.
Perhatikan bahawa Hamiltonian ini ialah contoh khusus bagi Hamiltonian elektron interaksi generik,
di mana terdiri daripada sebutan satu badan, yang bersifat kuadratik dalam operator penciptaan dan pemusnahan fermionic, dan terdiri daripada sebutan dua badan, yang bersifat kuartik. Untuk SIAM,
dan mengandungi selebihnya sebutan dalam Hamiltonian. Bagi mewakili Hamiltonian secara aturcara, kami menyimpan matriks dan tensor .
Asas kedudukan dan momentumβ
Disebabkan simetri translasi hampir dalam , kami tidak menjangkakan keadaan dasar akan jarang dalam asas kedudukan (asas orbital di mana Hamiltonian ditentukan di atas). Prestasi SQD dijamin hanya jika keadaan dasar adalah jarang, iaitu ia mempunyai berat ketara hanya pada sebilangan kecil keadaan asas pengiraan. Untuk meningkatkan kejarangan keadaan dasar, kami menjalankan simulasi dalam asas orbital di mana adalah pepenjuru. Kami menyebut asas ini sebagai asas momentum. Kerana ialah Hamiltonian fermionic kuadratik, ia boleh dipepenjurukan dengan cekap melalui putaran orbital.
Evolusi masa hampiran oleh Hamiltonianβ
Untuk menghampiri evolusi masa oleh Hamiltonian, kami menggunakan penguraian Trotter-Suzuki peringkat kedua,
Di bawah transformasi Jordan-Wigner, evolusi masa oleh bersamaan dengan satu gate CPhase antara orbital spin-atas dan spin-bawah pada tapak impuriti. Kerana ialah Hamiltonian fermionic kuadratik, evolusi masa oleh bersamaan dengan putaran orbital.
Keadaan asas Krylov , di mana ialah dimensi subruang Krylov, dibentuk melalui penggunaan berulang satu langkah Trotter, jadi
Dalam aliran kerja berasaskan SQD berikut, kami akan mengambil sampel daripada set litar ini dan memproses gabungan set rentetan bit dengan SQD. Pendekatan ini berbeza dengan yang digunakan dalam tutorial berkaitan Pepenjuruan kuantum berasaskan sampel bagi Hamiltonian kimia, di mana sampel diambil daripada satu litar variasi heuristik.
Keperluanβ
Sebelum memulakan tutorial ini, pastikan anda telah memasang yang berikut:
-
Qiskit SDK v1.0 atau lebih baru, dengan sokongan visualisasi
-
Qiskit Runtime v0.22 atau lebih baru (
pip install qiskit-ibm-runtime) -
SQD Qiskit addon v0.14 atau lebih baru (
pip install qiskit-addon-sqd) -
ffsim v0.0.72 atau lebih baru (
pip install ffsim)
Contoh simulator berskala kecilβ
Langkah 1: Petakan masalah ke Circuit kuantumβ
Pertama, kami menjana Hamiltonian SIAM dalam asas kedudukan. Hamiltonian diwakili oleh matriks dan tensor . Kemudian, kami memutarnya ke asas momentum. Dalam asas kedudukan, kami meletakkan impuriti di tapak pertama. Namun, apabila kami berputar ke asas momentum, kami memindahkan impuriti ke tapak tengah untuk memudahkan interaksi dengan orbital lain.
# Added by doQumentation β installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"ffsim": "ffsim", "matplotlib": "matplotlib", "numpy": "numpy", "pyscf": "pyscf", "qiskit": "qiskit", "qiskit_addon_sqd": "qiskit-addon-sqd", "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")
import numpy as np
import pyscf.fci
def siam_hamiltonian(
norb: int,
hopping: float,
onsite: float,
hybridization: float,
chemical_potential: float,
) -> tuple[np.ndarray, np.ndarray]:
"""Hamiltonian for the single-impurity Anderson model."""
# Place the impurity on the first site
impurity_orb = 0
# One body matrix elements in the "position" basis
h1e = np.zeros((norb, norb))
np.fill_diagonal(h1e[:, 1:], -hopping)
np.fill_diagonal(h1e[1:, :], -hopping)
h1e[impurity_orb, impurity_orb + 1] = -hybridization
h1e[impurity_orb + 1, impurity_orb] = -hybridization
h1e[impurity_orb, impurity_orb] = chemical_potential
# Two body matrix elements in the "position" basis
h2e = np.zeros((norb, norb, norb, norb))
h2e[impurity_orb, impurity_orb, impurity_orb, impurity_orb] = onsite
return h1e, h2e
def momentum_basis(norb: int) -> np.ndarray:
"""Get the orbital rotation to change from the position to the momentum basis."""
n_bath = norb - 1
# Orbital rotation that diagonalizes the bath (non-interacting system)
hopping_matrix = np.zeros((n_bath, n_bath))
np.fill_diagonal(hopping_matrix[:, 1:], -1)
np.fill_diagonal(hopping_matrix[1:, :], -1)
_, vecs = np.linalg.eigh(hopping_matrix)
# Expand to include impurity
orbital_rotation = np.zeros((norb, norb))
# Impurity is on the first site
orbital_rotation[0, 0] = 1
orbital_rotation[1:, 1:] = vecs
# Move the impurity to the center
new_index = n_bath // 2
perm = np.r_[1 : (new_index + 1), 0, (new_index + 1) : norb]
orbital_rotation = orbital_rotation[:, perm]
return orbital_rotation
def rotated(
h1e: np.ndarray, h2e: np.ndarray, orbital_rotation: np.ndarray
) -> tuple[np.ndarray, np.ndarray]:
"""Rotate the orbital basis of a Hamiltonian."""
h1e_rotated = np.einsum(
"ab,Aa,Bb->AB",
h1e,
orbital_rotation,
orbital_rotation.conj(),
optimize="greedy",
)
h2e_rotated = np.einsum(
"abcd,Aa,Bb,Cc,Dd->ABCD",
h2e,
orbital_rotation,
orbital_rotation.conj(),
orbital_rotation,
orbital_rotation.conj(),
optimize="greedy",
)
return h1e_rotated, h2e_rotated
# Total number of spatial orbitals, including the bath sites and the impurity
# This should be an even number
norb = 8
# System is half-filled
nelec = (norb // 2, norb // 2)
# One orbital is the impurity, the rest are bath sites
n_bath = norb - 1
# Hamiltonian parameters
hybridization = 1.0
hopping = 1.0
onsite = 10.0
chemical_potential = -0.5 * onsite
# Generate Hamiltonian in position basis
h1e, h2e = siam_hamiltonian(
norb=norb,
hopping=hopping,
onsite=onsite,
hybridization=hybridization,
chemical_potential=chemical_potential,
)
# Rotate to momentum basis
orbital_rotation = momentum_basis(norb)
h1e_momentum, h2e_momentum = rotated(h1e, h2e, orbital_rotation.T.conj())
# In the momentum basis, the impurity is placed in the center
impurity_index = n_bath // 2
# Use PySCF to compute the exact ground state energy
reference_energy, _ = pyscf.fci.direct_spin1.kernel(h1e, h2e, norb, nelec)
Seterusnya, kami menjana litar untuk menghasilkan keadaan asas Krylov. Bagi setiap spesies spin, keadaan awal diberikan oleh superposisi semua kemungkinan pengujaan tiga elektron yang paling hampir dengan paras Fermi ke dalam 4 mod kosong yang paling hampir bermula dari keadaan , dan direalisasikan melalui penggunaan tujuh XXPlusYYGate. Keadaan yang telah berevolusi masa dihasilkan melalui penggunaan berturut-turut langkah Trotter peringkat kedua.
Untuk penerangan yang lebih terperinci tentang model ini dan cara litar direka bentuk, rujuk "Quantum-Centric Algorithm for Sample-Based Krylov Diagonalization".
from collections.abc import Sequence
import ffsim
import scipy
from qiskit import QuantumCircuit, QuantumRegister
from qiskit.circuit import CircuitInstruction, Qubit
from qiskit.circuit.library import CPhaseGate, XGate, XXPlusYYGate
def prepare_initial_state(qubits: Sequence[Qubit], norb: int, nocc: int):
"""Prepare initial state."""
assert norb >= 8
x_gate = XGate()
rot = XXPlusYYGate(0.5 * np.pi, -0.5 * np.pi)
for i in range(nocc):
yield CircuitInstruction(x_gate, [qubits[i]])
yield CircuitInstruction(x_gate, [qubits[norb + i]])
for i in range(3):
for j in range(nocc - i - 1, nocc + i, 2):
yield CircuitInstruction(rot, [qubits[j], qubits[j + 1]])
yield CircuitInstruction(
rot, [qubits[norb + j], qubits[norb + j + 1]]
)
yield CircuitInstruction(rot, [qubits[j + 1], qubits[j + 2]])
yield CircuitInstruction(
rot, [qubits[norb + j + 1], qubits[norb + j + 2]]
)
def trotter_step(
qubits: Sequence[Qubit],
time_step: float,
one_body_evolution: np.ndarray,
h2e: np.ndarray,
impurity_index: int,
norb: int,
):
"""A Trotter step."""
# Assume the two-body interaction is just the on-site interaction of the impurity
onsite = h2e[
impurity_index, impurity_index, impurity_index, impurity_index
]
# Two-body evolution for half the time
yield CircuitInstruction(
CPhaseGate(-0.5 * time_step * onsite),
[qubits[impurity_index], qubits[norb + impurity_index]],
)
# One-body evolution for the full time
yield CircuitInstruction(
ffsim.qiskit.OrbitalRotationJW(norb, one_body_evolution), qubits
)
# Two-body evolution for half the time
yield CircuitInstruction(
CPhaseGate(-0.5 * time_step * onsite),
[qubits[impurity_index], qubits[norb + impurity_index]],
)
# Time step
time_step = 0.2
# Number of Krylov basis states
krylov_dim = 8
# Initialize circuit
qubits = QuantumRegister(2 * norb, name="q")
circuit = QuantumCircuit(qubits)
# Generate initial state
for instruction in prepare_initial_state(qubits, norb=norb, nocc=norb // 2):
circuit.append(instruction)
circuit.measure_all()
# Create list of circuits, starting with the initial state circuit
circuits = [circuit.copy()]
# Add time evolution circuits to the list
one_body_evolution = scipy.linalg.expm(-1j * time_step * h1e_momentum)
for i in range(krylov_dim - 1):
# Remove measurements
circuit.remove_final_measurements()
# Append another Trotter step
for instruction in trotter_step(
qubits,
time_step,
one_body_evolution,
h2e_momentum,
impurity_index,
norb,
):
circuit.append(instruction)
# Measure qubits
circuit.measure_all()
# Add a copy of the circuit to the list
circuits.append(circuit.copy())
circuits[0].draw("mpl", scale=0.4, fold=-1)
circuits[-1].draw("mpl", scale=0.4, fold=-1)

Langkah 2: Optimumkan masalah untuk pelaksanaan kuantumβ
Seterusnya, kami mengoptimumkan litar untuk perkakasan sasaran. Buat masa ini, kami akan mencipta Backend generik dengan bilangan qubit yang ditentukan dan set gate yang secara semula jadi menjadi hasil penguraian litar evolusi masa.
from qiskit.providers.fake_provider import GenericBackendV2
rng = np.random.default_rng(12345)
backend = GenericBackendV2(
2 * norb, basis_gates=["cp", "xx_plus_yy", "p", "x"], seed=rng
)
Kini, kami menggunakan Qiskit untuk mentranspil litar bagi Backend sasaran.
from qiskit.transpiler import generate_preset_pass_manager
pass_manager = generate_preset_pass_manager(
optimization_level=3,
backend=backend,
seed_transpiler=int(rng.integers(2**31)),
)
isa_circuits = pass_manager.run(circuits)
Langkah 3: Jalankan menggunakan primitif Qiskitβ
Selepas mengoptimumkan litar untuk pelaksanaan perkakasan, kami sedia untuk menjalankannya pada perkakasan sasaran dan mengumpul sampel untuk anggaran tenaga keadaan dasar. Selepas menggunakan primitif Sampler untuk mengambil sampel rentetan bit daripada setiap litar, kami menggabungkan semua keputusan ke dalam satu kamus kiraan dan memplot 20 rentetan bit yang paling kerap disampling.
from qiskit.primitives import StatevectorSampler
from qiskit.visualization import plot_histogram
# Sample from the circuits
sampler = StatevectorSampler(seed=rng)
job = sampler.run(isa_circuits, shots=500)
from qiskit.primitives import BitArray
# Combine the shots from the individual Trotter circuits
bit_array = BitArray.concatenate_shots(
[result.data.meas for result in job.result()]
)
plot_histogram(bit_array.get_counts(), number_to_keep=20)

Langkah 4: Pasca-proses dan kembalikan keputusan ke format klasik yang dikehendakiβ
Kini, kami menjalankan algoritma SQD menggunakan fungsi diagonalize_fermionic_hamiltonian. Lihat dokumentasi API untuk penjelasan argumen fungsi ini.
Kami menggunakan trim SQD, yang diaktifkan dengan menghantar TrimPolicy sebagai argumen policy. Dalam setiap lelaran pemulihan konfigurasi, trim SQD mendiagonalkan setiap kelompok rentetan bit, memangkas setiap kelompok kepada rentetan CI (separuh spin- dan spin- bagi rentetan bit) yang membawa pemberat terbesar dalam anggaran keadaan dasarnya, dan mendiagonalkan sekali lagi hasil yang bertahan setelah digabungkan. Dengan dua kelompok, setiap lelaran memerlukan tiga pendiagonalan. Callback hanya menerima keputusan yang digabungkan, jadi setiap lelaran mencetak satu tenaga. Pilihan trim_ratio menetapkan pecahan rentetan CI setiap kelompok yang bertahan selepas pemangkasan, dan max_dim menghadkan bilangan rentetan CI bagi setiap sektor spin dalam setiap subruang, termasuk subruang yang digabungkan. Lihat panduan trim SQD untuk butiran lanjut.
from qiskit_addon_sqd.fermion import (
SCIResult,
diagonalize_fermionic_hamiltonian,
)
from qiskit_addon_sqd.trim import TrimPolicy
# List to capture intermediate results
result_history = []
def callback(results: list[SCIResult]):
result_history.append(results)
iteration = len(result_history)
print(f"Iteration {iteration}")
for i, result in enumerate(results):
print(f"\tEnergy: {result.energy}")
print(
f"\tSubspace dimension: {np.prod(result.sci_state.amplitudes.shape)}"
)
result = diagonalize_fermionic_hamiltonian(
h1e_momentum,
h2e_momentum,
bit_array,
samples_per_batch=1000,
norb=norb,
nelec=nelec,
num_batches=2,
policy=TrimPolicy(
trim_ratio=0.8,
carryover_threshold=1e-4,
carryover_ratio=0.5,
),
max_dim=100,
max_iterations=5,
symmetrize_spin=True,
callback=callback,
seed=rng,
)
Iteration 1
Energy: -13.422366648114345
Subspace dimension: 729
Iteration 2
Energy: -13.422366646833664
Subspace dimension: 729
Sel kod berikut memplot keputusan. Plot pertama menunjukkan ralat tenaga yang dikira sebagai fungsi bilangan iterasi pemulihan konfigurasi, dan plot kedua menunjukkan purata penghunian setiap orbital ruang selepas iterasi akhir. Memandangkan ini masalah yang sangat kecil, iterasi pertama sudah membawa kita sangat hampir dengan tenaga tepat.
import matplotlib.pyplot as plt
min_es = [
min(result, key=lambda res: res.energy).energy
for result in result_history
]
min_id, min_e = min(enumerate(min_es), key=lambda x: x[1])
# Data for energies plot
x1 = range(len(result_history))
e_diff = [abs(e - reference_energy) for e in min_es]
# Data for avg spatial orbital occupancy
y2 = np.sum(result.orbital_occupancies, axis=0)
x2 = range(len(y2))
fig, axs = plt.subplots(1, 2, figsize=(12, 6))
# Plot energies
axs[0].plot(x1, e_diff, label="energy error", marker="o")
axs[0].set_xticks(x1)
axs[0].set_xticklabels(x1)
axs[0].set_yscale("log")
axs[0].set_ylim(1e-5, 1e-2)
axs[0].set_title("Approximated Ground State Energy Error vs SQD Iterations")
axs[0].set_xlabel("Iteration Index", fontdict={"fontsize": 12})
axs[0].set_ylabel("Energy Error", fontdict={"fontsize": 12})
axs[0].legend()
# Plot orbital occupancy
axs[1].bar(x2, y2, width=0.8)
axs[1].set_xticks(x2)
axs[1].set_xticklabels(x2)
axs[1].set_title("Avg Occupancy per Spatial Orbital")
axs[1].set_xlabel("Orbital Index", fontdict={"fontsize": 12})
axs[1].set_ylabel("Avg Occupancy", fontdict={"fontsize": 12})
print(f"Reference energy: {reference_energy:.5f}")
print(f"SQD energy: {min_e:.5f}")
print(f"Absolute error: {abs(min_e - reference_energy):.5f}")
plt.tight_layout()
plt.show()
Reference energy: -13.42249
SQD energy: -13.42237
Absolute error: 0.00012

Sahkan tenagaβ
Tenaga yang dikembalikan oleh SQD dijamin sebagai had atas kepada tenaga keadaan dasar sebenar. Nilai tenaga boleh disahkan kerana SQD juga mengembalikan pekali vektor keadaan yang menghampiri keadaan dasar. Anda boleh mengira tenaga daripada vektor keadaan menggunakan matriks ketumpatan tereduksi satu- dan dua-zarah, seperti yang ditunjukkan dalam sel kod berikut.
rdm1 = result.sci_state.rdm(rank=1, spin_summed=True)
rdm2 = result.sci_state.rdm(rank=2, spin_summed=True)
energy = np.sum(h1e_momentum * rdm1) + 0.5 * np.sum(h2e_momentum * rdm2)
print(f"Recomputed energy: {energy:.5f}")
Recomputed energy: -13.42237
Contoh perkakasan berskala besarβ
Sekarang, kami menjalankan contoh yang lebih besar pada QPU sebenar.
Untuk tenaga rujukan, kami menggunakan keputusan pengiraan DMRG yang dilakukan secara berasingan.
Oleh kerana sistem ini mempunyai lebih banyak rentetan CI yang mungkin, kami merendahkan trim_ratio kepada 0.5 dan menaikkan max_dim kepada 200.
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler
# Model parameters
norb = 20
nelec = (norb // 2, norb // 2)
n_bath = norb - 1
hybridization = 1.0
hopping = 1.0
onsite = 10.0
chemical_potential = -0.5 * onsite
# Generate Hamiltonian and orbital rotation
h1e, h2e = siam_hamiltonian(
norb=norb,
hopping=hopping,
onsite=onsite,
hybridization=hybridization,
chemical_potential=chemical_potential,
)
orbital_rotation = momentum_basis(norb)
h1e_momentum, h2e_momentum = rotated(h1e, h2e, orbital_rotation.T.conj())
impurity_index = n_bath // 2
# Set reference energy to DMRG value computed separately
reference_energy = -28.70659686
# Algorithm parameters
time_step = 0.2
krylov_dim = 8
# Construct circuits
qubits = QuantumRegister(2 * norb, name="q")
circuit = QuantumCircuit(qubits)
for instruction in prepare_initial_state(qubits, norb=norb, nocc=norb // 2):
circuit.append(instruction)
circuit.measure_all()
circuits = [circuit.copy()]
one_body_evolution = scipy.linalg.expm(-1j * time_step * h1e_momentum)
for i in range(krylov_dim - 1):
circuit.remove_final_measurements()
for instruction in trotter_step(
qubits,
time_step,
one_body_evolution,
h2e_momentum,
impurity_index,
norb,
):
circuit.append(instruction)
circuit.measure_all()
circuits.append(circuit.copy())
# Initialize hardware backend
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
print(f"Using backend {backend.name}")
# Transpile to backend
pass_manager = generate_preset_pass_manager(
optimization_level=3, backend=backend
)
isa_circuits = pass_manager.run(circuits)
# Sample from the circuits
sampler = Sampler(backend)
sampler.options.environment.job_tags = ["TUT_SKQD"]
job = sampler.run(isa_circuits, shots=500)
# Combine the shots from the individual Trotter circuits
bit_array = BitArray.concatenate_shots(
[result.data.meas for result in job.result()]
)
# Run configuration recovery and diagonalization
result_history = []
def callback(results: list[SCIResult]):
result_history.append(results)
iteration = len(result_history)
print(f"Iteration {iteration}")
for i, result in enumerate(results):
print(f"\tEnergy: {result.energy}")
print(
f"\tSubspace dimension: {np.prod(result.sci_state.amplitudes.shape)}"
)
result = diagonalize_fermionic_hamiltonian(
h1e_momentum,
h2e_momentum,
bit_array,
samples_per_batch=1000,
norb=norb,
nelec=nelec,
num_batches=2,
policy=TrimPolicy(
trim_ratio=0.5,
carryover_threshold=1e-4,
carryover_ratio=0.5,
),
max_dim=200,
max_iterations=5,
symmetrize_spin=True,
callback=callback,
seed=rng,
)
# Plot results
min_es = [
min(result, key=lambda res: res.energy).energy
for result in result_history
]
min_id, min_e = min(enumerate(min_es), key=lambda x: x[1])
x1 = range(len(result_history))
e_diff = [abs(e - reference_energy) for e in min_es]
y2 = np.sum(result.orbital_occupancies, axis=0)
x2 = range(len(y2))
fig, axs = plt.subplots(1, 2, figsize=(12, 6))
axs[0].plot(x1, e_diff, label="energy error", marker="o")
axs[0].set_xticks(x1)
axs[0].set_xticklabels(x1)
axs[0].set_yscale("log")
axs[0].set_ylim(1e-3, 1e-1)
axs[0].set_title("Approximated Ground State Energy Error vs SQD Iterations")
axs[0].set_xlabel("Iteration Index", fontdict={"fontsize": 12})
axs[0].set_ylabel("Energy Error", fontdict={"fontsize": 12})
axs[0].legend()
axs[1].bar(x2, y2, width=0.8)
axs[1].set_xticks(x2)
axs[1].set_xticklabels(x2)
axs[1].set_title("Avg Occupancy per Spatial Orbital")
axs[1].set_xlabel("Orbital Index", fontdict={"fontsize": 12})
axs[1].set_ylabel("Avg Occupancy", fontdict={"fontsize": 12})
print(f"Reference energy: {reference_energy:.5f}")
print(f"SQD energy: {min_e:.5f}")
print(f"Absolute error: {abs(min_e - reference_energy):.5f}")
plt.tight_layout()
plt.show()
Using backend ibm_fez
Iteration 1
Energy: -28.632642067246973
Subspace dimension: 13689
Iteration 2
Energy: -28.7030144883651
Subspace dimension: 18769
Iteration 3
Energy: -28.70519592695907
Subspace dimension: 13456
Iteration 4
Energy: -28.705288597420616
Subspace dimension: 15129
Iteration 5
Energy: -28.70502224787334
Subspace dimension: 15129
Reference energy: -28.70660
SQD energy: -28.70529
Absolute error: 0.00131

Langkah seterusnyaβ
Jika anda mendapati karya ini menarik, anda mungkin berminat dengan bahan berikut:
- Pepenjuruan kuantum berasaskan sampel bagi Hamiltonian kimia - tutorial berkaitan yang menggunakan ansatz variasi heuristik berbanding litar Trotter
- Pepenjuruan kuantum Krylov bagi Hamiltonian kekisi - tutorial tentang kaedah KQD
- Dokumentasi API addon SQD - rujukan untuk fungsi
diagonalize_fermionic_hamiltonian - Quantum-Centric Algorithm for Sample-Based Krylov Diagonalization - kertas yang tutorial ini berdasarkan padanya