Download this notebook (.ipynb + data files)

7. AutoHF Crash Course#

In this tutorial, we will cover the basics of using AutoHF for the purpose of generating trial wavefunctions for SAFIRE.

πŸ› οΈ To cover:

  • providing a Hamiltonian

    • pinning fields!!

  • characterizing the HF SD

    • charge and spin densities

    • spin-spin correlation

  • using sufficient parallel trials.

  • choosing the ansatz

  • choosing the optimization method.

7.1. Software Preliminaries#

While AutoHF is a stand alone tool with it’s own input format for Hamiltonians. However, it is able to convert SAFIRE format hamiltonians to it’s own internal format very easily. In this tutorial, we will exclusively use afqmctools to build Hamiltonians, then use the SAFIRE to AutoHF converter to provide AutoHF with the desired Hamiltonian.

7.2. ▢️ Run the cell below to setup the tutorial#

%load_ext autoreload
%autoreload
from pathlib import Path
# simple setup
from tutorial_utils import run_afqmc

scratch_dir = Path("data")
scratch_dir.mkdir(parents=True, exist_ok=True)

7.3. Set up the model#

In this tutorial, we will perform Hartree-Fock calculations using AutoHF for the Hubbard model on a 4x4 lattice. The Hamiltonian is given by,

\[ \hat{H} = -t \sum_{\langle i,j\rangle} \hat{c}^\dagger_i \hat{c}_j + U \sum_{i} \hat{n}^{\uparrow}_i \hat{n}^{\downarrow}_i, \]
where \(i,j\) are lattice site indices, angle brackets indicate nearest-neighbors, \(\hat{c}^\dagger_i\)/\(\hat{c}_i\) are electronic creation/annihilation operators, and \(\hat{n}^{\sigma}_i\) are spin-resolved number operators corresponding to site \(i\).

from afqmctools.hamiltonian.model.director import HamiltonianDirector
import afqmctools.utils.io as io
from afqmctools.systems.lattice import get_lattice
import afqmctools.utils.visualize as vis

lattice_params = dict(
    L1 = 4,
    L2 = 4,
    boundary1 = 'PBC',
    boundary2 = 'PBC',
)

lattice = get_lattice(
    params=lattice_params
)

vis.plot_lattice(lattice)

# Step 2. define Hamiltonian parameters
hamiltonian_params = {
    'hamiltonian' : {
        "t" : 1.0,  # note: we could omit this, nearest-neighbor hoping with t=1 is included by default
        "U" : 4.0
    }
}

# Step 3.b. build the lattice model Ha
hamiltonian = HamiltonianDirector(
    lattice=lattice,
    source=hamiltonian_params
).build()

We now have a β€œstandard” Hubbard model with nearest-neighbor hopping and onsite U in the hamiltonian object.

7.4. Setting up a calculation in AutoHF#

AutoHF requires only two inputs.

  1. a Hamiltonian in the AutoHF format

  2. Python dictionary containing settings

7.4.1. 1. Getting a Hamiltonian in AutoHF#

We can easily convert afqmctools Hamiltonians to the AutoHF format using,

from autohf import AutoHFHamiltonian

hamiltonian_autohf=AutoHFHamiltonian(source=hamiltonian_safire)

where hamiltonian_safire is a Hamiltonian generated by afqmctools, and hamiltonian_autohf is the same Hamiltonian in the AutoHF format.

7.4.2. 2. AutoHF settings dictionary#

AutoHF is controlled via a Python dictionary containing various keyword arguments. Think of this as an input file. Below is a sample settings dictionary with most common settings exposed.

hf_settings = dict(
    ansatz = 'SD',
    steps = 100,
    nelec = (5,5),
    batch_size = 30,
    seed = 42,
    measure_spin = True,
    noncollinear = False,
    verbose = False,
    riemannian = False
)

Covering all of the settings is beyond the scope of this tutorial; however, we will focus on a few key settings which are as follows.

  • ansatz

  • steps

  • batch_size

When you run AutoHF, it will echo the settings. See sample output below.

    -----------------------
    - ╔═╗ ------- β•– ╓╔═╕ --
    - ╠═╣ ╖╖╒╦╕╔╗ ╠═╣╠╕ ---
    - β•œ β•™ β•šβ• β•œ β•šβ• β•œ ╙╨ ----
    -----------------------
    

 ===== AutoHF Settings =====

        noncollinear                                             True
        gpu                                                     False
        approx_expm                                             False
        force_complex                                           False
        ansatz                                                     SD
        opt_method                                              lbfgs
        riemannian                                              False
        seed                                                       42
        eta                                                       0.1
        steps                                                     100
        batch_size                                                100
        plot                                                    False
        verbose                                                 False
        output                                                   None
        dump_batch                                              False
        measure_spin                                             True
        random_initial_phi0                                     False
        state0_scale                                             0.01

7.5. ▢️ Your Turn: Trying running AutoHF for the 4x4 Hubbard Model#

Complete the labelled line of code and run the cell below.

from autohf import lattice_hf,AutoHFHamiltonian
from afqmctools.inputs.from_autohf import autohf_to_afqmc

# "hamiltonian" contains the afqmctools format Hamiltonian
hamiltonian_autohf = AutoHFHamiltonian(hamiltonian) # Your Turn: convert to the autoHF format

# define settings
hf_settings = dict(
    ansatz = 'SD',
    steps = 100,
    nelec = (5,5),
    batch_size = 30,
    seed = 42,
    measure_spin = True,
    random_initial_phi0 = False,
    noncollinear = True,
    verbose = False
)

results = lattice_hf(
    hamiltonian=hamiltonian_autohf,
    settings=hf_settings
)

7.6. βœ… Solution#

You should have completed the line of code as,

# "hamiltonian" contains the afqmctools format Hamiltonian
hamiltonian_autohf = AutoHFHamiltonian(hamiltonian)

You should have gotten a HF energy of -17.7499999549238 \(t\) (to within about \(1 \times 10^{-8} t\)) when you ran AutoHF.

autohf_to_afqmc(
    results,
    output_fname = scratch_dir/"uhf_wfn.h5"
)

7.7. Analyzing the HF Wavefunction#

It is very important to analyze / characterize the HF solution before using it as a trial wavefunction to ensure that it is the desired properties.

# get the density from autohf
state_results, functions = results

makeRDMs = functions['makeRDMs']
state = state_results['state']

rdm = makeRDMs(state)

# get lattice dimensions from the Lattice instance
L = lattice.L

# collinear
#rho_up = rdm[0].diagonal().reshape(*L)
#rho_down = rdm[1].diagonal().reshape(*L)

# noncollinear
M = lattice.N_sites
rho_up = rdm[:M,:M].diagonal().reshape(*L)
rho_down = rdm[M:,M:].diagonal().reshape(*L)

rho_charge = rho_up + rho_down
rho_spin = rho_up - rho_down

vis.plot_lattice(
    lattice,
    density=rho_charge.real,
    density_label="charge density",
    vmin=0.0,
    vmax=2.0
)
from afqmctools.observables.spin import local_spin

vis.plot_lattice(
    lattice,
    density=rho_spin,
    density_label="spin density",
    cmap="bwr",
    vmin=-0.5,
    vmax=0.5
)