Skip to content
/ dmrg Public

Density Matrix Renormalization Group (DMRG) in Julia

License

Notifications You must be signed in to change notification settings

frenkield/dmrg

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Julia DMRG and Quantum Chemistry Experiments

This project contains a Julia implementation of the DMRG (Density Matrix Renormalization Group) algorithm and some miscellaneous Julia experiments. The experiments were used during the development of the DMRG code, and to explore the structure the molecular hamiltonian matrix.

The DMRG implementation exactly follows the algorithm presented in Schollwöck's paper, The density-matrix renormalization group in the age of matrix product states:

https://arxiv.org/abs/1008.3477

The code is organized into several directories:

  1. dmrg: contains all the DMRG code

  2. experiments: contains various Julia experiments

  3. itensor: ITensor examples

  4. lapack: contains an example of how to call a ScaLAPACK function from Julia (qrcp.jl). The example specifically calls the ScaLAPACK function dlaqps().

  5. operator_mpo: contains an MPO implementation that follows Snajberk's paper (https://edoc.ub.uni-muenchen.de/21974/1/Snajberg_Philipp.pdf). This was initially developed to generate MPOs for use with DMRG. However, ITensor's MPO generation mechanism is vastly superior. The tests contain examples of using both MPO implementations, however this code doesn't have much value. It's definitely recommended to generate MPOs using ITensor.

Julia Version

This code has been tested with Julia Version 1.6.0 on macOS:

Julia Version 1.6.0
Commit f9720dc2eb (2021-03-24 12:55 UTC)
Platform Info:
    OS: macOS (x86_64-apple-darwin19.6.0)
    CPU: Intel(R) Core(TM) i7-4960HQ CPU @ 2.60GHz
    WORD_SIZE: 64
    LIBM: libopenlibm
    LLVM: libLLVM-11.0.1 (ORCJIT, haswell)

Slow Julia Package Loading

Julia packages have notoriously long load times. This code uses some fairly heavy packages and so, not surprisingly, the initial startup time is very long. There are some ways to reduce this initial load time.

ITensor

For ITensor specifically, one can use its compile() function:

using ITensors
ITensors.compile()

For more details see section 2.7 of https://arxiv.org/pdf/2007.14822.pdf.

PackageCompiler

A more general solution is to use PackageCompiler. This can speed up loading of any package. The only penalty is that one needs to rerun the compilation to upgrade the package to a new version.

See https://julialang.github.io/PackageCompiler.jl/dev for details.

Julia Packages

To install the Julia packages required by the DMRG code and its associated tests, execute the following commands from the Julia REPL:

import Pkg
Pkg.add("TensorOperations")
Pkg.add("KrylovKit")
Pkg.add("ITensors")

The Snajberk-based MPO code requires some additional packages:

import Pkg
Pkg.add("SymPy")
Pkg.add("Parameters")

To directly compute the molecular hamiltonian matrix and compute its eigenvalue decomposition, install Arpack.

import Pkg
Pkg.add("Arpack")

DMRG Execution

The following code runs DMRG on a Heisenberg spin chain containing 2 spins (sites):

include("src/dmrg/dmrg.jl")
mpo = MPO(create_heisenberg_mpo(2))
mps = MPS(randn(2, 2))
energy = compute_ground_state!(mpo, mps)

The test test_dmrg.jl contains some good examples of DMRG execution on a few different types of systems. From the root directory of the project run

julia
include("test/dmrg/test_dmrg.jl")    

Execution time for this test is pretty long. It's partially due to package loading. The bulk of the time is spent computing the ground state of a water molecule.

ITensor

