Block Davidson Algorithm

Posted by on August 24, 2017 · 7 mins read

The Block Davidson algorithm. My attempt at understanding the algorithm so that I can manipulate it specifically for calculating linear response properties of the generalized Kohn-Sham based random phase approximation (GKS-RPA) method. (Polarizabilities and excitation energies)

import numpy as np
import math
import time

# Build a fake sparse symmetric matrix
n = 1900
print('Dimension of the matrix',n,'*',n)
sparsity = 0.001
A = np.zeros((n,n))
for i in range(0,n) :
    A[i,i] = i-9
A = A + sparsity*np.random.randn(n,n)
A = (A.T + A)/2

tol = 1e-9             # Convergence tolerance
mmax = 20              # Maximum number of iterations

# Setup the subspace trial vectors
k = 4
print ('No. of start vectors:',k)
neig = 3
print ('No. of desired Eigenvalues:',neig)
t = np.eye(n,k) # initial trial vectors
v = np.zeros((n,n)) # holder for trial vectors as iterations progress
I = np.eye(n) # n*n identity matrix
ritz = np.zeros((n,n))
f = np.zeros((n,n))
#-------------------------------------------------------------------------------
# Begin iterations
#-------------------------------------------------------------------------------
start = time.time()
iter = 0
for m in range(k,mmax,k):
    iter = iter + 1
    print ("Iteration no:", iter)
    if iter==1:  # for first iteration add normalized guess vectors to matrix v
        for l in range(m):
            v[:,l] = t[:,l]/(np.linalg.norm(t[:,l]))
    # Matrix-vector products, form the projected Hamiltonian in the subspace
    T = np.linalg.multi_dot([v[:,:m].T,A,v[:,:m]]) # selects fastest evaluation order
    w, vects = np.linalg.eigh(T) # Diagonalize the subspace Hamiltonian
    jj = 0
    s = w.argsort()
    ss = w[s]
    #***************************************************************************
    # For each eigenvector of T build a Ritz vector, precondition it and check
    # if the norm is greater than a set threshold.
    #***************************************************************************
    for ii in range(m): #for each new eigenvector of T
        f = np.diag(1./ np.diag((np.diag(np.diag(A)) - w[ii]*I)))
#        print (f)
        ritz[:,ii] = np.dot(f,np.linalg.multi_dot([(A-w[ii]*I),v[:,:m],vects[:,ii]]))
        if np.linalg.norm(ritz[:,ii]) > 1e-7 :
            ritz[:,ii] = ritz[:,ii]/(np.linalg.norm(ritz[:,ii]))
            v[:,m+jj] = ritz[:,ii]
            jj = jj + 1
    q, r = np.linalg.qr(v[:,:m+jj-1])
    for kk in range(m+jj-1):
        v[:,kk] = q[:,kk]
    for ii in range(neig):
        print (ss[ii])
    if iter==1:
        check_old = ss[:neig]
        check_new = 1
    elif iter==2:
        check_new = ss[:neig]
    else:
        check_old = check_new
        check_new = ss[:neig]
    check = np.linalg.norm(check_new - check_old)
    if check < tol:
        print('Block Davidson converged at iteration no.:',iter)
        break
end = time.time()
print ('Block Davidson time:',end-start)
start = time.time()
eig, eigvecs = np.linalg.eig(A)
end = time.time()
s = eig.argsort()
ss = eig[s]
print('Exact Diagonalization:')
for ii in range(neig):
    print(ss[ii])
#print(ss[:neig])
print ('Exact Diagonalization time:',end-start)

Check out the algorithm discussion