|
| 1 | +import numpy as np |
| 2 | +from pyscf import lib |
| 3 | +from pyscf.lib import logger |
| 4 | +from pyscf.ao2mo import _ao2mo |
| 5 | +from pyscf import __config__ |
| 6 | +from pyscf.cc import ccsd |
| 7 | +from pyscf.cc.ccsd import _ChemistsERIs |
| 8 | + |
| 9 | +BLKMIN = getattr(__config__, 'cc_ccsd_blkmin', 4) |
| 10 | +MEMORYMIN = getattr(__config__, 'cc_ccsd_memorymin', 2000) |
| 11 | +''' |
| 12 | +CC2 T2 equations |
| 13 | ++'iajb' |
| 14 | ++'cajb,ic->ijab' |
| 15 | +-'ikjb,ka->ijab' |
| 16 | ++'iadb,jd->ijab' |
| 17 | +-'iajl,lb->ijab' |
| 18 | +-'ckjb,ic,ka->ijab' |
| 19 | ++'cadb,ic,jd->ijab' |
| 20 | +-'cajl,ic,lb->ijab' |
| 21 | +-'ikdb,ka,jd->ijab' |
| 22 | ++'ikjl,ka,lb->ijab' |
| 23 | +-'iadl,jd,lb->ijab' |
| 24 | +-'ckdb,ic,ka,jd->ijab' |
| 25 | ++'ckjl,ic,ka,lb->ijab' |
| 26 | +-'cadl,ic,jd,lb->ijab' |
| 27 | ++'ikdl,ka,jd,lb->ijab' |
| 28 | ++'ckdl,ic,ak,jd,lb->ijab' |
| 29 | +''' |
| 30 | + |
| 31 | +def form_t2i(cc, t1, eris, i): |
| 32 | + Loo = eris.Loo |
| 33 | + Lov = eris.Lov |
| 34 | + Lvv = eris.Lvv |
| 35 | + nocc, nvir = t1.shape |
| 36 | + mo_e_o = eris.mo_energy[:nocc] |
| 37 | + mo_e_v = eris.mo_energy[nocc:] + cc.level_shift |
| 38 | + eia = mo_e_o[:,None] - mo_e_v |
| 39 | + t2i = lib.einsum('La,Ljb->jab', Lov[:, i, :], Lov) |
| 40 | + t2i += lib.einsum('Lca,Ljb,c->jab', Lvv, Lov, t1[i]) |
| 41 | + t2i -= lib.einsum('Lk,Ljb,ka->jab', Loo[:, i, :], Lov, t1) |
| 42 | + t2i += lib.einsum('La,Ldb,jd->jab', Lov[:, i, :], Lvv, t1) |
| 43 | + t2i -= lib.einsum('La,Ljl,lb->jab', Lov[:, i, :], Loo, t1) |
| 44 | + t2i -= lib.einsum('Lkc,Ljb,c,ka->jab', Lov, Lov, t1[i], t1) # transpose o->v, v->o |
| 45 | + t2i += lib.einsum('Lca,Ldb,c,jd->jab', Lvv, Lvv, t1[i], t1) |
| 46 | + t2i -= lib.einsum('Lca,Ljl,c,lb->jab', Lvv, Loo, t1[i], t1) |
| 47 | + t2i -= lib.einsum('Lk,Ldb,ka,jd->jab', Loo[:, i, :], Lvv, t1, t1) |
| 48 | + t2i += lib.einsum('Lk,Ljl,ka,lb->jab', Loo[:, i, :], Loo, t1, t1) |
| 49 | + t2i -= lib.einsum('La,Lld,jd,lb->jab', Lov[:, i, :], Lov, t1, t1) # transpose o->v, v->o |
| 50 | + t2i -= lib.einsum('Lkc,Ldb,c,ka,jd->jab', Lov, Lvv, t1[i], t1, t1) # transpose |
| 51 | + t2i += lib.einsum('Lkc,Ljl,c,ka,lb->jab', Lov, Loo, t1[i], t1, t1) # transpose |
| 52 | + t2i -= lib.einsum('Lca,Lld,c,jd,lb->jab', Lvv, Lov, t1[i], t1, t1) # transpose |
| 53 | + t2i += lib.einsum('Lk,Lld,ka,jd,lb->jab', Loo[:, i, :], Lov, t1, t1, t1) # transpose |
| 54 | + t2i += lib.einsum('Lkc,Lld,c,ka,jd,lb->jab', Lov, Lov, t1[i], t1, t1, t1) # transpose |
| 55 | + ejab = lib.direct_sum('jb+a->jab', eia, eia[i]) |
| 56 | + t2i /= ejab |
| 57 | + return t2i |
| 58 | + |
| 59 | + |
| 60 | +def energy(cc, t1=None, eris=None): |
| 61 | + '''RCCSD correlation energy''' |
| 62 | + if t1 is None: t1 = cc.t1 |
| 63 | + if eris is None: eris = cc.ao2mo() |
| 64 | + |
| 65 | + nocc, nvir = t1.shape |
| 66 | + fock = eris.fock |
| 67 | + |
| 68 | + Lov = eris.Lov |
| 69 | + Lvv = eris.Lvv |
| 70 | + |
| 71 | + mo_e_o = eris.mo_energy[:nocc] |
| 72 | + mo_e_v = eris.mo_energy[nocc:] + cc.level_shift |
| 73 | + eia = mo_e_o[:,None] - mo_e_v |
| 74 | + |
| 75 | + e = 2*np.einsum('ia,ia', fock[:nocc,nocc:], t1) |
| 76 | + e += 2*lib.einsum('Lia,Ljb,ia,jb', Lov, Lov, t1, t1) |
| 77 | + e += -lib.einsum('Lib,Lja,ia,jb', Lov, Lov, t1, t1) |
| 78 | + for i in range(nocc): |
| 79 | + t2i = form_t2i(cc, t1, eris, i) |
| 80 | + e += 2*lib.einsum('jab,La,Ljb', t2i, Lov[:, i, :], Lov) |
| 81 | + e += -lib.einsum('jab,Lb,Lja', t2i, Lov[:, i, :], Lov) |
| 82 | + |
| 83 | + if abs(e.imag) > 1e-4: |
| 84 | + logger.warn(cc, 'Non-zero imaginary part found in RCCSD energy %s', e) |
| 85 | + return e.real |
| 86 | + |
| 87 | +def update_t1(cc, t1, eris): |
| 88 | + # Ref: Hirata et al., J. Chem. Phys. 120, 2581 (2004) Eqs.(35)-(36) |
| 89 | + assert(isinstance(eris, ccsd._ChemistsERIs)) |
| 90 | + nocc, nvir = t1.shape |
| 91 | + naux = eris.naux |
| 92 | + fock = eris.fock |
| 93 | + mo_e_o = eris.mo_energy[:nocc] |
| 94 | + mo_e_v = eris.mo_energy[nocc:] + cc.level_shift |
| 95 | + Loo = eris.Loo |
| 96 | + Lov = eris.Lov |
| 97 | + Lvv = eris.Lvv |
| 98 | + |
| 99 | + fov = fock[:nocc,nocc:].copy() |
| 100 | + foo = fock[:nocc,:nocc].copy() |
| 101 | + fvv = fock[nocc:,nocc:].copy() |
| 102 | + |
| 103 | + eia = mo_e_o[:,None] - mo_e_v |
| 104 | + |
| 105 | + Foo = foo.copy() |
| 106 | + Fvv = fvv.copy() |
| 107 | + Fov = fov.copy() |
| 108 | + Fov += 2*lib.einsum('Lkc,Lld,ld->kc', Lov, Lov, t1) |
| 109 | + Fov -= lib.einsum('Lkd,Llc,ld->kc', Lov, Lov, t1) |
| 110 | + |
| 111 | + t1new = np.zeros_like(t1) |
| 112 | + # T2 contractions |
| 113 | + for i in range(nocc): |
| 114 | + t2i = form_t2i(cc, t1, eris, i) |
| 115 | + |
| 116 | + Foo[:, i] += 2*lib.einsum('Lkc,Lld,lcd->k', Lov, Lov, t2i) |
| 117 | + Foo[:, i] -= lib.einsum('Lkd,Llc,lcd->k', Lov, Lov, t2i) |
| 118 | + |
| 119 | + vov = lib.einsum('Lc,Lld->cld', Lov[:, i, :], Lov) |
| 120 | + Fvv -= 2*lib.einsum('cld,lad->ac', vov, t2i) |
| 121 | + Fvv += lib.einsum('dlc,lad->ac', vov, t2i) |
| 122 | + |
| 123 | + t1new += 2*lib.einsum('c,ica->ia', Fov[i], t2i) |
| 124 | + t1new[i] += -lib.einsum('kc,kca->a', Fov, t2i) |
| 125 | + t1new[i] += 2*lib.einsum('Lkd,Lac,kcd->a', Lov, Lvv, t2i) |
| 126 | + t1new[i] += -lib.einsum('Lkc,Lad,kcd->a', Lov, Lvv, t2i) |
| 127 | + t1new +=-2*lib.einsum('Llc,Li,lac->ia', Lov,Loo[:, i, :], t2i) |
| 128 | + t1new += lib.einsum('Lc,Lli,lac->ia', Lov[:, i, :], Loo, t2i) |
| 129 | + |
| 130 | + Foo += 2*lib.einsum('Lkc,Lld,ic,ld->k', Lov, Lov, t1, t1) |
| 131 | + Foo -= lib.einsum('Lkd,Llc,ic,ld->k', Lov, Lov, t1, t1) |
| 132 | + Fvv -= 2*lib.einsum('Lkc,Lld,ka,ld->ac', Lov, Lov, t1, t1) |
| 133 | + Fvv += lib.einsum('Lkd,Llc,la,ld->ac', Lov, Lov, t1, t1) |
| 134 | + Foo[np.diag_indices(nocc)] -= mo_e_o |
| 135 | + Fvv[np.diag_indices(nvir)] -= mo_e_v |
| 136 | + |
| 137 | + # T1 equation |
| 138 | + t1new +=-2*np.einsum('kc,ka,ic->ia', fov, t1, t1) |
| 139 | + t1new += np.einsum('ac,ic->ia', Fvv, t1) |
| 140 | + t1new += -np.einsum('ki,ka->ia', Foo, t1) |
| 141 | + t1new += np.einsum('kc,ic,ka->ia', Fov, t1, t1) |
| 142 | + t1new += fov.conj() |
| 143 | + t1new += 2*lib.einsum('Lkc,Lia,kc->ia', Lov, Lov, t1) |
| 144 | + t1new += -lib.einsum('Lki,Lac,kc->ia', Loo, Lvv, t1) |
| 145 | + t1new += 2*lib.einsum('Lkd,Lac,kd,ic->ia', Lov, Lvv, t1, t1) |
| 146 | + t1new += -lib.einsum('Lkc,Lad,kd,ic->ia', Lov, Lvv, t1, t1) |
| 147 | + t1new +=-2*lib.einsum('Llc,Lki,lc,ka->ia', Lov, Loo, t1, t1) |
| 148 | + t1new += lib.einsum('Lkc,Lli,lc,ka->ia', Lov, Loo, t1, t1) |
| 149 | + |
| 150 | + t1new /= eia |
| 151 | + |
| 152 | + return t1new |
| 153 | + |
| 154 | +def update_amps(cc, t1, eris): |
| 155 | + t1new = update_t1(cc, t1, eris) |
| 156 | + return t1new |
| 157 | + |
| 158 | +def amplitudes_to_vector(t1, out=None): |
| 159 | + nocc, nvir = t1.shape |
| 160 | + nov = nocc * nvir |
| 161 | + size = nov |
| 162 | + vector = np.ndarray(size, t1.dtype, buffer=out) |
| 163 | + vector = t1.ravel() |
| 164 | + return vector |
| 165 | + |
| 166 | +def vector_to_amplitudes(vector, nmo, nocc): |
| 167 | + nvir = nmo - nocc |
| 168 | + nov = nocc * nvir |
| 169 | + t1 = vector.reshape((nocc, nvir)) |
| 170 | + return t1 |
| 171 | + |
| 172 | +# t1: ia |
| 173 | +# t2: ijab |
| 174 | +def kernel(mycc, eris=None, t1=None, t2=None, max_cycle=50, tol=1e-8, |
| 175 | + tolnormt=1e-6, verbose=None): |
| 176 | + log = logger.new_logger(mycc, verbose) |
| 177 | + if eris is None: |
| 178 | + eris = mycc.ao2mo(mycc.mo_coeff) |
| 179 | + if t1 is None: |
| 180 | + t1 = mycc.get_init_guess(eris) |
| 181 | + |
| 182 | + cput1 = cput0 = (logger.process_clock(), logger.perf_counter()) |
| 183 | + eold = 0 |
| 184 | + eccsd = mycc.energy(t1, eris) |
| 185 | + log.info('Init E_corr(CCSD) = %.15g', eccsd) |
| 186 | + |
| 187 | + if isinstance(mycc.diis, lib.diis.DIIS): |
| 188 | + adiis = mycc.diis |
| 189 | + elif mycc.diis: |
| 190 | + adiis = lib.diis.DIIS(mycc, mycc.diis_file, incore=mycc.incore_complete) |
| 191 | + adiis.space = mycc.diis_space |
| 192 | + else: |
| 193 | + adiis = None |
| 194 | + |
| 195 | + conv = False |
| 196 | + for istep in range(max_cycle): |
| 197 | + t1new = mycc.update_amps(t1, eris) |
| 198 | + print(t1) |
| 199 | + print(t1new) |
| 200 | + tmpvec = mycc.amplitudes_to_vector(t1new) |
| 201 | + print(tmpvec) |
| 202 | + tmpvec -= mycc.amplitudes_to_vector(t1) |
| 203 | + print(tmpvec) |
| 204 | + normt = np.linalg.norm(tmpvec) |
| 205 | + tmpvec = None |
| 206 | + if mycc.iterative_damping < 1.0: |
| 207 | + alpha = mycc.iterative_damping |
| 208 | + t1new = (1-alpha) * t1 + alpha * t1new |
| 209 | + t1 = t1new |
| 210 | + t1new = None |
| 211 | + t1 = mycc.run_diis(t1, istep, normt, eccsd-eold, adiis) |
| 212 | + eold, eccsd = eccsd, mycc.energy(t1, eris) |
| 213 | + log.info('cycle = %d E_corr(CCSD) = %.15g dE = %.9g norm(t1) = %.6g', |
| 214 | + istep+1, eccsd, eccsd - eold, normt) |
| 215 | + cput1 = log.timer('CCSD iter', *cput1) |
| 216 | + if abs(eccsd-eold) < tol and normt < tolnormt: |
| 217 | + conv = True |
| 218 | + break |
| 219 | + log.timer('CCSD', *cput0) |
| 220 | + return conv, eccsd, t1 |
| 221 | + |
| 222 | +def _make_df_eris(cc, mo_coeff=None): |
| 223 | + eris = _ChemistsERIs() |
| 224 | + eris._common_init_(cc, mo_coeff) |
| 225 | + nocc = eris.nocc |
| 226 | + nmo = eris.fock.shape[0] |
| 227 | + nvir = nmo - nocc |
| 228 | + with_df = cc._scf.with_df |
| 229 | + naux = eris.naux = with_df.get_naoaux() |
| 230 | + |
| 231 | + Loo = np.empty((naux,nocc,nocc)) |
| 232 | + Lov = np.empty((naux,nocc,nvir)) |
| 233 | + Lvv = np.empty((naux,nvir,nvir)) |
| 234 | + mo = np.asarray(eris.mo_coeff, order='F') |
| 235 | + ijslice = (0, nmo, 0, nmo) |
| 236 | + p1 = 0 |
| 237 | + Lpq = None |
| 238 | + for k, eri1 in enumerate(with_df.loop()): |
| 239 | + Lpq = _ao2mo.nr_e2(eri1, mo, ijslice, aosym='s2', mosym='s1', out=Lpq) |
| 240 | + p0, p1 = p1, p1 + Lpq.shape[0] |
| 241 | + Lpq = Lpq.reshape(p1-p0,nmo,nmo) |
| 242 | + Loo[p0:p1] = Lpq[:,:nocc,:nocc] |
| 243 | + Lov[p0:p1] = Lpq[:,:nocc,nocc:] |
| 244 | + Lvv[p0:p1] = Lpq[:,nocc:,nocc:] |
| 245 | + Lpq = None |
| 246 | + eris.Loo = Loo |
| 247 | + eris.Lov = Lov |
| 248 | + eris.Lvv = Lvv |
| 249 | + |
| 250 | + return eris |
| 251 | + |
| 252 | +class DFRCC2(ccsd.CCSD): |
| 253 | + '''restricted CCSD with IP-EOM, EA-EOM, EE-EOM, and SF-EOM capabilities |
| 254 | +
|
| 255 | + Ground-state CCSD is performed in optimized ccsd.CCSD and EOM is performed here. |
| 256 | + ''' |
| 257 | + energy = energy |
| 258 | + kernel = kernel |
| 259 | + update_amps = update_amps |
| 260 | + |
| 261 | + def update_amps(cc, t1, eris): |
| 262 | + t1new = update_t1(cc, t1, eris) |
| 263 | + return t1new |
| 264 | + |
| 265 | + def get_init_guess(self, eris=None): |
| 266 | + if eris is None: |
| 267 | + eris = self.ao2mo(self.mo_coeff) |
| 268 | + nocc = self.nocc |
| 269 | + mo_e = eris.mo_energy |
| 270 | + eia = mo_e[:nocc,None] - mo_e[None,nocc:] |
| 271 | + t1 = eris.fock[:nocc,nocc:] / eia |
| 272 | + return t1 |
| 273 | + |
| 274 | + def amplitudes_to_vector(self, t1, out=None): |
| 275 | + return amplitudes_to_vector(t1, out) |
| 276 | + |
| 277 | + def vector_to_amplitudes(self, vec, nmo=None, nocc=None): |
| 278 | + if nocc is None: nocc = self.nocc |
| 279 | + if nmo is None: nmo = self.nmo |
| 280 | + return vector_to_amplitudes(vec, nmo, nocc) |
| 281 | + |
| 282 | + def run_diis(self, t1, istep, normt, de, adiis): |
| 283 | + if (adiis and |
| 284 | + istep >= self.diis_start_cycle and |
| 285 | + abs(de) < self.diis_start_energy_diff): |
| 286 | + vec = self.amplitudes_to_vector(t1) |
| 287 | + t1 = self.vector_to_amplitudes(adiis.update(vec)) |
| 288 | + logger.debug1(self, 'DIIS for step %d', istep) |
| 289 | + return t1 |
| 290 | + |
| 291 | + def ao2mo(self, mo_coeff=None): |
| 292 | + return _make_df_eris(self, mo_coeff) |
0 commit comments