The Julia version of ITensor (https://github.com/ITensor/ITensors.jl) provides a well optimized implementation of the DMRG algorithm.

The file itensor_utils.jl provides some convenience methods for the execution of DMRG with ITensor. Note also that the examples and tests in this project use ITensor to generate MPOs. For example,

h,v = read_electron_integral_tensors("data/h2/h2.ezfio.FCIDUMP")
energy,state = molecular_dmrg(h, v)
nuclear_repulsion_energy = 0.71510433908108118
total_energy = energy + nuclear_repulsion_energy
@test isapprox(total_energy, -1.1372838344894254, atol=0.00000001)

As noted above, this project contains code (OperatorMPO) that generates MPOs using the Snajberk-based construction. However, this code is effectively worthless. The ITensor AutoMPO method is vastly superior.

One-Electron and Two-Electron Tensor Data

FCIDUMP

The file fcicump.jl provides a function to read one-electron and two-electron tensor from an FCIDUMP file:

read_electron_integral_tensors(fcidump_filename::String)

This code was specifically written to read FCIDUMP files generated by Quantum Package (https://quantumpackage.github.io/qp2).

Note that the two-electron is permuted at the end of the function:

two_electron_integral_tensor = permutedims(two_electron_integral_tensor, [3,2,1,4])

It's not clear why this permutation is necessary - and it may be possible that it's actually not necessary. However, quantum chemistry calculations using the non-permuted two-electron tensor seem to be incorrect. Additionally, the datasets at Quantaggle (https://github.com/qulacs/Quantaggle_dataset/tree/master/datasets/Small_Molecules_1) don't require this permutation.

One logical explanation is that the difference is due to the ordering of the orbitals. However, if that's the case, then the ground state energy should be the same regardless of the permutation of orbitals. So the problem seems to lie elsewhere.

Note that the hydrogen and water molecule calculations provided in this project use the permuted two-electron tensor. And the results are correct - at least as far as the author can tell.

In any case, there's some weirdness somewhere. So if you use this function for molecules other than hydrogen and water, be sure to consult with a chemist regarding this little mystery.

Quantaggle

As noted above Quantaggle provides quantum chemistry data:

https://github.com/qulacs/Quantaggle_dataset/tree/master/datasets/Small_Molecules_1

The data is conveniently stored in HDF5 format. The code below reads the one-electron and two-electron tensors (and full configuration interaction Energy) for a water molecule. First install the HDF5 package:

import Pkg
Pkg.add("HDF5")

And then:

using HDF5
io = h5open("data/h2o/H2O_sto-3g_singlet_0.96_104.5deg_0.96.hdf5", "r")
data = read(io)
one_electron_tensor = data["one_body_integrals"]
two_electron_tensor = data["two_body_integrals"]
fci_energy = data["fci_energy"]

As also noted above, there's some confusion - for the author - regarding the correct representation of the two-electron tensor. In the case of Quantaggle, it's not clear if the two-electron tensor is stored in row-major or column-major format. And, given the symmetries of this tensor, it's hard to figure out which data storage format is being used.

In any case, as previously stated, there's some weirdness somewhere. So if you use this data for molecules other than hydrogen and water, be sure to consult with a chemist regarding this other little mystery.

Direct Hamiltonian Matrix Calculation

The file molecular_hamiltonian_matrix.jl contains functions to directly generate the molecular hamiltonian matrix for any (not too large) molecule. The matrix is computed by directly evaluating the second quantization expression given arbitrary one-electron and two-electron tensors.

The hamiltonian matrix is stored in sparse format, and it's eigenvalue decomposition can be readily computed using Arpack.

For example, after installing Arpack:

import Arpack
include("src/experiments/molecules/molecular_hamiltonian_matrix.jl")

h,v = read_electron_integral_tensors("data/h2/h2.ezfio.FCIDUMP")
hamiltonian = molecular_hamiltonian_matrix(h, v)

# add nuclear repulsion part to hamiltonian
m_qp_full = m_qp + (I(16) * 0.71510433908108118)

eigenvalues,eigenvectors = Arpack.eigs(hamiltonian)
@test isapprox(eigenvalues[1], -1.1372838344894252, atol=0.0000001)

Note also that the computation of the hamiltonian matrix is optionally multithreaded. To exploit this, simply launch Julia with several threads. For example,

julia --threads 4

Matrix and MPO for Hydrogen Molecule

The file data/h2/h2_matrix_and_mpo.jl contains the hamiltonian for a hydrogen molecule, both in matrix format and MPO format.

This data is generated as follows:

h,v = read_electron_integral_tensors("data/h2/h2.ezfio.FCIDUMP")

h2_matrix = molecular_hamiltonian_matrix(h, v)

sites = ITensors.siteinds("Electron", 2)
one_electron_itensor_mpo = create_one_electron_mpo(sites, h)
two_electron_itensor_mpo = create_two_electron_mpo(sites, v)
one_electron_mpo = MPO(one_electron_itensor_mpo)
two_electron_mpo = MPO(two_electron_itensor_mpo)
h2_mpo = one_electron_mpo + two_electron_mpo

About

Density Matrix Renormalization Group (DMRG) in Julia

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published