FCI and VQE Calculations

See also

Full scripts: examples/fci_vqe.py, examples/fci_vqe_wannier.py, examples/vqe_adapt.py

This page covers three closely related workflows: a standard FCI + VQE run, the same calculation with a Wannier transformation applied to the active space, and an ADAPT-VQE run. All examples use Beryllium as the test system.

Basic FCI + VQE

Configuration

Set run_fci=True and run_vqe=True to activate both solvers. Choose a VQE optimizer and an excitation pool for the UCCSD ansatz:

import os
import dopyqo

config = dopyqo.DopyqoConfig(
    base_folder=os.path.join("qe_files", "Be"),
    prefix="Be",
    active_electrons=2,
    active_orbitals=4,
    run_fci=True,
    run_vqe=True,
    vqe_optimizer=dopyqo.VQEOptimizers.L_BFGS_B,
    vqe_excitations=dopyqo.ExcitationPools.SINGLES_DOUBLES,
    n_threads=10,
)

Running and extracting energies

run() executes both solvers and collects all energies in energy_dict:

energy_dict, wfc_obj, h_ks, mats = dopyqo.run(config)

dft_energy = energy_dict["dft_energy"]
fci_energy = energy_dict["fci_energy"]
vqe_energy = energy_dict["vqe_energy"]

Plotting VQE convergence

The Hamiltonian object records every optimizer step. Plotting the absolute error relative to the FCI reference gives the convergence curve:

import numpy as np
import matplotlib.pyplot as plt

plt.plot(h_ks.vqe_counts, np.abs(h_ks.vqe_values - fci_energy),
         linestyle="-", marker="x")
plt.yscale("log")
plt.xlabel("Function evaluations")
plt.ylabel("|E_VQE - E_FCI| (Ha)")
plt.grid()
plt.show()

With Wannier transformation

Dopyqo can transform the Kohn-Sham active-space orbitals into maximally localised Wannier functions before constructing the Hamiltonian. This requires a Wannier90 _u.mat file generated from a prior Wannier90 run.

Set wannier_transform=True and point base_folder at the Wannier output directory. Increase active_electrons to match the Wannier orbital count:

config = dopyqo.DopyqoConfig(
    base_folder=os.path.join("qe_files", "Be_wannier"),
    prefix="Be",
    active_electrons=4,
    active_orbitals=4,
    run_fci=True,
    run_vqe=True,
    vqe_optimizer=dopyqo.VQEOptimizers.L_BFGS_B,
    vqe_excitations=dopyqo.ExcitationPools.SINGLES_DOUBLES,
    wannier_transform=True,
    n_threads=10,
)

The rest of the workflow — calling run() and inspecting energies — is identical to the basic case above.

If wannier_umat is not provided, Dopyqo looks for {base_folder}/{prefix}_u.mat by default. Pass wannier_input_file to also validate the active space against the Wannier90 .win file.

ADAPT-VQE

ADAPT-VQE builds the ansatz iteratively by appending the gradient-largest operator from a pool at each step, rather than using a fixed UCCSD circuit. Enable it with vqe_adapt=True:

config = dopyqo.DopyqoConfig(
    base_folder=os.path.join("qe_files", "Be"),
    prefix="Be",
    active_electrons=2,
    active_orbitals=4,
    run_fci=True,
    run_vqe=True,
    vqe_adapt=True,
    vqe_adapt_drain_pool=True,
    vqe_optimizer=dopyqo.VQEOptimizers.L_BFGS_B,
    vqe_excitations=dopyqo.ExcitationPools.SINGLES_DOUBLES,
    n_threads=10,
)

vqe_adapt_drain_pool=True removes an operator from the pool once it has been appended to the ansatz, so each operator appears at most once. Set it to False to allow repeated use of the same operator.

The call to run() and the convergence plot follow the same pattern as the basic FCI + VQE example.