Fulqrum による SQD 化学ワークフローのスケール
Fulqrum は、部分空間に射影されたハミルトニアンの固有値と固有ベクトルを計算するソフトウェアパッケージです。射影されたハミルトニアンのポータブルな表現を構築し、標準的な既製のソルバーを含め、任意の固有値ソルバーに渡すことができます。これらのソルバーは、SQD アドオンが対角化に使用するデフォルトの PySCF ソルバーよりもはるかに高速な代替手段になり得ます。Fulqrum のインストール方法と使い方については、Fulqrum のドキュメントを参照してください。
Fulqrum の addon_sqd_support モジュールは、diagonalize_fermionic_hamiltonian が直接受け付けるソルバーを構築します。そのため、両方に同じ積分を渡せば、射影と対角化は Fulqrum が処理します。
この例で使用している SQD コードの詳細については、化学ハミルトニアンのチュートリアルを参照してください。
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"fulqrum": "fulqrum", "numpy": "numpy", "primme": "primme", "pyscf": "pyscf", "qiskit_addon_sqd": "qiskit-addon-sqd"}
_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
import pyscf.cc
import pyscf.mcscf
from fulqrum.addon_sqd_support import sci_solver_from_integrals
from qiskit_addon_sqd.counts import generate_bit_array_uniform
from qiskit_addon_sqd.fermion import diagonalize_fermionic_hamiltonian
# Build N2 molecule
mol = pyscf.gto.Mole()
mol.build(
atom=[["N", (0, 0, 0)], ["N", (1.0, 0, 0)]],
basis="6-31g",
symmetry="Dooh",
)
# Define active space
n_frozen = 2
active_space = range(n_frozen, mol.nao_nr())
# Get molecular integrals
scf = pyscf.scf.RHF(mol).run()
num_orbitals = len(active_space)
n_electrons = int(sum(scf.mo_occ[active_space]))
num_elec_a = (n_electrons + mol.spin) // 2
num_elec_b = (n_electrons - mol.spin) // 2
cas = pyscf.mcscf.CASCI(scf, num_orbitals, (num_elec_a, num_elec_b))
mo = cas.sort_mo(active_space, base=0)
hcore, nuclear_repulsion_energy = cas.get_h1cas(mo)
eri = pyscf.ao2mo.restore(1, cas.get_h2cas(mo), num_orbitals)
# Compute exact energy
exact_energy = cas.run().e_tot
# Create a seed to control randomness throughout this workflow
rng = np.random.default_rng(24)
# Generate random samples
bit_array = generate_bit_array_uniform(
10_000, num_orbitals * 2, rand_seed=rng
)
# Build a solver that diagonalizes with Fulqrum. It converts the integrals to a
# Fulqrum operator once, then projects onto each subspace the SQD loop requests.
sci_solver = sci_solver_from_integrals(hcore, eri, tol=1e-12)
# Run SQD
result = diagonalize_fermionic_hamiltonian(
hcore,
eri,
bit_array,
samples_per_batch=300,
norb=num_orbitals,
nelec=(num_elec_a, num_elec_b),
num_batches=5,
max_iterations=5,
sci_solver=sci_solver,
symmetrize_spin=True,
seed=rng,
)
WARN: Unable to to identify input symmetry using original axes.
Different symmetry axes will be used.
converged SCF energy = -108.835236570774
CASCI E = -109.046671778080 E(CI) = -32.8155692383188 S^2 = 0.0000000
print(f"Exact energy: {exact_energy}")
print(f"Estimated energy: {result.energy + nuclear_repulsion_energy}")
Exact energy: -109.04667177808024
Estimated energy: -109.03402667559365
別の固有値ソルバーを使用する
Fulqrum は射影されたハミルトニアンを標準の scipy.sparse.linalg.LinearOperator として公開するため、scipy.sparse.linalg.eigsh に縛られることなく、任意の固有値ソルバーで対角化できます。これにより、問題のサイズやハードウェアにより適したソルバーに簡単に置き換えられます。
使用したい固有値ソルバーを eigensolver 引数として渡します。例として、このガイドでは高性能な固有値ソルバーライブラリである PRIMME を使用します。その Python インターフェースである primme.eigsh は scipy.sparse.linalg.eigsh と似た呼び出し規約に従うため、そのまま置き換えて使用できます。
import primme
# Build a solver that diagonalizes with PRIMME instead of the default,
# scipy.sparse.linalg.eigsh
sci_solver = sci_solver_from_integrals(
hcore, eri, eigensolver=primme.eigsh, tol=1e-12
)
# Run SQD using the PRIMME-based solver
result = diagonalize_fermionic_hamiltonian(
hcore,
eri,
bit_array,
samples_per_batch=300,
norb=num_orbitals,
nelec=(num_elec_a, num_elec_b),
num_batches=5,
max_iterations=5,
sci_solver=sci_solver,
symmetrize_spin=True,
seed=rng,
)
print(f"Exact energy: {exact_energy}")
print(f"Estimated energy: {result.energy + nuclear_repulsion_energy}")
Exact energy: -109.04667177808024
Estimated energy: -109.03937429196168