import time
import sys
import numpy as np
import scipy
from fcdmft.solver import gmres
import pyscf
import pyscf.cc
from pyscf.lib import logger
from pyscf.cc.eom_rccsd import amplitudes_to_vector_ip, amplitudes_to_vector_ea
'''
CCSD Green's function
'''
[docs]
def greens_b_singles_ea_rhf(t1, p):
nocc, nvir = t1.shape
ds_type = t1.dtype
if p < nocc:
return -t1[p,:]
else:
p = p-nocc
result = np.zeros((nvir,), dtype=ds_type)
result[p] = 1.0
return result
[docs]
def greens_b_doubles_ea_rhf(t2, p):
nocc, _, nvir, _ = t2.shape
ds_type = t2.dtype
if p < nocc:
return -t2[p,:,:,:]
else:
return np.zeros((nocc,nvir,nvir), dtype=ds_type)
[docs]
def greens_b_vector_ea_rhf(cc, p):
return amplitudes_to_vector_ea(
greens_b_singles_ea_rhf(cc.t1, p),
greens_b_doubles_ea_rhf(cc.t2, p),
)
[docs]
def greens_e_singles_ea_rhf(t1, t2, l1, l2, p):
nocc, nvir = t1.shape
ds_type = t1.dtype
if p < nocc:
return l1[p, :]
else:
p = p-nocc
result = np.zeros((nvir,), dtype=ds_type)
result[p] = -1.0
result += np.einsum('ia,i->a', l1, t1[:,p])
result += 2*np.einsum('klca,klc->a', l2, t2[:,:,:,p])
result -= np.einsum('klca,lkc->a', l2, t2[:,:,:,p])
return result
[docs]
def greens_e_doubles_ea_rhf(t1, l1, l2, p):
nocc, nvir = t1.shape
ds_type = t1.dtype
if p < nocc:
return 2*l2[p,:,:,:] - l2[:,p,:,:]
else:
p = p-nocc
result = np.zeros((nocc,nvir,nvir), dtype=ds_type)
result[:,p,:] += -2*l1
result[:,:,p] += l1
result += 2*np.einsum('k,jkba->jab', t1[:,p], l2)
result -= np.einsum('k,jkab->jab', t1[:,p], l2)
return result
[docs]
def greens_e_vector_ea_rhf(cc, p):
return amplitudes_to_vector_ea(
greens_e_singles_ea_rhf(cc.t1, cc.t2, cc.l1, cc.l2, p),
greens_e_doubles_ea_rhf(cc.t1, cc.l1, cc.l2, p),
)
[docs]
def greens_b_singles_ip_rhf(t1, p):
nocc, nvir = t1.shape
ds_type = t1.dtype
if p < nocc:
result = np.zeros((nocc,), dtype=ds_type)
result[p] = 1.0
return result
else:
p = p-nocc
return t1[:,p]
[docs]
def greens_b_doubles_ip_rhf(t2, p):
nocc, _, nvir, _ = t2.shape
ds_type = t2.dtype
if p < nocc:
return np.zeros((nocc,nocc,nvir), dtype=ds_type)
else:
p = p-nocc
return t2[:,:,p,:]
[docs]
def greens_b_vector_ip_rhf(cc, p):
return amplitudes_to_vector_ip(
greens_b_singles_ip_rhf(cc.t1, p),
greens_b_doubles_ip_rhf(cc.t2, p),
)
[docs]
def greens_e_singles_ip_rhf(t1, t2, l1, l2, p):
nocc, nvir = t1.shape
ds_type = t1.dtype
if p < nocc:
result = np.zeros((nocc,), dtype=ds_type)
result[p] = -1.0
result += np.einsum('ia,a->i', l1, t1[p,:])
result += 2*np.einsum('ilcd,lcd->i', l2, t2[p,:,:,:])
result -= np.einsum('ilcd,ldc->i', l2, t2[p,:,:,:])
return result
else:
p = p-nocc
return -l1[:,p]
[docs]
def greens_e_doubles_ip_rhf(t1, l1, l2, p):
nocc, nvir = t1.shape
ds_type = t1.dtype
if p < nocc:
result = np.zeros((nocc, nocc, nvir), dtype=ds_type)
result[p, :, :] += -2*l1
result[:, p, :] += l1
result += 2*np.einsum('c,ijcb->ijb', t1[p,:], l2)
result -= np.einsum('c,jicb->ijb', t1[p,:], l2)
return result
else:
p = p-nocc
return -2*l2[:,:,p,:] + l2[:,:,:,p]
[docs]
def greens_e_vector_ip_rhf(cc, p):
return amplitudes_to_vector_ip(
greens_e_singles_ip_rhf(cc.t1, cc.t2, cc.l1, cc.l2, p),
greens_e_doubles_ip_rhf(cc.t1, cc.l1, cc.l2, p),
)
[docs]
def greens_func_multiply(ham, vector, linear_part, **kwargs):
return np.array(ham(vector, **kwargs) + linear_part * vector)
[docs]
def greens_func_multiply_mor(ham, vector, linear_part):
return np.array(np.dot(ham, vector) + linear_part * vector)
[docs]
def ip_shape(cc):
nocc, nvir = cc.t1.shape
return nocc + nocc*nocc*nvir
[docs]
def ea_shape(cc):
nocc, nvir = cc.t1.shape
return nvir + nocc*nvir*nvir
[docs]
class CCGF(object):
def __init__(self, mycc, tol=1e-4, verbose=None):
self._cc = mycc
self.tol = tol
if verbose:
self.verbose = verbose
else:
self.verbose = self._cc.verbose
self.stdout = sys.stdout
[docs]
def ipccsd_ao(self, ps, omega_list, mo_coeff, broadening, MOR=False, omega_mor=None):
'''
Compute IP-CCSD-GF in AO basis
MOR: model order reduction (ref: JCTC, 15, 3185-3196, 2019)
'''
eomip = pyscf.cc.eom_rccsd.EOMIP(self._cc)
eomip_imds = eomip.make_imds()
diag = eomip.get_diag()
nmo = mo_coeff.shape[1]
e_vector_mo = np.zeros([nmo, ip_shape(self._cc)], dtype=np.complex128)
for i in range(nmo):
e_vector_mo[i,:] = greens_e_vector_ip_rhf(self._cc, i)
e_vector_ao = np.einsum("pi,ix->px", mo_coeff[ps,:], e_vector_mo)
b_vector_mo = np.zeros([ip_shape(self._cc), nmo], dtype=np.complex128)
for i in range(nmo):
b_vector_mo[:,i] = greens_b_vector_ip_rhf(self._cc, i)
b_vector_ao = np.einsum("xi,ip->xp", b_vector_mo, mo_coeff.T[:,ps])
gf_ao = np.zeros((len(ps), len(ps), len(omega_list)), dtype=np.complex128)
if MOR:
omega_comp = omega_mor
else:
omega_comp = omega_list
for ip, p in enumerate(ps):
if MOR:
X_vec = np.zeros((len(b_vector_ao[:,ip]), len(omega_mor)), dtype=complex)
x0 = None
for iomega in range(len(omega_comp))[::-1]:
curr_omega = omega_comp[iomega]
def matr_multiply(vector, args=None):
return greens_func_multiply(eomip.matvec, vector, curr_omega - 1j * broadening, imds=eomip_imds)
diag_w = diag + curr_omega-1j*broadening
if x0 is None or MOR:
x0 = b_vector_ao[:,ip]/diag_w
solver = gmres.GMRES(matr_multiply, b_vector_ao[:,ip], x0, diag_w, tol=self.tol)
cput1 = (time.process_time(), time.perf_counter())
sol = solver.solve().reshape(-1)
cput1 = logger.timer(self, 'IPGF GMRES orbital p = %d/%d, freq w = %d/%d (%d iterations)'%(
ip+1,len(ps),iomega+1,len(omega_comp),solver.niter), *cput1)
x0 = sol
if MOR:
# Construct MOR subspace vectors
X_vec[:,iomega] = sol
if not MOR:
for iq, q in enumerate(ps):
gf_ao[ip,iq,iomega] = -np.dot(e_vector_ao[iq,:], sol)
if MOR:
# QR decomposition to orthorgonalize X_vec
S_vec, R_vec = scipy.linalg.qr(X_vec, mode='economic')
# Construct reduced order model
n_mor = len(omega_mor)
HS = np.zeros_like(S_vec)
for i in range(n_mor):
HS[:,i] = greens_func_multiply(eomip.matvec, S_vec[:,i], 0., imds=eomip_imds)
# Reduced Hamiltonian, b_vector, diag
H_mor = np.dot(S_vec.T.conj(), HS)
b_vector_mor = np.dot(S_vec.T.conj(), b_vector_ao[:,ip])
diag_mor = H_mor.diagonal()
x0 = None
for iomega in range(len(omega_list))[::-1]:
curr_omega = omega_list[iomega]
def matr_multiply_mor(vector, args=None):
return greens_func_multiply_mor(H_mor, vector, curr_omega - 1j * broadening)
diag_w = diag_mor + curr_omega-1j*broadening
if x0 is None:
x0 = b_vector_mor/diag_w
solver = gmres.GMRES(matr_multiply_mor, b_vector_mor, x0, diag_w, tol=self.tol*0.01)
cput1 = (time.process_time(), time.perf_counter())
sol = solver.solve().reshape(-1)
cput1 = logger.timer(self, 'MOR IPGF GMRES orbital p = %d/%d, freq w = %d/%d (%d iterations)'%(
ip+1,len(ps),iomega+1,len(omega_list),solver.niter), *cput1)
x0 = sol
for iq, q in enumerate(ps):
e_vector_mor = np.dot(e_vector_ao[iq], S_vec)
gf_ao[ip,iq,iomega] = -np.dot(e_vector_mor, sol)
return gf_ao
[docs]
def eaccsd_ao(self, ps, omega_list, mo_coeff, broadening, MOR=False, omega_mor=None):
'''
Compute EA-CCSD-GF in AO basis
MOR: model order reduction (ref: JCTC, 15, 3185-3196, 2019)
'''
eomea = pyscf.cc.eom_rccsd.EOMEA(self._cc)
eomea_imds = eomea.make_imds()
diag = eomea.get_diag()
nmo = mo_coeff.shape[1]
e_vector_mo = np.zeros([nmo, ea_shape(self._cc)], dtype=np.complex128)
for i in range(nmo):
e_vector_mo[i,:] = greens_e_vector_ea_rhf(self._cc, i)
e_vector_ao = np.einsum("pi,ix->px", mo_coeff[ps,:], e_vector_mo)
b_vector_mo = np.zeros([ea_shape(self._cc), nmo], dtype=np.complex128)
for i in range(nmo):
b_vector_mo[:,i] = greens_b_vector_ea_rhf(self._cc, i)
b_vector_ao = np.einsum("xi,ip->xp", b_vector_mo, mo_coeff.T[:,ps])
gf_ao = np.zeros((len(ps), len(ps), len(omega_list)), dtype=np.complex128)
if MOR:
omega_comp = omega_mor
else:
omega_comp = omega_list
for iq, q in enumerate(ps):
if MOR:
X_vec = np.zeros((len(b_vector_ao[:,iq]), len(omega_mor)), dtype=complex)
x0 = None
for iomega in range(len(omega_comp)):
curr_omega = omega_comp[iomega]
def matr_multiply(vector, args=None):
return greens_func_multiply(eomea.matvec, vector, -curr_omega - 1j * broadening, imds=eomea_imds)
diag_w = diag + (-curr_omega-1j*broadening)
if x0 is None or MOR:
x0 = b_vector_ao[:,iq]/diag_w
solver = gmres.GMRES(matr_multiply, b_vector_ao[:,iq], x0, diag_w, tol=self.tol)
cput1 = (time.process_time(), time.perf_counter())
sol = solver.solve().reshape(-1)
if MOR:
# Construct MOR subspace vectors
X_vec[:,iomega] = sol
cput1 = logger.timer(self, 'EAGF GMRES orbital q = %d/%d, freq w = %d/%d (%d iterations)'%(
iq+1,len(ps),iomega+1,len(omega_comp),solver.niter), *cput1)
x0 = sol
if not MOR:
for ip, p in enumerate(ps):
gf_ao[ip,iq,iomega] = np.dot(e_vector_ao[ip,:], sol)
if MOR:
# QR decomposition to orthorgonalize X_vec
S_vec, R_vec = scipy.linalg.qr(X_vec, mode='economic')
# Construct reduced order model
n_mor = len(omega_mor)
HS = np.zeros_like(S_vec)
for i in range(n_mor):
HS[:,i] = greens_func_multiply(eomea.matvec, S_vec[:,i], 0., imds=eomea_imds)
# Reduced Hamiltonian, b_vector, diag
H_mor = np.dot(S_vec.T.conj(), HS)
b_vector_mor = np.dot(S_vec.T.conj(), b_vector_ao[:,iq])
diag_mor = H_mor.diagonal()
x0 = None
for iomega in range(len(omega_list)):
curr_omega = omega_list[iomega]
def matr_multiply_mor(vector, args=None):
return greens_func_multiply_mor(H_mor, vector, -curr_omega - 1j * broadening)
diag_w = diag_mor + (-curr_omega-1j*broadening)
if x0 is None:
x0 = b_vector_mor/diag_w
solver = gmres.GMRES(matr_multiply_mor, b_vector_mor, x0, diag_w, tol=self.tol*0.01)
cput1 = (time.process_time(), time.perf_counter())
sol = solver.solve().reshape(-1)
cput1 = logger.timer(self, 'MOR EAGF GMRES orbital q = %d/%d, freq w = %d/%d (%d iterations)'%(
iq+1,len(ps),iomega+1,len(omega_list),solver.niter), *cput1)
x0 = sol
for ip, p in enumerate(ps):
e_vector_mor = np.dot(e_vector_ao[ip], S_vec)
gf_ao[ip,iq,iomega] = np.dot(e_vector_mor, sol)
return gf_ao
[docs]
def ipccsd_mo(self, ps, qs, omega_list, broadening, MOR=False, omega_mor=None):
'''
Compute IP-CCSD-GF in MO basis
MOR: model order reduction (ref: JCTC, 15, 3185-3196, 2019)
'''
eomip = pyscf.cc.eom_rccsd.EOMIP(self._cc)
eomip_imds = eomip.make_imds()
diag = eomip.get_diag()
e_vector = list()
for q in qs:
e_vector.append(greens_e_vector_ip_rhf(self._cc, q))
gfvals = np.zeros((len(ps), len(qs), len(omega_list)), dtype=complex)
if MOR:
omega_comp = omega_mor
else:
omega_comp = omega_list
for ip, p in enumerate(ps):
b_vector = greens_b_vector_ip_rhf(self._cc, p)
if MOR:
X_vec = np.zeros((len(b_vector), len(omega_mor)), dtype=complex)
for iomega in range(len(omega_comp)):
curr_omega = omega_comp[iomega]
def matr_multiply(vector, args=None):
return greens_func_multiply(eomip.matvec, vector, curr_omega - 1j * broadening, imds=eomip_imds)
diag_w = diag + curr_omega-1j*broadening
x0 = b_vector/diag_w
solver = gmres.GMRES(matr_multiply, b_vector, x0, diag_w, tol=self.tol)
cput1 = (time.process_time(), time.perf_counter())
sol = solver.solve().reshape(-1)
if MOR:
# Construct MOR subspace vectors
X_vec[:,iomega] = sol
cput1 = logger.timer(self, 'IPGF GMRES orbital p = %d/%d, freq w = %d/%d (%d iterations)'%(
ip+1,len(ps),iomega+1,len(omega_comp),solver.niter), *cput1)
x0 = sol
if not MOR:
for iq, q in enumerate(qs):
gfvals[ip,iq,iomega] = -np.dot(e_vector[iq], sol)
if MOR:
# QR decomposition to orthorgonalize X_vec
S_vec, R_vec = scipy.linalg.qr(X_vec, mode='economic')
# Construct reduced order model
n_mor = len(omega_mor)
HS = np.zeros_like(S_vec)
for i in range(n_mor):
HS[:,i] = greens_func_multiply(eomip.matvec, S_vec[:,i], 0., imds=eomip_imds)
# Reduced Hamiltonian, b_vector, diag
H_mor = np.dot(S_vec.T.conj(), HS)
b_vector_mor = np.dot(S_vec.T.conj(), b_vector)
diag_mor = H_mor.diagonal()
for iomega in range(len(omega_list)):
curr_omega = omega_list[iomega]
def matr_multiply_mor(vector, args=None):
return greens_func_multiply_mor(H_mor, vector, curr_omega - 1j * broadening)
diag_w = diag_mor + curr_omega-1j*broadening
x0 = b_vector_mor/diag_w
solver = gmres.GMRES(matr_multiply_mor, b_vector_mor, x0, diag_w, tol=self.tol*0.01)
cput1 = (time.process_time(), time.perf_counter())
sol = solver.solve().reshape(-1)
cput1 = logger.timer(self, 'MOR IPGF GMRES orbital p = %d/%d, freq w = %d/%d (%d iterations)'%(
ip+1,len(ps),iomega+1,len(omega_list),solver.niter), *cput1)
for iq, q in enumerate(qs):
e_vector_mor = np.dot(e_vector[iq], S_vec)
gfvals[ip,iq,iomega] = -np.dot(e_vector_mor, sol)
return gfvals
[docs]
def eaccsd_mo(self, ps, qs, omega_list, broadening, MOR=False, omega_mor=None):
'''
Compute EA-CCSD-GF in MO basis
MOR: model order reduction (ref: JCTC, 15, 3185-3196, 2019)
'''
eomea = pyscf.cc.eom_rccsd.EOMEA(self._cc)
eomea_imds = eomea.make_imds()
diag = eomea.get_diag()
e_vector = list()
for p in ps:
e_vector.append(greens_e_vector_ea_rhf(self._cc, p))
gfvals = np.zeros((len(ps), len(qs), len(omega_list)), dtype=complex)
if MOR:
omega_comp = omega_mor
else:
omega_comp = omega_list
for iq, q in enumerate(qs):
b_vector = greens_b_vector_ea_rhf(self._cc, q)
if MOR:
X_vec = np.zeros((len(b_vector), len(omega_mor)), dtype=complex)
for iomega in range(len(omega_comp)):
curr_omega = omega_comp[iomega]
def matr_multiply(vector, args=None):
return greens_func_multiply(eomea.matvec, vector, -curr_omega - 1j * broadening, imds=eomea_imds)
diag_w = diag + (-curr_omega-1j*broadening)
x0 = b_vector/diag_w
solver = gmres.GMRES(matr_multiply, b_vector, x0, diag_w, tol=self.tol)
cput1 = (time.process_time(), time.perf_counter())
sol = solver.solve().reshape(-1)
if MOR:
# Construct MOR subspace vectors
X_vec[:,iomega] = sol
cput1 = logger.timer(self, 'EAGF GMRES orbital q = %d/%d, freq w = %d/%d (%d iterations)'%(
iq+1,len(ps),iomega+1,len(omega_comp),solver.niter), *cput1)
x0 = sol
if not MOR:
for ip, p in enumerate(ps):
gfvals[ip,iq,iomega] = np.dot(e_vector[ip], sol)
if MOR:
# QR decomposition to orthorgonalize X_vec
S_vec, R_vec = scipy.linalg.qr(X_vec, mode='economic')
# Construct reduced order model
n_mor = len(omega_mor)
HS = np.zeros_like(S_vec)
for i in range(n_mor):
HS[:,i] = greens_func_multiply(eomea.matvec, S_vec[:,i], 0., imds=eomea_imds)
# Reduced Hamiltonian, b_vector, diag
H_mor = np.dot(S_vec.T.conj(), HS)
b_vector_mor = np.dot(S_vec.T.conj(), b_vector)
diag_mor = H_mor.diagonal()
for iomega in range(len(omega_list)):
curr_omega = omega_list[iomega]
def matr_multiply_mor(vector, args=None):
return greens_func_multiply_mor(H_mor, vector, -curr_omega - 1j * broadening)
diag_w = diag_mor + (-curr_omega-1j*broadening)
x0 = b_vector_mor/diag_w
solver = gmres.GMRES(matr_multiply_mor, b_vector_mor, x0, diag_w, tol=self.tol*0.01)
cput1 = (time.process_time(), time.perf_counter())
sol = solver.solve().reshape(-1)
cput1 = logger.timer(self, 'MOR EAGF GMRES orbital q = %d/%d, freq w = %d/%d (%d iterations)'%(
iq+1,len(ps),iomega+1,len(omega_list),solver.niter), *cput1)
x0 = sol
for ip, p in enumerate(ps):
e_vector_mor = np.dot(e_vector[ip], S_vec)
gfvals[ip,iq,iomega] = np.dot(e_vector_mor, sol)
return gfvals
[docs]
def get_gf(self, p, q, omega_list, broadening):
return (self.ipccsd_mo(p, q, omega_list, broadening),
self.eaccsd_mo(p, q, omega_list, broadening))