def setup(self): self.h1e = self.hf.h1e_mo self.eri = self.hf.eri_mo unrestricted = self.h1e.ndim == 3 if self.options['dm0'] is None: self.rdm1 = self.hf.rdm1_mo else: self.rdm1 = np.array(self.options['dm0'], dtype=types.float64) if unrestricted and self.rdm1.ndim == 2: self.rdm1 = np.stack([self.rdm1, self.rdm1], axis=0) self.converged = False self.iteration = 0 nact = self.hf.nao - sum(self.options['frozen']) if not unrestricted: self.se = aux.Aux([], [[],]*nact, chempot=self.hf.chempot) self.gf = self.se.new(self.hf.e, np.eye(nact)) self._se_prev = None else: self.se = (aux.Aux([], [[],]*nact, chempot=self.hf.chempot[0]), aux.Aux([], [[],]*nact, chempot=self.hf.chempot[1])) self.gf = (self.se[0].new(self.hf.e[0], np.eye(nact)), self.se[1].new(self.hf.e[1], np.eye(nact))) self._se_prev = (None, None) self._timings = {} self._energies = {}
def test_build_ump2_iter(self): eo, ev, xija, xabi = aux.ump2._parse_uhf(self.uhf.e, self.eri_uhf[0], self.uhf.chempot) sea_null = aux.Aux([], [ [], ] * self.uhf.nao, chempot=self.uhf.chempot[0]) seb_null = aux.Aux([], [ [], ] * self.uhf.nao, chempot=self.uhf.chempot[1]) se1a, se1b = aux.mp2.build_mp2_iter((sea_null, seb_null), self.uhf.fock_mo, self.eri_uhf, wtol=0) se2a, se2b = aux.ump2.build_ump2_iter((sea_null, seb_null), self.uhf.fock_mo, self.eri_uhf, wtol=0) self.assertAlmostEqual(np.max(np.absolute(se1a.e - se2a.e)), 0, 12) self.assertAlmostEqual(np.max(np.absolute(se1b.e - se2b.e)), 0, 12) self.assertAlmostEqual( np.max( np.absolute( np.dot(se1a.v, se1a.v.T) - np.dot(se2a.v, se2a.v.T))), 0, 12) self.assertAlmostEqual( np.max( np.absolute( np.dot(se1b.v, se1b.v.T) - np.dot(se2b.v, se2b.v.T))), 0, 12)
def setup(self): self.h1e = self.hf.h1e_mo self.eri = self.hf.eri_mo if self.options['dm0'] is None: self.rdm1 = self.hf.rdm1_mo else: self.rdm1 = np.array(self.options['dm0'], dtype=types.float64) self.converged = False self.iteration = 0 chempot = self.hf.chempot self.gf = aux.Aux(self.hf.e, np.eye(self.hf.nao), chempot=chempot) self.se = aux.Aux([], [ [], ] * self.hf.nao, chempot=chempot) self._se_prev = None self.rpa = None self.e_qp = self.hf.e.copy() self._timings = {} self._energies = {} log.title('Options', self.verbose) log.options(self.options, self.verbose) log.title('Input', self.verbose) log.molecule(self.hf.mol, self.verbose) log.write('Basis = %s\n' % self.hf.mol.basis, self.verbose) log.write('E(nuc) = %.12f\n' % self.hf.e_nuc, self.verbose) log.write('E(hf) = %.12f\n' % self.hf.e_tot, self.verbose) log.write('nao = %d\n' % self.hf.nao, self.verbose) log.write('nmom = (%s, %s)\n' % self.nmom, self.verbose)
def build_rmp2_direct(e, eri, chempot=0.0, **kwargs): ''' Builds a set of auxiliaries representing all (i,j,a) and (a,b,i) diagrams for a restricted reference. Uses a generator which iterates over blocks. Parameters ---------- e : (n) ndarray MO or QMO energies eri : (n,m,m,m) two-electron integrals where the first index is in the physical basis chempot : float, optional chemical potential wtol : float, optional threshold for an eigenvalue to be considered zero ss_factor : float, optional same spin factor, default 1.0 os_factor : float, optional opposite spin factor, deafult 1.0 Yields ------ poles : Aux auxiliaries ''' eo, ev, xija, xabi = _parse_rhf(e, eri, chempot) for e, v in build_rmp2_part_direct(eo, ev, xija, **kwargs): yield aux.Aux(e, v, chempot=chempot) for e, v in build_rmp2_part_direct(ev, eo, xabi, **kwargs): yield aux.Aux(e, v, chempot=chempot)
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.uhf = hf.UHF(self.m).run() self.gf = aux.Aux(self.rhf.e, np.eye(self.rhf.nao), chempot=self.rhf.chempot) self.se = aux.build_rmp2(self.rhf.e, self.rhf.eri_mo, chempot=self.rhf.chempot) self.gf_a = aux.Aux(self.uhf.e[0], np.eye(self.uhf.nao), chempot=self.uhf.chempot[0]) self.gf_b = aux.Aux(self.uhf.e[1], np.eye(self.uhf.nao), chempot=self.uhf.chempot[1]) self.se_a = aux.build_ump2(self.uhf.e, self.uhf.eri_mo[0], chempot=self.uhf.chempot) self.se_b = aux.build_ump2(self.uhf.e[::-1], self.uhf.eri_mo[1][::-1], chempot=self.uhf.chempot[::-1]) self.e_rmp2 = -0.20905684700662164 self.e_ump2 = -0.20905685057662993
def test_scs_build_ump2_iter(self): (eoa, eob), (eva, evb), (xija_aa, xija_ab), (xabi_aa, xabi_ab) = aux.ump2._parse_uhf( self.uhf.e, self.eri[0], self.uhf.chempot) _, _, (xija_bb, xija_ba), (xabi_bb, xabi_ba) = aux.ump2._parse_uhf( self.uhf.e[::-1], self.eri[1][::-1], self.uhf.chempot[::-1]) nocca, noccb, nvira, nvirb = eoa.size, eob.size, eva.size, evb.size oa, ob, va, vb = slice(None, nocca), slice(None, noccb), slice( nocca, None), slice(noccb, None) se_a_null = aux.Aux([], [ [], ] * self.uhf.nao, chempot=self.uhf.chempot[0]) se_b_null = aux.Aux([], [ [], ] * self.uhf.nao, chempot=self.uhf.chempot[1]) se_a, se_b = aux.build_ump2_iter((se_a_null, se_b_null), self.uhf.fock_mo, self.eri, wtol=0, os_factor=1.2, ss_factor=0.33) e_mp2_a = self.get_emp2(se_a.e_vir, se_a.v_vir, oa, 0) e_mp2_a += self.get_emp2(se_b.e_vir, se_b.v_vir, ob, 1) e_mp2_b = self.get_emp2(se_a.e_occ, se_a.v_occ, va, 0) e_mp2_b += self.get_emp2(se_b.e_occ, se_b.v_occ, vb, 1) self.assertAlmostEqual(self.e_mp2_scs, e_mp2_a, 7) self.assertAlmostEqual(self.e_mp2_scs, e_mp2_b, 7)
def test_build_dfump2_direct(self): (eoa, eob), (eva, evb), (ixq_a, _), (qja_a, qja_b), (axq_a, _), (qbi_a, qbi_b) = aux.dfump2._parse_uhf( self.uhf_df.e, self.eri_df, self.eri_df, self.uhf_df.chempot) (eob, eoa), (evb, eva), (ixq_b, _), (qja_b, qja_a), (axq_b, _), (qbi_b, qbi_a) = aux.dfump2._parse_uhf( self.uhf_df.e[::-1], self.eri_df[::-1], self.eri_df[::-1], self.uhf_df.chempot[::-1]) nocca, noccb, nvira, nvirb = eoa.size, eob.size, eva.size, evb.size oa, ob, va, vb = slice(None, nocca), slice(None, noccb), slice( nocca, None), slice(noccb, None) se_a_null = aux.Aux([], [ [], ] * self.uhf.nao, chempot=self.uhf_df.chempot[0]) se_b_null = aux.Aux([], [ [], ] * self.uhf.nao, chempot=self.uhf_df.chempot[1]) se_a = [ x for x in aux.build_dfump2_direct(self.uhf_df.e, self.eri_df, self.eri_df, chempot=self.uhf_df.chempot, wtol=0) ] se_b = [ x for x in aux.build_dfump2_direct(self.uhf_df.e[::-1], self.eri_df[::-1], self.eri_df[::-1], chempot=self.uhf_df.chempot[::-1], wtol=0) ] se_a = sum(se_a, se_a_null) se_b = sum(se_b, se_b_null) e_mp2_a = self.get_emp2(se_a.e_vir, se_a.v_vir, oa, 0) e_mp2_a += self.get_emp2(se_b.e_vir, se_b.v_vir, ob, 1) e_mp2_b = self.get_emp2(se_a.e_occ, se_a.v_occ, va, 0) e_mp2_b += self.get_emp2(se_b.e_occ, se_b.v_occ, vb, 1) self.assertAlmostEqual(self.e_mp2, e_mp2_a, 4) self.assertAlmostEqual(self.e_mp2, e_mp2_b, 4)
def build(self): se = [] 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_ooov = self.eri[a, a][occa, occa, occa, vira] eri_ab_ooov = self.eri[a, b][occa, occa, 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] eo = (eo_a, eo_b) ev = (ev_a, ev_b) xija = (eri_aa_ooov, eri_ab_ooov) e, v = aux.build_ump2_part(eo, ev, xija, **self.options['_build']) se.append(aux.Aux(e, v)) self.se = tuple(se) log.write('naux (build,alpha) = %d\n' % (self.naux[0]), self.verbose) log.write('naux (build,beta) = %d\n' % (self.naux[1]), self.verbose)
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_imfq(self): se_guess = aux.Aux(self.rhf.e, np.eye(self.rhf.nao)) se_fit = aux.fit.run(se_guess, self.se.as_spectrum(self.imfq), self.imfq, hessian=False) print( np.linalg.norm(se_guess.as_spectrum(self.imfq), se_fit.as_spectrum(self.imfq)))
def test_scs_build_dfump2_iter(self): (eoa, eob), (eva, evb), (ixq_a, _), (qja_a, qja_b), (axq_a, _), (qbi_a, qbi_b) = aux.dfump2._parse_uhf( self.uhf_df.e, self.eri_df, self.eri_df, self.uhf_df.chempot) (eob, eoa), (evb, eva), (ixq_b, _), (qja_b, qja_a), (axq_b, _), (qbi_b, qbi_a) = aux.dfump2._parse_uhf( self.uhf_df.e[::-1], self.eri_df[::-1], self.eri_df[::-1], self.uhf_df.chempot[::-1]) nocca, noccb, nvira, nvirb = eoa.size, eob.size, eva.size, evb.size oa, ob, va, vb = slice(None, nocca), slice(None, noccb), slice( nocca, None), slice(noccb, None) se_a_null = aux.Aux([], [ [], ] * self.uhf.nao, chempot=self.uhf.chempot[0]) se_b_null = aux.Aux([], [ [], ] * self.uhf.nao, chempot=self.uhf.chempot[1]) se_a, se_b = aux.build_dfump2_iter((se_a_null, se_b_null), self.uhf.fock_mo, self.eri_df, wtol=0, os_factor=1.2, ss_factor=0.33) e_mp2_a = self.get_emp2(se_a.e_vir, se_a.v_vir, oa, 0) e_mp2_a += self.get_emp2(se_b.e_vir, se_b.v_vir, ob, 1) e_mp2_b = self.get_emp2(se_a.e_occ, se_a.v_occ, va, 0) e_mp2_b += self.get_emp2(se_b.e_occ, se_b.v_occ, vb, 1) self.assertAlmostEqual(self.e_mp2_scs, e_mp2_a, 3) self.assertAlmostEqual(self.e_mp2_scs, e_mp2_b, 3)
def build_ump2_direct(e, eri, chempot=0.0, **kwargs): ''' Builds a set of auxiliaries representing all (i,j,a) and (a,b,i) diagrams for an unrestricted reference. Uses a generator which iterates over blocks. Parameters ---------- e : tuple of (n) ndarray MO or QMO energies for alpha, beta (beta, alpha) spin eri : tuple of (n,m,m,m) two-electron integrals where the first index is in the physical basis for (aa|aa), (aa|bb) [(bb|bb), (bb|aa)] spin chempot : tuple of float, optional chemical potential for alpha, beta (beta, alpha) spin wtol : float, optional threshold for an eigenvalue to be considered zero ss_factor : float, optional same spin factor, default 1.0 os_factor : float, optional opposite spin factor, default 1.0 Yields ------ poles_a : Aux auxiliaries for alpha spin poles_b : Aux auxiliaries for beta spin ''' if not _is_tuple(chempot): chempot = (chempot, chempot) assert _is_tuple(e) and len(e) == 2 assert _is_tuple(eri) and len(eri) == 2 eo, ev, xija, xabi = _parse_uhf(e, eri, chempot) for e, v in build_ump2_part_direct(eo, ev, xija, **kwargs): yield aux.Aux(e, v, chempot=chempot[0]) for e, v in build_ump2_part_direct(ev, eo, xabi, **kwargs): yield aux.Aux(e, v, chempot=chempot[1])
def build_dfump2_direct(e, qpx, qyz, chempot=0.0, **kwargs): ''' Builds a set of auxiliaries representing all (i,j,a) and (a,b,i) diagrams for an unrestricted reference. Uses a generator which iterates over blocks. Parameters ---------- e : tuple of (n) ndarray MO or QMO energies for alpha, beta (beta, alpha) spin qpx : 2-tuple of (q,p,x) ndarray density-fitted two-electron integrals where first index is in the physical basis for alpha, beta (beta, alpha) spin qyz : 2-tuple of (q,y,z) ndarray density-fitted two-electron integrals for alpha, beta (beta, alpha) spin chempot : tuple of float, optional chemical potential for alpha, beta (beta, alpha) spin wtol : float, optional threshold for an eigenvalue to be considered zero ss_factor : float, optional same spin factor, default 1.0 os_factor : float, optional opposite spin factor, default 1.0 Yields ------ poles_a : Aux auxiliaries for alpha spin poles_b : Aux auxiliaries for beta spin ''' if not _is_tuple(chempot): chempot = (chempot, chempot) eo, ev, ixq, qja, axq, qbi = _parse_uhf(e, qpx, qyz, chempot) for e,v in build_dfump2_part_direct(eo, ev, ixq, qja, **kwargs): yield aux.Aux(e, v, chempot=chempot[0]) for e,v in build_dfump2_part_direct(ev, eo, axq, qbi, **kwargs): yield aux.Aux(e, v, chempot=chempot[1])
def build(self): occ, vir = self._get_slices() eri_ooov = self.eri[occ, occ, occ, vir] eo = self.hf.e[occ] ev = self.hf.e[vir] e, v = aux.build_rmp2_part(eo, ev, eri_ooov, **self.options['_build']) self.se = aux.Aux(e, v) #, chempot=self.hf.chempot) log.write('naux (build) = %d\n' % self.naux, self.verbose)
def test_build_ump2_direct(self): (eoa, eob), (eva, evb), (xija_aa, xija_ab), (xabi_aa, xabi_ab) = aux.ump2._parse_uhf( self.uhf.e, self.eri[0], self.uhf.chempot) _, _, (xija_bb, xija_ba), (xabi_bb, xabi_ba) = aux.ump2._parse_uhf( self.uhf.e[::-1], self.eri[1][::-1], self.uhf.chempot[::-1]) nocca, noccb, nvira, nvirb = eoa.size, eob.size, eva.size, evb.size oa, ob, va, vb = slice(None, nocca), slice(None, noccb), slice( nocca, None), slice(noccb, None) se_a_null = aux.Aux([], [ [], ] * self.uhf.nao, chempot=self.uhf.chempot[0]) se_b_null = aux.Aux([], [ [], ] * self.uhf.nao, chempot=self.uhf.chempot[1]) se_a = [ x for x in aux.build_ump2_direct( self.uhf.e, self.eri[0], chempot=self.uhf.chempot, wtol=0) ] se_b = [ x for x in aux.build_ump2_direct(self.uhf.e[::-1], self.eri[1][::-1], chempot=self.uhf.chempot[::-1], wtol=0) ] se_a = sum(se_a, se_a_null) se_b = sum(se_b, se_b_null) e_mp2_a = self.get_emp2(se_a.e_vir, se_a.v_vir, oa, 0) e_mp2_a += self.get_emp2(se_b.e_vir, se_b.v_vir, ob, 1) e_mp2_b = self.get_emp2(se_a.e_occ, se_a.v_occ, va, 0) e_mp2_b += self.get_emp2(se_b.e_occ, se_b.v_occ, vb, 1) self.assertAlmostEqual(self.e_mp2, e_mp2_a, 8) self.assertAlmostEqual(self.e_mp2, e_mp2_b, 8)
def build_gf(self): e, c = self.se.eig(self.get_fock()) self.gf = aux.Aux(e, c, chempot=self.chempot) c_occ = c[:self.nphys, e < self.chempot] self.rdm1 = np.dot(c_occ, c_occ.T) * 2 log.write( 'HOQMO = %.6f\n' % util.amax(self.gf.e[self.gf.e < self.chempot]), self.verbose) log.write( 'LUQMO = %.6f\n' % util.amin(self.gf.e[self.gf.e >= self.chempot]), self.verbose) log.array(self.rdm1, 'Density matrix (physical)', self.verbose) return self.gf
def test_build_dfrmp2_iter(self): eo, ev, ixq, qja, axq, qbi = aux.dfrmp2._parse_rhf( self.rhf_df.e, self.eri_df, self.eri_df, self.rhf_df.chempot) nocc, nvir = eo.size, ev.size o, v = slice(None, nocc), slice(nocc, None) se_null = aux.Aux([], [ [], ] * self.rhf.nao, chempot=self.rhf_df.chempot) se = aux.build_dfrmp2_iter(se_null, self.rhf_df.fock_mo, self.eri_df, wtol=0) e_mp2_a = self.get_emp2(se.e_vir, se.v_vir, o) e_mp2_b = self.get_emp2(se.e_occ, se.v_occ, v) self.assertAlmostEqual(self.e_mp2, e_mp2_a, 4) self.assertAlmostEqual(self.e_mp2, e_mp2_b, 4)
def test_build_rmp2_iter(self): eo, ev, xija, xabi = aux.rmp2._parse_rhf(self.rhf.e, self.eri_rhf, self.rhf.chempot) se_null = aux.Aux([], [ [], ] * self.rhf.nao, chempot=self.rhf.chempot) se1 = aux.mp2.build_mp2_iter(se_null, self.rhf.fock_mo, self.eri_rhf, wtol=0) se2 = aux.rmp2.build_rmp2_iter(se_null, self.rhf.fock_mo, self.eri_rhf, wtol=0) self.assertAlmostEqual(np.max(np.absolute(se1.e - se2.e)), 0, 12) self.assertAlmostEqual( np.max( np.absolute(np.dot(se1.v, se1.v.T) - np.dot(se2.v, se2.v.T))), 0, 12)
def test_build_dfrmp2_direct(self): eo, ev, ixq, qja, axq, qbi = aux.dfrmp2._parse_rhf( self.rhf_df.e, self.eri_df, self.eri_df, self.rhf_df.chempot) nocc, nvir = eo.size, ev.size o, v = slice(None, nocc), slice(nocc, None) se = [ x for x in aux.build_dfrmp2_direct(self.rhf_df.e, self.eri_df, self.eri_df, self.rhf_df.chempot, wtol=0) ] se = sum(se, aux.Aux([], [ [], ] * self.rhf.nao)) e_mp2_a = self.get_emp2(se.e_vir, se.v_vir, o) e_mp2_b = self.get_emp2(se.e_occ, se.v_occ, v) self.assertAlmostEqual(self.e_mp2, e_mp2_a, 4) self.assertAlmostEqual(self.e_mp2, e_mp2_b, 4)
def build_dfump2(e, qpx, qyz, chempot=0.0, **kwargs): ''' Builds a set of auxiliaries representing all (i,j,a) and (a,b,i) diagrams for an unrestricted reference. Parameters ---------- e : tuple of (n) ndarray MO or QMO energies for alpha, beta (beta, alpha) spin qpx : 2-tuple of (q,p,x) ndarray density-fitted two-electron integrals where first index is in the physical basis for alpha, beta (beta, alpha) spin qyz : 2-tuple of (q,y,z) ndarray density-fitted two-electron integrals for alpha, beta (beta, alpha) spin chempot : tuple of float, optional chemical potential for alpha, beta (beta, alpha) spin wtol : float, optional threshold for an eigenvalue to be considered zero ss_factor : float, optional same spin factor, default 1.0 os_factor : float, optional opposite spin factor, default 1.0 Returns ------- poles : Aux auxiliaries ''' if not _is_tuple(chempot): chempot = (chempot, chempot) eo, ev, ixq, qja, axq, qbi = _parse_uhf(e, qpx, qyz, chempot) eija, vija = build_dfump2_part(eo, ev, ixq, qja, **kwargs) eabi, vabi = build_dfump2_part(ev, eo, axq, qbi, **kwargs) e = np.concatenate((eija, eabi), axis=0) v = np.concatenate((vija, vabi), axis=1) poles = aux.Aux(e, v, chempot=chempot[0]) return poles
def build_rmp2(e, eri, chempot=0.0, **kwargs): ''' Builds a set of auxiliaries representing all (i,j,a) and (a,b,i) diagrams for a restricted reference. Parameters ---------- e : (n) ndarray MO or QMO energies eri : (n,m,m,m) two-electron integrals where the first index is in the physical basis chempot : float, optional chemical potential wtol : float, optional threshold for an eigenvalue to be considered zero ss_factor : float, optional same spin factor, default 1.0 os_factor : float, optional opposite spin factor, deafult 1.0 Returns ------- poles : Aux auxiliaries ''' eo, ev, xija, xabi = _parse_rhf(e, eri, chempot) eija, vija = build_rmp2_part(eo, ev, xija, **kwargs) eabi, vabi = build_rmp2_part(ev, eo, xabi, **kwargs) e = np.concatenate((eija, eabi), axis=0) v = np.concatenate((vija, vabi), axis=1) poles = aux.Aux(e, v, chempot=chempot) return poles
# Build the RHF object: rhf = hf.RHF(m) rhf.run() # Build some MO quantities: e_mo = rhf.e eri_mo = rhf.eri_mo fock_mo = rhf.fock_mo chempot = rhf.chempot # We can build the auxiliaries via the MO energies and MO integrals: se_rmp2_a = aux.build_rmp2(e_mo, eri_mo, chempot=chempot) # We can also do this by iterating (once) an empty auxiliary space: s0 = aux.Aux(np.zeros(0), np.zeros((rhf.nao, 0)), chempot=chempot) se_rmp2_b = aux.build_rmp2_iter(s0, fock_mo, eri_mo) # In the above, chempot is inherited from s0 # Calculate the MP2 energies and compare them to canonical MP2: e_mp2_a = aux.energy_mp2(rhf.e, se_rmp2_a) e_mp2_b = aux.energy_mp2(rhf.e, se_rmp2_b) e_mp2_c = mp.MP2(rhf).run().e_corr print('a) E(mp2) = %.12f' % e_mp2_a) print('b) E(mp2) = %.12f' % e_mp2_b) print('c) E(mp2) = %.12f' % e_mp2_c) # a) and c) should be exact, because they use the same eigenvalue and eigenvectors # to sum over. b) will be a little different, because the iteration rediagonalises # the physical-space Fock matrix - but should be very close.
# Build the UHF object: uhf = hf.UHF(m) uhf.run() # Run some UAGF2 calculations: gf2_a = agf2.UAGF2(uhf, nmom=(1, 1), verbose=False).run() gf2_b = agf2.UAGF2(uhf, nmom=(2, 2), verbose=False).run() gf2_c = agf2.UAGF2(uhf, nmom=(3, 3), verbose=False).run() # Method 2: using aux.Aux more: fock = uhf.get_fock(gf2_b.rdm1, basis='mo') ea, ca = gf2_b.se[0].eig(fock[0]) eb, cb = gf2_b.se[1].eig(fock[1]) ca = ca[:uhf.nao] cb = cb[:uhf.nao] gfa = aux.Aux(ea, ca[:uhf.nao], chempot=gf2_b.chempot[0]) gfb = aux.Aux(eb, cb[:uhf.nao], chempot=gf2_b.chempot[1]) ea_hoqmo, ea_luqmo = gfa.as_occupied().e[-1], gfa.as_virtual().e[0] eb_hoqmo, eb_luqmo = gfb.as_occupied().e[-1], gfb.as_virtual().e[0] va_hoqmo, va_luqmo = gfa.as_occupied().v[:, -1], gfa.as_virtual().v[:, 0] vb_hoqmo, vb_luqmo = gfb.as_occupied().v[:, -1], gfb.as_virtual().v[:, 0] e_hoqmo, v_hoqmo = (ea_hoqmo, va_hoqmo) if ea_hoqmo < eb_hoqmo else (eb_hoqmo, vb_hoqmo) e_luqmo, v_luqmo = (ea_luqmo, va_luqmo) if ea_luqmo > eb_luqmo else (eb_luqmo, vb_luqmo) print('UAGF2(2,2):') print('IP = %.6f weight = %.6f' % (-e_hoqmo, np.linalg.norm(v_hoqmo))) print('EA = %.6f weight = %.6f' % (e_luqmo, np.linalg.norm(v_luqmo))) print('Gap = %.6f' % (e_luqmo - e_hoqmo)) # CCSD:
from auxgf.util import Timer timer = Timer() # Build the Molecule object: m = mol.Molecule(atoms='H 0 0 0; Li 0 0 1.64', basis='cc-pvdz') # Build the RHF object: rhf = hf.RHF(m) rhf.run() # Build the grid: refq = grids.ReFqGrid(2**8, minpt=-5, maxpt=5, eta=0.1) # Build the Hartree-Fock Green's function: g_hf = aux.Aux(np.zeros(0), np.zeros((rhf.nao, 0)), chempot=rhf.chempot) # Build the MP2 self-energy: s_mp2 = aux.build_rmp2_iter(g_hf, rhf.fock_mo, rhf.eri_mo) # Build the second-iteration Green's function, which corresponds to the QP spectrum at MP2 level or G^(2): e, c = s_mp2.eig(rhf.fock_mo) g_2 = g_hf.new(e, c[:rhf.nao]) # inherits g_hf.chempot # Run an RAGF2 calcuation and get the converged Green's function and self-energy (we also use the RAGF2 density): gf2 = agf2.RAGF2(rhf, nmom=(2, 3), verbose=False) gf2.run() s_gf2 = gf2.se e, c = s_gf2.eig(rhf.get_fock(gf2.rdm1, basis='mo')) g_gf2 = s_gf2.new(e, c[:rhf.nao])
f_ext = np.block(([[rhf.get_fock(gf2_a.rdm1, basis='mo'), gf2_a.se.v], [gf2_a.se.v.T, np.diag(gf2_a.se.e)]])) e, c = np.linalg.eigh(f_ext) c = c[:rhf.nao] occ, vir = e < gf2_a.chempot, e >= gf2_a.chempot e_hoqmo, e_luqmo = e[occ][-1], e[vir][0] # only works because the eigenvalues are sorted v_hoqmo, v_luqmo = c[:,occ][:,-1], c[:,vir][:,0] print('RAGF2(1,1):') print('IP = %.6f weight = %.6f' % (-e_hoqmo, np.linalg.norm(v_hoqmo))) print('EA = %.6f weight = %.6f' % (e_luqmo, np.linalg.norm(v_luqmo))) print('Gap = %.6f' % (e_luqmo - e_hoqmo)) # Method 2: using aux.Aux more: e, c = gf2_b.se.eig(rhf.get_fock(gf2_b.rdm1, basis='mo')) c = c[:rhf.nao] gf = aux.Aux(e, c[:rhf.nao], chempot=gf2_b.chempot) e_hoqmo, e_luqmo = gf.as_occupied().e[-1], gf.as_virtual().e[0] v_hoqmo, v_luqmo = gf.as_occupied().v[:,-1], gf.as_virtual().v[:,0] print('RAGF2(2,2):') print('IP = %.6f weight = %.6f' % (-e_hoqmo, np.linalg.norm(v_hoqmo))) print('EA = %.6f weight = %.6f' % (e_luqmo, np.linalg.norm(v_luqmo))) print('Gap = %.6f' % (e_luqmo - e_hoqmo)) # Method 3: using a more efficient solver: f_phys = rhf.get_fock(gf2_c.rdm1, basis='mo') nroots = 5 def aop(x): return gf2_c.se.dot(f_phys, x) def precond(dx, e, x0): return dx / (np.concatenate([np.diag(f_phys), gf2_c.se.e]) - e) def pick(w, v, nroots, callback): mask = np.argsort(abs(w-gf2_c.chempot)) return w[mask], v[:,mask], 0
from auxgf.util import Timer timer = Timer() # Set the number of physical and auxiliary degrees of freedom: nphys = 20 naux = 1000 # Randomise the energies, couplings and physical Hamiltonian: energies = np.random.random((naux)) - 0.5 couplings = 0.1 * (np.random.random((nphys, naux)) - 0.5) hamiltonian = np.random.random((nphys, nphys)) - 0.5 hamiltonian = hamiltonian + hamiltonian.T # Build the auxiliary object: se = aux.Aux(energies, couplings, chempot=0.0) # Get the 'extended Fock matrix': f_ext = se.as_hamiltonian(hamiltonian) # Get the occupied (or virtual) parts of the self-energy: se_occ = se.as_occupied() assert np.all(se_occ.e < se.chempot) # Directly compute a dot product between f_ext and a vector without building f_ext: a = se.dot(hamiltonian, np.random.random((nphys + naux))) # Save and load the object: se.save('poles') se = aux.Aux.load('poles') os.remove('poles')