Tutorial: H2 Ground-State Energy

This tutorial walks through computing the ground-state energy of H2 using the full NF-SKQD pipeline. H2 is the simplest molecule in the registry (4 qubits), making it ideal for learning the workflow.

Setup

from qvartools import PipelineConfig, FlowGuidedKrylovPipeline
from qvartools.molecules import get_molecule
from qvartools.solvers import FCISolver

Step 1: Load the Molecule

hamiltonian, mol_info = get_molecule("H2")

print(f"Molecule: {mol_info['name']}")
print(f"Qubits: {mol_info['n_qubits']}")
print(f"Basis set: {mol_info['basis']}")

This returns a MolecularHamiltonian built from PySCF-computed integrals and a metadata dictionary.

Step 2: Compute the Exact Reference

fci_result = FCISolver().solve(hamiltonian, mol_info)
exact_energy = fci_result.energy
print(f"Exact (FCI) energy: {exact_energy:.10f} Ha")

For H2 in sto-3g, the exact ground-state energy is approximately -1.137 Ha.

Step 3: Configure the Pipeline

config = PipelineConfig(
    skip_nf_training=False,
    subspace_mode="classical_krylov",
    teacher_weight=0.5,
    physics_weight=0.4,
    entropy_weight=0.1,
)

pipeline = FlowGuidedKrylovPipeline(
    hamiltonian=hamiltonian,
    config=config,
    exact_energy=exact_energy,
    auto_adapt=True,    # auto-scale parameters to H2's size
)

With auto_adapt=True, the pipeline automatically scales network sizes, training epochs, and sampling budgets to the system’s Hilbert-space dimension.

Step 4: Train the NF-NQS Model

history = pipeline.train_flow_nqs(progress=True)

print(f"Epochs: {len(history.get('total_loss', []))}")
print(f"Final loss: {history['total_loss'][-1]:.4f}")

The training loop optimizes a joint objective combining:

  • Teacher loss: KL divergence between the flow and the NQS

  • Physics loss: variational energy estimate

  • Entropy loss: encourages exploration of configuration space

Step 5: Extract and Select Basis

basis = pipeline.extract_and_select_basis()
print(f"Selected {basis.shape[0]} configurations")

The diversity selector ensures representation across excitation ranks (singles, doubles, and higher excitations relative to the Hartree-Fock state).

Step 6: SKQD Diagonalization

skqd_results = pipeline.run_subspace_diag(progress=True)

final_energy = pipeline.results["final_energy"]
error_mha = (final_energy - exact_energy) * 1000.0
print(f"Final energy: {final_energy:.10f} Ha")
print(f"Error: {error_mha:.4f} mHa")

The SKQD solver constructs Krylov states via time evolution \(|\\psi_k\\rangle = e^{-iH \\Delta t \\cdot k} |\\psi_0\\rangle\), samples configurations from each state, builds the projected Hamiltonian in the combined basis, and solves the generalized eigenvalue problem.

For H2, the error should be well within chemical accuracy (1.6 mHa).

Running from the Command Line

The same pipeline can be run as a standalone experiment script:

python experiments/pipelines/02_nf_dci/nf_dci_krylov_classical.py h2 --device cuda

# Or with a YAML config
python experiments/pipelines/02_nf_dci/nf_dci_krylov_classical.py h2 \
    --config experiments/pipelines/configs/02_nf_dci.yaml

# Run all 24 pipelines and compare
python experiments/pipelines/run_all_pipelines.py h2 --device cuda