Beispiel #1
0
    def get_wm_1RDM_from_OEI(self, OEI, nelec=None, loc2wrk=None):

        nelec = nelec or self.nelec_idem
        loc2wrk = loc2wrk if np.any(loc2wrk) else self.loc2idem
        nocc = nelec // 2

        OEI_wrk = represent_operator_in_basis(OEI, loc2wrk)
        oneRDM_wrk = 2 * get_1RDM_from_OEI(OEI_wrk, nocc)
        oneRDM_loc = represent_operator_in_basis(oneRDM_wrk, loc2wrk.T)
        return oneRDM_loc + self.oneRDMcorr_loc
Beispiel #2
0
def debug_Etot (dmet_obj):
    raise NotImplementedError ("Hopelessly broken since last used.")
    if dmet_obj.CC_E_TYPE == 'CASCI':
        print ("debug_Etot :: CASCI calculation; passing to debug_Eimp")
        return debug_Eimp (dmet_obj, dmet_obj.fragments[0])
    frags = dmet_obj.fragments 
    for f in frags:
        debug_Eimp (dmet_obj, f)
        f.E1_test = 0.0
        f.E2_test = 0.0
    E0 = dmet_obj.ints.const ()
    E1 = 0.0
    E2 = 0.0
    print ("debug_Etot :: constant = {0}".format (E0))

    for f in itertools.product (frags, frags):
        fname    = "{0} + {1}".format (f[0].frag_name, f[1].frag_name)
        loc2frag = [i.loc2frag for i in f] 
        OEI_i    = [represent_operator_in_basis (dmet_obj.ints.loc_rhf_fock_bis (0.5 * i.oneRDM_loc), *loc2frag) for i in f]
        oneRDM_i = [0.5 * represent_operator_in_basis (i.oneRDM_loc, *loc2frag) for i in f]
        E1_i     = [np.einsum ('ij,ij->', OEI, oneRDM) for OEI, oneRDM in zip (OEI_i, oneRDM_i)]
        E1      += sum(E1_i)
        print ("debug_Etot :: fragments {0} E1 = {1}".format (fname, sum(E1_i)))
        for E, i in zip (E1_i, f):
            i.E1_test += E
    print ("debug_Etot :: one-body = {0}".format (E1))

    for f in itertools.product (frags, frags, frags, frags):
        fname    = "{0} + {1} + {2} + {3}".format (f[0].frag_name, f[1].frag_name, f[2].frag_name, f[3].frag_name) 
        loc2frag = [i.loc2frag for i in f]
        TEI      = dmet_obj.ints.general_tei (loc2frag)
        twoRDM_i = [0.25 * i.get_twoCDM(*loc2frag) for i in f]
        E2_i     = [0.5 * np.einsum ('ijkl,ijkl->', TEI, twoRDM) for twoRDM in twoRDM_i]
        E2      += sum(E2_i)
        print ("debug_Etot :: fragments {0} E2 = {1}".format (fname, sum(E2_i)))
        for E, i in zip (E2_i, f):
            i.E2_test += E
    print ("debug_Etot :: two-body = {0}".format (E2))
    Etot = E0 + E1 + E2
    print ("debug_Etot :: object energy = {0:.5f}, test energy = {1:.5f}, difference = {2:.5f}".format(
            dmet_obj.energy, Etot, dmet_obj.energy - Etot))
    print ("debug_Etot :: fragment energy decomposition:")
    for f in frags:
        E_test = f.E1_test + f.E2_test
        E_diff = f.E_frag - E_test
        print ("{0} fragment energy = {1:.5f}, test E1 = {2:.5f}, test E2 = {3:.5f}, test Etot = {4:.5f}, difference = {5:.5f}".format(
               f.frag_name, f.E_frag, f.E1_test, f.E2_test, E_test, E_diff))
        del f.E1_test
        del f.E2_test

    return Etot
Beispiel #3
0
 def loc_rhf_jk_bis(self, DMloc):
     '''    
     if self._eri is not None:
         j, k = dot_eri_dm (self._eri, self._eri.loc2eri_op (DMloc), hermi=1)
         JK_loc = self._eri.eri2loc_op (j - 0.5*k)
     else:
     '''
     DM_ao = represent_operator_in_basis(DMloc, self.ao2loc.T)
     JK_ao = self.get_veff_ao(DM_ao, 0, 0,
                              1)  #Last 3 numbers: dm_last, vhf_last, hermi
     if JK_ao.ndim == 3:
         JK_ao = JK_ao[0]
     JK_loc = represent_operator_in_basis(JK_ao, self.ao2loc)
     return JK_loc
Beispiel #4
0
 def dmet_const(self, loc2dmet, norbs_imp, oneRDMfroz_loc):
     norbs_core = self.norbs_tot - norbs_imp
     if norbs_core == 0:
         return 0.0
     loc2core = loc2dmet[:, norbs_imp:]
     GAMMA = represent_operator_in_basis(oneRDMfroz_loc, loc2core)
     OEI = self.dmet_oei(loc2core, norbs_core)
     FOCK = self.dmet_fock(loc2core, norbs_core, oneRDMfroz_loc)
     return 0.5 * np.einsum('ij,ij->', GAMMA, OEI + FOCK)
Beispiel #5
0
def solve(frag, guess_1RDM, chempot_imp):

    # Augment OEI with the chemical potential
    OEI = frag.impham_OEI_C - chempot_imp

    # Get the RHF solution
    mol = gto.Mole()
    mol.spin = int(round(2 * frag.target_MS))
    mol.verbose = 0 if frag.mol_output is None else 4
    mol.output = frag.mol_output
    mol.build()
    mol.atom.append(('H', (0, 0, 0)))
    mol.nelectron = frag.nelec_imp
    #mol.incore_anyway = True
    #mf.get_hcore = lambda *args: OEI
    #mf.get_ovlp = lambda *args: np.eye(frag.norbs_imp)
    #mf._eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)
    h1e = OEI
    eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)

    ed = fci.FCI(mol, singlet=(frag.target_S == 0))
    if frag.target_S != 0:
        s2_eval = frag.target_S * (frag.target_S + 1)
        fix_spin_(ed, ss=s2_eval)

    # Guess vector
    ci = None
    if len(frag.imp_cache) == 1:
        ci = frag.imp_cache[0]
        print("Taking initial ci vector from cache")

    t_start = time.time()
    ed.conv_tol = 1e-12
    E_FCI, ci = ed.kernel(h1e, eri, frag.norbs_imp, frag.nelec_imp, ci0=ci)
    assert (ed.converged)
    frag.imp_cache = [ci]
    t_end = time.time()
    print(
        'Impurity FCI energy (incl chempot): {}; spin multiplicity: {}; time to solve: {}'
        .format(frag.impham_CONST + E_FCI,
                ed.spin_square(ci, frag.norbs_imp, frag.nelec_imp)[1],
                t_end - t_start))

    # oneRDM and twoCDM
    oneRDM_imp, twoRDM_imp = ed.make_rdm12(ci, frag.norbs_imp, frag.nelec_imp)
    oneRDMs_imp = ed.make_rdm1s(ci, frag.norbs_imp, frag.nelec_imp)
    twoCDM_imp = get_2CDM_from_2RDM(twoRDM_imp, oneRDMs_imp)

    # General impurity data
    frag.oneRDM_loc = symmetrize_tensor(
        frag.oneRDMfroz_loc +
        represent_operator_in_basis(oneRDM_imp, frag.imp2loc))
    frag.twoCDM_imp = symmetrize_tensor(twoCDM_imp)
    frag.E_imp = frag.impham_CONST + E_FCI + np.einsum('ab,ab->', chempot_imp,
                                                       oneRDM_imp)

    return None
Beispiel #6
0
 def restore_wm_full_scf(self):
     self.activeFOCK = represent_operator_in_basis(self.fullFOCKao,
                                                   self.ao2loc)
     self.activeJKidem = self.activeFOCK - self.activeOEI
     self.activeJKcorr = np.zeros((self.norbs_tot, self.norbs_tot),
                                  dtype=self.activeOEI.dtype)
     self.oneRDMcorr_loc = np.zeros((self.norbs_tot, self.norbs_tot),
                                    dtype=self.activeOEI.dtype)
     self.loc2idem = np.eye(self.norbs_tot, dtype=self.activeOEI.dtype)
     self.nelec_idem = self.nelec_tot
Beispiel #7
0
    def get_wm_1RDM_from_scf_on_OEI(self,
                                    OEI,
                                    nelec=None,
                                    loc2wrk=None,
                                    oneRDMguess_loc=None,
                                    output=None):

        nelec = nelec or self.nelec_idem
        loc2wrk = loc2wrk if np.any(loc2wrk) else self.loc2idem
        oneRDM_wrk = represent_operator_in_basis(
            oneRDMguess_loc, loc2wrk) if np.any(oneRDMguess_loc) else None
        nocc = nelec // 2

        # DON'T call self.get_wm_1RDM_from_OEIidem here because you need to hold oneRDMcorr_loc frozen until the end of the scf!
        OEI_wrk = represent_operator_in_basis(OEI, loc2wrk)
        if oneRDM_wrk is None:
            oneRDM_wrk = 2 * get_1RDM_from_OEI(OEI_wrk, nocc)
        '''
        if self._eri is not None:
            # I just need a view of self._eri with different tags. ao2loc . loc2wrk = ao2wrk
            wrk2loc    = loc2wrk.conjugate ().T
            ERI_wrk    = self._eri.view ()
            ERI_wrk    = tag_array (ERI_wrk, loc2eri_bas = lambda x: self._eri.loc2eri_bas (np.dot (loc2wrk, x)))
            ERI_wrk    = tag_array (ERI_wrk, loc2eri_op = lambda x: self._eri.loc2eri_op (reduce (np.dot, (loc2wrk, x, wrk2loc))))
            ERI_wrk    = tag_array (ERI_wrk, eri2loc_bas = lambda x: np.dot (wrk2loc, self._eri.eri2loc_bas (x)))
            ERI_wrk    = tag_array (ERI_wrk, eri2loc_op = lambda x: reduce (np.dot, (wrk2loc, self._eri.eri2loc_op (x), loc2wrk)))
            oneRDM_wrk = wm_rhf.solve_ERI(OEI_wrk, ERI_wrk, oneRDM_wrk, nocc, self.num_mf_stab_checks)
        else:
        '''
        ao2wrk = np.dot(self.ao2loc, loc2wrk)
        oneRDM_wrk = wm_rhf.solve_JK(OEI_wrk,
                                     ao2wrk,
                                     oneRDM_wrk,
                                     nocc,
                                     self.num_mf_stab_checks,
                                     self.get_veff_ao,
                                     self.get_jk_ao,
                                     output=output)
        oneRDM_loc = represent_operator_in_basis(oneRDM_wrk, loc2wrk.T)
        return oneRDM_loc + self.oneRDMcorr_loc
Beispiel #8
0
def solve(frag, guess_1RDM, chempot_imp):

    # Augment OEI with the chemical potential
    OEI = frag.impham_OEI_C - chempot_imp

    # Get the RHF solution
    mol = gto.Mole()
    mol.build(verbose=0)
    mol.atom.append(('C', (0, 0, 0)))
    mol.nelectron = frag.nelec_imp
    mol.incore_anyway = True
    mf = scf.RHF(mol)
    mf.get_hcore = lambda *args: OEI
    mf.get_ovlp = lambda *args: np.eye(frag.norbs_imp)
    mf._eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)
    mf.scf(guess_1RDM)
    DMloc = np.dot(np.dot(mf.mo_coeff, np.diag(mf.mo_occ)), mf.mo_coeff.T)
    if (mf.converged == False):
        mf = mf.newton()
        mf.kernel()

    # Get the MP2 solution
    mp2 = mp.MP2(mf)
    mp2.kernel()
    imp2mo = mf.mo_coeff
    mo2imp = imp2mo.conjugate().T
    oneRDMimp_imp = mf.make_rdm1()
    twoRDMimp_mo = mp2.make_rdm2()
    twoRDMimp_imp = represent_operator_in_basis(twoRDMimp_mo, mo2imp)
    twoCDM_imp = get_2CDM_from_2RDM(twoRDMimp_imp, oneRDMimp_imp)

    # General impurity data
    frag.oneRDM_loc = symmetrize_tensor(
        frag.oneRDMfroz_loc +
        represent_operator_in_basis(oneRDMimp_imp, frag.imp2loc))
    frag.twoCDM_imp = symmetrize_tensor(twoCDM_imp)
    frag.E_imp = frag.impham_CONST + mp2.e_tot + np.einsum(
        'ab,ab->', oneRDMimp_imp, chempot_imp)

    return None
Beispiel #9
0
    def relocalize_states(self,
                          loc2bas,
                          fragments,
                          oneRDM_loc,
                          natorb=False,
                          canonicalize=False):
        '''Do Boys localization on a subspace and assign resulting states to the various fragments using projection operators.
           Optionally diagonalize either the fock or the density matrix inside each subspace. Canonicalize overrides natorb'''

        fock_loc = self.loc_rhf_fock_bis(oneRDM_loc)
        ao2bas = boys.Boys(self.mol, np.dot(self.ao2loc, loc2bas)).kernel()
        loc2bas = reduce(np.dot,
                         [self.ao2loc.conjugate().T, self.ao_ovlp, ao2bas])

        weights = np.asarray([
            np.einsum('ip,ip->p', loc2bas[f.frag_orb_list, :].conjugate(),
                      loc2bas[f.frag_orb_list, :]) for f in fragments
        ])
        frag_assignments = np.argmax(weights, axis=0)

        loc2bas_assigned = []
        for idx, frag in enumerate(fragments):
            pick_orbs = (frag_assignments == idx)
            norbs = np.count_nonzero(pick_orbs)
            print("{} states found for fragment {}".format(
                norbs, frag.frag_name))
            loc2pick = loc2bas[:, pick_orbs]
            if canonicalize and norbs:
                f = represent_operator_in_basis(fock_loc, loc2pick)
                evals, evecs = matrix_eigen_control_options(
                    f, sort_vecs=1, only_nonzero_vals=False)
                loc2pick = np.dot(loc2pick, evecs)
            elif natorb and norbs:
                f = represent_operator_in_basis(oneRDM_loc, loc2pick)
                evals, evecs = matrix_eigen_control_options(
                    f, sort_vecs=-1, only_nonzero_vals=False)
                loc2pick = np.dot(loc2pick, evecs)
            loc2bas_assigned.append(loc2pick)
        return loc2bas_assigned
