def energy_mp2_aux(mo, se, both_sides=False): ''' Calculates the two-body contribution to the electronic energy using the MOs and the auxiliary representation of the self-energy according the the MP2 form of the Galitskii-Migdal formula. Parameters ---------- mo : (n) ndarray MO energies se : Aux auxiliary representation of self-energy both_sides : bool, optional if True, calculate both halves of the functional and return the mean, default False Returns ------- e2b : float two-body contribution to electronic energy ''' if isinstance(se, (tuple, list)): n = len(se) if util.iter_depth(mo) == 2: return sum([ energy_mp2_aux(mo[i], se[i], both_sides=both_sides) for i in range(n) ]) / n else: return sum([ energy_mp2_aux(mo, se[i], both_sides=both_sides) for i in range(n) ]) / n nphys = se.nphys occ = mo < se.chempot vir = mo >= se.chempot vxk = se.v_vir[occ] dxk = 1.0 / util.outer_sum([mo[occ], -se.e_vir]) e2b = util.einsum('xk,xk,xk->', vxk, vxk.conj(), dxk) if both_sides: vxk = se.v_occ[vir] dxk = -1.0 / util.outer_sum([mo[vir], -se.e_occ]) e2b += util.einsum('xk,xk,xk->', vxk, vxk.conj(), dxk) e2b *= 0.5 return np.ravel(e2b.real)[0]
def test_2d_uu(self): m = np.random.random((2, 5, 5)) c1 = np.random.random((2, 5, 10)) c2 = np.random.random((2, 5, 10)) t1 = util.ao2mo(m, c1, c2) t2 = util.einsum('spq,spi,sqj->sij', m, c1, c2) self.assertAlmostEqual(np.max(np.absolute(t1 - t2)), 0, 8)
def test_2d_rr(self): m = np.random.random((5, 5)) c1 = np.random.random((5, 10)) c2 = np.random.random((5, 10)) t1 = util.ao2mo(m, c1, c2) t2 = util.einsum('pq,pi,qj->ij', m, c1, c2) self.assertAlmostEqual(np.max(np.absolute(t1 - t2)), 0, 8)
def build_se(self): if self.rpa is None: self.solve_casida() e_rpa, v_rpa, xpy = self.rpa naux_gf = self.gf.naux chempot = self.chempot c = self.gf.v[:self.nphys] co = c[:, self.gf.e < chempot] cv = c[:, self.gf.e >= chempot] xyia = util.mo2qo(self.eri, c, co, cv).reshape(self.nphys, naux_gf, -1) omega = util.einsum('xyk,ks->xys', xyia, xpy) e_gf = self.gf.e e_rpa_s = np.outer(np.sign(e_gf - chempot), e_rpa) e = util.dirsum('i,ij->ij', e_gf, e_rpa_s).flatten() v = omega.reshape((self.nphys, -1)) self.se = aux.Aux(e, v, chempot=self.chempot) log.write('naux (se,build) = %d\n' % self.se.naux, self.verbose) return self.se
def test_mo2qo(self): m = np.random.random((5, 5, 5, 5)) c1 = np.random.random((5, 10)) c2 = np.random.random((5, 10)) c3 = np.random.random((5, 10)) t1 = util.mo2qo(m, c1, c2, c3) t2 = util.einsum('pqrs,qi,rj,sk->pijk', m, c1, c2, c3) self.assertAlmostEqual(np.max(np.absolute(t1 - t2)), 0, 8)
def test_4d_rr(self): m = np.random.random((5, 5, 5, 5)) c1 = np.random.random((5, 10)) c2 = np.random.random((5, 10)) c3 = np.random.random((5, 10)) c4 = np.random.random((5, 10)) t1 = util.ao2mo(m, c1, c2, c3, c4) t2 = util.einsum('pqrs,pi,qj,rk,sl->ijkl', m, c1, c2, c3, c4) self.assertAlmostEqual(np.max(np.absolute(t1 - t2)), 0, 8)
def test_4d_uu(self): m = np.random.random((2, 2, 5, 5, 5, 5)) c1 = np.random.random((2, 5, 10)) c2 = np.random.random((2, 5, 10)) c3 = np.random.random((2, 5, 10)) c4 = np.random.random((2, 5, 10)) t1 = util.ao2mo(m, c1, c2, c3, c4) t2 = util.einsum('abpqrs,api,aqj,brk,bsl->abijkl', m, c1, c2, c3, c4) self.assertAlmostEqual(np.max(np.absolute(t1 - t2)), 0, 8)
def block_lanczos_1block(se, h_phys, **kwargs): ''' The above function simplifies significantly in the case of nmom=1. ''' qr = _get_qr_function(method=kwargs.get('qr', 'cholesky')) v, b = qr(se.v.T) m = util.einsum('ip,i,iq->pq', v, se.e, v) return [h_phys, m], [ b, ]
def function(x, fit): ''' Computes the target function. Parameters ---------- x : (n) ndarray vector of fitting variables fit : FitHelper helper class Returns ------- f : float sum of f(x) ''' e, v, sf, dsf = fit.generate(x) f = util.einsum('w,wxy->', fit.wts, dsf.real**2) f += util.einsum('w,wxy->', fit.wts, dsf.imag**2) return f
def setUpClass(self): import warnings warnings.simplefilter('ignore', FutureWarning) self.m = mol.Molecule(atoms='O 0 0 0; H 0 0 1', basis='cc-pvdz', spin=1) self.uhf = hf.UHF(self.m).run() self.uhf_df = hf.UHF(self.m, with_df=True).run() self.eri = self.uhf.eri_mo self.eri_df = self.uhf_df.eri_mo self.se = (aux.build_ump2(self.uhf_df.e, util.einsum('qij,sqkl->sijkl', self.eri_df[0], self.eri_df), chempot=self.uhf_df.chempot, wtol=0), aux.build_ump2(self.uhf_df.e[::-1], util.einsum('qij,sqkl->sijkl', self.eri_df[1], self.eri_df[::-1]), chempot=self.uhf.chempot[::-1], wtol=0)) self.e_mp2 = -0.15197757655845123 self.e_mp2_scs = -0.1502359064727459
def get_fock(self, rdm1, basis='ao'): ''' Builds the Fock matrix according to the UHF functional. Input arrays may be spin-free or indexed for alpha and beta spins. Parameters ---------- rdm1 : (n,n) or (2,n,n) array one-body reduced density matrix Returns ------- fock : (2,n,n) array Fock matrix ''' c = self.c if basis == 'mo' else np.eye(self.nao) dm = util.einsum('...ij,...pi,...qj->...pq', rdm1, c, c) fock = self._pyscf.get_fock(dm=dm) fock = util.einsum('...pq,...pi,...qj->...ij', fock, c, c) return fock
def get_fock(self, rdm1, basis='ao'): ''' Builds the Fock matrix according to the RHF functional. Parameters ---------- rdm1 : (n,n) ndarray one-body reduced density matrix basis : str, optional input basis of `rdm1`, and output basis of `fock`, default 'ao' Returns ------- fock : (n,n) array Fock matrix ''' c = self.c if basis == 'mo' else np.eye(self.nao) dm = util.einsum('ij,pi,qj->pq', rdm1, c, c) fock = self._pyscf.get_fock(dm=dm) fock = util.einsum('pq,pi,qj->ij', fock, c, c) return fock
def setUpClass(self): import warnings warnings.simplefilter('ignore', FutureWarning) self.m = mol.Molecule(atoms='O 0 0 0; H 0 0 1; H 0 1 0', basis='cc-pvdz') self.rhf = hf.RHF(self.m).run() self.rhf_df = hf.RHF(self.m, with_df=True).run() self.eri = self.rhf.eri_mo self.eri_df = self.rhf_df.eri_mo self.se = aux.build_rmp2(self.rhf_df.e, util.einsum('qij,qkl->ijkl', self.eri_df, self.eri_df), chempot=self.rhf_df.chempot, wtol=0) self.e_mp2 = -0.20905684700662164 self.e_mp2_scs = -0.20503556854447708
def ao2mo_2d(array, c_a, c_b): ''' Transforms the basis of a 2d array. Parameters ---------- array : (p,q) array array to be transformed c_a : (p,i) array vectors to transform first dimension of `array` c_b : (q,j) array vectors to transform second dimension of `array` Returns ------- trans : (i,j) ndarray transformed array ''' trans = einsum('pq,pi,qj->ij', array, c_a, c_b) return trans
def objective(x, fit): ''' Computes the objective function and gradient. Parameters ---------- x : (n) ndarray vector of fitting variables fit : FitHelper helper class Returns ------- f : float sum of f(x) dx : (n) ndarray vector of the derivative of f(x) ''' e, v, sf, dsf = fit.generate(x) r = fit.build_denominator(e, v) gv = util.einsum('xk,wk->wxk', v, r) ge = util.einsum('wxk,wyk->wxyk', gv, gv) f = util.einsum('w,wxy->', fit.wts, dsf.real**2) f += util.einsum('w,wxy->', fit.wts, dsf.imag**2) de = util.einsum('w,wxy,wxyk->k', fit.wts, dsf.real, ge.real) de += util.einsum('w,wxy,wxyk->k', fit.wts, dsf.imag, ge.imag) de *= 2 dv = util.einsum('w,wzx,wxk->zk', fit.wts, dsf.real, gv.real) dv += util.einsum('w,wzx,wxk->zk', fit.wts, dsf.imag, ge.imag) dv *= 4 return f, fit.pack(de, dv)
def solve_casida(self): #TODO: this step is n^6 and inefficient in memory, rethink e_ia = util.outer_sum([-self.gf.e_occ, self.gf.e_vir]) co = self.gf.v[:self.nphys, self.gf.e < self.chempot] cv = self.gf.v[:self.nphys, self.gf.e >= self.chempot] iajb = util.ao2mo(self.eri, co, cv, co, cv).reshape((e_ia.size, ) * 2) apb = np.diag(e_ia.flatten()) + 4.0 * iajb amb = np.diag(np.sqrt(e_ia.flatten())) h_rpa = util.dots((amb, apb, amb)) e_rpa, v_rpa = util.eigh(h_rpa) e_rpa = np.sqrt(e_rpa) xpy = util.einsum('ij,jk,k->ik', amb, v_rpa, 1.0 / np.sqrt(e_rpa)) xpy *= np.sqrt(2.0) self.rpa = (e_rpa, v_rpa, xpy) return self.rpa
def get_emp2(self, e, v, mask, spin): fac = 0.5 if np.all( self.uhf.e[spin][mask] < self.uhf.chempot[spin]) else -0.5 return util.einsum( 'xk,xk->', v[mask]**2, fac / (self.uhf.e[spin][mask, None] - e[None, :])).ravel()[0]
def build_ump2_part_se_direct(eo, ev, xija, grid, chempot=0.0, ordering='feynman'): ''' Builds a set of auxiliaries representing all (i,j,a) or (a,b,i) diagrams for an unrestricted reference. Poles are summed straight into the self-energy and returns an `ndarray` instead of `Aux`. Parameters ---------- eo : (o) ndarray occupied (virtual) energies ev : (v) ndarray virtual (occupied) energies xija : (n,o,o,v) two-electron integrals indexed as physical, occupied, occupied, virtual (physical, virtual, virtual, occupied) grid : (k) ImFqGrid, ImFqQuad or ReFqGrid grid chempot : tuple of float, optional chemical potential for alpha, beta (beta, alpha) spin ordering : str ordering of the poles {'feynman', 'advanced', 'retarded'} (default 'feynman') Returns ------- se : (k,n,n) ndarray frequency-dependent self-energy ''' if not _is_tuple(chempot): chempot = (chempot, chempot) #TODO write in C if grid.axis == 'imag': if ordering == 'feynman': get_s = lambda x: np.sign(x) elif ordering == 'advanced': get_s = lambda x: np.ones(x.shape, dtype=types.int64) elif ordering == 'retarded': get_s = lambda x: -np.ones(x.shape, dtype=types.int64) else: get_s = lambda x: 0.0 w = grid.prefac * grid.values nphys, nocca, _, nvira = xija[0].shape se = np.zeros((grid.shape[0], nphys, nphys), dtype=types.complex128) eova = util.outer_sum([eo[0], -ev[0]]).flatten() - chempot[0] eovb = util.outer_sum([eo[1], -ev[1]]).flatten() - chempot[0] for i in range(nocca): ei_a = eo[0][i] + eova ei_b = eo[0][i] + eovb vi_a = xija[0][:, i].reshape((nphys, -1)) vip_a = xija[0][:, :, i].reshape((nphys, -1)) vi_b = xija[1][:, i].reshape((nphys, -1)) di_a = 1.0 / util.outer_sum([w, -ei_a + get_s(ei_a) * grid.eta * 1.0j]) di_b = 1.0 / util.outer_sum([w, -ei_b + get_s(ei_b) * grid.eta * 1.0j]) se += util.einsum('wk,xk,yk->wxy', di_a, vi_a, (vi_a - vip_a).conj()) se += util.einsum('wk,xk,yk->wxy', di_b, vi_b, (vi_b).conj()) return se
def hessian(x, fit): ''' Computes the Hessian of the function. Parameters ---------- x : (n) ndarray vector of fitting variables fit : FitHelper helper class Returns ------- hx : (n,n) ndarray matrix of the Hessian of f(x) ''' e, v, sf, dsf = fit.generate(x) r = fit.build_denominator(e, v) gv = util.einsum('xk,wk->wxk', v, r) ge = util.einsum('wxk,wyk->wxyk', gv, gv) gee = util.einsum('wk,wxyk->wxyk', r, ge) * 2 gev = util.einsum('wk,wxk->wxk', r, gv) gvv = r hee = util.einsum('w,wxyk,wxyl->kl', fit.wts, ge.real, ge.real) hee += util.einsum('w,wxyk,wxyl->kl', fit.wts, ge.imag, ge.imag) hee *= 2 dee = util.einsum('w,wxy,wxyk->k', fit.wts, dsf.real, gee.real) dee += util.einsum('w,wxy,wxyk->k', fit.wts, dsf.imag, gee.imag) dee *= 2 diag = util.diagonal(hee) diag += dee hev = util.einsum('w,wxzk,wxl->kzl', fit.wts, ge.real, gv.real) hev += util.einsum('w,wxzk,wxl->kzl', fit.wts, ge.imag, gv.imag) hev *= 4 dev = util.einsum('w,wxz,wxl->zl', fit.wts, dsf.real, gev.real) dev += util.einsum('w,wxz,wxl->zl', fit.wts, dsf.imag, gev.imag) dev *= 4 diag = util.diagonal(hev, axis1=0, axis2=2) diag += dev hev = hev.reshape((fit.naux, fit.ncoup)) hvv = util.einsum('w,wyk,wxl->xkyl', fit.wts, gv.real, gv.real) hvv += util.einsum('w,wyk,wxl->xkyl', fit.wts, gv.imag, gv.imag) hvv *= 4 dvv = util.einsum('w,wxk,wxl->kl', fit.wts, gv.real, gv.real) dvv += util.einsum('w,wxk,wxl->kl', fit.wts, gv.imag, gv.imag) dvv *= 4 diag = util.diagonal(hvv, axis1=0, axis2=2) diag += dvv[:, :, None] dvv = util.einsum('w,wxy,wl->xyl', fit.wts, dsf.real, gvv.real) dvv += util.einsum('w,wxy,wl->xyl', fit.wts, dsf.imag, gvv.imag) dvv *= 4 diag = util.diagonal(hvv, axis1=1, axis2=3) diag += dvv hvv = hvv.reshape((fit.ncoup, fit.ncoup)) h = np.block([[hee, hev], [hev.T, hvv]]) return h
def test_as_spectrum(self): se = self.se f1 = util.einsum('xk,yk,wk->wxy', se.v, se.v, se.build_denominator(self.imfq, se.e, se.v, chempot=se.chempot)) f2 = se.as_spectrum(self.imfq) self.assertAlmostEqual(np.max(np.absolute(f1 - f2)), 0, 8)
def get_1p_or_1h(self): occa, occb, vira, virb = self._get_slices() self.e_mp2 = 0 hs = [] for a, b in [(0, 1), (1, 0)]: if a == 0: occa, occb, vira, virb = self._get_slices() else: occb, occa, virb, vira = self._get_slices() eri_aa_ovov = self.eri[a, a][occa, vira, occa, vira] eri_ab_ovov = self.eri[a, b][occa, vira, occb, virb] eo_a = self.hf.e[a][occa] eo_b = self.hf.e[b][occb] ev_a = self.hf.e[a][vira] ev_b = self.hf.e[b][virb] t2_aa = eri_aa_ovov.copy() t2_aa /= util.dirsum('i,a,j,b->iajb', eo_a, -ev_a, eo_a, -ev_a) t2a_aa = t2_aa - t2_aa.swapaxes(0, 2).copy() t2_ab = eri_ab_ovov.copy() t2_ab /= util.dirsum('i,a,j,b->iajb', eo_a, -ev_a, eo_b, -ev_b) self.e_mp2 += util.einsum('iajb,iajb->', t2a_aa, eri_aa_ovov) * 0.5 self.e_mp2 += util.einsum('iajb,iajb->', t2_ab, eri_ab_ovov) * 0.5 h = np.diag(eo_a) h += util.einsum('a,iakb,jakb->ij', ev_a, t2a_aa, t2a_aa) h += util.einsum('a,iakb,jakb->ij', ev_a, t2_ab, t2_ab) h += util.einsum('a,ibka,jbka->ij', ev_b, t2_ab, t2_ab) h -= util.einsum('k,iakb,jakb->ij', eo_a, t2a_aa, t2a_aa) * 0.5 h -= util.einsum('k,iakb,jakb->ij', eo_b, t2_ab, t2_ab) * 0.5 h -= util.einsum('k,iakb,jakb->ij', eo_b, t2_ab, t2_ab) * 0.5 h -= util.einsum('i,iakb,jakb->ij', eo_a, t2a_aa, t2a_aa) * 0.25 h -= util.einsum('i,iakb,jakb->ij', eo_a, t2_ab, t2_ab) * 0.25 h -= util.einsum('i,iakb,jakb->ij', eo_a, t2_ab, t2_ab) * 0.25 h -= util.einsum('j,iakb,jakb->ij', eo_a, t2a_aa, t2a_aa) * 0.25 h -= util.einsum('j,iakb,jakb->ij', eo_a, t2_ab, t2_ab) * 0.25 h -= util.einsum('j,iakb,jakb->ij', eo_a, t2_ab, t2_ab) * 0.25 h += util.einsum('iakb,jakb->ij', t2a_aa, eri_aa_ovov) * 0.5 h -= util.einsum('iakb,jbka->ij', t2a_aa, eri_aa_ovov) * 0.5 h += util.einsum('iakb,jakb->ij', t2_ab, eri_ab_ovov) h += util.einsum('jakb,iakb->ij', t2a_aa, eri_aa_ovov) * 0.5 h -= util.einsum('jakb,kaib->ij', t2a_aa, eri_aa_ovov) * 0.5 h += util.einsum('jakb,iakb->ij', t2_ab, eri_ab_ovov) hs.append(h) self.h_1p_or_1h = tuple(hs)
def test_build_derivative(self): se = self.se df1 = -util.einsum('xk,yk,wk->wxy', se.v, se.v, se.build_denominator(self.imfq, se.e, se.v, chempot=se.chempot)**2) df2 = se.build_derivative(self.imfq, se.e, se.v, chempot=se.chempot) self.assertAlmostEqual(np.max(np.absolute(df1 - df2)), 0, 8)
def run(self): t_amp = np.zeros((self.nvir, self.nvir, self.nocc, self.nocc), dtype=types.float64) o = slice(None, self.nocc) v = slice(self.nocc, None) opdm_corr = np.zeros((self.nso,)*2, dtype=types.float64) opdm_ref = opdm_corr.copy() opdm_ref[o,o] = np.eye(self.nocc) tpdm_corr = np.zeros((self.nso,)*4, dtype=types.float64) x = opdm_corr.copy() eija = util.outer_sum([-self.e[v], -self.e[v], self.e[o], self.e[o]]) eija = 1.0 / eija if self.diis: diis = util.DIIS(self.diis_space) for niter in range(1, self.maxiter+1): f = self.h1e_mo + util.einsum('piqi->pq', self.eri_mo[:,o,:,o]) fp = f.copy() np.fill_diagonal(fp, 0.0) e = f.diagonal() t1 = self.eri_mo[v,v,o,o] t2 = util.einsum('ac,cbij->abij', fp[v,v], t_amp) t3 = util.einsum('ki,abkj->abij', fp[o,o], t_amp) t_amp = t1.copy() t_amp += t2 - t2.transpose(1,0,2,3) t_amp -= t3 - t3.transpose(0,1,3,2) t_amp *= eija if niter > 1: if self.diis: t_amp = diis.update(t_amp) if self.damping > 0.0: damping = self.damping t_amp = (1.0 - damping) * t_amp + damping * self._t_prev if self.damping > 0.0: self._t_prev = t_amp.copy() opdm_corr[v,v] = util.einsum('ijac,bcij->ba', t_amp.T, t_amp) * 0.5 opdm_corr[o,o] = util.einsum('jkab,abik->ji', t_amp.T, t_amp) * -0.5 opdm = opdm_corr + opdm_ref tpdm_corr[v,v,o,o] = t_amp tpdm_corr[o,o,v,v] = t_amp.T tpdm2 = util.einsum('rp,sq->rspq', opdm_corr, opdm_ref) tpdm3 = util.einsum('rp,sq->rspq', opdm_ref, opdm_ref) tpdm = tpdm_corr.copy() tpdm += tpdm2 - tpdm2.transpose(1,0,2,3) tpdm -= tpdm2.transpose(0,1,3,2) - tpdm2.transpose(1,0,3,2) tpdm += tpdm3 - tpdm3.transpose(1,0,2,3) fnr = util.einsum('pr,rq->pq', self.h1e_mo, opdm) fnr += util.einsum('prst,stqr->pq', self.eri_mo, tpdm) * 0.5 x[v,o] = ((fnr - fnr.T)[v,o]) / util.outer_sum([-self.e[v], self.e[o]]) u = expm(x - x.T) c = np.dot(self.c, u) self.c = c self.h1e_mo = util.ao2mo(self.h1e_ao, c, c) self.eri_mo = util.ao2mo(self.eri_ao, c, c, c, c) e_prev = self.e_tot self.e_1body = util.einsum('pq,qp->', self.h1e_mo, opdm) self.e_1body += self.hf.e_nuc self.e_2body = 0.25 * util.einsum('pqrs,rspq->', self.eri_mo, tpdm) if abs(self.e_tot - e_prev) < self.etol: break return self
def build_rmp2_part_se_direct(eo, ev, xija, grid, chempot=0.0, ordering='feynman'): ''' Builds a set of auxiliaries representing all (i,j,a) or (a,b,i) diagrams for a restricted reference. Poles are summed straight into the self-energy and returns an `ndarray` instead of `Aux`. Parameters ---------- eo : (o) ndarray occupied (virtual) energies ev : (v) ndarray virtual (occupied) energies xija : (n,o,o,v) two-electron integrals indexed as physical, occupied, occupied, virtual (physical, virtual, virtual, occupied) grid : (k) ImFqGrid, ImFqQuad or ReFqGrid grid chempot : float, optional chemical potential ordering : str ordering of the poles {'feynman', 'advanced', 'retarded'} (default 'feynman') Returns ------- se : (k,n,n) ndarray frequency-dependent self-energy ''' #TODO write in C if grid.axis == 'imag': if ordering == 'feynman': get_s = lambda x: np.sign(x) elif ordering == 'advanced': get_s = lambda x: np.ones(x.shape, dtype=types.int64) elif ordering == 'retarded': get_s = lambda x: -np.ones(x.shape, dtype=types.int64) else: get_s = lambda x: 0.0 w = grid.prefac * grid.values nphys, nocc, _, nvir = xija.shape se = np.zeros((grid.shape[0], nphys, nphys), dtype=types.complex128) eov = util.outer_sum([eo, -ev]).flatten() for i in range(nocc): ei = eo[i] + eov - chempot vi = xija[:, i].reshape((nphys, -1)) vip = xija[:, :, i].reshape((nphys, -1)) di = 1.0 / util.outer_sum([w, -ei + get_s(ei) * grid.eta * 1.0j]) se += util.einsum('wk,xk,yk->wxy', di, vi, (2 * vi - vip).conj()) return se
def test_moment(self): se = self.se m1 = util.einsum('xk,nk,yk->nxy', se.v, se.e[None,:] ** np.arange(4)[:,None], se.v) m2 = se.moment(range(4)) self.assertAlmostEqual(np.max(np.absolute(m1 - m2)), 0, 8)
def build_dfrmp2_part_se_direct(eo, ev, ixq, qja, grid, chempot=0.0, ordering='feynman'): ''' Builds a set of auxiliaries representing all (i,j,a) or (a,b,i) diagrams for a restricted reference. Poles are summed straight into the self-energy and returns an `ndarray` instead of `Aux`. Parameters ---------- eo : (o) ndarray occupied (virtual) energies ev : (v) ndarray virtual (occupied) energies ixq : (o,n,q) ndarray density-fitted two-electron integrals indexed as occupied, physical, auxiliary (physical, virtual, auxiliary) qja : (q,o,v) ndarray density-fitted two-electron integrals indexed as auxiliary, occupied, virtual (auxiliary, virtual, occupied) grid : (k) ImFqGrid, ImFqQuad or ReFqGrid grid chempot : float, optional chemical potential ordering : str ordering of the poles {'feynman', 'advanced', 'retarded'} (default 'feynman') Returns ------- se : (k,n,n) ndarray frequency-dependent self-energy ''' if grid.axis == 'imag': if ordering == 'feynman': get_s = lambda x: np.sign(x) elif ordering == 'advanced': get_s = lambda x: np.ones(x.shape, dtype=types.int64) elif ordering == 'retarded': get_s = lambda x: -np.ones(x.shape, dtype=types.int64) else: get_s = lambda x: 0.0 w = grid.prefac * grid.values nphys = ixq.shape[1] ndf, nocc, nvir = qja.shape npoles = nocc * nocc * nvir se = np.zeros((grid.shape[0], nphys, nphys), dtype=types.complex128) ixq = ixq.reshape((nocc * nphys, ndf)) qja = qja.reshape((ndf, nocc * nvir)) eov = util.outer_sum([eo, -ev]).flatten() for i in range(nocc): ei = eo[i] + eov - chempot vi = np.dot(ixq[i * nphys:(i + 1) * nphys].conj(), qja).reshape( (nphys, -1)) vip = np.dot(ixq.conj(), qja[:, i * nvir:(i + 1) * nvir]) vip = util.reshape_internal(vip, (nocc, nphys, -1), (0, 1), (nphys, -1)) di = 1.0 / util.outer_sum([w, -ei + get_s(ei) * grid.eta * 1.0j]) se += util.einsum('wk,xk,yk->wxy', di, vi, (2 * vi - vip).conj()) return se
def get_1p_or_1h(self): occ, vir = self._get_slices() eri_ovov = self.eri[occ, vir, occ, vir] eo = self.hf.e[occ] ev = self.hf.e[vir] t2 = eri_ovov.copy() t2 /= util.dirsum('i,a,j,b->iajb', eo, -ev, eo, -ev) t2a = t2 - t2.swapaxes(0, 2).copy() os = self.options['os_factor'] ss = self.options['ss_factor'] self.e_mp2 = util.einsum('iajb,iajb->', t2, eri_ovov) * (os + ss) self.e_mp2 -= util.einsum('iajb,ibja->', t2, eri_ovov) * ss h = np.diag(eo) h += util.einsum('a,iakb,jakb->ij', ev, t2a, t2a) h += util.einsum('a,iakb,jakb->ij', ev, t2, t2) h += util.einsum('a,ibka,jbka->ij', ev, t2, t2) h -= util.einsum('k,iakb,jakb->ij', eo, t2a, t2a) * 0.5 h -= util.einsum('k,iakb,jakb->ij', eo, t2, t2) * 0.5 h -= util.einsum('k,iakb,jakb->ij', eo, t2, t2) * 0.5 h -= util.einsum('i,iakb,jakb->ij', eo, t2a, t2a) * 0.25 h -= util.einsum('i,iakb,jakb->ij', eo, t2, t2) * 0.25 h -= util.einsum('i,iakb,jakb->ij', eo, t2, t2) * 0.25 h -= util.einsum('j,iakb,jakb->ij', eo, t2a, t2a) * 0.25 h -= util.einsum('j,iakb,jakb->ij', eo, t2, t2) * 0.25 h -= util.einsum('j,iakb,jakb->ij', eo, t2, t2) * 0.25 h += util.einsum('iakb,jakb->ij', t2a, eri_ovov) * 0.5 h -= util.einsum('iakb,jbka->ij', t2a, eri_ovov) * 0.5 h += util.einsum('iakb,jakb->ij', t2, eri_ovov) h += util.einsum('jakb,iakb->ij', t2a, eri_ovov) * 0.5 h -= util.einsum('jakb,kaib->ij', t2a, eri_ovov) * 0.5 h += util.einsum('jakb,iakb->ij', t2, eri_ovov) self.h_1p_or_1h = h
def build_dfump2_part_se_direct(eo, ev, ixq, qja, grid, chempot=0.0, ordering='feynman'): ''' Builds a set of auxiliaries representing all (i,j,a) or (a,b,i) diagrams for an unrestricted reference. Poles are summed straight into the self-energy and returns an `ndarray` instead of `Aux`. Parameters ---------- eo : (o) ndarray occupied (virtual) energies ev : (v) ndarray virtual (occupied) energies ixq : 1-tuple or 2-tuple of (n,o,o,v) density-fitted two-electron integrals index as occupied, physical, auxiliary (virtual, physical, auxiliary) for alpha, beta (beta, alpha) spin. Only alpha (beta) is required. qja : 2-tuple of (n,o,o,v) density-fitted two-electron integrals index as auxiliary, occupied, virtual (auxiliary, virtual, occupied) for alpha, beta (beta, alpha) spin. grid : (k) ImFqGrid, ImFqQuad or ReFqGrid grid chempot : tuple of float, optional chemical potential for alpha, beta (beta, alpha) spin ordering : str ordering of the poles {'feynman', 'advanced', 'retarded'} (default 'feynman') Returns ------- se : (k,n,n) ndarray frequency-dependent self-energy ''' if not _is_tuple(chempot): chempot = (chempot, chempot) #TODO write in C if grid.axis == 'imag': if ordering == 'feynman': get_s = lambda x : np.sign(x) elif ordering == 'advanced': get_s = lambda x : np.ones(x.shape, dtype=types.int64) elif ordering == 'retarded': get_s = lambda x : -np.ones(x.shape, dtype=types.int64) else: get_s = lambda x : 0.0 w = grid.prefac * grid.values nphys = ixq[0].shape[1] ndf, nocca, nvira = qja[0].shape _, noccb, nvirb = qja[1].shape se = np.zeros((grid.shape[0], nphys, nphys), dtype=types.complex128) ixq = (ixq[0].reshape((nocca*nphys, ndf)),) qja = (qja[0].reshape((ndf, nocca*nvira)), qja[1].reshape((ndf, noccb*nvirb))) eova = util.outer_sum([eo[0], -ev[0]]).flatten() - chempot[0] eovb = util.outer_sum([eo[1], -ev[1]]).flatten() - chempot[0] for i in range(nocca): ei_a = eo[0][i] + eova ei_b = eo[0][i] + eovb xq_a = ixq[0][i*nphys:(i+1)*nphys] vi_a = np.dot(xq_a.conj(), qja[0]).reshape((nphys, -1)) vi_b = np.dot(xq_a.conj(), qja[1]).reshape((nphys, -1)) vip_a = np.dot(ixq[0].conj(), qja[0][:,i*nvira:(i+1)*nvira]) vip_a = util.reshape_internal(vip_a, (nocca, nphys, nvira), (0,1), (nphys, nocca*nvira)) di_a = 1.0 / util.outer_sum([w, -ei_a + get_s(ei_a) * grid.eta * 1.0j]) di_b = 1.0 / util.outer_sum([w, -ei_b + get_s(ei_b) * grid.eta * 1.0j]) se += util.einsum('wk,xk,yk->wxy', di_a, vi_a, (vi_a - vip_a).conj()) se += util.einsum('wk,xk,yk->wxy', di_b, vi_b, vi_b.conj()) return se
def energy_2body_aux(gf, se, both_sides=False): ''' Calculates the two-body contribution to the electronic energy using the auxiliary representation of the Green's function and self-energy, according to the Galitskii-Migdal formula. Parameters ---------- gf : Aux auxiliary representation of Green's function se : Aux auxiliary representation of self-energy both_sides : bool, optional if True, calculate both halves of the functional and return the mean, default False Returns ------- e2b : float two-body contribution to electronic energy ''' #TODO in C if isinstance(se, (tuple, list)): n = len(se) if isinstance(gf, (tuple, list)): return sum([ energy_2body_aux(gf[i], se[i], both_sides=both_sides) for i in range(n) ]) / n else: return sum([ energy_2body_aux(gf, se[i], both_sides=both_sides) for i in range(n) ]) / n nphys = se.nphys e2b = 0.0 for l in range(gf.nocc): vxl = gf.v[:nphys, l] vxk = se.v[:, se.nocc:] dlk = 1.0 / (gf.e[l] - se.e[se.nocc:]) e2b += util.einsum('xk,yk,x,y,k->', vxk, vxk.conj(), vxl, vxl.conj(), dlk) if both_sides: for l in range(gf.nocc, gf.naux): vxl = gf.v[:nphys, l] vxk = se.v[:, :se.nocc] dlk = -1.0 / (gf.e[l] - se.e[:se.nocc]) e2b += util.einsum('xk,yk,x,y,k->', vxk, vxk.conj(), vxl, vxl.conj(), dlk) else: e2b *= 2.0 return np.ravel(e2b.real)[0]
def get_emp2(self, e, v, mask): fac = 1 if np.all(self.rhf.e[mask] < self.rhf.chempot) else -1 return util.einsum('xk,xk->', v[mask]**2, fac / (self.rhf.e[mask, None] - e[None, :])).ravel()[0]