Source code for pyscf.cc.eom_rccsd

#!/usr/bin/env python
# Copyright 2014-2019 The PySCF Developers. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Author: Qiming Sun <osirpt.sun@gmail.com>
#         James D. McClain
#         Timothy Berkelbach <tim.berkelbach@gmail.com>
#


import numpy as np

from pyscf import lib
from pyscf import ao2mo
from pyscf.lib import logger, module_method
from pyscf.cc import ccsd
from pyscf.cc import rintermediates as imd
from pyscf import __config__


[docs] def kernel(eom, nroots=1, koopmans=False, guess=None, left=False, eris=None, imds=None, **kwargs): cput0 = (logger.process_clock(), logger.perf_counter()) log = logger.Logger(eom.stdout, eom.verbose) if eom.verbose >= logger.WARN: eom.check_sanity() eom.dump_flags() if imds is None: imds = eom.make_imds(eris) matvec, diag = eom.gen_matvec(imds, left=left, **kwargs) size = eom.vector_size() nroots = min(nroots, size) if guess is not None: user_guess = True for g in guess: assert g.size == size else: user_guess = False guess = eom.get_init_guess(nroots, koopmans, diag) def precond(r, e0, x0): return r/(e0-diag+1e-12) # GHF or customized RHF/UHF may be of complex type real_system = (eom._cc._scf.mo_coeff[0].dtype == np.double) eig = lib.davidson_nosym1 if user_guess or koopmans: assert len(guess) == nroots def eig_close_to_init_guess(w, v, nroots, envs): x0 = lib.linalg_helper._gen_x0(envs['v'], envs['xs']) s = np.dot(np.asarray(guess).conj(), np.asarray(x0).T) snorm = np.einsum('pi,pi->i', s.conj(), s) idx = np.argsort(-snorm)[:nroots] return lib.linalg_helper._eigs_cmplx2real(w, v, idx, real_system) conv, es, vs = eig(matvec, guess, precond, pick=eig_close_to_init_guess, tol=eom.conv_tol, max_cycle=eom.max_cycle, max_space=eom.max_space, nroots=nroots, verbose=log) else: def pickeig(w, v, nroots, envs): real_idx = np.where(abs(w.imag) < 1e-3)[0] return lib.linalg_helper._eigs_cmplx2real(w, v, real_idx, real_system) conv, es, vs = eig(matvec, guess, precond, pick=pickeig, tol=eom.conv_tol, max_cycle=eom.max_cycle, max_space=eom.max_space, nroots=nroots, verbose=log) if eom.verbose >= logger.INFO: for n, en, vn, convn in zip(range(nroots), es, vs, conv): r1, r2 = eom.vector_to_amplitudes(vn) if isinstance(r1, np.ndarray): qp_weight = np.linalg.norm(r1)**2 else: # for EOM-UCCSD r1 = np.hstack([x.ravel() for x in r1]) qp_weight = np.linalg.norm(r1)**2 logger.info(eom, 'EOM-CCSD root %d E = %.16g qpwt = %.6g conv = %s', n, en, qp_weight, convn) log.timer('EOM-CCSD', *cput0) if nroots == 1: return conv[0], es[0].real, vs[0] else: return conv, es.real, vs
[docs] class EOM(lib.StreamObject): _keys = set(( 'mol', 'max_space', 'max_cycle', 'conv_tol', 'partition', 'e', 'v', 'nocc', 'nmo', )) def __init__(self, cc): self.mol = cc.mol self._cc = cc self.verbose = cc.verbose self.stdout = cc.stdout self.max_memory = cc.max_memory self.max_space = getattr(__config__, 'eom_rccsd_EOM_max_space', 20) self.max_cycle = getattr(__config__, 'eom_rccsd_EOM_max_cycle', cc.max_cycle) self.conv_tol = getattr(__config__, 'eom_rccsd_EOM_conv_tol', cc.conv_tol) self.partition = getattr(__config__, 'eom_rccsd_EOM_partition', None) ################################################## # don't modify the following attributes, they are not input options self.e = None self.v = None self.nocc = cc.nocc self.nmo = cc.nmo
[docs] def dump_flags(self, verbose=None): logger.info(self, '') logger.info(self, '******** %s ********', self.__class__) logger.info(self, 'max_space = %d', self.max_space) logger.info(self, 'max_cycle = %d', self.max_cycle) logger.info(self, 'conv_tol = %s', self.conv_tol) logger.info(self, 'partition = %s', self.partition) #logger.info(self, 'nocc = %d', self.nocc) #logger.info(self, 'nmo = %d', self.nmo) logger.info(self, 'max_memory %d MB (current use %d MB)', self.max_memory, lib.current_memory()[0]) return self
[docs] def reset(self, mol=None): self._cc.reset(mol) return self
def _sort_left_right_eigensystem(eom, right_converged, right_evals, right_evecs, left_converged, left_evals, left_evecs, tol=1e-6): '''Ensures the left and right eigenvectors correspond to the same eigenvalue. Note: Useful for perturbative methods that need both eigenstates. Right now, just simply checks for equality between left and right eigenvalues, but can be extended to make sure the overlap between states is sufficiently large. Kwargs: eom : :class:`EOM` Class holding EOM results. right_converged : array-like of bool Whether the right eigenstates converged. right_evals : array-like Eigenvalues of right eigenstates. right_evecs : array-like of ndarray Eigenvectors of right eigenstates. left_converged : array-like of bool Whether the left eigenstates converged. left_evals : array-like Eigenvalues of left eigenstates. left_evecs : array-like of ndarray Eigenvectors of left eigenstates. tol : float Tolerance for determining whether a left and right eigenvalue should be considered equal. ''' log = logger.Logger(eom.stdout, eom.verbose) right_evecs, left_evecs = [np.atleast_2d(x) for x in [right_evecs, left_evecs]] right_evals, left_evals = [np.atleast_1d(x) for x in [right_evals, left_evals]] right_converged, left_converged = [np.atleast_1d(x) for x in [right_converged, left_converged]] srt_right_idx = [] srt_left_idx = [] left_idx = [idx for idx in range(len(left_evals)) if left_converged[idx]] right_idx = [idx for idx in range(len(right_evals)) if right_converged[idx]] if len(right_idx) != len(left_idx): log.warn('Number of converged left and right eigenvalues are not equal.\n' ' No. Left = %3d, No. Right = %3d.' % (len(left_idx), len(right_idx))) for ir_idx, ir in enumerate(right_idx): found = False for il_idx, il in enumerate(left_idx): if abs(right_evals[ir] - left_evals[il]) < tol: found = True srt_right_idx.append(ir) srt_left_idx.append(il) break if found: left_idx.pop(il_idx) else: log.warn('No converged left eigenvalue corresponding to right eigenvalue ' '%.6g (right idx=%3d).\nWill not perform perturbation on this state.' % (right_evals[ir], ir)) log.info('Resulting left/right eigenstates:') log.info('Left Eigen (idx) <-> Right Eigen (idx)') for il, ir in zip(srt_left_idx, srt_right_idx): log.info('%10.6g (%3d) %10.6g (%3d)', left_evals[il], il, right_evals[ir], ir) return (right_evals[srt_right_idx], right_evecs[srt_right_idx], left_evecs[srt_left_idx])
[docs] def perturbed_ccsd_kernel(eom, nroots=1, koopmans=False, right_guess=None, left_guess=None, eris=None, imds=None): '''Wrapper for running perturbative excited-states that require both left and right amplitudes.''' if imds is None: imds = eom.make_imds(eris=eris) # Right eigenvectors r_converged, r_e, r_v = \ kernel(eom, nroots, koopmans=koopmans, guess=right_guess, left=False, eris=eris, imds=imds) # Left eigenvectors l_converged, l_e, l_v = \ kernel(eom, nroots, koopmans=koopmans, guess=right_guess, left=True, eris=eris, imds=imds) e, r_v, l_v = _sort_left_right_eigensystem(eom, r_converged, r_e, r_v, l_converged, l_e, l_v) e_star = eom.ccsd_star_contract(e, r_v, l_v, imds=imds) return e_star
######################################## # EOM-IP-CCSD ########################################
[docs] def ipccsd(eom, nroots=1, left=False, koopmans=False, guess=None, partition=None, eris=None, imds=None): '''Calculate (N-1)-electron charged excitations via IP-EOM-CCSD. Kwargs: nroots : int Number of roots (eigenvalues) requested partition : bool or str Use a matrix-partitioning for the doubles-doubles block. Can be None, 'mp' (Moller-Plesset, i.e. orbital energies on the diagonal), or 'full' (full diagonal elements). koopmans : bool Calculate Koopmans'-like (quasiparticle) excitations only, targeting via overlap. guess : list of ndarray List of guess vectors to use for targeting via overlap. ''' if partition is not None: eom.partition = partition.lower() assert eom.partition in ['mp','full'] eom.converged, eom.e, eom.v \ = kernel(eom, nroots, koopmans, guess, left, eris=eris, imds=imds) return eom.e, eom.v
[docs] def ipccsd_star(eom, nroots=1, koopmans=False, right_guess=None, left_guess=None, eris=None, imds=None): """Calculates CCSD* perturbative correction. Simply calls the relevant `kernel()` function and `perturb_star` of the `eom` class. Returns: e_t_a_star (list of float): The IP-CCSD* energy. """ return perturbed_ccsd_kernel(eom, nroots=nroots, koopmans=koopmans, right_guess=right_guess, left_guess=left_guess, eris=eris, imds=imds)
[docs] def vector_to_amplitudes_ip(vector, nmo, nocc): nvir = nmo - nocc r1 = vector[:nocc].copy() r2 = vector[nocc:].copy().reshape(nocc,nocc,nvir) return r1, r2
[docs] def amplitudes_to_vector_ip(r1, r2): vector = np.hstack((r1, r2.ravel())) return vector
[docs] def spatial2spin_ip(rx, orbspin=None): from pyscf.cc import eom_uccsd if rx.ndim == 1: r1 = (rx, np.zeros_like(rx)) return eom_uccsd.spatial2spin_ip(r1, orbspin) else: rx0 = np.zeros_like(rx) r2 = (rx.transpose(1,0,2)-rx, rx0, -rx, rx0) return eom_uccsd.spatial2spin_ip(r2, orbspin)
[docs] def ipccsd_matvec(eom, vector, imds=None, diag=None): # Ref: Nooijen and Snijders, J. Chem. Phys. 102, 1681 (1995) Eqs.(8)-(9) if imds is None: imds = eom.make_imds() nocc = eom.nocc nmo = eom.nmo r1, r2 = eom.vector_to_amplitudes(vector, nmo, nocc) # 1h-1h block Hr1 = -np.einsum('ki,k->i', imds.Loo, r1) #1h-2h1p block Hr1 += 2*np.einsum('ld,ild->i', imds.Fov, r2) Hr1 += -np.einsum('kd,kid->i', imds.Fov, r2) Hr1 += -2*np.einsum('klid,kld->i', imds.Wooov, r2) Hr1 += np.einsum('lkid,kld->i', imds.Wooov, r2) # 2h1p-1h block Hr2 = -np.einsum('kbij,k->ijb', imds.Wovoo, r1) # 2h1p-2h1p block if eom.partition == 'mp': fock = imds.eris.fock foo = fock[:nocc,:nocc] fvv = fock[nocc:,nocc:] Hr2 += lib.einsum('bd,ijd->ijb', fvv, r2) Hr2 += -lib.einsum('ki,kjb->ijb', foo, r2) Hr2 += -lib.einsum('lj,ilb->ijb', foo, r2) elif eom.partition == 'full': diag_matrix2 = eom.vector_to_amplitudes(diag, nmo, nocc)[1] Hr2 += diag_matrix2 * r2 else: Hr2 += lib.einsum('bd,ijd->ijb', imds.Lvv, r2) Hr2 += -lib.einsum('ki,kjb->ijb', imds.Loo, r2) Hr2 += -lib.einsum('lj,ilb->ijb', imds.Loo, r2) Hr2 += lib.einsum('klij,klb->ijb', imds.Woooo, r2) Hr2 += 2*lib.einsum('lbdj,ild->ijb', imds.Wovvo, r2) Hr2 += -lib.einsum('kbdj,kid->ijb', imds.Wovvo, r2) Hr2 += -lib.einsum('lbjd,ild->ijb', imds.Wovov, r2) #typo in Ref Hr2 += -lib.einsum('kbid,kjd->ijb', imds.Wovov, r2) tmp = 2*np.einsum('lkdc,kld->c', imds.Woovv, r2) tmp += -np.einsum('kldc,kld->c', imds.Woovv, r2) Hr2 += -np.einsum('c,ijcb->ijb', tmp, imds.t2) vector = eom.amplitudes_to_vector(Hr1, Hr2) return vector
[docs] def lipccsd_matvec(eom, vector, imds=None, diag=None): '''For left eigenvector''' # Note this is not the same left EA equations used by Nooijen and Bartlett. # Small changes were made so that the same type L2 basis was used for both the # left EA and left IP equations. You will note more similarity for these # equations to the left IP equations than for the left EA equations by Nooijen. if imds is None: imds = eom.make_imds() nocc = eom.nocc nmo = eom.nmo r1, r2 = eom.vector_to_amplitudes(vector, nmo, nocc) # 1h-1h block Hr1 = -np.einsum('ki,i->k', imds.Loo, r1) #1h-2h1p block Hr1 += -np.einsum('kbij,ijb->k', imds.Wovoo, r2) # 2h1p-1h block Hr2 = -np.einsum('kd,l->kld', imds.Fov, r1) Hr2 += 2.*np.einsum('ld,k->kld', imds.Fov, r1) Hr2 += -np.einsum('klid,i->kld', 2.*imds.Wooov-imds.Wooov.transpose(1,0,2,3), r1) # 2h1p-2h1p block if eom.partition == 'mp': fock = imds.eris.fock foo = fock[:nocc,:nocc] fvv = fock[nocc:,nocc:] Hr2 += lib.einsum('bd,klb->kld', fvv, r2) Hr2 += -lib.einsum('ki,ild->kld', foo, r2) Hr2 += -lib.einsum('lj,kjd->kld', foo, r2) elif eom.partition == 'full': diag_matrix2 = eom.vector_to_amplitudes(diag, nmo, nocc)[1] Hr2 += diag_matrix2 * r2 else: Hr2 += lib.einsum('bd,klb->kld', imds.Lvv, r2) Hr2 += -lib.einsum('ki,ild->kld', imds.Loo, r2) Hr2 += -lib.einsum('lj,kjd->kld', imds.Loo, r2) Hr2 += lib.einsum('lbdj,kjb->kld', 2.*imds.Wovvo-imds.Wovov.transpose(0,1,3,2), r2) Hr2 += -lib.einsum('kbdj,ljb->kld', imds.Wovvo, r2) Hr2 += lib.einsum('klij,ijd->kld', imds.Woooo, r2) Hr2 += -lib.einsum('kbid,ilb->kld', imds.Wovov, r2) tmp = np.einsum('ijcb,ijb->c', imds.t2, r2) Hr2 += -np.einsum('lkdc,c->kld', 2.*imds.Woovv-imds.Woovv.transpose(1,0,2,3), tmp) vector = eom.amplitudes_to_vector(Hr1, Hr2) return vector
[docs] def ipccsd_diag(eom, imds=None): if imds is None: imds = eom.make_imds() t1, t2 = imds.t1, imds.t2 dtype = np.result_type(t1, t2) nocc, nvir = t1.shape fock = imds.eris.fock foo = fock[:nocc,:nocc] fvv = fock[nocc:,nocc:] Hr1 = -np.diag(imds.Loo) Hr2 = np.zeros((nocc,nocc,nvir), dtype) for i in range(nocc): for j in range(nocc): for b in range(nvir): if eom.partition == 'mp': Hr2[i,j,b] += fvv[b,b] Hr2[i,j,b] += -foo[i,i] Hr2[i,j,b] += -foo[j,j] else: Hr2[i,j,b] += imds.Lvv[b,b] Hr2[i,j,b] += -imds.Loo[i,i] Hr2[i,j,b] += -imds.Loo[j,j] Hr2[i,j,b] += imds.Woooo[i,j,i,j] Hr2[i,j,b] +=2*imds.Wovvo[j,b,b,j] Hr2[i,j,b] += -imds.Wovvo[i,b,b,i]*(i==j) Hr2[i,j,b] += -imds.Wovov[j,b,j,b] Hr2[i,j,b] += -imds.Wovov[i,b,i,b] Hr2[i,j,b] += -2*np.dot(imds.Woovv[j,i,b,:], t2[i,j,:,b]) Hr2[i,j,b] += np.dot(imds.Woovv[i,j,b,:], t2[i,j,:,b]) vector = eom.amplitudes_to_vector(Hr1, Hr2) return vector
[docs] def ipccsd_star_contract(eom, ipccsd_evals, ipccsd_evecs, lipccsd_evecs, imds=None): from pyscf.cc.ccsd_t import _sort_eri, _sort_t2_vooo_ cpu1 = (logger.process_clock(), logger.perf_counter()) log = logger.Logger(eom.stdout, eom.verbose) if imds is None: imds = eom.make_imds() t1, t2 = imds.t1, imds.t2 eris = imds.eris nocc, nvir = t1.shape nmo = nocc + nvir dtype = np.result_type(t1, t2, eris.ovoo.dtype) if eom._cc.incore_complete: ftmp = None eris_vvop = np.zeros((nvir,nvir,nocc,nmo), dtype) else: ftmp = lib.H5TmpFile() eris_vvop = ftmp.create_dataset('vvop', (nvir,nvir,nocc,nmo), dtype) orbsym = _sort_eri(eom._cc, eris, nocc, nvir, eris_vvop, log) mo_energy, t1T, t2T, vooo, fvo, restore_t2_inplace = \ _sort_t2_vooo_(eom._cc, orbsym, t1, t2, eris) cpu1 = log.timer_debug1('CCSD(T) sort_eri', *cpu1) mem_now = lib.current_memory()[0] max_memory = max(0, eom.max_memory - mem_now) blksize = min(nvir, max(ccsd.BLKMIN, int(max_memory*1e6/8/(nocc**3*6)))) mo_e_occ = np.asarray(mo_energy[:nocc]) mo_e_vir = np.asarray(mo_energy[nocc:]) def contract_l2p(l1, l2, a0, a1, b0, b1, cache_vvop, out=None): '''Create perturbed l2.''' if out is None: out = np.zeros((nocc,)*3 + (a1-a0,b1-b0), dtype=dtype) out += 0.5*np.einsum('abij,k->ijkab', cache_vvop[:,:,:,:nocc].conj(), l1) out += lib.einsum('abie,jke->ijkab', cache_vvop[:,:,:,nocc:].conj(), l2) out += -lib.einsum('bjkm,ima->ijkab', vooo[b0:b1], l2[:,:,a0:a1]) out += -lib.einsum('bjim,mka->ijkab', vooo[b0:b1], l2[:,:,a0:a1]) return out def contract_pl2p(l1, l2, a0, a1, b0, b1, cache_vvop_a, cache_vvop_b): '''Create P(ia|jb) of perturbed l2.''' out = contract_l2p(l1, l2, a0, a1, b0, b1, cache_vvop_a) out += contract_l2p(l1, l2, b0, b1, a0, a1, cache_vvop_b).transpose(1,0,2,4,3) # P(ia|jb) return out def contract_r2p(r1, r2, a0, a1, b0, b1, cache_vvop, out=None): '''Create perturbed r2.''' if out is None: out = np.zeros((nocc,)*3 + (a1-a0,b1-b0), dtype=dtype) tmp = np.einsum('mkbe,m->bke', eris.oovv[:,:,b0:b1,:], r1) out += -lib.einsum('bke,aeji->ijkab', tmp, t2T[a0:a1]) tmp = np.einsum('mebj,m->bej', eris.ovvo[:,:,b0:b1,:], r1) out += -lib.einsum('bej,aeki->ijkab', tmp, t2T[a0:a1]) tmp = np.einsum('mjnk,n->mjk', eris.oooo, r1) out += lib.einsum('mjk,abmi->ijkab', tmp, t2T[a0:a1,b0:b1]) out += lib.einsum('abie,kje->ijkab', cache_vvop[:,:,:,nocc:], r2) out += -lib.einsum('bjkm,mia->ijkab', vooo[b0:b1].conj(), r2[:,:,a0:a1]) out += -lib.einsum('bjim,kma->ijkab', vooo[b0:b1].conj(), r2[:,:,a0:a1]) return out def contract_pr2p(r1, r2, a0, a1, b0, b1, cache_vvop_a, cache_vvop_b): '''Create P(ia|jb) of perturbed r2.''' out = contract_r2p(r1, r2, a0, a1, b0, b1, cache_vvop_a) out += contract_r2p(r1, r2, b0, b1, a0, a1, cache_vvop_b).transpose(1,0,2,4,3) # P(ia|jb) return out ipccsd_evecs = np.array(ipccsd_evecs) lipccsd_evecs = np.array(lipccsd_evecs) e = [] ipccsd_evecs, lipccsd_evecs = [np.atleast_2d(x) for x in [ipccsd_evecs, lipccsd_evecs]] ipccsd_evals = np.atleast_1d(ipccsd_evals) for eval_, evec_, levec_ in zip(ipccsd_evals, ipccsd_evecs, lipccsd_evecs): l1, l2 = eom.vector_to_amplitudes(levec_) r1, r2 = eom.vector_to_amplitudes(evec_) ldotr = np.dot(l1, r1) + np.dot(l2.ravel(), r2.ravel()) l1 /= ldotr l2 /= ldotr l2 = 1./3*(l2 + 2.*l2.transpose(1,0,2)) deltaE = 0.0 eijk = (mo_e_occ[:,None,None,None,None] + mo_e_occ[None,:,None,None,None] + mo_e_occ[None,None,:,None,None] + eval_) for a0, a1 in lib.prange_tril(0, nvir, blksize): b0, b1 = 0, a1 eijkab = (eijk - mo_e_vir[a0:a1][None,None,None,:,None] - mo_e_vir[b0:b1][None,None,None,None,:]) eijkab = 1./eijkab vvov_a = eris_vvop[a0:a1,b0:b1,:,:] vvov_b = eris_vvop[b0:b1,a0:a1,:,:] lijkab = contract_pl2p(l1, l2, a0, a1, b0, b1, vvov_a, vvov_b) rijkab = contract_pr2p(r1, r2, a0, a1, b0, b1, vvov_a, vvov_b) lijkab = 4.*lijkab \ - 2.*lijkab.transpose(1,0,2,3,4) \ - 2.*lijkab.transpose(2,1,0,3,4) \ - 2.*lijkab.transpose(0,2,1,3,4) \ + 1.*lijkab.transpose(1,2,0,3,4) \ + 1.*lijkab.transpose(2,0,1,3,4) # Symmetry factors (1 for a == b, 2 for a < b) fac = 2*np.ones_like(rijkab, dtype=int) triu_idx = np.triu_indices(a1-a0, a0+1, m=b1-b0) fac[:,:,:,triu_idx[0],triu_idx[1]] = 0 fac[:,:,:,np.arange(a1-a0),np.arange(a0,b1)] = 1 eijkab *= fac deltaE += np.einsum('ijkab,ijkab,ijkab', lijkab, rijkab, eijkab) deltaE = 0.5*deltaE.real logger.info(eom, "ipccsd energy, star energy, delta energy = %16.12f, %16.12f, %16.12f", eval_, eval_+deltaE, deltaE) e.append(eval_+deltaE) t2 = restore_t2_inplace(t2T) return e
[docs] class EOMIP(EOM):
[docs] def get_init_guess(self, nroots=1, koopmans=True, diag=None): size = self.vector_size() nroots = min(nroots, size) guess = [] if koopmans: dtype = getattr(diag, 'dtype', np.double) for n in range(nroots): g = np.zeros(int(size), dtype) g[self.nocc-n-1] = 1.0 guess.append(g) else: if diag is None: diag = self.get_diag() dtype = getattr(diag, 'dtype', np.double) idx = diag.argsort()[:nroots] for i in idx: g = np.zeros(int(size), dtype) g[i] = 1.0 guess.append(g) return guess
kernel = ipccsd ipccsd = ipccsd ipccsd_star = ipccsd_star matvec = ipccsd_matvec l_matvec = lipccsd_matvec get_diag = ipccsd_diag ccsd_star_contract = ipccsd_star_contract
[docs] def ipccsd_star_contract(self, ipccsd_evals, ipccsd_evecs, lipccsd_evecs, imds=None): return self.ccsd_star_contract(ipccsd_evals, ipccsd_evecs, lipccsd_evecs, imds=imds)
[docs] def gen_matvec(self, imds=None, left=False, **kwargs): if imds is None: imds = self.make_imds() diag = self.get_diag(imds) if left: matvec = lambda xs: [self.l_matvec(x, imds, diag) for x in xs] else: matvec = lambda xs: [self.matvec(x, imds, diag) for x in xs] return matvec, diag
amplitudes_to_vector = staticmethod(amplitudes_to_vector_ip) vector_to_amplitudes = module_method(vector_to_amplitudes_ip, absences=['nmo', 'nocc']) spatial2spin = staticmethod(spatial2spin_ip)
[docs] def vector_size(self): nocc = self.nocc nvir = self.nmo - nocc return nocc + nocc*nocc*nvir
[docs] def make_imds(self, eris=None): imds = _IMDS(self._cc, eris=eris) imds.make_ip(self.partition) return imds
@property def eip(self): return self.e
[docs] class EOMIP_Ta(EOMIP): '''Class for EOM IPCCSD(T)*(a) method by Matthews and Stanton.'''
[docs] def make_imds(self, eris=None): imds = _IMDS(self._cc, eris=eris) imds.make_t3p2_ip(self._cc, self.partition) return imds
######################################## # EOM-EA-CCSD ########################################
[docs] def eaccsd(eom, nroots=1, left=False, koopmans=False, guess=None, partition=None, eris=None, imds=None): '''Calculate (N+1)-electron charged excitations via EA-EOM-CCSD. Args: See also ipccd() ''' return ipccsd(eom, nroots, left, koopmans, guess, partition, eris, imds)
[docs] def eaccsd_star(eom, nroots=1, koopmans=False, right_guess=None, left_guess=None, eris=None, imds=None, **kwargs): """Calculates CCSD* perturbative correction. Args: See also ipccd_star() """ return perturbed_ccsd_kernel(eom, nroots=nroots, koopmans=koopmans, right_guess=right_guess, left_guess=left_guess, eris=eris, imds=imds)
[docs] def vector_to_amplitudes_ea(vector, nmo, nocc): nvir = nmo - nocc r1 = vector[:nvir].copy() r2 = vector[nvir:].copy().reshape(nocc,nvir,nvir) return r1, r2
[docs] def amplitudes_to_vector_ea(r1, r2): vector = np.hstack((r1, r2.ravel())) return vector
[docs] def spatial2spin_ea(rx, orbspin=None): from pyscf.cc import eom_uccsd if rx.ndim == 1: r1 = (rx, np.zeros_like(rx)) return eom_uccsd.spatial2spin_ea(r1, orbspin) else: rx0 = np.zeros_like(rx) r2 = (rx-rx.transpose(0,2,1), rx0, rx, rx0) return eom_uccsd.spatial2spin_ea(r2, orbspin)
[docs] def eaccsd_matvec(eom, vector, imds=None, diag=None): # Ref: Nooijen and Bartlett, J. Chem. Phys. 102, 3629 (1995) Eqs.(30)-(31) if imds is None: imds = eom.make_imds() nocc = eom.nocc nmo = eom.nmo nvir = nmo - nocc r1, r2 = eom.vector_to_amplitudes(vector, nmo, nocc) # Eq. (37) # 1p-1p block Hr1 = np.einsum('ac,c->a', imds.Lvv, r1) # 1p-2p1h block Hr1 += np.einsum('ld,lad->a', 2.*imds.Fov, r2) Hr1 += np.einsum('ld,lda->a', -imds.Fov, r2) Hr1 += np.einsum('alcd,lcd->a', 2.*imds.Wvovv-imds.Wvovv.transpose(0,1,3,2), r2) # Eq. (38) # 2p1h-1p block Hr2 = np.einsum('abcj,c->jab', imds.Wvvvo, r1) # 2p1h-2p1h block if eom.partition == 'mp': fock = imds.eris.fock foo = fock[:nocc,:nocc] fvv = fock[nocc:,nocc:] Hr2 += lib.einsum('ac,jcb->jab', fvv, r2) Hr2 += lib.einsum('bd,jad->jab', fvv, r2) Hr2 += -lib.einsum('lj,lab->jab', foo, r2) elif eom.partition == 'full': diag_matrix2 = eom.vector_to_amplitudes(diag, nmo, nocc)[1] Hr2 += diag_matrix2 * r2 else: Hr2 += lib.einsum('ac,jcb->jab', imds.Lvv, r2) Hr2 += lib.einsum('bd,jad->jab', imds.Lvv, r2) Hr2 += -lib.einsum('lj,lab->jab', imds.Loo, r2) Hr2 += lib.einsum('lbdj,lad->jab', 2.*imds.Wovvo-imds.Wovov.transpose(0,1,3,2), r2) Hr2 += -lib.einsum('lajc,lcb->jab', imds.Wovov, r2) Hr2 += -lib.einsum('lbcj,lca->jab', imds.Wovvo, r2) for a in range(nvir): Hr2[:,a,:] += lib.einsum('bcd,jcd->jb', imds.Wvvvv[a], r2) tmp = np.einsum('klcd,lcd->k', 2.*imds.Woovv-imds.Woovv.transpose(0,1,3,2), r2) Hr2 += -np.einsum('k,kjab->jab', tmp, imds.t2) vector = eom.amplitudes_to_vector(Hr1,Hr2) return vector
[docs] def leaccsd_matvec(eom, vector, imds=None, diag=None): # Note this is not the same left EA equations used by Nooijen and Bartlett. # Small changes were made so that the same type L2 basis was used for both the # left EA and left IP equations. You will note more similarity for these # equations to the left IP equations than for the left EA equations by Nooijen. if imds is None: imds = eom.make_imds() nocc = eom.nocc nmo = eom.nmo nvir = nmo - nocc r1, r2 = eom.vector_to_amplitudes(vector, nmo, nocc) # Eq. (30) # 1p-1p block Hr1 = np.einsum('ac,a->c', imds.Lvv, r1) # 1p-2p1h block Hr1 += np.einsum('abcj,jab->c', imds.Wvvvo, r2) # Eq. (31) # 2p1h-1p block Hr2 = 2.*np.einsum('c,ld->lcd', r1, imds.Fov) Hr2 += -np.einsum('d,lc->lcd', r1, imds.Fov) Hr2 += np.einsum('a,alcd->lcd', r1, 2.*imds.Wvovv-imds.Wvovv.transpose(0,1,3,2)) # 2p1h-2p1h block if eom.partition == 'mp': fock = imds.eris.fock foo = fock[:nocc,:nocc] fvv = fock[nocc:,nocc:] Hr2 += lib.einsum('lad,ac->lcd', r2, fvv) Hr2 += lib.einsum('lcb,bd->lcd', r2, fvv) Hr2 += -lib.einsum('jcd,lj->lcd', r2, foo) elif eom.partition == 'full': diag_matrix2 = eom.vector_to_amplitudes(diag, nmo, nocc)[1] Hr2 += diag_matrix2 * r2 else: Hr2 += lib.einsum('lad,ac->lcd', r2, imds.Lvv) Hr2 += lib.einsum('lcb,bd->lcd', r2, imds.Lvv) Hr2 += -lib.einsum('jcd,lj->lcd', r2, imds.Loo) Hr2 += lib.einsum('jcb,lbdj->lcd', r2, 2.*imds.Wovvo-imds.Wovov.transpose(0,1,3,2)) Hr2 += -lib.einsum('lajc,jab->lcb', imds.Wovov, r2) Hr2 += -lib.einsum('lbcj,jab->lca', imds.Wovvo, r2) for a in range(nvir): Hr2 += lib.einsum('lb,bcd->lcd', r2[:,a,:], imds.Wvvvv[a]) tmp = np.einsum('ijcb,ibc->j', imds.t2, r2) Hr2 += -np.einsum('kjfe,j->kef', 2.*imds.Woovv-imds.Woovv.transpose(0,1,3,2),tmp) vector = eom.amplitudes_to_vector(Hr1,Hr2) return vector
[docs] def eaccsd_diag(eom, imds=None): if imds is None: imds = eom.make_imds() t1, t2 = imds.t1, imds.t2 dtype = np.result_type(t1, t2) nocc, nvir = t1.shape fock = imds.eris.fock foo = fock[:nocc,:nocc] fvv = fock[nocc:,nocc:] Hr1 = np.diag(imds.Lvv) Hr2 = np.zeros((nocc,nvir,nvir), dtype) for a in range(nvir): if eom.partition != 'mp': _Wvvvva = np.array(imds.Wvvvv[a]) for b in range(nvir): for j in range(nocc): if eom.partition == 'mp': Hr2[j,a,b] += fvv[a,a] Hr2[j,a,b] += fvv[b,b] Hr2[j,a,b] += -foo[j,j] else: Hr2[j,a,b] += imds.Lvv[a,a] Hr2[j,a,b] += imds.Lvv[b,b] Hr2[j,a,b] += -imds.Loo[j,j] Hr2[j,a,b] += 2*imds.Wovvo[j,b,b,j] Hr2[j,a,b] += -imds.Wovov[j,b,j,b] Hr2[j,a,b] += -imds.Wovov[j,a,j,a] Hr2[j,a,b] += -imds.Wovvo[j,b,b,j]*(a==b) Hr2[j,a,b] += _Wvvvva[b,a,b] Hr2[j,a,b] += -2*np.dot(imds.Woovv[:,j,a,b], t2[:,j,a,b]) Hr2[j,a,b] += np.dot(imds.Woovv[:,j,b,a], t2[:,j,a,b]) vector = eom.amplitudes_to_vector(Hr1,Hr2) return vector
[docs] def eaccsd_star_contract(eom, eaccsd_evals, eaccsd_evecs, leaccsd_evecs, imds=None): cpu1 = (logger.process_clock(), logger.perf_counter()) log = logger.Logger(eom.stdout, eom.verbose) if imds is None: imds = eom.make_imds() t1, t2 = imds.t1, imds.t2 eris = imds.eris nocc, nvir = t1.shape dtype = np.result_type(t1, t2, eris.ovoo.dtype) # Notice we do not use `sort_eri` as compared to the eaccsd_star. # The sort_eri does not produce eri's that are read-in quickly for the current contraction # scheme. Here, we have that the block loop is over occupied indices whereas in the # sort_eri it is done over virtual indices (due to the permutation over occupied indices # in ipccsd_star versus virtual indices in eaccsd_star). cpu1 = log.timer_debug1('CCSD(T) sort_eri', *cpu1) # Left if new sort_eri implemented mem_now = lib.current_memory()[0] max_memory = max(0, eom.max_memory - mem_now) blksize = min(nocc, max(ccsd.BLKMIN, int(max_memory*1e6/8/(nvir**3*6)))) mo_energy = np.asarray(eris.mo_energy) mo_e_occ = np.asarray(mo_energy[:nocc]) mo_e_vir = np.asarray(mo_energy[nocc:]) def contract_l2p(l1, l2, i0, i1, j0, j1, cache_ovvv_i, cache_ovvv_j, out=None): '''Create perturbed l2.''' if out is None: out = np.zeros((i1-i0,j1-j0) + (nvir,)*3, dtype=dtype) out += -0.5*np.einsum('iajb,c->ijabc', eris.ovov[i0:i1,:,j0:j1], l1) out += lib.einsum('iajm,mbc->ijabc', eris.ovoo[i0:i1,:,j0:j1], l2) out -= lib.einsum('iaeb,jec->ijabc', cache_ovvv_i, l2[j0:j1]) out -= lib.einsum('jbec,iae->ijabc', cache_ovvv_j, l2[i0:i1]) return out def contract_pl2p(l1, l2, i0, i1, j0, j1, cache_ovvv_i, cache_ovvv_j): '''Create P(ia|jb) of perturbed l2.''' out = contract_l2p(l1, l2, i0, i1, j0, j1, cache_ovvv_i, cache_ovvv_j) tmp = contract_l2p(l1, l2, j0, j1, i0, i1, cache_ovvv_j, cache_ovvv_i) tmp = tmp.transpose(1,0,3,2,4) # P(ia|jb) out = out + tmp return out def _get_vvvv(eris): if eris.vvvv is None and getattr(eris, 'vvL', None) is not None: # DF eris vvL = np.asarray(eris.vvL) nvir = int(np.sqrt(eris.vvL.shape[0]*2)) return ao2mo.restore(1, lib.dot(vvL, vvL.T), nvir) elif eris.vvvv.ndim == 2: nvir = int(np.sqrt(eris.vvvv.shape[0]*2)) return ao2mo.restore(1, np.asarray(eris.vvvv), nvir) else: return eris.vvvv def contract_r2p(r1, r2, i0, i1, j0, j1, cache_ovvv_i, cache_ovvv_j, out=None): '''Create perturbed r2.''' if out is None: out = np.zeros((i1-i0,j1-j0) + (nvir,)*3, dtype=dtype) tmp = np.einsum('becf,f->bce', _get_vvvv(eris), r1) out += -lib.einsum('bce,ijae->ijabc', tmp, t2[i0:i1,j0:j1]) tmp = np.einsum('mjce,e->mcj', eris.oovv[:,j0:j1], r1) out += lib.einsum('mcj,imab->ijabc', tmp, t2[i0:i1]) tmp = np.einsum('jbem,e->mbj', eris.ovvo[j0:j1], r1) out += lib.einsum('mbj,imac->ijabc', tmp, t2[i0:i1]) out += lib.einsum('iajm,mbc->ijabc', eris.ovoo[i0:i1,:,j0:j1].conj(), r2) out += -lib.einsum('iaeb,jec->ijabc', cache_ovvv_i.conj(), r2[j0:j1]) out += -lib.einsum('jbec,iae->ijabc', cache_ovvv_j.conj(), r2[i0:i1]) return out def contract_pr2p(r1, r2, i0, i1, j0, j1, cache_ovvv_i, cache_ovvv_j): '''Create P(ia|jb) of perturbed r2.''' out = contract_r2p(r1, r2, i0, i1, j0, j1, cache_ovvv_i, cache_ovvv_j) tmp = contract_r2p(r1, r2, j0, j1, i0, i1, cache_ovvv_j, cache_ovvv_i) tmp = tmp.transpose(1,0,3,2,4) # P(ia|jb) out = out + tmp return out eaccsd_evecs = np.array(eaccsd_evecs) leaccsd_evecs = np.array(leaccsd_evecs) e = [] eaccsd_evecs, leaccsd_evecs = [np.atleast_2d(x) for x in [eaccsd_evecs, leaccsd_evecs]] eaccsd_evals = np.atleast_1d(eaccsd_evals) for eval_, evec_, levec_ in zip(eaccsd_evals, eaccsd_evecs, leaccsd_evecs): l1, l2 = eom.vector_to_amplitudes(levec_) r1, r2 = eom.vector_to_amplitudes(evec_) ldotr = np.dot(l1, r1) + np.dot(l2.ravel(),r2.ravel()) l1 /= ldotr l2 /= ldotr l2 = 1./3*(1.*l2 + 2.*l2.transpose(0,2,1)) r2 = r2.transpose(0,2,1) deltaE = 0.0 eabc = (mo_e_vir[None,None,:,None,None] + mo_e_vir[None,None,None,:,None] + mo_e_vir[None,None,None,None,:] - eval_) for i0, i1 in lib.prange_tril(0, nocc, blksize): j0, j1 = 0, i1 eijabc = (mo_e_occ[i0:i1][:,None,None,None,None] + mo_e_occ[j0:j1][None,:,None,None,None] - eabc) eijabc = 1./eijabc cache_ovvv_i = eris.get_ovvv(slice(i0,i1)) cache_ovvv_j = eris.get_ovvv(slice(j0,j1)) lijabc = contract_pl2p(l1, l2, i0, i1, j0, j1, cache_ovvv_i, cache_ovvv_j) rijabc = contract_pr2p(r1, r2, i0, i1, j0, j1, cache_ovvv_i, cache_ovvv_j) lijabc = 4.*lijabc \ - 2.*lijabc.transpose(0,1,3,2,4) \ - 2.*lijabc.transpose(0,1,4,3,2) \ - 2.*lijabc.transpose(0,1,2,4,3) \ + 1.*lijabc.transpose(0,1,3,4,2) \ + 1.*lijabc.transpose(0,1,4,2,3) # Symmetry factors (1 for a == b, 2 for a < b) fac = 2*np.ones_like(rijabc, dtype=int) triu_idx = np.triu_indices(i1-i0,i0+1,m=j1-j0) fac[triu_idx[0],triu_idx[1],:,:,:] = 0 fac[np.arange(i1-i0),np.arange(i0,j1)] = 1 eijabc *= fac deltaE += np.einsum('ijabc,ijabc,ijabc',lijabc,rijabc,eijabc) deltaE = 0.5*deltaE.real logger.info(eom, "eaccsd energy, star energy, delta energy = %16.12f, %16.12f, %16.12f", eval_, eval_+deltaE, deltaE) e.append(eval_+deltaE) return e
[docs] class EOMEA(EOM):
[docs] def get_init_guess(self, nroots=1, koopmans=True, diag=None): size = self.vector_size() nroots = min(nroots, size) guess = [] if koopmans: dtype = getattr(diag, 'dtype', np.double) for n in range(nroots): g = np.zeros(size, dtype) g[n] = 1.0 guess.append(g) else: if diag is None: diag = self.get_diag() dtype = getattr(diag, 'dtype', np.double) idx = diag.argsort()[:nroots] for i in idx: g = np.zeros(size, dtype) g[i] = 1.0 guess.append(g) return guess
kernel = eaccsd eaccsd = eaccsd eaccsd_star = eaccsd_star matvec = eaccsd_matvec l_matvec = leaccsd_matvec get_diag = eaccsd_diag ccsd_star_contract = eaccsd_star_contract
[docs] def eaccsd_star_contract(self, eaccsd_evals, eaccsd_evecs, leaccsd_evecs, imds=None): return self.ccsd_star_contract(eaccsd_evals, eaccsd_evecs, leaccsd_evecs, imds=imds)
[docs] def gen_matvec(self, imds=None, left=False, **kwargs): if imds is None: imds = self.make_imds() diag = self.get_diag(imds) if left: matvec = lambda xs: [self.l_matvec(x, imds, diag) for x in xs] else: matvec = lambda xs: [self.matvec(x, imds, diag) for x in xs] return matvec, diag
amplitudes_to_vector = staticmethod(amplitudes_to_vector_ea) vector_to_amplitudes = module_method(vector_to_amplitudes_ea, absences=['nmo', 'nocc']) spatial2spin = staticmethod(spatial2spin_ea)
[docs] def vector_size(self): nocc = self.nocc nvir = self.nmo - nocc return nvir + nocc*nvir*nvir
[docs] def make_imds(self, eris=None): imds = _IMDS(self._cc, eris=eris) imds.make_ea(self.partition) return imds
@property def eea(self): return self.e
[docs] class EOMEA_Ta(EOMEA): '''Class for EOM EACCSD(T)*(a) method by Matthews and Stanton.'''
[docs] def make_imds(self, eris=None): imds = _IMDS(self._cc, eris=eris) imds.make_t3p2_ea(self._cc, self.partition) return imds
######################################## # EOM-EE-CCSD ######################################## #TODO: double spin-flip EOM-EE
[docs] def eeccsd(eom, nroots=1, koopmans=False, guess=None, eris=None, imds=None): '''Calculate N-electron neutral excitations via EOM-EE-CCSD. Kwargs: nroots : int Number of roots (eigenvalues) requested koopmans : bool Calculate Koopmans'-like (1p1h) excitations only, targeting via overlap. guess : list of ndarray List of guess vectors to use for targeting via overlap. ''' if eris is None: eris = eom._cc.ao2mo() if imds is None: imds = eom.make_imds(eris) spinvec_size = eom.vector_size() nroots = min(nroots, spinvec_size) diag_eeS, diag_eeT, diag_sf = eom.get_diag(imds) guess_eeS = [] guess_eeT = [] guess_sf = [] if guess: for g in guess: if g is None: # beta->alpha spin-flip excitation pass elif g.size == diag_eeS.size: guess_eeS.append(g) elif g.size == diag_eeT.size: guess_eeT.append(g) else: guess_sf.append(g) nroots_eeS = len(guess_eeS) nroots_eeT = len(guess_eeT) nroots_sf = len(guess_sf) if len(guess) != nroots: logger.warn(eom, 'Number of states in initial guess %d does not ' 'equal to nroots %d.', len(guess), nroots) else: deeS = np.sort(diag_eeS)[:nroots] deeT = np.sort(diag_eeT)[:nroots] dsf = np.sort(diag_sf)[:nroots] dmax = np.sort(np.hstack([deeS,deeT,dsf,dsf]))[nroots-1] nroots_eeS = np.count_nonzero(deeS <= dmax) nroots_eeT = np.count_nonzero(deeT <= dmax) nroots_sf = np.count_nonzero(dsf <= dmax) guess_eeS = guess_eeT = guess_sf = None def eomee_sub(cls, nroots, guess, diag): ee_sub = cls(eom._cc) ee_sub.__dict__.update(eom.__dict__) e, v = ee_sub.kernel(nroots, koopmans, guess, eris, imds, diag=diag) if nroots == 1: e, v = [e], [v] ee_sub.converged = [ee_sub.converged] return list(ee_sub.converged), list(e), list(v) e0 = e1 = e2 = [] v0 = v1 = v2 = [] conv0 = conv1 = conv2 = [] if nroots_eeS > 0: conv0, e0, v0 = eomee_sub(EOMEESinglet, nroots_eeS, guess_eeS, diag_eeS) if nroots_eeT > 0: conv2, e2, v2 = eomee_sub(EOMEETriplet, nroots_eeT, guess_eeT, diag_eeT) if nroots_sf > 0: conv1, e1, v1 = eomee_sub(EOMEESpinFlip, nroots_sf, guess_sf, diag_sf) # The associated solutions of beta->alpha excitations e1 = e1 + e1 conv1 = conv1 + conv1 v1 = v1 + [None] * len(v1) # beta->alpha spin-flip excitations, the coefficients are (-r1, (-r2[0], r2[1])) # as below. The EOMEESpinFlip class only handles alpha->beta excitations. # Setting beta->alpha to None to bypass the vectors in initial guess #for i in range(nroots_sf): # r1, r2 = vector_to_amplitudes_eomsf(v1[i], eom.nmo, eom.nocc) # v1.append(amplitudes_to_vector_eomsf(-r1, (-r2[0], r2[1]))) e = np.hstack([e0,e2,e1]) idx = e.argsort() e = e[idx] conv = conv0 + conv2 + conv1 conv = [conv[x] for x in idx] v = v0 + v2 + v1 v = [v[x] for x in idx] if nroots == 1: conv = conv[0] e = e[0] v = v[0] eom.converged = conv eom.e = e eom.v = v return eom.e, eom.v
[docs] def eomee_ccsd_singlet(eom, nroots=1, koopmans=False, guess=None, eris=None, imds=None, diag=None): '''EOM-EE-CCSD singlet ''' eom.converged, eom.e, eom.v \ = kernel(eom, nroots, koopmans, guess, eris=eris, imds=imds, diag=diag) return eom.e, eom.v
[docs] def eomee_ccsd_triplet(eom, nroots=1, koopmans=False, guess=None, eris=None, imds=None, diag=None): '''EOM-EE-CCSD triplet ''' return eomee_ccsd_singlet(eom, nroots, koopmans, guess, eris, imds, diag)
[docs] def eomsf_ccsd(eom, nroots=1, koopmans=False, guess=None, eris=None, imds=None, diag=None): '''Spin flip EOM-EE-CCSD ''' return eomee_ccsd_singlet(eom, nroots, koopmans, guess, eris, imds, diag)
vector_to_amplitudes_ee = vector_to_amplitudes_singlet = ccsd.vector_to_amplitudes amplitudes_to_vector_ee = amplitudes_to_vector_singlet = ccsd.amplitudes_to_vector
[docs] def spatial2spin_singlet(rx, orbspin=None): from pyscf.cc import eom_uccsd if rx.ndim == 2: r1a = rx * .5**.5 r1 = (r1a, r1a) return eom_uccsd.spatial2spin_eomee(r1, orbspin) else: r2ab = rx * .5**.5 r2aa = r2ab - r2ab.transpose(1,0,2,3) r2 = (r2aa, r2ab, r2aa) return eom_uccsd.spatial2spin_eomee(r2, orbspin)
[docs] def amplitudes_to_vector_eomsf(t1, t2, out=None): nocc, nvir = t1.shape t2baaa, t2aaba = t2 otril = np.tril_indices(nocc, k=-1) vtril = np.tril_indices(nvir, k=-1) baaa = np.take(t2baaa.reshape(nocc*nocc,nvir*nvir), vtril[0]*nvir+vtril[1], axis=1) vector = np.hstack((t1.ravel(), baaa.ravel(), t2aaba[otril].ravel())) return vector
[docs] def vector_to_amplitudes_eomsf(vector, nmo, nocc): nvir = nmo - nocc t1 = vector[:nocc*nvir].reshape(nocc,nvir).copy() pvec = vector[t1.size:] nbaaa = nocc*nocc*nvir*(nvir-1)//2 naaba = nocc*(nocc-1)//2*nvir*nvir t2baaa = np.zeros((nocc*nocc,nvir*nvir), dtype=vector.dtype) t2aaba = np.zeros((nocc*nocc,nvir*nvir), dtype=vector.dtype) otril = np.tril_indices(nocc, k=-1) vtril = np.tril_indices(nvir, k=-1) v = pvec[:nbaaa].reshape(nocc*nocc,nvir*(nvir-1)//2) t2baaa[:,vtril[0]*nvir+vtril[1]] = v t2baaa[:,vtril[1]*nvir+vtril[0]] = -v v = pvec[nbaaa:nbaaa+naaba].reshape(-1,nvir*nvir) t2aaba[otril[0]*nocc+otril[1]] = v t2aaba[otril[1]*nocc+otril[0]] = -v t2baaa = t2baaa.reshape(nocc,nocc,nvir,nvir) t2aaba = t2aaba.reshape(nocc,nocc,nvir,nvir) return t1, (t2baaa, t2aaba)
[docs] def spatial2spin_eomsf(rx, orbspin=None): from pyscf.cc import eom_uccsd if isinstance(rx, np.ndarray) and rx.ndim == 2: r1 = (rx, np.zeros_like(rx)) return eom_uccsd.spatial2spin_eomsf(r1, orbspin) else: rx0 = np.zeros_like(rx[0]) r2 = (rx0, rx[1], rx[0], rx0) return eom_uccsd.spatial2spin_eomsf(r2, orbspin)
[docs] def amplitudes_to_vector_triplet(t1, t2, out=None): t2aa, t2ab = t2 dtype = np.result_type(t1, t2aa, t2ab) nocc, nvir = t1.shape nov = nocc * nvir size1 = nov + nocc*(nocc-1)//2*nvir*(nvir-1)//2 size = size1 + nov*(nov+1)//2 vector = np.ndarray(size, dtype, buffer=out) ccsd.amplitudes_to_vector_s4(t1, t2[0], out=vector) t2ab = t2[1].transpose(0,2,1,3).reshape(nov,nov) lib.pack_tril(t2ab, out=vector[size1:]) return vector
[docs] def vector_to_amplitudes_triplet(vector, nmo, nocc): nvir = nmo - nocc nov = nocc * nvir size1 = nov + nocc*(nocc-1)//2*nvir*(nvir-1)//2 size = size1 + nov*(nov+1)//2 t1, t2aa = ccsd.vector_to_amplitudes_s4(vector[:size1], nmo, nocc) t2ab = lib.unpack_tril(vector[size1:size], filltriu=2) t2ab = t2ab.reshape(nocc,nvir,nocc,nvir).transpose(0,2,1,3).copy() return t1, (t2aa, t2ab)
[docs] def spatial2spin_triplet(rx, orbspin=None): from pyscf.cc import eom_uccsd if isinstance(rx, np.ndarray) and rx.ndim == 2: r1a = rx * .5**.5 r1 = (r1a, r1a) return eom_uccsd.spatial2spin_eomee(r1, orbspin) else: r2ab = rx * .5**.5 r2aa, r2ab = rx r2aa = r2ab - r2ab.transpose(1,0,2,3) r2 = (r2aa, r2ab, -r2aa) return eom_uccsd.spatial2spin_eomee(r2, orbspin)
[docs] def eeccsd_matvec_singlet(eom, vector, imds=None): if imds is None: imds = eom.make_imds() nocc = eom.nocc nmo = eom.nmo nvir = nmo - nocc r1, r2 = eom.vector_to_amplitudes(vector, nmo, nocc) t1, t2, eris = imds.t1, imds.t2, imds.eris nocc, nvir = t1.shape Hr1 = lib.einsum('ae,ie->ia', imds.Fvv, r1) Hr1 -= lib.einsum('mi,ma->ia', imds.Foo, r1) Hr1 += np.einsum('me,imae->ia',imds.Fov, r2) * 2 Hr1 -= np.einsum('me,imea->ia',imds.Fov, r2) #:eris_vvvv = ao2mo.restore(1,np.asarray(eris.vvvv), t1.shape[1]) #:Hr2 += lib.einsum('ijef,aebf->ijab', tau2, eris_vvvv) * .5 tau2 = _make_tau(r2, r1, t1, fac=2) Hr2 = eom._cc._add_vvvv(None, tau2, eris, with_ovvv=False, t2sym='jiba') woOoO = np.asarray(imds.woOoO) Hr2 += lib.einsum('mnij,mnab->ijab', woOoO, r2) Hr2 *= .5 woOoO = None Hr2 += lib.einsum('be,ijae->ijab', imds.Fvv , r2) Hr2 -= lib.einsum('mj,imab->ijab', imds.Foo , r2) mem_now = lib.current_memory()[0] max_memory = max(0, eom.max_memory - mem_now - Hr2.size*8e-6) blksize = min(nocc, max(ccsd.BLKMIN, int(max_memory*1e6/8/(nvir**3*3)))) for p0,p1 in lib.prange(0, nocc, blksize): ovvv = eris.get_ovvv(slice(p0,p1)) # ovvv = eris.ovvv[p0:p1] theta = r2[p0:p1] * 2 - r2[p0:p1].transpose(0,1,3,2) Hr1 += lib.einsum('mfae,mife->ia', ovvv, theta) theta = None tmp = lib.einsum('meaf,ijef->maij', ovvv, tau2) Hr2 -= lib.einsum('ma,mbij->ijab', t1[p0:p1], tmp) tmp = lib.einsum('meaf,me->af', ovvv, r1[p0:p1]) * 2 tmp -= lib.einsum('mfae,me->af', ovvv, r1[p0:p1]) Hr2 += lib.einsum('af,ijfb->ijab', tmp, t2) ovvv = tmp = None tau2 = None Hr2 -= lib.einsum('mbij,ma->ijab', imds.woVoO, r1) blksize = min(nvir, max(ccsd.BLKMIN, int(max_memory*1e6/8/(nocc*nvir**2*2)))) for p0, p1 in lib.prange(0, nvir, nocc): Hr2 += lib.einsum('ejab,ie->ijab', np.asarray(imds.wvOvV[p0:p1]), r1[:,p0:p1]) woVVo = np.asarray(imds.woVVo) tmp = lib.einsum('mbej,imea->jiab', woVVo, r2) Hr2 += tmp tmp *= .5 Hr2 += tmp.transpose(0,1,3,2) tmp = None woVvO = woVVo * .5 woVVo = None woVvO += np.asarray(imds.woVvO) theta = r2*2 - r2.transpose(0,1,3,2) Hr1 += np.einsum('maei,me->ia', woVvO, r1) * 2 Hr2 += lib.einsum('mbej,imae->ijab', woVvO, theta) woVvO = None woOoV = np.asarray(imds.woOoV) Hr1-= lib.einsum('mnie,mnae->ia', woOoV, theta) tmp = lib.einsum('nmie,me->ni', woOoV, r1) * 2 tmp-= lib.einsum('mnie,me->ni', woOoV, r1) Hr2 -= lib.einsum('ni,njab->ijab', tmp, t2) tmp = woOoV = None eris_ovov = np.asarray(eris.ovov) tmp = np.einsum('mfne,mf->en', eris_ovov, r1) * 2 tmp -= np.einsum('menf,mf->en', eris_ovov, r1) tmp = np.einsum('en,nb->eb', tmp, t1) tmp += lib.einsum('menf,mnbf->eb', eris_ovov, theta) Hr2 -= lib.einsum('eb,ijea->jiab', tmp, t2) tmp = None tmp = lib.einsum('nemf,imef->ni', eris_ovov, theta) Hr1 -= lib.einsum('na,ni->ia', t1, tmp) Hr2 -= lib.einsum('mj,miab->ijba', tmp, t2) tmp = theta = None tau2 = _make_tau(r2, r1, t1, fac=2) tmp = lib.einsum('menf,ijef->mnij', eris_ovov, tau2) tau2 = None tau = _make_tau(t2, t1, t1) tau *= .5 Hr2 += lib.einsum('mnij,mnab->ijab', tmp, tau) tau = tmp = eris_ovov = None Hr2 = Hr2 + Hr2.transpose(1,0,3,2) vector = eom.amplitudes_to_vector(Hr1, Hr2) return vector
[docs] def eeccsd_matvec_triplet(eom, vector, imds=None): if imds is None: imds = eom.make_imds() nocc = eom.nocc nmo = eom.nmo nvir = nmo - nocc r1, r2 = eom.vector_to_amplitudes(vector, nmo, nocc) r2aa, r2ab = r2 t1, t2, eris = imds.t1, imds.t2, imds.eris nocc, nvir = t1.shape Hr1 = lib.einsum('ae,ie->ia', imds.Fvv, r1) Hr1 -= lib.einsum('mi,ma->ia', imds.Foo, r1) Hr1 += np.einsum('me,imae->ia',imds.Fov, r2aa) Hr1 += np.einsum('ME,iMaE->ia',imds.Fov, r2ab) tau2ab = np.einsum('ia,jb->ijab', r1, t1) tau2ab-= np.einsum('ia,jb->ijab', t1, r1) tau2ab+= r2ab tau2aa = np.einsum('ia,jb->ijab', r1, t1) tau2aa-= np.einsum('ia,jb->jiab', r1, t1) tau2aa = tau2aa - tau2aa.transpose(0,1,3,2) tau2aa+= r2aa #:eris_vvvv = ao2mo.restore(1,np.asarray(eris.vvvv), t1.shape[1]) #:Hr2aa += lib.einsum('ijef,aebf->ijab', tau2aa, eris_vvvv) * .25 #:Hr2ab += lib.einsum('ijef,aebf->ijab', tau2ab, eris_vvvv) * .5 Hr2aa = eom._cc._add_vvvv(None, tau2aa, eris, with_ovvv=False, t2sym='jiba') Hr2ab = eom._cc._add_vvvv(None, tau2ab, eris, with_ovvv=False, t2sym='-jiba') woOoO = np.asarray(imds.woOoO) Hr2aa += lib.einsum('mnij,mnab->ijab', woOoO, r2aa) Hr2ab += lib.einsum('mNiJ,mNaB->iJaB', woOoO, r2ab) Hr2aa *= .25 Hr2ab *= .5 woOoO = None Hr2aa += lib.einsum('be,ijae->ijab', imds.Fvv*.5, r2aa) Hr2aa -= lib.einsum('mj,imab->ijab', imds.Foo*.5, r2aa) Hr2ab += lib.einsum('BE,iJaE->iJaB', imds.Fvv, r2ab) Hr2ab -= lib.einsum('MJ,iMaB->iJaB', imds.Foo, r2ab) mem_now = lib.current_memory()[0] max_memory = max(0, eom.max_memory - mem_now - Hr2aa.size*8e-6) blksize = min(nocc, max(ccsd.BLKMIN, int(max_memory*1e6/8/(nvir**3*3)))) tmp1 = np.zeros((nvir,nvir), dtype=r1.dtype) for p0,p1 in lib.prange(0, nocc, blksize): ovvv = eris.get_ovvv(slice(p0,p1)) # ovvv = eris.ovvv[p0:p1] theta = r2aa[:,p0:p1] + r2ab[:,p0:p1] Hr1 += lib.einsum('mfae,imef->ia', ovvv, theta) theta = None tmpaa = lib.einsum('meaf,ijef->maij', ovvv, tau2aa) tmpab = lib.einsum('meAF,iJeF->mAiJ', ovvv, tau2ab) tmp1 += lib.einsum('mfae,me->af', ovvv, r1[p0:p1]) Hr2aa+= lib.einsum('mb,maij->ijab', t1[p0:p1]*.5, tmpaa) Hr2ab-= lib.einsum('mb,mAiJ->iJbA', t1[p0:p1], tmpab) ovvv = tmpaa = tmpab = None tau2aa = tau2ab = None woVVo = np.asarray(imds.woVVo) Hr1 += np.einsum('maei,me->ia', woVVo, r1) Hr2aa += lib.einsum('mbej,imae->ijba', woVVo, r2ab) Hr2ab += lib.einsum('MBEJ,iMEa->iJaB', woVVo, r2aa) Hr2ab += lib.einsum('MbeJ,iMeA->iJbA', woVVo, r2ab) woVvO = np.asarray(imds.woVvO) wovvo = woVVo + woVvO theta = r2aa + r2ab tmp = lib.einsum('mbej,imae->ijab', wovvo, theta) woVVo = woVvO = wovvo = None woOoV = np.asarray(imds.woOoV) Hr1 -= lib.einsum('mnie,mnae->ia', woOoV, theta) tmpa = lib.einsum('mnie,me->ni', woOoV, r1) tmp += lib.einsum('ni,njab->ijab', tmpa, t2) tmp -= lib.einsum('af,ijfb->ijab', tmp1, t2) tmp -= lib.einsum('mbij,ma->ijab', imds.woVoO, r1) blksize = min(nvir, max(ccsd.BLKMIN, int(max_memory*1e6/8/(nocc*nvir**2*2)))) for p0,p1 in lib.prange(0, nvir, blksize): tmp += lib.einsum('ejab,ie->ijab', np.asarray(imds.wvOvV[p0:p1]), r1[:,p0:p1]) Hr2aa += tmp Hr2ab += tmp tmp = woOoV = None eris_ovov = np.asarray(eris.ovov) tmpa = -lib.einsum('menf,imfe->ni', eris_ovov, theta) Hr1 += lib.einsum('na,ni->ia', t1, tmpa) tmp = lib.einsum('mj,imab->ijab', tmpa, t2) tmp1 = np.einsum('menf,mf->en', eris_ovov, r1) tmpa = lib.einsum('en,nb->eb', tmp1, t1) tmpa-= lib.einsum('menf,mnbf->eb', eris_ovov, theta) tmp += lib.einsum('eb,ijae->ijab', tmpa, t2) Hr2aa += tmp Hr2ab -= tmp tmp = theta = tmp1 = tmpa = None tau2aa = np.einsum('ia,jb->ijab', r1, t1) tau2aa-= np.einsum('ia,jb->jiab', r1, t1) tau2aa = tau2aa - tau2aa.transpose(0,1,3,2) tau2aa+= r2aa tmpaa = lib.einsum('menf,ijef->mnij', eris_ovov, tau2aa) tau2aa = None tmpaa *= .25 tau = _make_tau(t2, t1, t1) Hr2aa += lib.einsum('mnij,mnab->ijab', tmpaa, tau) tmpaa = tau = None tau2ab = np.einsum('ia,jb->ijab', r1, t1) tau2ab-= np.einsum('ia,jb->ijab', t1, r1) tau2ab+= r2ab tmpab = lib.einsum('meNF,iJeF->mNiJ', eris_ovov, tau2ab) tau2ab = None tmpab *= .5 tau = _make_tau(t2, t1, t1) Hr2ab += lib.einsum('mNiJ,mNaB->iJaB', tmpab, tau) tmpab = tau = None eris_ovov = None Hr2aa = Hr2aa - Hr2aa.transpose(0,1,3,2) Hr2aa = Hr2aa - Hr2aa.transpose(1,0,2,3) Hr2ab = Hr2ab - Hr2ab.transpose(1,0,3,2) vector = eom.amplitudes_to_vector(Hr1, (Hr2aa,Hr2ab)) return vector
[docs] def eeccsd_matvec_sf(eom, vector, imds=None): '''Spin flip EOM-CCSD''' if imds is None: imds = eom.make_imds() nocc = eom.nocc nmo = eom.nmo nvir = nmo - nocc t1, t2, eris = imds.t1, imds.t2, imds.eris r1, r2 = eom.vector_to_amplitudes(vector, nmo, nocc) r2baaa, r2aaba = r2 nocc, nvir = t1.shape Hr1 = np.einsum('ae,ie->ia', imds.Fvv, r1) Hr1 -= np.einsum('mi,ma->ia', imds.Foo, r1) Hr1 += np.einsum('me,imae->ia', imds.Fov, r2baaa) Hr1 += np.einsum('me,imae->ia', imds.Fov, r2aaba) tau2baaa = np.einsum('ia,jb->ijab', r1, t1) tau2baaa += r2baaa * .5 tau2baaa = tau2baaa - tau2baaa.transpose(0,1,3,2) tau2aaba = np.einsum('ia,jb->ijab', r1, t1) tau2aaba += r2aaba * .5 tau2aaba = tau2aaba - tau2aaba.transpose(1,0,2,3) #:eris_vvvv = ao2mo.restore(1,np.asarray(eris.vvvv), t1.shape[1]) #:Hr2baaa += .5*lib.einsum('ijef,aebf->ijab', tau2baaa, eris_vvvv) #:Hr2aaba += .5*lib.einsum('ijef,aebf->ijab', tau2aaba, eris_vvvv) Hr2aaba = eom._cc._add_vvvv(None, tau2aaba, eris, with_ovvv=False, t2sym='-jiab') Hr2baaa = eom._cc._add_vvvv(None, tau2baaa, eris, with_ovvv=False, t2sym=False) woOoO = np.asarray(imds.woOoO) Hr2baaa += lib.einsum('mnij,mnab->ijab', woOoO, r2baaa) Hr2aaba += lib.einsum('mnij,mnab->ijab', woOoO, r2aaba) Hr2aaba *= .5 Hr2baaa *= .5 woOoO = None Hr2baaa -= lib.einsum('mj,imab->ijab', imds.Foo*.5, r2baaa) Hr2aaba -= lib.einsum('mj,imab->ijab', imds.Foo*.5, r2aaba) Hr2baaa -= lib.einsum('mj,miab->jiab', imds.Foo*.5, r2baaa) Hr2aaba -= lib.einsum('mj,miab->jiab', imds.Foo*.5, r2aaba) Hr2baaa += lib.einsum('be,ijae->ijab', imds.Fvv*.5, r2baaa) Hr2aaba += lib.einsum('be,ijae->ijab', imds.Fvv*.5, r2aaba) Hr2baaa += lib.einsum('be,ijea->ijba', imds.Fvv*.5, r2baaa) Hr2aaba += lib.einsum('be,ijea->ijba', imds.Fvv*.5, r2aaba) mem_now = lib.current_memory()[0] max_memory = max(0, eom.max_memory - mem_now - Hr2aaba.size*8e-6) blksize = min(nocc, max(ccsd.BLKMIN, int(max_memory*1e6/8/(nvir**3*3)))) for p0,p1 in lib.prange(0, nocc, blksize): ovvv = eris.get_ovvv(slice(p0,p1)) # ovvv = eris.ovvv[p0:p1] theta = r2baaa[:,p0:p1] + r2aaba[:,p0:p1] Hr1 += lib.einsum('mfae,imef->ia', ovvv, theta) theta = None tmp1aaba = lib.einsum('meaf,ijef->maij', ovvv, tau2baaa) Hr2baaa -= lib.einsum('mb,maij->ijba', t1[p0:p1]*.5, tmp1aaba) tmp1aaba = tmp1baaa = tmp1abaa = tmp2aaba = None tmp2aaba = lib.einsum('meaf,ijfe->maij', ovvv, tau2baaa) Hr2baaa -= lib.einsum('mb,maij->ijab', t1[p0:p1]*.5, tmp2aaba) tmp1aaba = tmp1baaa = tmp1abaa = tmp2aaba = None tmp1baaa = lib.einsum('meaf,ijef->maij', ovvv, tau2aaba) Hr2aaba -= lib.einsum('mb,maij->ijba', t1[p0:p1]*.5, tmp1baaa) tmp1aaba = tmp1baaa = tmp1abaa = tmp2aaba = None tmp1abaa = lib.einsum('meaf,ijfe->maij', ovvv, tau2aaba) Hr2aaba -= lib.einsum('mb,maij->ijab', t1[p0:p1]*.5, tmp1abaa) tmp1aaba = tmp1baaa = tmp1abaa = tmp2aaba = None tmp = lib.einsum('mfae,me->af', ovvv, r1[p0:p1]) tmp = lib.einsum('af,jibf->ijab', tmp, t2) Hr2baaa -= tmp Hr2aaba -= tmp tmp = ovvv = None tau2aaba = tau2baaa = None tmp = lib.einsum('mbij,ma->ijab', imds.woVoO, r1) Hr2baaa -= tmp Hr2aaba -= tmp tmp = None woOoV = np.asarray(imds.woOoV) Hr1 -= lib.einsum('mnie,mnae->ia', woOoV, r2aaba) Hr1 -= lib.einsum('mnie,mnae->ia', woOoV, r2baaa) tmp = lib.einsum('mnie,me->ni', woOoV, r1) tmp = lib.einsum('ni,njab->ijab', tmp, t2) Hr2baaa += tmp Hr2aaba += tmp tmp = woOoV = None for p0,p1 in lib.prange(0, nvir, nocc): tmp = lib.einsum('ejab,ie->ijab', np.asarray(imds.wvOvV[p0:p1]), r1[:,p0:p1]) Hr2baaa += tmp Hr2aaba += tmp tmp = None woVVo = np.asarray(imds.woVVo) Hr1 += np.einsum('maei,me->ia', woVVo, r1) Hr2baaa += lib.einsum('mbej,miea->jiba', woVVo, r2baaa) Hr2aaba += lib.einsum('mbej,miea->jiba', woVVo, r2aaba) woVVo = None woVvO = np.asarray(imds.woVvO) Hr2baaa += lib.einsum('mbej,imae->ijab', woVvO, r2aaba) Hr2aaba += lib.einsum('mbej,imae->ijab', woVvO, r2baaa) woVvO = woVvO + np.asarray(imds.woVVo) Hr2baaa += lib.einsum('mbej,imae->ijab', woVvO, r2baaa) Hr2aaba += lib.einsum('mbej,imae->ijab', woVvO, r2aaba) woVvO = None eris_ovov = np.asarray(eris.ovov) theta = r2aaba + r2baaa tmp = lib.einsum('nfme,imfe->ni', eris_ovov, theta) Hr1 -= np.einsum('na,ni->ia', t1, tmp) Hr2baaa -= lib.einsum('mj,imba->jiab', tmp, t2) Hr2aaba -= lib.einsum('mj,imba->jiab', tmp, t2) tmp = np.einsum('menf,mf->en', eris_ovov, r1) tmp = np.einsum('en,nb->eb', tmp, t1) tmp-= lib.einsum('menf,mnbf->eb', eris_ovov, theta) Hr2baaa += lib.einsum('ea,ijbe->jiab', tmp, t2) Hr2aaba += lib.einsum('ea,ijbe->jiab', tmp, t2) theta = tmp = None tau2baaa = np.einsum('ia,jb->ijab', r1, t1) tau2baaa += r2baaa * .5 tau2baaa = tau2baaa - tau2baaa.transpose(0,1,3,2) tau = _make_tau(t2, t1, t1) tmp1aaba = lib.einsum('menf,ijef->mnij', eris_ovov, tau2baaa) tau2baaa = None Hr2baaa += .5*lib.einsum('mnij,mnab->ijab', tmp1aaba, tau) tau = tmp1aaba = None tau2aaba = np.einsum('ia,jb->ijab', r1, t1) tau2aaba += r2aaba * .5 tau2aaba = tau2aaba - tau2aaba.transpose(1,0,2,3) tau = _make_tau(t2, t1, t1) tmp1baaa = lib.einsum('menf,ijef->mnij', eris_ovov, tau2aaba) tau2aaba = None Hr2aaba += .5*lib.einsum('mnij,mnab->ijab', tmp1baaa, tau) tau = tmp1baaa = None eris_ovov = None Hr2baaa = Hr2baaa - Hr2baaa.transpose(0,1,3,2) Hr2aaba = Hr2aaba - Hr2aaba.transpose(1,0,2,3) vector = eom.amplitudes_to_vector(Hr1, (Hr2baaa,Hr2aaba)) return vector
[docs] def eeccsd_diag(eom, imds=None): if imds is None: imds = eom.make_imds() eris = imds.eris t1, t2 = imds.t1, imds.t2 dtype = np.result_type(t1, t2) tau = _make_tau(t2, t1, t1) nocc, nvir = t1.shape Fo = imds.Foo.diagonal() Fv = imds.Fvv.diagonal() Wovab = np.einsum('iaai->ia', imds.woVVo) Wovaa = Wovab + np.einsum('iaai->ia', imds.woVvO) eia = lib.direct_sum('-i+a->ia', Fo, Fv) Hr1aa = eia + Wovaa Hr1ab = eia + Wovab eris_ovov = np.asarray(eris.ovov) Wvvab = np.einsum('mnab,manb->ab', tau, eris_ovov) Wvvaa = .5*Wvvab - .5*np.einsum('mnba,manb->ab', tau, eris_ovov) ijb = np.einsum('iejb,ijeb->ijb', eris_ovov, t2) Hr2ab = lib.direct_sum('iJB+a->iJaB',-ijb, Fv) jab = np.einsum('kajb,kjab->jab', eris_ovov, t2) Hr2ab+= lib.direct_sum('-i-jab->ijab', Fo, jab) jib = np.einsum('iejb,ijbe->jib', eris_ovov, t2) jib = jib + jib.transpose(1,0,2) jib-= ijb + ijb.transpose(1,0,2) jba = np.einsum('kajb,jkab->jba', eris_ovov, t2) jba = jba + jba.transpose(0,2,1) jba-= jab + jab.transpose(0,2,1) Hr2aa = lib.direct_sum('jib+a->jiba', jib, Fv) Hr2aa+= lib.direct_sum('-i+jba->ijba', Fo, jba) eris_ovov = None Hr2baaa = lib.direct_sum('ijb+a->ijba',-ijb, Fv) Hr2baaa += Wovaa.reshape(1,nocc,1,nvir) Hr2baaa += Wovab.reshape(nocc,1,1,nvir) Hr2baaa = Hr2baaa + Hr2baaa.transpose(0,1,3,2) Hr2baaa+= lib.direct_sum('-i+jab->ijab', Fo, jba) Hr2baaa-= Fo.reshape(1,-1,1,1) Hr2aaba = lib.direct_sum('-i-jab->ijab', Fo, jab) Hr2aaba += Wovaa.reshape(1,nocc,1,nvir) Hr2aaba += Wovab.reshape(1,nocc,nvir,1) Hr2aaba = Hr2aaba + Hr2aaba.transpose(1,0,2,3) Hr2aaba+= lib.direct_sum('ijb+a->ijab', jib, Fv) Hr2aaba+= Fv.reshape(1,1,1,-1) Hr2ab += Wovaa.reshape(1,nocc,1,nvir) Hr2ab += Wovab.reshape(nocc,1,1,nvir) Hr2ab = Hr2ab + Hr2ab.transpose(1,0,3,2) Hr2aa += Wovaa.reshape(1,nocc,1,nvir) * 2 Hr2aa = Hr2aa + Hr2aa.transpose(0,1,3,2) Hr2aa = Hr2aa + Hr2aa.transpose(1,0,2,3) Hr2aa *= .5 Wooab = np.einsum('ijij->ij', imds.woOoO) Wooaa = Wooab - np.einsum('ijji->ij', imds.woOoO) Hr2aa += Wooaa.reshape(nocc,nocc,1,1) Hr2ab += Wooab.reshape(nocc,nocc,1,1) Hr2baaa += Wooab.reshape(nocc,nocc,1,1) Hr2aaba += Wooaa.reshape(nocc,nocc,1,1) #:eris_ovvv = lib.unpack_tril(np.asarray(eris.ovvv).reshape(nocc*nvir,nvir**2)).reshape(nocc,nvir,nvir,nvir) #:tmp = np.einsum('mb,mbaa->ab', t1, eris_ovvv) #:Wvvaa += np.einsum('mb,maab->ab', t1, eris_ovvv) mem_now = lib.current_memory()[0] max_memory = max(0, eom.max_memory - mem_now) blksize = min(nocc, max(ccsd.BLKMIN, int(max_memory*1e6/8/(nvir**3*3)))) tmp = np.zeros((nvir,nvir), dtype=dtype) for p0,p1 in lib.prange(0, nocc, blksize): ovvv = eris.get_ovvv(slice(p0,p1)) # ovvv = eris.ovvv[p0:p1] tmp += np.einsum('mb,mbaa->ab', t1[p0:p1], ovvv) Wvvaa += np.einsum('mb,maab->ab', t1[p0:p1], ovvv) ovvv = None Wvvaa -= tmp Wvvab -= tmp Wvvab -= tmp.T Wvvaa = Wvvaa + Wvvaa.T if eris.vvvv is None: # AO-direct CCSD, vvvv is not generated. pass elif eris.vvvv.ndim == 4: eris_vvvv = ao2mo.restore(1,np.asarray(eris.vvvv), t1.shape[1]) tmp = np.einsum('aabb->ab', eris_vvvv) Wvvaa += tmp Wvvaa -= np.einsum('abba->ab', eris_vvvv) Wvvab += tmp else: for i in range(nvir): i0 = i*(i+1)//2 vvv = lib.unpack_tril(np.asarray(eris.vvvv[i0:i0+i+1])) tmp = np.einsum('bb->b', vvv[i]) Wvvaa[i] += tmp Wvvab[i] += tmp tmp = np.einsum('bb->b', vvv[:,:i+1,i]) Wvvaa[i,:i+1] -= tmp Wvvaa[:i ,i] -= tmp[:i] vvv = None Hr2aa += Wvvaa.reshape(1,1,nvir,nvir) Hr2ab += Wvvab.reshape(1,1,nvir,nvir) Hr2baaa += Wvvaa.reshape(1,1,nvir,nvir) Hr2aaba += Wvvab.reshape(1,1,nvir,nvir) vec_eeS = amplitudes_to_vector_singlet(Hr1aa, Hr2ab) vec_eeT = amplitudes_to_vector_triplet(Hr1aa, (Hr2aa,Hr2ab)) vec_sf = amplitudes_to_vector_eomsf(Hr1ab, (Hr2baaa,Hr2aaba)) return vec_eeS, vec_eeT, vec_sf
[docs] class EOMEE(EOM):
[docs] def get_init_guess(self, nroots=1, koopmans=True, diag=None): if diag is None: diag = self.get_diag() if koopmans: nocc = self.nocc nvir = self.nmo - nocc idx = diag[:nocc*nvir].argsort() else: idx = diag.argsort() size = self.vector_size() dtype = getattr(diag, 'dtype', np.double) nroots = min(nroots, size) guess = [] for i in idx[:nroots]: g = np.zeros(size, dtype) g[i] = 1.0 guess.append(g) return guess
kernel = eeccsd eeccsd = eeccsd get_diag = eeccsd_diag
[docs] def vector_size(self): '''size of the vector based on spin-orbital basis''' nocc = self.nocc nvir = self.nmo - nocc return nocc*nvir + nocc*nocc*nvir*nvir
[docs] def make_imds(self, eris=None): imds = _IMDS(self._cc, eris=eris) imds.make_ee() return imds
@property def eee(self): return self.e
[docs] class EOMEESinglet(EOMEE): kernel = eomee_ccsd_singlet eomee_ccsd_singlet = eomee_ccsd_singlet matvec = eeccsd_matvec_singlet
[docs] def get_diag(self, imds=None): return eeccsd_diag(self, imds=None)[0]
[docs] def gen_matvec(self, imds=None, diag=None, **kwargs): if imds is None: imds = self.make_imds() if diag is None: diag = self.get_diag(imds) matvec = lambda xs: [self.matvec(x, imds) for x in xs] return matvec, diag
amplitudes_to_vector = staticmethod(amplitudes_to_vector_singlet) vector_to_amplitudes = module_method(vector_to_amplitudes_singlet, absences=['nmo', 'nocc']) spatial2spin = staticmethod(spatial2spin_singlet)
[docs] def vector_size(self): nocc = self.nocc nvir = self.nmo - nocc nov = nocc * nvir return nov + nov*(nov+1)//2
[docs] class EOMEETriplet(EOMEE): kernel = eomee_ccsd_triplet eomee_ccsd_triplet = eomee_ccsd_triplet matvec = eeccsd_matvec_triplet
[docs] def get_diag(self, imds=None): return eeccsd_diag(self, imds=None)[1]
[docs] def gen_matvec(self, imds=None, diag=None, **kwargs): if imds is None: imds = self.make_imds() if diag is None: diag = self.get_diag(imds) matvec = lambda xs: [self.matvec(x, imds) for x in xs] return matvec, diag
amplitudes_to_vector = staticmethod(amplitudes_to_vector_triplet) vector_to_amplitudes = module_method(vector_to_amplitudes_triplet, absences=['nmo', 'nocc']) spatial2spin = staticmethod(spatial2spin_triplet)
[docs] def vector_size(self): nocc = self.nocc nvir = self.nmo - nocc nov = nocc * nvir return nov + nocc*(nocc-1)//2*nvir*(nvir-1)//2 + nov*(nov+1)//2
[docs] class EOMEESpinFlip(EOMEE): kernel = eomsf_ccsd eomsf_ccsd = eomsf_ccsd matvec = eeccsd_matvec_sf
[docs] def get_diag(self, imds=None): return eeccsd_diag(self, imds=None)[2]
[docs] def gen_matvec(self, imds=None, diag=None, **kwargs): if imds is None: imds = self.make_imds() if diag is None: diag = self.get_diag(imds) matvec = lambda xs: [self.matvec(x, imds) for x in xs] return matvec, diag
amplitudes_to_vector = staticmethod(amplitudes_to_vector_eomsf) vector_to_amplitudes = module_method(vector_to_amplitudes_eomsf, absences=['nmo', 'nocc']) spatial2spin = staticmethod(spatial2spin_eomsf)
[docs] def vector_size(self): nocc = self.nocc nvir = self.nmo - nocc nbaaa = nocc*nocc*nvir*(nvir-1)//2 naaba = nocc*(nocc-1)//2*nvir*nvir return nocc*nvir + nbaaa + naaba
#TODO: Check whether EOM methods works with rccsd.RCCSD when orbitals are complex ccsd.CCSD.EOMIP = lib.class_as_method(EOMIP) ccsd.CCSD.EOMIP_Ta = lib.class_as_method(EOMIP_Ta) ccsd.CCSD.EOMEA = lib.class_as_method(EOMEA) ccsd.CCSD.EOMEA_Ta = lib.class_as_method(EOMEA_Ta) ccsd.CCSD.EOMEE = lib.class_as_method(EOMEE) ccsd.CCSD.EOMEESinglet = lib.class_as_method(EOMEESinglet) ccsd.CCSD.EOMEETriplet = lib.class_as_method(EOMEETriplet) ccsd.CCSD.EOMEESpinFlip = lib.class_as_method(EOMEESpinFlip) class _IMDS: def __init__(self, cc, eris=None): self.verbose = cc.verbose self.stdout = cc.stdout self.max_memory = cc.max_memory self.t1 = cc.t1 self.t2 = cc.t2 if eris is None: eris = cc.ao2mo() self.eris = eris self._made_shared_2e = False def _make_shared_1e(self): cput0 = (logger.process_clock(), logger.perf_counter()) t1, t2, eris = self.t1, self.t2, self.eris self.Loo = imd.Loo(t1, t2, eris) self.Lvv = imd.Lvv(t1, t2, eris) self.Fov = imd.cc_Fov(t1, t2, eris) logger.timer_debug1(self, 'EOM-CCSD shared one-electron ' 'intermediates', *cput0) return self def _make_shared_2e(self): cput0 = (logger.process_clock(), logger.perf_counter()) log = logger.Logger(self.stdout, self.verbose) t1, t2, eris = self.t1, self.t2, self.eris # 2 virtuals self.Wovov = imd.Wovov(t1, t2, eris) self.Wovvo = imd.Wovvo(t1, t2, eris) self.Woovv = np.asarray(eris.ovov).transpose(0,2,1,3) self._made_shared_2e = True log.timer_debug1('EOM-CCSD shared two-electron intermediates', *cput0) return self def make_ip(self, ip_partition=None): self._make_shared_1e() if not self._made_shared_2e and ip_partition != 'mp': self._make_shared_2e() cput0 = (logger.process_clock(), logger.perf_counter()) log = logger.Logger(self.stdout, self.verbose) t1, t2, eris = self.t1, self.t2, self.eris # 0 or 1 virtuals if ip_partition != 'mp': self.Woooo = imd.Woooo(t1, t2, eris) self.Wooov = imd.Wooov(t1, t2, eris) self.Wovoo = imd.Wovoo(t1, t2, eris) log.timer_debug1('EOM-CCSD IP intermediates', *cput0) return self def make_t3p2_ip(self, cc, ip_partition=None): assert (ip_partition is None) cput0 = (logger.process_clock(), logger.perf_counter()) t1, t2, eris = cc.t1, cc.t2, self.eris delta_E_corr, pt1, pt2, Wovoo, Wvvvo = \ imd.get_t3p2_imds_slow(cc, t1, t2, eris) self.t1 = pt1 self.t2 = pt2 self._made_shared_2e = False # Force update self.make_ip() # Make after t1/t2 updated self.Wovoo = self.Wovoo + Wovoo logger.timer_debug1(self, 'EOM-CCSD(T)a IP intermediates', *cput0) return self def make_ea(self, ea_partition=None): self._make_shared_1e() if not self._made_shared_2e and ea_partition != 'mp': self._make_shared_2e() cput0 = (logger.process_clock(), logger.perf_counter()) log = logger.Logger(self.stdout, self.verbose) t1, t2, eris = self.t1, self.t2, self.eris # 3 or 4 virtuals self.Wvovv = imd.Wvovv(t1, t2, eris) if ea_partition == 'mp': self.Wvvvo = imd.Wvvvo(t1, t2, eris) else: self.Wvvvv = imd.Wvvvv(t1, t2, eris) self.Wvvvo = imd.Wvvvo(t1, t2, eris, self.Wvvvv) log.timer_debug1('EOM-CCSD EA intermediates', *cput0) return self def make_t3p2_ea(self, cc, ea_partition=None): assert (ea_partition is None) cput0 = (logger.process_clock(), logger.perf_counter()) t1, t2, eris = cc.t1, cc.t2, self.eris delta_E_corr, pt1, pt2, Wovoo, Wvvvo = \ imd.get_t3p2_imds_slow(cc, t1, t2, eris) self.t1 = pt1 self.t2 = pt2 self._made_shared_2e = False # Force update self.make_ea() # Make after t1/t2 updated self.Wvvvo = self.Wvvvo + Wvvvo logger.timer_debug1(self, 'EOM-CCSD(T)a EA intermediates', *cput0) return self def make_ee(self): cput0 = (logger.process_clock(), logger.perf_counter()) log = logger.Logger(self.stdout, self.verbose) t1, t2, eris = self.t1, self.t2, self.eris dtype = np.result_type(t1, t2) if np.iscomplexobj(t2): raise NotImplementedError('Complex integrals are not supported in EOM-EE-CCSD') nocc, nvir = t1.shape fswap = lib.H5TmpFile() self.saved = lib.H5TmpFile() self.wvOvV = self.saved.create_dataset('wvOvV', (nvir,nocc,nvir,nvir), dtype.char) self.woVvO = self.saved.create_dataset('woVvO', (nocc,nvir,nvir,nocc), dtype.char) self.woVVo = self.saved.create_dataset('woVVo', (nocc,nvir,nvir,nocc), dtype.char) self.woOoV = self.saved.create_dataset('woOoV', (nocc,nocc,nocc,nvir), dtype.char) foo = eris.fock[:nocc,:nocc] fov = eris.fock[:nocc,nocc:] fvv = eris.fock[nocc:,nocc:] self.Fov = np.zeros((nocc,nvir), dtype=dtype) self.Foo = np.zeros((nocc,nocc), dtype=dtype) self.Fvv = np.zeros((nvir,nvir), dtype=dtype) #:eris_ovvv = lib.unpack_tril(np.asarray(eris.ovvv).reshape(nocc*nvir,nvir**2)).reshape(nocc,nvir,nvir,nvir) #:self.Fvv = np.einsum('mf,mfae->ae', t1, eris_ovvv) * 2 #:self.Fvv -= np.einsum('mf,meaf->ae', t1, eris_ovvv) #:self.woVvO = lib.einsum('jf,mebf->mbej', t1, eris_ovvv) #:self.woVVo = lib.einsum('jf,mfbe->mbej',-t1, eris_ovvv) #:tau = _make_tau(t2, t1, t1) #:self.woVoO = 0.5 * lib.einsum('mebf,ijef->mbij', eris_ovvv, tau) #:self.woVoO += 0.5 * lib.einsum('mfbe,ijfe->mbij', eris_ovvv, tau) eris_ovoo = np.asarray(eris.ovoo) woVoO = np.empty((nocc,nvir,nocc,nocc), dtype=dtype) tau = _make_tau(t2, t1, t1) theta = t2*2 - t2.transpose(0,1,3,2) mem_now = lib.current_memory()[0] max_memory = max(0, self.max_memory - mem_now) blksize = min(nocc, max(ccsd.BLKMIN, int(max_memory*1e6/8/(nvir**3*3)))) for seg, (p0,p1) in enumerate(lib.prange(0, nocc, blksize)): ovvv = eris.get_ovvv(slice(p0,p1)) # ovvv = eris.ovvv[p0:p1] # transform integrals (ia|bc) -> (ac|ib) fswap['ebmf/%d'%seg] = np.einsum('mebf->ebmf', ovvv) self.Fvv += np.einsum('mf,mfae->ae', t1[p0:p1], ovvv) * 2 self.Fvv -= np.einsum('mf,meaf->ae', t1[p0:p1], ovvv) woVoO[p0:p1] = lib.einsum('mebf,ijef->mbij', ovvv, tau) woVvO = lib.einsum('jf,mebf->mbej', t1, ovvv) woVVo = lib.einsum('jf,mfbe->mbej',-t1, ovvv) ovvv = None eris_ovov = np.asarray(eris.ovov[p0:p1]) woOoV = lib.einsum('if,mfne->mnie', t1, eris_ovov) woOoV+= eris_ovoo[:,:,p0:p1].transpose(2,0,3,1) self.woOoV[p0:p1] = woOoV woOoV = None tmp = lib.einsum('njbf,mfne->mbej', t2, eris_ovov) woVvO -= tmp * .5 woVVo += tmp ovoo = lib.einsum('menf,jf->menj', eris_ovov, t1) woVvO -= lib.einsum('nb,menj->mbej', t1, ovoo) ovoo = lib.einsum('mfne,jf->menj', eris_ovov, t1) woVVo += lib.einsum('nb,menj->mbej', t1, ovoo) ovoo = None ovov = eris_ovov * 2 - eris_ovov.transpose(0,3,2,1) woVvO += lib.einsum('njfb,menf->mbej', theta, ovov) * .5 self.Fov[p0:p1] = np.einsum('nf,menf->me', t1, ovov) tilab = np.einsum('ia,jb->ijab', t1[p0:p1], t1) * .5 tilab += t2[p0:p1] self.Foo += lib.einsum('mief,menf->ni', tilab, ovov) self.Fvv -= lib.einsum('mnaf,menf->ae', tilab, ovov) eris_ovov = ovov = tilab = None woVvO -= lib.einsum('nb,menj->mbej', t1, eris_ovoo[p0:p1,:,:]) woVVo += lib.einsum('nb,nemj->mbej', t1, eris_ovoo[:,:,p0:p1]) woVvO += np.asarray(eris.ovvo[p0:p1]).transpose(0,2,1,3) woVVo -= np.asarray(eris.oovv[p0:p1]).transpose(0,2,3,1) self.woVvO[p0:p1] = woVvO self.woVVo[p0:p1] = woVVo self.Fov += fov self.Foo += foo + 0.5*np.einsum('me,ie->mi', self.Fov+fov, t1) self.Fvv += fvv - 0.5*np.einsum('me,ma->ae', self.Fov+fov, t1) # 0 or 1 virtuals woOoO = lib.einsum('je,nemi->mnij', t1, eris_ovoo) woOoO = woOoO + woOoO.transpose(1,0,3,2) woOoO += np.asarray(eris.oooo).transpose(0,2,1,3) tmp = lib.einsum('meni,jneb->mbji', eris_ovoo, t2) woVoO -= tmp.transpose(0,1,3,2) * .5 woVoO -= tmp tmp = None ovoo = eris_ovoo*2 - eris_ovoo.transpose(2,1,0,3) woVoO += lib.einsum('nemi,njeb->mbij', ovoo, theta) * .5 self.Foo += np.einsum('ne,nemi->mi', t1, ovoo) ovoo = None eris_ovov = np.asarray(eris.ovov) woOoO += lib.einsum('ijef,menf->mnij', tau, eris_ovov) self.woOoO = self.saved['woOoO'] = woOoO woVoO -= lib.einsum('nb,mnij->mbij', t1, woOoO) woOoO = None tmpoovv = lib.einsum('njbf,nemf->ejmb', t2, eris_ovov) ovov = eris_ovov*2 - eris_ovov.transpose(0,3,2,1) eris_ovov = None tmpovvo = lib.einsum('nifb,menf->eimb', theta, ovov) ovov = None tmpovvo *= -.5 tmpovvo += tmpoovv * .5 woVoO -= lib.einsum('ie,ejmb->mbij', t1, tmpovvo) woVoO -= lib.einsum('ie,ejmb->mbji', t1, tmpoovv) woVoO += eris_ovoo.transpose(3,1,2,0) # 3 or 4 virtuals eris_ovvo = np.asarray(eris.ovvo) tmpovvo -= eris_ovvo.transpose(1,3,0,2) fswap['ovvo'] = tmpovvo tmpovvo = None eris_oovv = np.asarray(eris.oovv) tmpoovv -= eris_oovv.transpose(3,1,0,2) fswap['oovv'] = tmpoovv tmpoovv = None woVoO += lib.einsum('mebj,ie->mbij', eris_ovvo, t1) woVoO += lib.einsum('mjbe,ie->mbji', eris_oovv, t1) woVoO += lib.einsum('me,ijeb->mbij', self.Fov, t2) self.woVoO = self.saved['woVoO'] = woVoO woVoO = eris_ovvo = eris_oovv = None #:theta = t2*2 - t2.transpose(0,1,3,2) #:eris_ovvv = lib.unpack_tril(np.asarray(eris.ovvv).reshape(nocc*nvir,nvir**2)).reshape(nocc,nvir,nvir,nvir) #:ovvv = eris_ovvv*2 - eris_ovvv.transpose(0,3,2,1) #:tmpab = lib.einsum('mebf,miaf->eiab', eris_ovvv, t2) #:tmpab = tmpab + tmpab.transpose(0,1,3,2) * .5 #:tmpab-= lib.einsum('mfbe,mifa->eiba', ovvv, theta) * .5 #:self.wvOvV += eris_ovvv.transpose(2,0,3,1).conj() #:self.wvOvV -= tmpab nsegs = len(fswap['ebmf']) def load_ebmf(slice): dat = [fswap['ebmf/%d'%i][slice] for i in range(nsegs)] return np.concatenate(dat, axis=2) mem_now = lib.current_memory()[0] max_memory = max(0, self.max_memory - mem_now) blksize = min(nocc, max(ccsd.BLKMIN, int(max_memory*1e6/8/(nocc*nvir**2*4)))) for p0, p1 in lib.prange(0, nvir, blksize): #:wvOvV = lib.einsum('mebf,miaf->eiab', ovvv, t2) #:wvOvV += lib.einsum('mfbe,miaf->eiba', ovvv, t2) #:wvOvV -= lib.einsum('mfbe,mifa->eiba', ovvv, t2)*2 #:wvOvV += lib.einsum('mebf,mifa->eiba', ovvv, t2) ebmf = load_ebmf(slice(p0, p1)) wvOvV = lib.einsum('ebmf,miaf->eiab', ebmf, t2) wvOvV = -.5 * wvOvV.transpose(0,1,3,2) - wvOvV # Using the permutation symmetry (em|fb) = (em|bf) efmb = load_ebmf((slice(None), slice(p0, p1))) wvOvV += np.einsum('ebmf->bmfe', efmb.conj()) # tmp = (mf|be) - (me|bf)*.5 tmp = -.5 * ebmf tmp += efmb.transpose(1,0,2,3) ebmf = None wvOvV += lib.einsum('efmb,mifa->eiba', tmp, theta) tmp = None wvOvV += lib.einsum('meni,mnab->eiab', eris_ovoo[:,p0:p1], tau) wvOvV -= lib.einsum('me,miab->eiab', self.Fov[:,p0:p1], t2) wvOvV += lib.einsum('ma,eimb->eiab', t1, fswap['ovvo'][p0:p1]) wvOvV += lib.einsum('ma,eimb->eiba', t1, fswap['oovv'][p0:p1]) self.wvOvV[p0:p1] = wvOvV self.made_ee_imds = True log.timer('EOM-CCSD EE intermediates', *cput0) return self def _make_tau(t2, t1, r1, fac=1, out=None): tau = np.einsum('ia,jb->ijab', t1, r1) tau = tau + tau.transpose(1,0,3,2) tau *= fac * .5 tau += t2 return tau