Beispiel #10
0
def solve (frag, guess_1RDM, chempot_imp):

    t_start = time.time ()

    # Augment OEI with the chemical potential
    OEI = frag.impham_OEI_C - chempot_imp
    sign_MS = np.sign (frag.target_MS) or 1

    # Get the RHF solution
    mol = gto.Mole()
    mol.spin = abs (int (round (2 * frag.target_MS)))
    mol.verbose = 0 
    if frag.mol_stdout is None:
        mol.output = frag.mol_output
        mol.verbose = 0 if frag.mol_output is None else lib.logger.DEBUG
    mol.build ()
    if frag.mol_stdout is None:
        frag.mol_stdout = mol.stdout
    else:
        mol.stdout = frag.mol_stdout
        mol.verbose = 0 if frag.mol_output is None else lib.logger.DEBUG
    mol.atom.append(('C', (0, 0, 0)))
    mol.nelectron = frag.nelec_imp
    mol.incore_anyway = True
    mf = scf.RHF( mol )
    mf.get_hcore = lambda *args: OEI
    mf.get_ovlp = lambda *args: np.eye( frag.norbs_imp )
    mf.energy_nuc = lambda *args: frag.impham_CONST
    if frag.quasidirect:
        mf.get_jk = frag.impham_get_jk 
    else:
        mf._eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)
    mf = fix_my_RHF_for_nonsinglet_env (mf, sign_MS * frag.impham_OEI_S)
    mf.__dict__.update (frag.mf_attr)
    mf.scf( guess_1RDM )
    if ( mf.converged == False ):
        if np.any (np.abs (frag.impham_OEI_S) > 1e-8) and mol.spin != 0:
            raise NotImplementedError('Gradient and Hessian fixes for nonsinglet environment of Newton-descent ROHF algorithm')
        mf = mf.newton ()
        mf.kernel ()

    # Instability check and repeat
    for i in range (frag.num_mf_stab_checks):
        if np.any (np.abs (frag.impham_OEI_S) > 1e-8) and mol.spin != 0:
            raise NotImplementedError('ROHF stability-check fixes for nonsinglet environment')
        new_mo = mf.stability ()[0]
        guess_1RDM = reduce (np.dot, (new_mo, np.diag (mf.mo_occ), new_mo.conjugate ().T))
        mf = scf.RHF( mol )
        mf.get_hcore = lambda *args: OEI
        mf.get_ovlp = lambda *args: np.eye( frag.norbs_imp )
        if frag.quasidirect:
            mf.get_jk = frag.impham_get_jk 
        else:
            mf._eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)
        mf = fix_my_RHF_for_nonsinglet_env (mf, sign_MS * frag.impham_OEI_S)
        mf.scf( guess_1RDM )
        if ( mf.converged == False ):
            mf = mf.newton ()
            mf.kernel ()

    oneRDM_imp = mf.make_rdm1()
    if np.asarray (oneRDM_imp).ndim == 3: 
        oneSDM_imp = oneRDM_imp[0] - oneRDM_imp[1]
        oneRDM_imp = oneRDM_imp[0] + oneRDM_imp[1]
    else:
        oneSDM_imp = np.zeros_like (oneRDM_imp)
    print ("Maximum distance between oneRDM_imp and guess_1RDM: {}".format (np.amax (np.abs (oneRDM_imp - guess_1RDM))))

    frag.oneRDM_loc = symmetrize_tensor (frag.oneRDMfroz_loc + represent_operator_in_basis (oneRDM_imp, frag.imp2loc))
    frag.oneSDM_loc = symmetrize_tensor (frag.oneSDMfroz_loc + represent_operator_in_basis (oneSDM_imp, frag.imp2loc))
    frag.twoCDM_imp = None
    frag.E_imp      = mf.e_tot + np.einsum ('ab,ab->', oneRDM_imp, chempot_imp)
    frag.loc2mo     = np.dot (frag.loc2imp, mf.mo_coeff)

    print ("Time for impurity RHF: {} seconds".format (time.time () - t_start))

    return None
Beispiel #11
0
def solve(frag, guess_1RDM, chempot_imp):

    # Augment OEI with the chemical potential
    OEI = frag.impham_OEI - chempot_imp

    # Do I need to get the full RHF solution?
    guess_orbs_av = len(frag.imp_cache) == 2 or frag.norbs_as > 0

    # Get the RHF solution
    mol = gto.Mole()
    mol.spin = int(round(2 * frag.target_MS))
    mol.verbose = 0 if frag.mol_output is None else lib.logger.DEBUG
    mol.output = frag.mol_output
    mol.atom.append(('H', (0, 0, 0)))
    mol.nelectron = frag.nelec_imp
    mol.build()
    #mol.incore_anyway = True
    mf = scf.RHF(mol)
    mf.get_hcore = lambda *args: OEI
    mf.get_ovlp = lambda *args: np.eye(frag.norbs_imp)
    mf.energy_nuc = lambda *args: frag.impham_CONST
    if frag.impham_CDERI is not None:
        mf = mf.density_fit()
        mf.with_df._cderi = frag.impham_CDERI
    else:
        mf._eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)
    mf.__dict__.update(frag.mf_attr)
    if guess_orbs_av: mf.max_cycle = 2
    mf.scf(guess_1RDM)
    if (not mf.converged) and (not guess_orbs_av):
        print(
            "CASSCF RHF-step not converged on fixed-point iteration; initiating newton solver"
        )
        mf = mf.newton()
        mf.kernel()

    # Instability check and repeat
    if not guess_orbs_av:
        for i in range(frag.num_mf_stab_checks):
            mf.mo_coeff = mf.stability()[0]
            guess_1RDM = mf.make_rdm1()
            mf = scf.RHF(mol)
            mf.get_hcore = lambda *args: OEI
            mf.get_ovlp = lambda *args: np.eye(frag.norbs_imp)
            mf._eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)
            mf.scf(guess_1RDM)
            if not mf.converged:
                mf = mf.newton()
                mf.kernel()

    print("CASSCF RHF-step energy: {}".format(mf.e_tot))
    #print(mf.mo_occ)
    '''    
    idx = mf.mo_energy.argsort()
    mf.mo_energy = mf.mo_energy[idx]
    mf.mo_coeff = mf.mo_coeff[:,idx]'''

    # Get the CASSCF solution
    CASe = frag.active_space[0]
    CASorb = frag.active_space[1]
    checkCAS = (CASe <= frag.nelec_imp) and (CASorb <= frag.norbs_imp)
    if (checkCAS == False):
        CASe = frag.nelec_imp
        CASorb = frag.norbs_imp
    if (frag.target_MS > frag.target_S):
        CASe = ((CASe // 2) + frag.target_S, (CASe // 2) - frag.target_S)
    else:
        CASe = ((CASe // 2) + frag.target_MS, (CASe // 2) - frag.target_MS)
    if frag.impham_CDERI is not None:
        mc = mcscf.DFCASSCF(mf, CASorb, CASe)
    else:
        mc = mcscf.CASSCF(mf, CASorb, CASe)
    norbs_amo = mc.ncas
    norbs_cmo = mc.ncore
    norbs_imo = frag.norbs_imp - norbs_amo
    nelec_amo = sum(mc.nelecas)
    norbs_occ = norbs_amo + norbs_cmo
    #mc.natorb = True

    # Guess orbitals
    ci0 = None
    if len(frag.imp_cache) == 2:
        imp2mo, ci0 = frag.imp_cache
        print("Taking molecular orbitals and ci vector from cache")
    elif frag.norbs_as > 0:
        nelec_imp_guess = int(round(np.trace(frag.oneRDMas_loc)))
        norbs_cmo_guess = (frag.nelec_imp - nelec_imp_guess) // 2
        print(
            "Projecting stored amos (frag.loc2amo; spanning {} electrons) onto the impurity basis and filling the remainder with default guess"
            .format(nelec_imp_guess))
        imp2mo, my_occ = project_amo_manually(
            frag.loc2imp,
            frag.loc2amo,
            mf.get_fock(dm=frag.get_oneRDM_imp()),
            norbs_cmo_guess,
            dm=frag.oneRDMas_loc)
    elif frag.loc2amo_guess is not None:
        print(
            "Projecting stored amos (frag.loc2amo_guess) onto the impurity basis (no dm available)"
        )
        imp2mo, my_occ = project_amo_manually(
            frag.loc2imp,
            frag.loc2amo_guess,
            mf.get_fock(dm=frag.get_oneRDM_imp()),
            norbs_cmo,
            dm=None)
        frag.loc2amo_guess = None
    else:
        imp2mo = mc.mo_coeff
        my_occ = mf.mo_occ
        print(
            "No stored amos; using mean-field canonical MOs as initial guess")

    # Guess orbital processing
    if callable(frag.cas_guess_callback):
        mo = reduce(np.dot, (frag.ints.ao2loc, frag.loc2imp, imp2mo))
        mo = frag.cas_guess_callback(frag.ints.mol, mc, mo)
        imp2mo = reduce(np.dot, (frag.imp2loc, frag.ints.ao2loc.conjugate().T,
                                 frag.ints.ao_ovlp, mo))
        frag.cas_guess_callback = None
    elif len(frag.active_orb_list) > 0:
        print('Applying caslst: {}'.format(frag.active_orb_list))
        imp2mo = mc.sort_mo(frag.active_orb_list, mo_coeff=imp2mo)
        frag.active_orb_list = []
    if len(frag.frozen_orb_list) > 0:
        mc.frozen = copy.copy(frag.frozen_orb_list)
        print("Applying frozen-orbital list (this macroiteration only): {}".
              format(frag.frozen_orb_list))
        frag.frozen_orb_list = []

    # Guess orbital printing
    if frag.mfmo_printed == False:
        ao2mfmo = reduce(np.dot, [frag.ints.ao2loc, frag.loc2imp, imp2mo])
        molden.from_mo(frag.ints.mol,
                       frag.filehead + frag.frag_name + '_mfmorb.molden',
                       ao2mfmo,
                       occ=my_occ)
        frag.mfmo_printed = True

    # Guess CI vector
    if len(frag.imp_cache) != 2 and frag.ci_as is not None:
        loc2amo_guess = np.dot(frag.loc2imp, imp2mo[:, norbs_cmo:norbs_occ])
        gOc = np.dot(loc2amo_guess.conjugate().T, frag.ci_as_orb)
        umat_g, svals, umat_c = matrix_svd_control_options(
            gOc, sort_vecs=-1, only_nonzero_vals=True)
        if (svals.size == norbs_amo):
            print(
                "Loading ci guess despite shifted impurity orbitals; singular value sum: {}"
                .format(np.sum(svals)))
            imp2mo[:, norbs_cmo:norbs_occ] = np.dot(
                imp2mo[:, norbs_cmo:norbs_occ], umat_g)
            ci0 = transform_ci_for_orbital_rotation(frag.ci_as, CASorb, CASe,
                                                    umat_c)
        else:
            print(
                "Discarding stored ci guess because orbitals are too different (missing {} nonzero svals)"
                .format(norbs_amo - svals.size))

    t_start = time.time()
    smult = 2 * frag.target_S + 1 if frag.target_S is not None else (
        frag.nelec_imp % 2) + 1
    mc.fcisolver = csf_solver(mf.mol, smult)
    mc.max_cycle_macro = 50 if frag.imp_maxiter is None else frag.imp_maxiter
    mc.ah_start_tol = 1e-10
    mc.ah_conv_tol = 1e-10
    mc.conv_tol = 1e-9
    mc.__dict__.update(frag.corr_attr)
    E_CASSCF = mc.kernel(imp2mo, ci0)[0]
    if not mc.converged:
        mc = mc.newton()
        E_CASSCF = mc.kernel(mc.mo_coeff, mc.ci)[0]
    if not mc.converged:
        print('Assuming ci vector is poisoned; discarding...')
        imp2mo = mc.mo_coeff.copy()
        mc = mcscf.CASSCF(mf, CASorb, CASe)
        smult = 2 * frag.target_S + 1 if frag.target_S is not None else (
            frag.nelec_imp % 2) + 1
        mc.fcisolver = csf_solver(mf.mol, smult)
        E_CASSCF = mc.kernel(imp2mo)[0]
        if not mc.converged:
            mc = mc.newton()
            E_CASSCF = mc.kernel(mc.mo_coeff, mc.ci)[0]
    assert (mc.converged)
    '''
    mc.conv_tol = 1e-12
    mc.ah_start_tol = 1e-10
    mc.ah_conv_tol = 1e-12
    E_CASSCF = mc.kernel(mc.mo_coeff, mc.ci)[0]
    if not mc.converged:
        mc = mc.newton ()
        E_CASSCF = mc.kernel(mc.mo_coeff, mc.ci)[0]
    #assert (mc.converged)
    '''

    # Get twoRDM + oneRDM. cs: MC-SCF core, as: MC-SCF active space
    # I'm going to need to keep some representation of the active-space orbitals
    imp2mo = mc.mo_coeff  #mc.cas_natorb()[0]
    loc2mo = np.dot(frag.loc2imp, imp2mo)
    imp2amo = imp2mo[:, norbs_cmo:norbs_occ]
    loc2amo = loc2mo[:, norbs_cmo:norbs_occ]
    frag.imp_cache = [mc.mo_coeff, mc.ci]
    frag.ci_as = mc.ci
    frag.ci_as_orb = loc2amo.copy()
    t_end = time.time()
    print(
        'Impurity CASSCF energy (incl chempot): {}; spin multiplicity: {}; time to solve: {}'
        .format(E_CASSCF,
                spin_square(mc)[1], t_end - t_start))

    # oneRDM
    oneRDM_imp = mc.make_rdm1()

    # twoCDM
    oneRDM_amo, twoRDM_amo = mc.fcisolver.make_rdm12(mc.ci, mc.ncas,
                                                     mc.nelecas)
    # Note that I do _not_ do the *real* cumulant decomposition; I do one assuming oneRDMs_amo_alpha = oneRDMs_amo_beta
    # This is fine as long as I keep it consistent, since it is only in the orbital gradients for this impurity that
    # the spin density matters. But it has to stay consistent!
    twoCDM_amo = get_2CDM_from_2RDM(twoRDM_amo, oneRDM_amo)
    twoCDM_imp = represent_operator_in_basis(twoCDM_amo, imp2amo.conjugate().T)

    # General impurity data
    frag.oneRDM_loc = symmetrize_tensor(
        frag.oneRDMfroz_loc +
        represent_operator_in_basis(oneRDM_imp, frag.imp2loc))
    frag.twoCDM_imp = None  # Experiment: this tensor is huge. Do I actually need to keep it? In principle, of course not.
    frag.E_imp = E_CASSCF + np.einsum('ab,ab->', chempot_imp, oneRDM_imp)

    # Active-space RDM data
    frag.oneRDMas_loc = symmetrize_tensor(
        represent_operator_in_basis(oneRDM_amo,
                                    loc2amo.conjugate().T))
    frag.twoCDMimp_amo = twoCDM_amo
    frag.loc2mo = loc2mo
    frag.loc2amo = loc2amo
    frag.E2_cum = 0.5 * np.tensordot(
        ao2mo.restore(1, mc.get_h2eff(), mc.ncas), twoCDM_amo, axes=4)

    return None
Beispiel #12
0
    def compare_basis_to_loc(self, loc2bas, frags, nlead=3, quiet=True):
        nfrags = len(frags)
        norbs_tot, norbs_bas = loc2bas.shape
        if norbs_bas == 0:
            return np.zeros(nfrags), loc2bas
        my_dtype = sum([[('weight{0}'.format(i), 'f8'),
                         ('frag{0}'.format(i), 'U3')]
                        for i in range(nfrags)], [])
        my_dtype += sum([[('coeff{0}'.format(i), 'f8'),
                          ('coord{0}'.format(i), 'U9')]
                         for i in range(nlead)], [])
        analysis = np.array([
            sum(((0, '-') for j in range(len(my_dtype) // 2)), tuple())
            for i in range(norbs_bas)
        ],
                            dtype=my_dtype)
        bas_weights = np.asarray([
            np.diag(
                represent_operator_in_basis(np.diag(f.is_frag_orb.astype(int)),
                                            loc2bas)) for f in frags
        ]).T
        bas_frags_idx = np.argsort(bas_weights, axis=1)[:, ::-1]
        bas_weights = np.sort(bas_weights, axis=1)[:, ::-1]
        for j in range(nfrags):
            analysis['weight{0}'.format(j)] = bas_weights[:, j]
            analysis['frag{0}'.format(j)] = [
                frags[i].frag_name for i in bas_frags_idx[:, j]
            ]

        def find_frag_fragorb(loc_orbs):
            thefrag = [
                np.where([f.is_frag_orb[i] for f in frags])[0][0]
                for i in loc_orbs
            ]
            thefragorb = [
                np.where(frags[i].frag_orb_list == j)[0][0]
                for i, j in zip(thefrag, loc_orbs)
            ]
            thefragname = [frags[i].frag_name for i in thefrag]
            thestring = [
                '{:d}:{:s}'.format(idx, name)
                for name, idx in zip(thefragname, thefragorb)
            ]
            return thestring

        weights_idx0 = np.argsort(np.absolute(loc2bas),
                                  axis=0)[:-nlead - 1:-1, :]
        weights_idx1 = np.array([range(norbs_bas) for i in range(nlead)])
        leading_coeffs = loc2bas[weights_idx0, weights_idx1].T
        overall_idx = np.argsort(weights_idx0[0, :])
        for j in range(nlead):
            analysis['coeff{0}'.format(j)] = leading_coeffs[:, j]
            analysis['coord{0}'.format(j)] = find_frag_fragorb(
                weights_idx0[j, :])
        analysis = analysis[overall_idx]

        if quiet == False:
            format_str = ' '.join([
                '{:' + str(len(name)) + 's}' for name in analysis.dtype.names
            ])
            print(format_str.format(*analysis.dtype.names))
            format_str = ' '.join(
                sum([[
                    '{:' + str(len(analysis.dtype.names[2 * i])) + '.2f}',
                    '{:>' + str(len(analysis.dtype.names[(2 * i) + 1])) + 's}'
                ] for i in range(nfrags + nlead)], []))
            for i in range(norbs_bas):
                print(format_str.format(*analysis[i]))
            print("Worst fragment localization: {:.2f}".format(
                np.amin(analysis['weight0'])))

        return loc2bas[:, overall_idx], np.array([
            np.count_nonzero(analysis['frag0'] == f.frag_name) for f in frags
        ])
Beispiel #13
0
    def __init__(self,
                 the_mf,
                 active_orbs,
                 localizationtype,
                 ao_rotation=None,
                 use_full_hessian=True,
                 localization_threshold=1e-6):

        assert ((localizationtype == 'meta_lowdin')
                or (localizationtype == 'boys')
                or (localizationtype == 'lowdin')
                or (localizationtype == 'iao'))
        self.num_mf_stab_checks = 0

        # Information on the full HF problem
        self.mol = the_mf.mol
        self.max_memory = the_mf.max_memory
        self.get_jk_ao = partial(the_mf.get_jk, self.mol)
        self.get_veff_ao = partial(the_mf.get_veff, self.mol)
        self.fullovlpao = the_mf.get_ovlp
        self.fullEhf = the_mf.e_tot
        self.fullDMao = np.dot(np.dot(the_mf.mo_coeff, np.diag(the_mf.mo_occ)),
                               the_mf.mo_coeff.T)
        self.fullJKao = self.get_veff_ao(
            dm=self.fullDMao, dm_last=0, vhf_last=0,
            hermi=1)  #Last 3 numbers: dm_last, vhf_last, hermi
        if self.fullJKao.ndim == 3:
            self.fullJKao = self.fullJKao[0]
            # Because I gave it a spin-summed 1-RDM, the two spins for JK will necessarily be identical
        self.fullFOCKao = the_mf.get_hcore() + self.fullJKao
        self.oneRDM_loc = np.asarray(the_mf.make_rdm1())
        if self.oneRDM_loc.ndim > 2:
            self.oneRDM_loc = self.oneRDM_loc[0] + self.oneRDM_loc[1]
        self.e_tot = the_mf.e_tot

        # Active space information
        self._which = localizationtype
        self.active = np.zeros([self.mol.nao_nr()], dtype=int)
        self.active[active_orbs] = 1
        self.norbs_tot = np.sum(self.active)  # Number of active space orbitals
        self.nelec_tot = int(
            np.rint(self.mol.nelectron -
                    np.sum(the_mf.mo_occ[self.active == 0]))
        )  # Total number of electrons minus frozen part

        # Localize the orbitals
        if ((self._which == 'meta_lowdin') or (self._which == 'boys')):
            if (self._which == 'meta_lowdin'):
                assert (self.norbs_tot == self.mol.nao_nr()
                        )  # Full active space required
            if (self._which == 'boys'):
                self.ao2loc = the_mf.mo_coeff[:, self.active == 1]
            if (self.norbs_tot == self.mol.nao_nr()
                ):  # If you want the full active, do meta-Lowdin
                nao.AOSHELL[4] = ['1s0p0d0f', '2s1p0d0f'
                                  ]  # redefine the valence shell for Be
                self.ao2loc = orth.orth_ao(self.mol, 'meta_lowdin')
                if (ao_rotation != None):
                    self.ao2loc = np.dot(self.ao2loc, ao_rotation.T)
            if (self._which == 'boys'):
                old_verbose = self.mol.verbose
                self.mol.verbose = 5
                loc = boys.Boys(self.mol, self.ao2loc)
                #                loc = localizer.localizer( self.mol, self.ao2loc, self._which, use_full_hessian )
                self.mol.verbose = old_verbose
                #                self.ao2loc = loc.optimize( threshold=localization_threshold )
                self.ao2loc = loc.kernel()
            self.TI_OK = False  # Check yourself if OK, then overwrite
        if (self._which == 'lowdin'):
            assert (self.norbs_tot == self.mol.nao_nr()
                    )  # Full active space required
            ovlp = self.mol.intor('cint1e_ovlp_sph')
            ovlp_eigs, ovlp_vecs = np.linalg.eigh(ovlp)
            assert (np.linalg.norm(
                np.dot(np.dot(ovlp_vecs, np.diag(ovlp_eigs)), ovlp_vecs.T) -
                ovlp) < 1e-10)
            self.ao2loc = np.dot(
                np.dot(ovlp_vecs, np.diag(np.power(ovlp_eigs, -0.5))),
                ovlp_vecs.T)
            self.TI_OK = False  # Check yourself if OK, then overwrite
        if (self._which == 'iao'):
            assert (self.norbs_tot == self.mol.nao_nr()
                    )  # Full active space assumed
            self.ao2loc = iao_helper.localize_iao(self.mol, the_mf)
            if (ao_rotation != None):
                self.ao2loc = np.dot(self.ao2loc, ao_rotation.T)
            self.TI_OK = False  # Check yourself if OK, then overwrite
            #self.molden( 'dump.molden' ) # Debugging mode
        assert (self.loc_ortho() < 1e-8)

        # Stored inverse overlap matrix
        self.ao_ovlp_inv = np.dot(self.ao2loc, self.ao2loc.conjugate().T)
        self.ao_ovlp = the_mf.get_ovlp()
        assert (is_matrix_eye(np.dot(self.ao_ovlp, self.ao_ovlp_inv)))

        # Effective Hamiltonian due to frozen part
        self.frozenDMmo = np.array(the_mf.mo_occ, copy=True)
        self.frozenDMmo[self.active ==
                        1] = 0  # Only the frozen MO occupancies nonzero
        self.frozenDMao = np.dot(
            np.dot(the_mf.mo_coeff, np.diag(self.frozenDMmo)),
            the_mf.mo_coeff.T)
        self.frozenJKao = self.get_veff_ao(
            self.frozenDMao, 0, 0,
            1)  #Last 3 numbers: dm_last, vhf_last, hermi
        if self.frozenJKao.ndim == 3:
            self.frozenJKao = self.frozenJKao[0]
            # Because I gave it a spin-summed 1-RDM, the two spins for JK will necessarily be identical
        self.frozenOEIao = self.fullFOCKao - self.fullJKao + self.frozenJKao

        # Localized OEI and ERI
        self.oneRDM_loc = reduce(np.dot,
                                 (self.ao2loc.conjugate().T, self.ao_ovlp,
                                  self.oneRDM_loc, self.ao_ovlp, self.ao2loc))
        assert (abs(np.trace(self.oneRDM_loc) - self.nelec_tot) <
                1e-8), '{} {}'.format(np.trace(self.oneRDM_loc),
                                      self.nelec_tot)
        self.activeCONST = self.mol.energy_nuc() + np.einsum(
            'ij,ij->', self.frozenOEIao - 0.5 * self.frozenJKao,
            self.frozenDMao)
        self.activeOEI = represent_operator_in_basis(self.frozenOEIao,
                                                     self.ao2loc)
        self.activeFOCK = represent_operator_in_basis(self.fullFOCKao,
                                                      self.ao2loc)
        self.activeJKidem = self.activeFOCK - self.activeOEI
        self.activeJKcorr = np.zeros((self.norbs_tot, self.norbs_tot),
                                     dtype=self.activeOEI.dtype)
        self.oneRDMcorr_loc = np.zeros((self.norbs_tot, self.norbs_tot),
                                       dtype=self.activeOEI.dtype)
        self.loc2idem = np.eye(self.norbs_tot, dtype=self.activeOEI.dtype)
        self.nelec_idem = self.nelec_tot
        self._eri = None
        self.with_df = None
        sys.stdout.flush()

        def _is_mem_enough():
            return 2 * (self.norbs_tot**
                        4) / 1e6 + current_memory()[0] < self.max_memory * 0.95

        # Unfortunately, there is currently no way to do the integral transformation directly on the antisymmetrized two-electron
        # integrals, at least none already implemented in PySCF. Therefore the smallest possible memory footprint involves
        # two arrays of fourfold symmetry, which works out to roughly one half of an array with no symmetry
        if hasattr(the_mf, 'with_df') and hasattr(
                the_mf.with_df,
                '_cderi') and the_mf.with_df._cderi is not None:
            print("Found density-fitting three-center integrals scf object")
            loc2ao = self.ao2loc.conjugate().T
            locOao = np.dot(loc2ao, self.ao_ovlp)
            self.with_df = the_mf.with_df
            self.with_df.loc2eri_bas = lambda x: np.dot(self.ao2loc, x)
            self.with_df.loc2eri_op = lambda x: reduce(np.dot, (self.ao2loc, x,
                                                                loc2ao))
            self.with_df.eri2loc_bas = lambda x: np.dot(locOao, x)
            self.with_df.eri2loc_op = lambda x: reduce(np.dot, (loc2ao, x, self
                                                                .ao2loc))
        elif the_mf._eri is not None:
            print("Found eris on scf object")
            loc2ao = self.ao2loc.conjugate().T
            locOao = np.dot(loc2ao, self.ao_ovlp)
            self._eri = the_mf._eri
            self._eri = tag_array(self._eri,
                                  loc2eri_bas=lambda x: np.dot(self.ao2loc, x))
            self._eri = tag_array(
                self._eri,
                loc2eri_op=lambda x: reduce(np.dot, (self.ao2loc, x, loc2ao)))
            self._eri = tag_array(self._eri,
                                  eri2loc_bas=lambda x: np.dot(locOao, x))
            self._eri = tag_array(
                self._eri,
                eri2loc_op=lambda x: reduce(np.dot, (loc2ao, x, self.ao2loc)))
        elif _is_mem_enough():
            print("Storing eris in memory")
            self._eri = ao2mo.restore(
                8,
                ao2mo.outcore.full_iofree(self.mol, self.ao2loc, compact=True),
                self.norbs_tot)
            self._eri = tag_array(self._eri, loc2eri_bas=lambda x: x)
            self._eri = tag_array(self._eri, loc2eri_op=lambda x: x)
            self._eri = tag_array(self._eri, eri2loc_bas=lambda x: x)
            self._eri = tag_array(self._eri, eri2loc_op=lambda x: x)
        else:
            print("Direct calculation")
        sys.stdout.flush()
Beispiel #14
0
def calc_mp2_ecorr_correction_to_dmetcasci_using_idempotent_1RDM(
        imp_case,
        DMET_object,
        idempotent_1RDM,
        correlated_1RDM,
        loc2def,
        norbs_frag,
        norbs_emb,
        num_zero_atol=params.num_zero_atol):
    norbs_tot = mrh.util.la.assert_matrix_square(idempotent_1RDM)
    mrh.util.la.assert_matrix_square(correlated_1RDM, matdim=norbs_tot)
    mrh.util.la.assert_matrix_square(loc2def, matdim=norbs_tot)
    assert (norbs_tot >= norbs_frag)
    assert (norbs_tot >= norbs_emb)
    norbs_froz = norbs_tot - norbs_emb
    if imp_case == "physical frag, overlapping bath":
        imp_case = "raw"

    # Do the Schmidt decomposition. The fragment basis functions are the first norbs_frag deformed basis functions by construction.
    def2frag = np.eye(norbs_emb, dtype=idempotent_1RDM.dtype)[:, :norbs_frag]
    loc2emb = loc2def[:, :norbs_emb]
    loc2froz = loc2def[:, norbs_emb:]
    idem_1RDM_def_basis = mrh.util.basis.represent_operator_in_basis(
        idempotent_1RDM, loc2emb)
    corr_1RDM_def_basis = mrh.util.basis.represent_operator_in_basis(
        correlated_1RDM, loc2emb)
    emb2dmeta_corr, norbs_bath_corr, nelec_imp_corr = schmidt_decompose_1RDM(
        corr_1RDM_def_basis, def2frag, norbs_frag)
    emb2dmeta, norbs_bath, nelec_imp = schmidt_decompose_1RDM(
        idem_1RDM_def_basis, def2frag, norbs_frag)
    loc2dmet = np.append(np.dot(loc2emb, emb2dmeta), loc2froz, axis=1)

    # Count orbitals and arrange coefficients
    assert (mrh.util.basis.is_basis_orthonormal_and_complete(loc2dmet))
    assert (norbs_frag + norbs_bath <= norbs_emb)
    norbs_imp = norbs_frag + norbs_bath
    norbs_core = (norbs_emb - norbs_imp) + norbs_froz
    assert (norbs_imp + norbs_core == norbs_tot)
    loc2imp = loc2dmet[:, :norbs_imp]
    loc2core = loc2dmet[:, norbs_imp:]
    norbs_imp_corr = norbs_frag + norbs_bath_corr

    # Partition up 1RDMs
    core_1RDM = mrh.util.basis.project_operator_into_subspace(
        idempotent_1RDM, loc2core) + (correlated_1RDM - idempotent_1RDM)
    imp_1RDM = correlated_1RDM - core_1RDM

    # Count electrons; compare results for schmidt-decomposing the whole thing to schmidt-decomposing only the idempotent 1RDM
    nelec_tot = np.trace(correlated_1RDM)
    nelec_bleed = mrh.util.basis.compute_nelec_in_subspace(core_1RDM, loc2imp)
    report_str1 = "Decomposing the correlated 1RDM in the {0} basis leads to a {1:.3f}-electron in {2} orbital impurity problem".format(
        imp_case, nelec_imp_corr, norbs_imp_corr)
    report_str2 = "Decomposing the idempotent 1RDM in the {0} basis leads to a {1:.3f}-electron in {2} orbital impurity problem".format(
        imp_case, nelec_imp, norbs_imp)
    report_str3 = report_str2 if imp_case == "raw" else report_str1 + "\n" + report_str2
    report_str4 = "in which {0} electrons from the correlated 1RDM were found bleeding on to the impurity space".format(
        nelec_bleed)
    print("{0}\n{1}".format(report_str3, report_str4))
    for space, nelec in (("impurity", nelec_imp), ("total", nelec_tot)):
        err_str = "{0} number of {1} electrons not an even integer ({2})".format(
            imp_case, space, nelec)
        err_measure = abs(round(nelec / 2) - (nelec / 2))
        assert (err_measure < num_zero_atol), err_str
    nelec_imp = int(round(nelec_imp))

    # Perform the solver calculation and report the energy
    # All I want to do is read off the extra correlation energy, so I'll use pyscf_rhf and pyscf_mp2 together
    # The chemical potential shouldn't matter because this is a post-facto one-off correction, so there's no breaking the number
    # (As long as I passed the assertions a few lines above!)
    imp_OEI = DMET_object.ints.dmet_oei(loc2dmet, norbs_imp)
    imp_FOCK = DMET_object.ints.dmet_fock(loc2dmet, norbs_imp, core_1RDM)
    imp_TEI = DMET_object.ints.dmet_tei(loc2dmet, norbs_imp)
    chempot = 0.0
    DMguessRHF = DMET_object.ints.dmet_init_guess_rhf(loc2dmet, norbs_imp,
                                                      nelec_imp // 2,
                                                      norbs_frag, chempot)
    mol = gto.Mole()
    mol.build(verbose=0)
    mol.atom.append(('C', (0, 0, 0)))
    mol.nelectron = nelec_imp
    mol.incore_anyway = True
    mf = scf.RHF(mol)
    mf.get_hcore = lambda *args: np.copy(imp_FOCK)
    mf.get_ovlp = lambda *args: np.eye(norbs_imp)
    mf._eri = ao2mo.restore(8, imp_TEI, norbs_imp)
    mf.scf(DMguessRHF)
    DMloc = np.dot(np.dot(mf.mo_coeff, np.diag(mf.mo_occ)), mf.mo_coeff.T)
    if (mf.converged == False):
        mf = mf.newton()
        mf.kernel()

    # Get the MP2 solution
    mp2 = mp.MP2(mf)
    mp2.kernel()

    OEI_eff = 0.5 * (imp_OEI + imp_FOCK)
    oneRDM_imp = mf.make_rdm1()
    twoRDM_imp = represent_operator_in_basis(mp2.make_rdm2(), mf.mo_coeff.T)
    JK = 0.5 * mf.get_veff(None, dm=oneRDM_imp)
    ehf_frag = 0.5 * np.einsum('ij,ij->', OEI_eff[:norbs_frag, :],
                               oneRDM_imp[:norbs_frag, :])
    ehf_frag += 0.5 * np.einsum('ij,ij->', OEI_eff[:, :norbs_frag],
                                oneRDM_imp[:, :norbs_frag])
    ehf_frag += 0.5 * np.einsum('ij,ij->', JK[:norbs_frag, :],
                                oneRDM_imp[:norbs_frag, :])
    ehf_frag += 0.5 * np.einsum('ij,ij->', JK[:, :norbs_frag],
                                oneRDM_imp[:, :norbs_frag])
    emp2_frag = 0.5 * np.einsum('ij,ij->', OEI_eff[:norbs_frag, :],
                                oneRDM_imp[:norbs_frag, :])
    emp2_frag += 0.5 * np.einsum('ij,ij->', OEI_eff[:, :norbs_frag],
                                 oneRDM_imp[:, :norbs_frag])
    emp2_frag += 0.125 * np.einsum('ijkl,ijkl->',
                                   imp_TEI[:norbs_frag, :, :, :],
                                   twoRDM_imp[:norbs_frag, :, :, :])
    emp2_frag += 0.125 * np.einsum('ijkl,ijkl->',
                                   imp_TEI[:, :norbs_frag, :, :],
                                   twoRDM_imp[:, :norbs_frag, :, :])
    emp2_frag += 0.125 * np.einsum('ijkl,ijkl->',
                                   imp_TEI[:, :, :norbs_frag, :],
                                   twoRDM_imp[:, :, :norbs_frag, :])
    emp2_frag += 0.125 * np.einsum('ijkl,ijkl->',
                                   imp_TEI[:, :, :, :norbs_frag],
                                   twoRDM_imp[:, :, :, :norbs_frag])
    ecorr_frag = emp2_frag - ehf_frag
    print(
        "ehf_frag = {0:.6f}; emp2_frag = {1:.6f}; ecorr_frag = {2:.6f}".format(
            ehf_frag, emp2_frag, ecorr_frag))

    return (mp2.e_tot - mf.e_tot)
Beispiel #15
0
def get_ontop_pair_density(ot,
                           rho,
                           ao,
                           oneCDMs,
                           twoCDM_amo,
                           ao2amo,
                           deriv=0,
                           non0tab=None):
    r''' Pi(r) = i(r)*j(r)*k(r)*l(r)*d_ijkl / 2
               = rho[0](r)*rho[1](r) + i(r)*j(r)*k(r)*l(r)*l_ijkl / 2

        Args:
            ot : on-top pair density functional object
            rho : ndarray of shape (2,*,ngrids) 
                contains spin density [and derivatives] 
            ao : ndarray of shape (*, ngrids, nao)
                contains values of aos [and derivatives] 
            oneCDMs : ndarray of shape (2, nao, nao)
                contains spin-separated 1-RDM
            twoCDM_amo : ndarray of shape (mc.ncas, mc.ncas, mc.ncas, mc.ncas)
                contains spin-summed two-body cumulant density matrix in active space
            ao2amo : ndarray of shape (nao, ncas)
                molecular-orbital coefficients for active-space orbitals

        Kwargs:
            deriv : derivative order through which to calculate. Default is 0. 
                deriv > 1 not implemented
            non0tab : as in pyscf.dft.gen_grid and pyscf.dft.numint

        Returns : ndarray of shape (*,ngrids)
            The on-top pair density and its derivatives if requested
            deriv = 0 : value (1d array)
            deriv = 1 : value, d/dx, d/dy, d/dz
            deriv = 2 : value, d/dx, d/dy, d/dz, d^2/d|r1-r2|^2_(r1=r2)
            

    '''
    # Fix dimensionality of rho and ao
    if rho.ndim == 2:
        rho = rho.reshape(rho.shape[0], 1, rho.shape[1])
    if ao.ndim == 2:
        ao = ao.reshape(1, ao.shape[0], ao.shape[1])

    # Debug code for ultra-slow, ultra-high-memory but very safe implementation
    if ot.verbose > logger.DEBUG:
        logger.debug(
            ot, 'Warning: memory-intensive cacheing of full 2RDM for testing '
            'purposes initiated; reduce verbosity to increase speed and memory efficiency'
        )
        twoRDM = represent_operator_in_basis(twoCDM_amo, ao2amo.conjugate().T)
        twoRDM = get_2RDM_from_2CDM(twoRDM, oneCDMs)

    # First cumulant and derivatives (chain rule! product rule!)
    t0 = (time.process_time(), time.time())
    Pi = np.zeros_like(rho[0])
    Pi[0] = rho[0, 0] * rho[1, 0]
    if deriv > 0:
        assert (rho.shape[1] >= 4), rho.shape
        assert (ao.shape[0] >= 4), ao.shape
        for ideriv in range(1, 4):
            Pi[ideriv] = rho[0, ideriv] * rho[1, 0] + rho[0, 0] * rho[1,
                                                                      ideriv]
    if deriv > 1:
        assert (rho.shape[1] >= 6), rho.shape
        assert (ao.shape[0] >= 10), ao.shape
        Pi[4] = -(rho[:, 1:4].sum(0).conjugate() *
                  rho[:, 1:4].sum(0)).sum(0) / 4
        Pi[4] += rho[0, 0] * (rho[1, 4] / 4 + rho[0, 5] * 2)
        Pi[4] += rho[1, 0] * (rho[0, 4] / 4 + rho[1, 5] * 2)
    t0 = logger.timer_debug1(ot, 'otpd first cumulant', *t0)

    # Second cumulant and derivatives (chain rule! product rule!)
    # dot, tensordot, and sum are hugely faster than np.einsum
    # but whether or when they actually multithread is unclear
    # Update 05/11/2020: ao is actually stored in row-major order
    # = (deriv,AOs,grids).
    #grid2amo_ref = np.tensordot (ao, ao2amo, axes=1) #np.einsum ('ijk,kl->ijl', ao, ao2amo)
    grid2amo = _grid_ao2mo(ot.mol, ao, ao2amo, non0tab=non0tab)
    t0 = logger.timer(ot, 'otpd ao2mo', *t0)
    gridkern = np.zeros(grid2amo.shape + (grid2amo.shape[2], ),
                        dtype=grid2amo.dtype)
    gridkern[0] = grid2amo[0, :, :, np.newaxis] * grid2amo[
        0, :, np.newaxis, :]  # r_0ai,  r_0aj  -> r_0aij
    wrk0 = np.tensordot(gridkern[0], twoCDM_amo,
                        axes=2)  # r_0aij, P_ijkl -> P_0akl
    Pi[0] += (gridkern[0] * wrk0).sum((1, 2)) / 2  # r_0aij, P_0aij -> P_0a
    t0 = logger.timer_debug1(ot, 'otpd second cumulant 0th derivative', *t0)
    if ot.verbose > logger.DEBUG:
        logger.debug(
            ot,
            'Warning: slow einsum-based testing calculation of Pi initiated; '
            'reduce verbosity to increase speed and memory efficiency')
        test_Pi = np.einsum('ijkl,ai,aj,ak,al->a', twoRDM, ao[0], ao[0], ao[0],
                            ao[0]) / 2
        logger.debug(ot, "Pi, |tensordot_formula - einsum_formula| = %s",
                     linalg.norm(Pi[0] - test_Pi))
        t0 = logger.timer(ot, 'otpd 0th derivative debug'.format(ideriv), *t0)
    if deriv > 0:
        for ideriv in range(1, 4):
            # Fourfold tensor symmetry ijkl = klij = jilk = lkji & product rule -> factor of 4
            gridkern[ideriv] = grid2amo[ideriv, :, :, np.newaxis] * grid2amo[
                0, :, np.newaxis, :]  # r_1ai,  r_0aj  -> r_1aij
            Pi[ideriv] += (gridkern[ideriv] * wrk0).sum(
                (1, 2)) * 2  # r_1aij, P_0aij -> P_1a
            t0 = logger.timer_debug1(
                ot, 'otpd second cumulant 1st derivative ({})'.format(ideriv),
                *t0)
            if ot.verbose > logger.DEBUG:
                logger.debug(
                    ot,
                    'Warning: slow einsum-based testing calculation of Pi\'s first derivatives initiated; '
                    'reduce verbosity to increase speed and memory efficiency')
                test_Pi = np.einsum('ijkl,ai,aj,ak,al->a', twoRDM, ao[ideriv],
                                    ao[0], ao[0], ao[0]) / 2
                test_Pi += np.einsum('ijkl,aj,ai,ak,al->a', twoRDM, ao[ideriv],
                                     ao[0], ao[0], ao[0]) / 2
                test_Pi += np.einsum('ijkl,ak,ai,aj,al->a', twoRDM, ao[ideriv],
                                     ao[0], ao[0], ao[0]) / 2
                test_Pi += np.einsum('ijkl,al,ai,aj,ak->a', twoRDM, ao[ideriv],
                                     ao[0], ao[0], ao[0]) / 2
                logger.debug(
                    ot,
                    "Pi derivative, |tensordot_formula - einsum_formula| = %s",
                    linalg.norm(Pi[ideriv] - test_Pi))
                t0 = logger.timer(
                    ot, 'otpd 1st derivative ({}) debug'.format(ideriv), *t0)
    if deriv > 1:  # The fifth slot is allocated to the "off-top Laplacian," i.e., nabla_(r1-r2)^2 Pi(r1,r2)|(r1=r2)
        # nabla_off^2 Pi = 1/2 d^ik_jl * ([nabla_r^2 phi_i] phi_j phi_k phi_l + {1 - p_jk - p_jl}[nabla_r phi_i . nabla_r phi_j] phi_k phi_l)
        # using four-fold symmetry a lot! be careful!
        if ot.verbose > logger.DEBUG:
            test2_Pi = Pi[4].copy()
        XX, YY, ZZ = 4, 7, 9
        gridkern[4] = grid2amo[[XX, YY, ZZ], :, :, np.newaxis].sum(
            0) * grid2amo[0, :, np.newaxis, :]  # r_2ai, r_0aj -> r_2aij
        gridkern[4] += (grid2amo[1:4, :, :, np.newaxis] *
                        grid2amo[1:4, :, np.newaxis, :]).sum(
                            0)  # r_1ai, r_1aj -> r_2aij
        wrk1 = np.tensordot(gridkern[1:4], twoCDM_amo,
                            axes=2)  # r_1aij, P_ijkl -> P_1akl
        Pi[4] += (gridkern[4] * wrk0).sum((1, 2)) / 2  # r_2aij, P_0aij -> P_2a
        Pi[4] -= (
            (gridkern[1:4] + gridkern[1:4].transpose(0, 1, 3, 2)) * wrk1).sum(
                (0, 2, 3)) / 2  # r_1aij, P_1aij -> P_2a
        t0 = logger.timer(ot, 'otpd second cumulant off-top Laplacian', *t0)
        if ot.verbose > logger.DEBUG:
            logger.debug(
                ot,
                'Warning: slow einsum-based testing calculation of Pi\'s second derivatives initiated; '
                'reduce verbosity to increase speed and memory efficiency')
            X, Y, Z = 1, 2, 3
            test_Pi = np.einsum('ijkl,ai,aj,ak,al->a', twoRDM, ao[XX], ao[0],
                                ao[0], ao[0]) / 2
            test_Pi += np.einsum('ijkl,ai,aj,ak,al->a', twoRDM, ao[YY], ao[0],
                                 ao[0], ao[0]) / 2
            test_Pi += np.einsum('ijkl,ai,aj,ak,al->a', twoRDM, ao[ZZ], ao[0],
                                 ao[0], ao[0]) / 2
            test_Pi += np.einsum('ijkl,ai,aj,ak,al->a', twoRDM, ao[X], ao[X],
                                 ao[0], ao[0]) / 2
            test_Pi += np.einsum('ijkl,ai,aj,ak,al->a', twoRDM, ao[Y], ao[Y],
                                 ao[0], ao[0]) / 2
            test_Pi += np.einsum('ijkl,ai,aj,ak,al->a', twoRDM, ao[Z], ao[Z],
                                 ao[0], ao[0]) / 2
            test_Pi -= np.einsum('ijkl,ai,aj,ak,al->a', twoRDM, ao[X], ao[0],
                                 ao[X], ao[0]) / 2
            test_Pi -= np.einsum('ijkl,ai,aj,ak,al->a', twoRDM, ao[Y], ao[0],
                                 ao[Y], ao[0]) / 2
            test_Pi -= np.einsum('ijkl,ai,aj,ak,al->a', twoRDM, ao[Z], ao[0],
                                 ao[Z], ao[0]) / 2
            test_Pi -= np.einsum('ijkl,ai,aj,ak,al->a', twoRDM, ao[X], ao[0],
                                 ao[0], ao[X]) / 2
            test_Pi -= np.einsum('ijkl,ai,aj,ak,al->a', twoRDM, ao[Y], ao[0],
                                 ao[0], ao[Y]) / 2
            test_Pi -= np.einsum('ijkl,ai,aj,ak,al->a', twoRDM, ao[Z], ao[0],
                                 ao[0], ao[Z]) / 2
            logger.debug(
                ot,
                'Pi off-top Laplacian, |tensordot formula - einsum_formula| = %s',
                linalg.norm(Pi[4] - test_Pi))

            test2_Pi += np.einsum('ijkl,ai,aj,ak,al->a', twoCDM_amo,
                                  grid2amo[XX], grid2amo[0], grid2amo[0],
                                  grid2amo[0]) / 2
            test2_Pi += np.einsum('ijkl,ai,aj,ak,al->a', twoCDM_amo,
                                  grid2amo[YY], grid2amo[0], grid2amo[0],
                                  grid2amo[0]) / 2
            test2_Pi += np.einsum('ijkl,ai,aj,ak,al->a', twoCDM_amo,
                                  grid2amo[ZZ], grid2amo[0], grid2amo[0],
                                  grid2amo[0]) / 2
            test2_Pi += np.einsum('ijkl,ai,aj,ak,al->a', twoCDM_amo,
                                  grid2amo[X], grid2amo[X], grid2amo[0],
                                  grid2amo[0]) / 2
            test2_Pi += np.einsum('ijkl,ai,aj,ak,al->a', twoCDM_amo,
                                  grid2amo[Y], grid2amo[Y], grid2amo[0],
                                  grid2amo[0]) / 2
            test2_Pi += np.einsum('ijkl,ai,aj,ak,al->a', twoCDM_amo,
                                  grid2amo[Z], grid2amo[Z], grid2amo[0],
                                  grid2amo[0]) / 2
            test2_Pi -= np.einsum('ijkl,ai,aj,ak,al->a', twoCDM_amo,
                                  grid2amo[X], grid2amo[0], grid2amo[X],
                                  grid2amo[0]) / 2
            test2_Pi -= np.einsum('ijkl,ai,aj,ak,al->a', twoCDM_amo,
                                  grid2amo[Y], grid2amo[0], grid2amo[Y],
                                  grid2amo[0]) / 2
            test2_Pi -= np.einsum('ijkl,ai,aj,ak,al->a', twoCDM_amo,
                                  grid2amo[Z], grid2amo[0], grid2amo[Z],
                                  grid2amo[0]) / 2
            test2_Pi -= np.einsum('ijkl,ai,aj,ak,al->a', twoCDM_amo,
                                  grid2amo[X], grid2amo[0], grid2amo[0],
                                  grid2amo[X]) / 2
            test2_Pi -= np.einsum('ijkl,ai,aj,ak,al->a', twoCDM_amo,
                                  grid2amo[Y], grid2amo[0], grid2amo[0],
                                  grid2amo[Y]) / 2
            test2_Pi -= np.einsum('ijkl,ai,aj,ak,al->a', twoCDM_amo,
                                  grid2amo[Z], grid2amo[0], grid2amo[0],
                                  grid2amo[Z]) / 2
            logger.debug(
                ot,
                'Pi off-top Laplacian, testing second cumulant only |tensordot formula - einsum_formula| = %s',
                linalg.norm(Pi[4] - test2_Pi))

            t0 = logger.timer(ot, 'otpd off-top Laplacian debug', *t0)

    # Unfix dimensionality of rho, ao, and Pi
    if Pi.shape[0] == 1:
        Pi = Pi.reshape(Pi.shape[1])
        rho = rho.reshape(rho.shape[0], rho.shape[2])
        ao = ao.reshape(ao.shape[1], ao.shape[2])

    return Pi
Beispiel #16
0
    def setup_wm_core_scf(self, fragments, calcname):

        self.restore_wm_full_scf()
        oneRDMcorr_loc = sum((frag.oneRDMas_loc for frag in fragments))
        if np.all(np.isclose(oneRDMcorr_loc, 0)):
            print("Null correlated 1-RDM; default settings for wm wvfn")
            self.activeFOCK = represent_operator_in_basis(
                self.fullFOCKao, self.ao2loc)
            self.activeJKidem = self.activeFOCK - self.activeOEI
            self.activeJKcorr = np.zeros((self.norbs_tot, self.norbs_tot))
            self.oneRDMcorr_loc = oneRDMcorr_loc
            self.loc2idem = np.eye(self.norbs_tot)
            self.nelec_idem = self.nelec_tot
            return

        loc2corr = np.concatenate([frag.loc2amo for frag in fragments], axis=1)
        loc2idem = get_complementary_states(loc2corr)
        evecs = matrix_eigen_control_options(represent_operator_in_basis(
            self.loc_oei(), loc2idem),
                                             sort_vecs=1,
                                             only_nonzero_vals=False)[1]
        loc2idem = np.dot(loc2idem, evecs)

        # I want to alter the outputs of self.loc_oei (), self.loc_rhf_fock (), and the get_wm_1RDM_etc () functions.
        # self.loc_oei ()      = P_idem * (activeOEI + JKcorr) * P_idem
        # self.loc_rhf_fock () = P_idem * (activeOEI + JKcorr + JKidem) * P_idem
        # The get_wm_1RDM_etc () functions will need to add oneRDMcorr_loc to their final return value
        # The chemical potential is so that identically zero eigenvalues from the projection into the idem space don't get confused
        # with numerically-zero eigenvalues in the idem space: all occupied orbitals must have negative energy

        # Make true output 1RDM from fragments to use as guess for wm mcscf calculation
        oneRDMguess_loc = np.zeros_like(oneRDMcorr_loc)
        for f in itertools.product(fragments, fragments):
            loc2frag = [i.loc2frag for i in f]
            oneRDMguess_loc += sum(
                (0.5 * project_operator_into_subspace(i.oneRDM_loc, *loc2frag)
                 for i in f))

        nelec_corr = np.trace(oneRDMcorr_loc)
        if is_close_to_integer(nelec_corr,
                               100 * params.num_zero_atol) == False:
            raise ValueError(
                "nelec_corr not an integer! {}".format(nelec_corr))
        nelec_idem = int(round(self.nelec_tot - nelec_corr))
        JKcorr = self.loc_rhf_jk_bis(oneRDMcorr_loc)
        oneRDMidem_loc = self.get_wm_1RDM_from_scf_on_OEI(
            self.loc_oei() + JKcorr,
            nelec=nelec_idem,
            loc2wrk=loc2idem,
            oneRDMguess_loc=oneRDMguess_loc,
            output=calcname + '_trial_wvfn.log')
        JKidem = self.loc_rhf_jk_bis(oneRDMidem_loc)
        print("trace of oneRDMcorr_loc = {}".format(np.trace(oneRDMcorr_loc)))
        print("trace of oneRDMidem_loc = {}".format(np.trace(oneRDMidem_loc)))
        print("trace of oneRDM_loc in corr basis = {}".format(
            np.trace(
                represent_operator_in_basis(
                    oneRDMcorr_loc + oneRDMidem_loc,
                    orthonormalize_a_basis(loc2corr)))))
        svals = get_overlapping_states(loc2idem, loc2corr)[2]
        print("trace of <idem|corr|idem> = {}".format(np.sum(svals * svals)))
        print(loc2corr.shape)
        print(loc2idem.shape)

        ########################################################################################################
        self.activeFOCK = self.activeOEI + JKidem + JKcorr
        self.activeJKidem = JKidem
        self.activeJKcorr = JKcorr
        self.oneRDMcorr_loc = oneRDMcorr_loc
        self.loc2idem = loc2idem
        self.nelec_idem = nelec_idem
        ########################################################################################################

        # Analysis: 1RDM and total energy
        print("Analyzing LASSCF trial wave function")
        oei = self.activeOEI + (JKcorr + JKidem) / 2
        fock = self.activeFOCK
        oneRDM = oneRDMidem_loc + oneRDMcorr_loc
        E = self.activeCONST + np.tensordot(oei, oneRDM, axes=2)
        for frag in fragments:
            if frag.norbs_as > 0:
                if frag.E2_cum == 0 and np.amax(np.abs(
                        frag.twoCDMimp_amo)) > 0:
                    V = self.dmet_tei(frag.loc2amo)
                    L = frag.twoCDMimp_amo
                    frag.E2_cum = np.tensordot(V, L, axes=4) / 2
                E += frag.E2_cum
        print("LASSCF trial wave function total energy: {:.6f}".format(E))
        self.oneRDM_loc = oneRDM
        self.e_tot = E

        # Molden
        fock_idem = represent_operator_in_basis(fock, loc2idem)
        oneRDM_corr = represent_operator_in_basis(oneRDM, loc2corr)
        idem_evecs = matrix_eigen_control_options(fock_idem,
                                                  sort_vecs=1,
                                                  only_nonzero_vals=False)[1]
        corr_evecs = matrix_eigen_control_options(oneRDM_corr,
                                                  sort_vecs=-1,
                                                  only_nonzero_vals=False)[1]
        loc2molden = np.append(np.dot(loc2idem, idem_evecs),
                               np.dot(loc2corr, corr_evecs),
                               axis=1)
        wm_ene = np.einsum('ip,ij,jp->p', loc2molden, fock, loc2molden)
        wm_ene[-loc2corr.shape[1]:] = 0
        wm_occ = np.einsum('ip,ij,jp->p', loc2molden, oneRDM, loc2molden)
        ao2molden = np.dot(self.ao2loc, loc2molden)
        molden.from_mo(self.mol,
                       calcname + '_trial_wvfn.molden',
                       ao2molden,
                       occ=wm_occ,
                       ene=wm_ene)
Beispiel #17
0
def project_amo_manually (loc2imp, loc2gamo, fock_mf, norbs_cmo, dm=None):
    norbs_amo = loc2gamo.shape[1]
    amo2imp = np.dot (loc2gamo.conjugate ().T, loc2imp)
    ovlp = np.dot (amo2imp, amo2imp.conjugate ().T)
    '''
    print ("Do impurity orbitals span guess amos?")
    print (prettyprint (ovlp, fmt='{:5.2f}'))
    if dm is not None:
        print ("Density matrix?")
        print (prettyprint (represent_operator_in_basis (dm, loc2gamo), fmt='{:5.2f}'))
    '''
    proj = np.dot (amo2imp.conjugate ().T, amo2imp)
    evals, evecs = matrix_eigen_control_options (proj, sort_vecs=-1, only_nonzero_vals=False)
    imp2amo = np.copy (evecs[:,:norbs_amo])
    imp2imo = np.copy (evecs[:,norbs_amo:])
    fock_imo = represent_operator_in_basis (fock_mf, imp2imo)
    _, evecs = matrix_eigen_control_options (fock_imo, sort_vecs=1, only_nonzero_vals=False)
    imp2imo = np.dot (imp2imo, evecs)
    imp2cmo = imp2imo[:,:norbs_cmo]
    imp2vmo = imp2imo[:,norbs_cmo:]
    # Sort amo in order to apply stored ci vector
    imp2gamo = np.dot (loc2imp.conjugate ().T, loc2gamo)
    amoOgamo = np.dot (imp2amo.conjugate ().T, imp2gamo)
    #print ("Overlap matrix between guess-active and active:")
    #print (prettyprint (amoOgamo, fmt='{:5.2f}'))
    Pgamo1_amo = np.einsum ('ik,jk->ijk', amoOgamo, amoOgamo.conjugate ())
    imp2ramo = np.zeros_like (imp2amo)
    ramo_evals = np.zeros (imp2ramo.shape[1], dtype=imp2ramo.dtype)
    while (Pgamo1_amo.shape[0] > 0):
        max_eval = 0
        argmax_eval = -1
        argmax_evecs = None
        for idx in range (Pgamo1_amo.shape[2]):
            evals, evecs = matrix_eigen_control_options (Pgamo1_amo[:,:,idx], sort_vecs=-1, only_nonzero_vals=False)
            if evals[0] > max_eval:
                max_eval = evals[0]
                max_evecs = evecs
                argmax_eval = idx
        #print ("With {} amos to go, assigned highest eigenvalue ({}) to {}".format (Pgamo1_amo.shape[0], max_eval, argmax_eval))
        ramo_evals[argmax_eval] = max_eval
        imp2ramo[:,argmax_eval] = np.einsum ('ij,j->i', imp2amo, max_evecs[:,0])
        imp2amo = np.dot (imp2amo, max_evecs[:,1:])
        amoOgamo = np.dot (imp2amo.conjugate ().T, imp2gamo)
        Pgamo1_amo = np.einsum ('ik,jk->ijk', amoOgamo, amoOgamo.conjugate ())
    imp2amo = imp2ramo
    print ("Fidelity of projection of guess active orbitals onto impurity space:\n{}".format (ramo_evals))
    amoOgamo = np.dot (imp2amo.conjugate ().T, imp2gamo)
    idx_signflip = np.diag (amoOgamo) < 0
    imp2amo[:,idx_signflip] *= -1
    amoOgamo = np.dot (imp2amo.conjugate ().T, imp2gamo)
    ''' 
    print ("Overlap matrix between guess-active and active:")
    print (prettyprint (amoOgamo, fmt='{:5.2f}'))
    O = np.dot (imp2amo.conjugate ().T, imp2amo) - np.eye (imp2amo.shape[1]) 
    print ("Overlap error between active and active: {}".format (linalg.norm (O)))
    O = np.dot (imp2amo.conjugate ().T, imp2cmo)    
    print ("Overlap error between active and occupied: {}".format (linalg.norm (O)))
    O = np.dot (imp2amo.conjugate ().T, imp2vmo)    
    print ("Overlap error between active and virtual: {}".format (linalg.norm (O)))
    '''
    my_occ = np.zeros (loc2imp.shape[1], dtype=np.float64)
    my_occ[:norbs_cmo] = 2
    my_occ[norbs_cmo:][:imp2amo.shape[1]] = 1
    if dm is not None:
        loc2amo = np.dot (loc2imp, imp2amo)
        evals, evecs = matrix_eigen_control_options (represent_operator_in_basis (dm, loc2amo), sort_vecs=-1, only_nonzero_vals=False)
        imp2amo = np.dot (imp2amo, evecs)
        print ("Guess density matrix eigenvalues for guess amo: {}".format (evals))
        my_occ[norbs_cmo:][:imp2amo.shape[1]] = evals
    imp2mo = np.concatenate ([imp2cmo, imp2amo, imp2vmo], axis=1)
    return imp2mo, my_occ
Beispiel #18
0
def solve (frag, guess_1RDM, chempot_imp):

    # Augment OEI with the chemical potential
    OEI = frag.impham_OEI_C - chempot_imp

    # Do I need to get the full RHF solution?
    guess_orbs_av = len (frag.imp_cache) == 2 or frag.norbs_as > 0 

    # Get the RHF solution
    mol = gto.Mole()
    abs_2MS = int (round (2 * abs (frag.target_MS)))
    abs_2S = int (round (2 * abs (frag.target_S)))
    sign_MS = int (np.sign (frag.target_MS)) or 1
    mol.spin = abs_2MS
    mol.verbose = 0 
    if frag.mol_stdout is None:
        mol.output = frag.mol_output
        mol.verbose = 0 if frag.mol_output is None else lib.logger.DEBUG
    mol.atom.append(('H', (0, 0, 0)))
    mol.nelectron = frag.nelec_imp
    if frag.enforce_symmetry:
        mol.groupname  = frag.symmetry
        mol.symm_orb   = get_subspace_symmetry_blocks (frag.loc2imp, frag.loc2symm)
        mol.irrep_name = frag.ir_names
        mol.irrep_id   = frag.ir_ids
    mol.max_memory = frag.ints.max_memory
    mol.build ()
    if frag.mol_stdout is None:
        frag.mol_stdout = mol.stdout
    else:
        mol.stdout = frag.mol_stdout
        mol.verbose = 0 if frag.mol_output is None else lib.logger.DEBUG
    if frag.enforce_symmetry: mol.symmetry = True
    #mol.incore_anyway = True
    mf = scf.RHF(mol)
    mf.get_hcore = lambda *args: OEI
    mf.get_ovlp = lambda *args: np.eye(frag.norbs_imp)
    mf.energy_nuc = lambda *args: frag.impham_CONST
    if frag.impham_CDERI is not None:
        mf = mf.density_fit ()
        mf.with_df._cderi = frag.impham_CDERI
    else:
        mf._eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)
    mf = fix_my_RHF_for_nonsinglet_env (mf, frag.impham_OEI_S)
    mf.__dict__.update (frag.mf_attr)
    if guess_orbs_av: mf.max_cycle = 2
    mf.scf (guess_1RDM)
    if (not mf.converged) and (not guess_orbs_av):
        if np.any (np.abs (frag.impham_OEI_S) > 1e-8) and mol.spin != 0:
            raise NotImplementedError('Gradient and Hessian fixes for nonsinglet environment of Newton-descent ROHF algorithm')
        print ("CASSCF RHF-step not converged on fixed-point iteration; initiating newton solver")
        mf = mf.newton ()
        mf.kernel ()

    # Instability check and repeat
    if not guess_orbs_av:
        for i in range (frag.num_mf_stab_checks):
            if np.any (np.abs (frag.impham_OEI_S) > 1e-8) and mol.spin != 0:
                raise NotImplementedError('ROHF stability-check fixes for nonsinglet environment')
            mf.mo_coeff = mf.stability ()[0]
            guess_1RDM = mf.make_rdm1 ()
            mf = scf.RHF(mol)
            mf.get_hcore = lambda *args: OEI
            mf.get_ovlp = lambda *args: np.eye(frag.norbs_imp)
            mf._eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)
            mf = fix_my_RHF_for_nonsinglet_env (mf, frag.impham_OEI_S)
            mf.scf (guess_1RDM)
            if not mf.converged:
                mf = mf.newton ()
                mf.kernel ()

    E_RHF = mf.e_tot
    print ("CASSCF RHF-step energy: {}".format (E_RHF))

    # Get the CASSCF solution
    CASe = frag.active_space[0]
    CASorb = frag.active_space[1] 
    checkCAS =  (CASe <= frag.nelec_imp) and (CASorb <= frag.norbs_imp)
    if (checkCAS == False):
        CASe = frag.nelec_imp
        CASorb = frag.norbs_imp
    if (abs_2MS > abs_2S):
        CASe = ((CASe + sign_MS * abs_2S) // 2, (CASe - sign_MS * abs_2S) // 2)
    else:
        CASe = ((CASe + sign_MS * abs_2MS) // 2, (CASe - sign_MS * abs_2MS) // 2)
    if frag.impham_CDERI is not None:
        mc = mcscf.DFCASSCF(mf, CASorb, CASe)
    else:
        mc = mcscf.CASSCF(mf, CASorb, CASe)
    smult = abs_2S + 1 if frag.target_S is not None else (frag.nelec_imp % 2) + 1
    mc.fcisolver = csf_solver (mf.mol, smult, symm=frag.enforce_symmetry)
    if frag.enforce_symmetry: mc.fcisolver.wfnsym = frag.wfnsym
    mc.max_cycle_macro = 50 if frag.imp_maxiter is None else frag.imp_maxiter
    mc.conv_tol = min (1e-9, frag.conv_tol_grad**2)  
    mc.ah_start_tol = mc.conv_tol / 10
    mc.ah_conv_tol = mc.conv_tol / 10
    mc.__dict__.update (frag.corr_attr)
    mc = fix_my_CASSCF_for_nonsinglet_env (mc, frag.impham_OEI_S)
    norbs_amo = mc.ncas
    norbs_cmo = mc.ncore
    norbs_imo = frag.norbs_imp - norbs_amo
    nelec_amo = sum (mc.nelecas)
    norbs_occ = norbs_amo + norbs_cmo
    #mc.natorb = True

    # Guess orbitals
    ci0 = None
    dm_imp = frag.get_oneRDM_imp ()
    fock_imp = mf.get_fock (dm=dm_imp)
    if len (frag.imp_cache) == 2:
        imp2mo, ci0 = frag.imp_cache
        print ("Taking molecular orbitals and ci vector from cache")
    elif frag.norbs_as > 0:
        nelec_imp_guess = int (round (np.trace (frag.oneRDMas_loc)))
        norbs_cmo_guess = (frag.nelec_imp - nelec_imp_guess) // 2
        print ("Projecting stored amos (frag.loc2amo; spanning {} electrons) onto the impurity basis and filling the remainder with default guess".format (nelec_imp_guess))
        imp2mo, my_occ = project_amo_manually (frag.loc2imp, frag.loc2amo, fock_imp, norbs_cmo_guess, dm=frag.oneRDMas_loc)
    elif frag.loc2amo_guess is not None:
        print ("Projecting stored amos (frag.loc2amo_guess) onto the impurity basis (no amo dm available)")
        imp2mo, my_occ = project_amo_manually (frag.loc2imp, frag.loc2amo_guess, fock_imp, norbs_cmo, dm=None)
        frag.loc2amo_guess = None
    else:
        dm_imp = np.asarray (mf.make_rdm1 ())
        while dm_imp.ndim > 2:
            dm_imp = dm_imp.sum (0)
        imp2mo = mf.mo_coeff
        fock_imp = mf.get_fock (dm=dm_imp)
        fock_mo = represent_operator_in_basis (fock_imp, imp2mo)
        _, evecs = matrix_eigen_control_options (fock_mo, sort_vecs=1)
        imp2mo = imp2mo @ evecs
        my_occ = ((dm_imp @ imp2mo) * imp2mo).sum (0)
        print ("No stored amos; using mean-field canonical MOs as initial guess")
    # Guess orbital processing
    if callable (frag.cas_guess_callback):
        mo = reduce (np.dot, (frag.ints.ao2loc, frag.loc2imp, imp2mo))
        mo = frag.cas_guess_callback (frag.ints.mol, mc, mo)
        imp2mo = reduce (np.dot, (frag.imp2loc, frag.ints.ao2loc.conjugate ().T, frag.ints.ao_ovlp, mo))
        frag.cas_guess_callback = None

    # Guess CI vector
    if len (frag.imp_cache) != 2 and frag.ci_as is not None:
        loc2amo_guess = np.dot (frag.loc2imp, imp2mo[:,norbs_cmo:norbs_occ])
        metric = np.arange (CASorb) + 1
        gOc = np.dot (loc2amo_guess.conjugate ().T, (frag.ci_as_orb * metric[None,:]))
        umat_g, svals, umat_c = matrix_svd_control_options (gOc, sort_vecs=1, only_nonzero_vals=True)
        if (svals.size == norbs_amo):
            print ("Loading ci guess despite shifted impurity orbitals; singular value error sum: {}".format (np.sum (svals - metric)))
            imp2mo[:,norbs_cmo:norbs_occ] = np.dot (imp2mo[:,norbs_cmo:norbs_occ], umat_g)
            ci0 = transform_ci_for_orbital_rotation (frag.ci_as, CASorb, CASe, umat_c)
        else:
            print ("Discarding stored ci guess because orbitals are too different (missing {} nonzero svals)".format (norbs_amo-svals.size))

    # Symmetry align if possible
    imp2unac = frag.align_imporbs_symm (np.append (imp2mo[:,:norbs_cmo], imp2mo[:,norbs_occ:], axis=1), sorting_metric=fock_imp,
        sort_vecs=1, orbital_type='guess unactive', mol=mol)[0]
    imp2mo[:,:norbs_cmo] = imp2unac[:,:norbs_cmo]
    imp2mo[:,norbs_occ:] = imp2unac[:,norbs_cmo:]
    #imp2mo[:,:norbs_cmo] = frag.align_imporbs_symm (imp2mo[:,:norbs_cmo], sorting_metric=fock_imp, sort_vecs=1, orbital_type='guess inactive', mol=mol)[0]
    imp2mo[:,norbs_cmo:norbs_occ], umat = frag.align_imporbs_symm (imp2mo[:,norbs_cmo:norbs_occ], sorting_metric=fock_imp,
        sort_vecs=1, orbital_type='guess active', mol=mol)
    #imp2mo[:,norbs_occ:] = frag.align_imporbs_symm (imp2mo[:,norbs_occ:], sorting_metric=fock_imp, sort_vecs=1, orbital_type='guess external', mol=mol)[0]
    if frag.enforce_symmetry:
        imp2mo = cleanup_subspace_symmetry (imp2mo, mol.symm_orb)
        err_symm = measure_subspace_blockbreaking (imp2mo, mol.symm_orb)
        err_orth = measure_basis_nonorthonormality (imp2mo)
        print ("Initial symmetry error after cleanup = {}".format (err_symm))
        print ("Initial orthonormality error after cleanup = {}".format (err_orth))
    if ci0 is not None: ci0 = transform_ci_for_orbital_rotation (ci0, CASorb, CASe, umat)
        

    # Guess orbital printing
    if frag.mfmo_printed == False and frag.ints.mol.verbose:
        ao2mfmo = reduce (np.dot, [frag.ints.ao2loc, frag.loc2imp, imp2mo])
        print ("Writing {} {} orbital molden".format (frag.frag_name, 'CAS guess'))
        molden.from_mo (frag.ints.mol, frag.filehead + frag.frag_name + '_mfmorb.molden', ao2mfmo, occ=my_occ)
        frag.mfmo_printed = True
    elif len (frag.active_orb_list) > 0: # This is done AFTER everything else so that the _mfmorb.molden always has consistent ordering
        print('Applying caslst: {}'.format (frag.active_orb_list))
        imp2mo = mc.sort_mo(frag.active_orb_list, mo_coeff=imp2mo)
        frag.active_orb_list = []
    if len (frag.frozen_orb_list) > 0:
        mc.frozen = copy.copy (frag.frozen_orb_list)
        print ("Applying frozen-orbital list (this macroiteration only): {}".format (frag.frozen_orb_list))
        frag.frozen_orb_list = []

    if frag.enforce_symmetry: imp2mo = lib.tag_array (imp2mo, orbsym=label_orb_symm (mol, mol.irrep_id, mol.symm_orb, imp2mo, s=mf.get_ovlp (), check=False))

    t_start = time.time()
    E_CASSCF = mc.kernel(imp2mo, ci0)[0]
    if (not mc.converged) and np.all (np.abs (frag.impham_OEI_S) < 1e-8):
        mc = mc.newton ()
        E_CASSCF = mc.kernel(mc.mo_coeff, mc.ci)[0]
    if not mc.converged:
        print ('Assuming ci vector is poisoned; discarding...')
        imp2mo = mc.mo_coeff.copy ()
        mc = mcscf.CASSCF(mf, CASorb, CASe)
        smult = abs_2S + 1 if frag.target_S is not None else (frag.nelec_imp % 2) + 1
        mc.fcisolver = csf_solver (mf.mol, smult)
        E_CASSCF = mc.kernel(imp2mo)[0]
        if not mc.converged:
            if np.any (np.abs (frag.impham_OEI_S) > 1e-8):
                raise NotImplementedError('Gradient and Hessian fixes for nonsinglet environment of Newton-descent CASSCF algorithm')
            mc = mc.newton ()
            E_CASSCF = mc.kernel(mc.mo_coeff, mc.ci)[0]
    assert (mc.converged)

    '''
    mc.conv_tol = 1e-12
    mc.ah_start_tol = 1e-10
    mc.ah_conv_tol = 1e-12
    E_CASSCF = mc.kernel(mc.mo_coeff, mc.ci)[0]
    if not mc.converged:
        mc = mc.newton ()
        E_CASSCF = mc.kernel(mc.mo_coeff, mc.ci)[0]
    #assert (mc.converged)
    '''
    
    # Get twoRDM + oneRDM. cs: MC-SCF core, as: MC-SCF active space
    # I'm going to need to keep some representation of the active-space orbitals

    # Symmetry align if possible
    oneRDM_amo, twoRDM_amo = mc.fcisolver.make_rdm12 (mc.ci, mc.ncas, mc.nelecas)
    fock_imp = mc.get_fock ()
    mc.mo_coeff[:,:norbs_cmo] = frag.align_imporbs_symm (mc.mo_coeff[:,:norbs_cmo], sorting_metric=fock_imp, sort_vecs=1, orbital_type='optimized inactive', mol=mol)[0]
    mc.mo_coeff[:,norbs_cmo:norbs_occ], umat = frag.align_imporbs_symm (mc.mo_coeff[:,norbs_cmo:norbs_occ],
        sorting_metric=oneRDM_amo, sort_vecs=-1, orbital_type='optimized active', mol=mol)
    mc.mo_coeff[:,norbs_occ:] = frag.align_imporbs_symm (mc.mo_coeff[:,norbs_occ:], sorting_metric=fock_imp, sort_vecs=1, orbital_type='optimized external', mol=mol)[0]
    if frag.enforce_symmetry:
        amo2imp = mc.mo_coeff[:,norbs_cmo:norbs_occ].conjugate ().T
        mc.mo_coeff = cleanup_subspace_symmetry (mc.mo_coeff, mol.symm_orb)
        umat = umat @ (amo2imp @ mc.mo_coeff[:,norbs_cmo:norbs_occ])
        err_symm = measure_subspace_blockbreaking (mc.mo_coeff, mol.symm_orb)
        err_orth = measure_basis_nonorthonormality (mc.mo_coeff)
        print ("Final symmetry error after cleanup = {}".format (err_symm))
        print ("Final orthonormality error after cleanup = {}".format (err_orth))
    mc.ci = transform_ci_for_orbital_rotation (mc.ci, CASorb, CASe, umat)

    # Cache stuff
    imp2mo = mc.mo_coeff #mc.cas_natorb()[0]
    loc2mo = np.dot (frag.loc2imp, imp2mo)
    imp2amo = imp2mo[:,norbs_cmo:norbs_occ]
    loc2amo = loc2mo[:,norbs_cmo:norbs_occ]
    frag.imp_cache = [mc.mo_coeff, mc.ci]
    frag.ci_as = mc.ci
    frag.ci_as_orb = loc2amo.copy ()
    t_end = time.time()

    # oneRDM
    oneRDM_imp = mc.make_rdm1 ()

    # twoCDM
    oneRDM_amo, twoRDM_amo = mc.fcisolver.make_rdm12 (mc.ci, mc.ncas, mc.nelecas)
    oneRDMs_amo = np.stack (mc.fcisolver.make_rdm1s (mc.ci, mc.ncas, mc.nelecas), axis=0)
    oneSDM_amo = oneRDMs_amo[0] - oneRDMs_amo[1] if frag.target_MS >= 0 else oneRDMs_amo[1] - oneRDMs_amo[0]
    oneSDM_imp = represent_operator_in_basis (oneSDM_amo, imp2amo.conjugate ().T)
    print ("Norm of spin density: {}".format (linalg.norm (oneSDM_amo)))
    # Note that I do _not_ do the *real* cumulant decomposition; I do one assuming oneSDM_amo = 0.
    # This is fine as long as I keep it consistent, since it is only in the orbital gradients for this impurity that
    # the spin density matters. But it has to stay consistent!
    twoCDM_amo = get_2CDM_from_2RDM (twoRDM_amo, oneRDM_amo)
    twoCDM_imp = represent_operator_in_basis (twoCDM_amo, imp2amo.conjugate ().T)
    print('Impurity CASSCF energy (incl chempot): {}; spin multiplicity: {}; time to solve: {}'.format (E_CASSCF, spin_square (mc)[1], t_end - t_start))

    # Active-space RDM data
    frag.oneRDMas_loc  = symmetrize_tensor (represent_operator_in_basis (oneRDM_amo, loc2amo.conjugate ().T))
    frag.oneSDMas_loc  = symmetrize_tensor (represent_operator_in_basis (oneSDM_amo, loc2amo.conjugate ().T))
    frag.twoCDMimp_amo = twoCDM_amo
    frag.loc2mo  = loc2mo
    frag.loc2amo = loc2amo
    frag.E2_cum  = np.tensordot (ao2mo.restore (1, mc.get_h2eff (), mc.ncas), twoCDM_amo, axes=4) / 2
    frag.E2_cum += (mf.get_k (dm=oneSDM_imp) * oneSDM_imp).sum () / 4
    # The second line compensates for my incorrect cumulant decomposition. Anything to avoid changing the checkpoint files...

    # General impurity data
    frag.oneRDM_loc = frag.oneRDMfroz_loc + symmetrize_tensor (represent_operator_in_basis (oneRDM_imp, frag.imp2loc))
    frag.oneSDM_loc = frag.oneSDMfroz_loc + frag.oneSDMas_loc
    frag.twoCDM_imp = None # Experiment: this tensor is huge. Do I actually need to keep it? In principle, of course not.
    frag.E_imp      = E_CASSCF + np.einsum ('ab,ab->', chempot_imp, oneRDM_imp)

    return None
Beispiel #19
0
    def loc_ortho(self):

        #        ShouldBeI = np.dot( np.dot( self.ao2loc.T , self.mol.intor('cint1e_ovlp_sph') ) , self.ao2loc )
        ShouldBeI = represent_operator_in_basis(self.fullovlpao(), self.ao2loc)
        return np.linalg.norm(ShouldBeI - np.eye(ShouldBeI.shape[0]))
Beispiel #20
0
def get_ontop_pair_density (ot, rho, ao, oneCDMs, twoCDM_amo, ao2amo, deriv=0):
    r''' Pi(r) = i(r)*j(r)*k(r)*l(r)*g_ijkl / 2
               = rho[0](r)*rho[1](r) + i(r)*j(r)*k(r)*l(r)*l_ijkl / 2

        Args:
            ot : on-top pair density functional object
            rho : ndarray of shape (2,*,ngrids) 
                contains spin density [and derivatives] 
            ao : ndarray of shape (*, ngrids, nao)
                contains values of aos [and derivatives] 
            oneCDMs : ndarray of shape (2, nao, nao)
                contains spin-separated 1-RDM
            twoCDM_amo : ndarray of shape (mc.ncas, mc.ncas, mc.ncas, mc.ncas)
                contains spin-summed two-body cumulant density matrix in active space
            ao2amo : ndarray of shape (nao, ncas)
                molecular-orbital coefficients for active-space orbitals

        Kwargs:
            deriv : derivative order through which to calculate. Default is 0. 
                deriv > 1 not implemented

        Returns : ndarray of shape (*,ngrids)
            The on-top pair density and its derivatives if requested
            deriv = 0 : value (1d array)
            deriv = 1 : value, d/dx, d/dy, d/dz
            deriv = 2 : value, d/dx, d/dy, d/dz, d^2/d|r1-r2|^2_(r1=r2)
            

    '''
    assert (rho.ndim == ao.ndim), "rho.shape={0}; ao.shape={1}".format (rho.shape, ao.shape)

    # Fix dimensionality of rho and ao
    if rho.ndim == 2:
        rho = rho.reshape (rho.shape[0], 1, rho.shape[1])
        ao = ao.reshape (1, ao.shape[0], ao.shape[1])

    # Debug code for ultra-slow, ultra-high-memory but very safe implementation
    if ot.verbose > logger.DEBUG:
        twoRDM = represent_operator_in_basis (twoCDM_amo, ao2amo.conjugate ().T)
        twoRDM = get_2RDM_from_2CDM (twoRDM, oneCDMs)

    # First cumulant and derivatives (chain rule! product rule!)
    t0 = (time.clock (), time.time ())
    Pi = np.zeros_like (rho[0])
    Pi[0] = rho[0,0] * rho[1,0]
    if deriv > 0:
        assert (rho.shape[1] >= 4), rho.shape
        assert (ao.shape[0] >= 4), ao.shape
        for ideriv in range(1,4):
            Pi[ideriv] = rho[0,ideriv]*rho[1,0] + rho[0,0]*rho[1,ideriv]
    if deriv > 1:
        assert (rho.shape[1] >= 6), rho.shape
        assert (ao.shape[0] >= 10), ao.shape
        Pi[4] = -(rho[:,1:4].sum (0).conjugate () * rho[:,1:4].sum (0)).sum (0) / 4
        Pi[4] += rho[0,0]*(rho[1,4]/4 + rho[0,5]*2) 
        Pi[4] += rho[1,0]*(rho[0,4]/4 + rho[1,5]*2)
    t0 = logger.timer (ot, 'otpd first cumulant', *t0)

    # Second cumulant and derivatives (chain rule! product rule!)
    # np.multiply, np.sum, and np.tensordot are linked against compiled libraries with multithreading, but np.einsum is not
    # Therefore I abandon the use of np.einsum
    # ijkl, ai, aj, ak, al -> a
    grid2amo = np.tensordot (ao, ao2amo, axes=1) #np.einsum ('ijk,kl->ijl', ao, ao2amo)
    gridkern = np.zeros (grid2amo.shape + (grid2amo.shape[2],), dtype=grid2amo.dtype)
    gridkern[0] = grid2amo[0,:,:,np.newaxis] * grid2amo[0,:,np.newaxis,:]  # r_0ai,  r_0aj  -> r_0aij
    wrk0 = np.tensordot (gridkern[0], twoCDM_amo, axes=2)                  # r_0aij, P_ijkl -> P_0akl
    Pi[0] += (gridkern[0] * wrk0).sum ((1,2)) / 2                          # r_0aij, P_0aij -> P_0a
    t0 = logger.timer (ot, 'otpd second cumulant 0th derivative', *t0)
    if ot.verbose > logger.DEBUG:
        test_Pi = np.einsum ('ijkl,ai,aj,ak,al->a', twoRDM, ao[0], ao[0], ao[0], ao[0]) / 2
        logger.debug (ot, "Pi, |tensordot_formula - einsum_formula| = %s", linalg.norm (Pi[0] - test_Pi)) 
        t0 = logger.timer (ot, 'otpd 0th derivative debug'.format (ideriv), *t0)
    if deriv > 0:
        for ideriv in range (1, 4):
            # Fourfold tensor symmetry ijkl = klij = jilk = lkji & product rule -> factor of 4
            gridkern[ideriv] = grid2amo[ideriv,:,:,np.newaxis] * grid2amo[0,:,np.newaxis,:]    # r_1ai,  r_0aj  -> r_1aij
            Pi[ideriv] += (gridkern[ideriv] * wrk0).sum ((1,2)) * 2                            # r_1aij, P_0aij -> P_1a  
            t0 = logger.timer (ot, 'otpd second cumulant 1st derivative ({})'.format (ideriv), *t0)
            if ot.verbose > logger.DEBUG:
                test_Pi  = np.einsum ('ijkl,ai,aj,ak,al->a', twoRDM, ao[ideriv], ao[0], ao[0], ao[0]) / 2
                test_Pi += np.einsum ('ijkl,aj,ai,ak,al->a', twoRDM, ao[ideriv], ao[0], ao[0], ao[0]) / 2
                test_Pi += np.einsum ('ijkl,ak,ai,aj,al->a', twoRDM, ao[ideriv], ao[0], ao[0], ao[0]) / 2
                test_Pi += np.einsum ('ijkl,al,ai,aj,ak->a', twoRDM, ao[ideriv], ao[0], ao[0], ao[0]) / 2
                logger.debug (ot, "Pi derivative, |tensordot_formula - einsum_formula| = %s", linalg.norm (Pi[ideriv] - test_Pi)) 
                t0 = logger.timer (ot, 'otpd 1st derivative ({}) debug'.format (ideriv), *t0)
    if deriv > 1: # The fifth slot is allocated to the "off-top Laplacian," i.e., nabla_(r1-r2)^2 Pi(r1,r2)|(r1=r2) 
        # nabla_off^2 Pi = 1/2 d^ik_jl * ([nabla_r^2 phi_i] phi_j phi_k phi_l + {1 - p_jk - p_jl}[nabla_r phi_i . nabla_r phi_j] phi_k phi_l)
        # using four-fold symmetry a lot! be careful!
        if ot.verbose > logger.DEBUG:
            test2_Pi = Pi[4].copy ()
        XX, YY, ZZ = 4, 7, 9
        gridkern[4]  = grid2amo[[XX,YY,ZZ],:,:,np.newaxis].sum (0) * grid2amo[0,:,np.newaxis,:]    # r_2ai, r_0aj -> r_2aij
        gridkern[4] += (grid2amo[1:4,:,:,np.newaxis] * grid2amo[1:4,:,np.newaxis,:]).sum (0)       # r_1ai, r_1aj -> r_2aij
        wrk1 = np.tensordot (gridkern[1:4], twoCDM_amo, axes=2)                                    # r_1aij, P_ijkl -> P_1akl
        Pi[4] += (gridkern[4] * wrk0).sum ((1,2)) / 2                                              # r_2aij, P_0aij -> P_2a
        Pi[4] -= ((gridkern[1:4] + gridkern[1:4].transpose (0, 1, 3, 2)) * wrk1).sum ((0,2,3)) / 2 # r_1aij, P_1aij -> P_2a
        t0 = logger.timer (ot, 'otpd second cumulant off-top Laplacian', *t0)
        if ot.verbose > logger.DEBUG:
            X, Y, Z = 1, 2, 3
            test_Pi  = np.einsum ('ijkl,ai,aj,ak,al->a', twoRDM, ao[XX], ao[0], ao[0], ao[0]) / 2
            test_Pi += np.einsum ('ijkl,ai,aj,ak,al->a', twoRDM, ao[YY], ao[0], ao[0], ao[0]) / 2
            test_Pi += np.einsum ('ijkl,ai,aj,ak,al->a', twoRDM, ao[ZZ], ao[0], ao[0], ao[0]) / 2
            test_Pi += np.einsum ('ijkl,ai,aj,ak,al->a', twoRDM, ao[X], ao[X], ao[0], ao[0]) / 2
            test_Pi += np.einsum ('ijkl,ai,aj,ak,al->a', twoRDM, ao[Y], ao[Y], ao[0], ao[0]) / 2
            test_Pi += np.einsum ('ijkl,ai,aj,ak,al->a', twoRDM, ao[Z], ao[Z], ao[0], ao[0]) / 2
            test_Pi -= np.einsum ('ijkl,ai,aj,ak,al->a', twoRDM, ao[X], ao[0], ao[X], ao[0]) / 2
            test_Pi -= np.einsum ('ijkl,ai,aj,ak,al->a', twoRDM, ao[Y], ao[0], ao[Y], ao[0]) / 2
            test_Pi -= np.einsum ('ijkl,ai,aj,ak,al->a', twoRDM, ao[Z], ao[0], ao[Z], ao[0]) / 2
            test_Pi -= np.einsum ('ijkl,ai,aj,ak,al->a', twoRDM, ao[X], ao[0], ao[0], ao[X]) / 2
            test_Pi -= np.einsum ('ijkl,ai,aj,ak,al->a', twoRDM, ao[Y], ao[0], ao[0], ao[Y]) / 2
            test_Pi -= np.einsum ('ijkl,ai,aj,ak,al->a', twoRDM, ao[Z], ao[0], ao[0], ao[Z]) / 2
            logger.debug (ot, 'Pi off-top Laplacian, |tensordot formula - einsum_formula| = %s', linalg.norm (Pi[4] - test_Pi))

            test2_Pi += np.einsum ('ijkl,ai,aj,ak,al->a', twoCDM_amo, grid2amo[XX], grid2amo[0], grid2amo[0], grid2amo[0]) / 2
            test2_Pi += np.einsum ('ijkl,ai,aj,ak,al->a', twoCDM_amo, grid2amo[YY], grid2amo[0], grid2amo[0], grid2amo[0]) / 2
            test2_Pi += np.einsum ('ijkl,ai,aj,ak,al->a', twoCDM_amo, grid2amo[ZZ], grid2amo[0], grid2amo[0], grid2amo[0]) / 2
            test2_Pi += np.einsum ('ijkl,ai,aj,ak,al->a', twoCDM_amo, grid2amo[X], grid2amo[X], grid2amo[0], grid2amo[0]) / 2
            test2_Pi += np.einsum ('ijkl,ai,aj,ak,al->a', twoCDM_amo, grid2amo[Y], grid2amo[Y], grid2amo[0], grid2amo[0]) / 2
            test2_Pi += np.einsum ('ijkl,ai,aj,ak,al->a', twoCDM_amo, grid2amo[Z], grid2amo[Z], grid2amo[0], grid2amo[0]) / 2
            test2_Pi -= np.einsum ('ijkl,ai,aj,ak,al->a', twoCDM_amo, grid2amo[X], grid2amo[0], grid2amo[X], grid2amo[0]) / 2
            test2_Pi -= np.einsum ('ijkl,ai,aj,ak,al->a', twoCDM_amo, grid2amo[Y], grid2amo[0], grid2amo[Y], grid2amo[0]) / 2
            test2_Pi -= np.einsum ('ijkl,ai,aj,ak,al->a', twoCDM_amo, grid2amo[Z], grid2amo[0], grid2amo[Z], grid2amo[0]) / 2
            test2_Pi -= np.einsum ('ijkl,ai,aj,ak,al->a', twoCDM_amo, grid2amo[X], grid2amo[0], grid2amo[0], grid2amo[X]) / 2
            test2_Pi -= np.einsum ('ijkl,ai,aj,ak,al->a', twoCDM_amo, grid2amo[Y], grid2amo[0], grid2amo[0], grid2amo[Y]) / 2
            test2_Pi -= np.einsum ('ijkl,ai,aj,ak,al->a', twoCDM_amo, grid2amo[Z], grid2amo[0], grid2amo[0], grid2amo[Z]) / 2
            logger.debug (ot, 'Pi off-top Laplacian, testing second cumulant only |tensordot formula - einsum_formula| = %s', linalg.norm (Pi[4] - test2_Pi))
            
            t0 = logger.timer (ot, 'otpd off-top Laplacian debug', *t0)

    # Unfix dimensionality of rho, ao, and Pi
    if Pi.shape[0] == 1:
        Pi = Pi.reshape (Pi.shape[1])
        rho = rho.reshape (rho.shape[0], rho.shape[2])
        ao = ao.reshape (ao.shape[1], ao.shape[2])

    return Pi
Beispiel #21
0
def solve(frag, guess_1RDM, chempot_imp):

    t_start = time.time()

    # Augment OEI with the chemical potential
    OEI = frag.impham_OEI - chempot_imp

    # Get the RHF solution
    mol = gto.Mole()
    mol.spin = int(round(2 * frag.target_MS))
    mol.verbose = 0 if frag.mol_output is None else 4
    mol.output = frag.mol_output
    mol.build()
    mol.atom.append(('C', (0, 0, 0)))
    mol.nelectron = frag.nelec_imp
    mol.incore_anyway = True
    mf = scf.RHF(mol)
    mf.get_hcore = lambda *args: OEI
    mf.get_ovlp = lambda *args: np.eye(frag.norbs_imp)
    if frag.quasidirect:
        mf.get_jk = frag.impham_get_jk
    else:
        mf._eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)
    mf.__dict__.update(frag.mf_attr)
    mf.scf(guess_1RDM)
    if (mf.converged == False):
        mf = mf.newton()
        mf.kernel()

    # Instability check and repeat
    for i in range(frag.num_mf_stab_checks):
        new_mo = mf.stability()[0]
        guess_1RDM = reduce(np.dot,
                            (new_mo, np.diag(mf.mo_occ), new_mo.conjugate().T))
        mf = scf.RHF(mol)
        mf.get_hcore = lambda *args: OEI
        mf.get_ovlp = lambda *args: np.eye(frag.norbs_imp)
        if frag.quasidirect:
            mf.get_jk = frag.impham_get_jk
        else:
            mf._eri = ao2mo.restore(8, frag.impham_TEI, frag.norbs_imp)
        mf.scf(guess_1RDM)
        if (mf.converged == False):
            mf = mf.newton()
            mf.kernel()

    oneRDMimp_imp = mf.make_rdm1()
    print("Maximum distance between oneRDMimp_imp and guess_1RDM: {}".format(
        np.amax(np.abs(oneRDMimp_imp - guess_1RDM))))

    frag.oneRDM_loc = symmetrize_tensor(
        frag.oneRDMfroz_loc +
        represent_operator_in_basis(oneRDMimp_imp, frag.imp2loc))
    frag.twoCDM_imp = None
    frag.E_imp = frag.impham_CONST + mf.e_tot + np.einsum(
        'ab,ab->', oneRDMimp_imp, chempot_imp)
    frag.loc2mo = np.dot(frag.loc2imp, mf.mo_coeff)

    print("Time for impurity RHF: {} seconds".format(time.time() - t_start))

    return None