Ejemplo n.º 1
0
 def test_mo_1to1map(self):
     mol1 = gto.M(atom = '''O 0 0 0; O 0 0 1''', basis='6-31g')
     mol2 = gto.M(atom = '''O 0 0 0; O 0 0 1''', basis='ccpvdz')
     s = gto.intor_cross('cint1e_ovlp_sph', mol1, mol2)
     idx = mo_mapping.mo_1to1map(s)
     self.assertTrue(numpy.allclose(idx,
         [0, 1, 2, 3, 4, 5, 6, 7, 8, 14, 15, 16, 17, 18, 19, 20, 21, 22]))
Ejemplo n.º 2
0
 def test_mo_1to1map(self):
     mol1 = gto.M(atom = '''O 0 0 0; O 0 0 1''', basis='6-31g')
     mol2 = gto.M(atom = '''O 0 0 0; O 0 0 1''', basis='ccpvdz')
     s = gto.intor_cross('cint1e_ovlp_sph', mol1, mol2)
     idx = mo_mapping.mo_1to1map(s)
     self.assertTrue(numpy.allclose(idx,
         [0, 1, 2, 3, 4, 5, 6, 7, 8, 14, 15, 16, 17, 18, 19, 20, 21, 22]))
Ejemplo n.º 3
0
def cas_natorb(mc,
               mo_coeff=None,
               ci=None,
               eris=None,
               sort=False,
               casdm1=None,
               verbose=None,
               with_meta_lowdin=WITH_META_LOWDIN):
    '''Transform active orbitals to natrual orbitals, and update the CI wfn

    Args:
        mc : a CASSCF/CASCI object or RHF object

    Kwargs:
        sort : bool
            Sort natural orbitals wrt the occupancy.

    Returns:
        A tuple, the first item is natural orbitals, the second is updated CI
        coefficients, the third is the natural occupancy associated to the
        natural orbitals.
    '''
    from pyscf.lo import orth
    from pyscf.tools import dump_mat
    from pyscf.tools.mo_mapping import mo_1to1map
    if mo_coeff is None: mo_coeff = mc.mo_coeff
    if ci is None: ci = mc.ci
    log = logger.new_logger(mc, verbose)
    ncore = mc.ncore
    ncas = mc.ncas
    nocc = ncore + ncas
    nelecas = mc.nelecas
    if casdm1 is None:
        casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas)
    # orbital symmetry is reserved in this _eig call
    occ, ucas = mc._eig(-casdm1, ncore, nocc)
    if sort:
        casorb_idx = numpy.argsort(occ.round(9), kind='mergesort')
        occ = occ[casorb_idx]
        ucas = ucas[:, casorb_idx]

    occ = -occ
    mo_occ = numpy.zeros(mo_coeff.shape[1])
    mo_occ[:ncore] = 2
    mo_occ[ncore:nocc] = occ

    mo_coeff1 = mo_coeff.copy()
    mo_coeff1[:, ncore:nocc] = numpy.dot(mo_coeff[:, ncore:nocc], ucas)
    if getattr(mo_coeff, 'orbsym', None) is not None:
        orbsym = numpy.copy(mo_coeff.orbsym)
        if sort:
            orbsym[ncore:nocc] = orbsym[ncore:nocc][casorb_idx]
        mo_coeff1 = lib.tag_array(mo_coeff1, orbsym=orbsym)

    if isinstance(ci, numpy.ndarray):
        fcivec = fci.addons.transform_ci_for_orbital_rotation(
            ci, ncas, nelecas, ucas)
    elif isinstance(ci, (tuple, list)) and isinstance(ci[0], numpy.ndarray):
        # for state-average eigenfunctions
        fcivec = [
            fci.addons.transform_ci_for_orbital_rotation(
                x, ncas, nelecas, ucas) for x in ci
        ]
    else:
        log.info('FCI vector not available, call CASCI for wavefunction')
        mocas = mo_coeff1[:, ncore:nocc]
        hcore = mc.get_hcore()
        dm_core = numpy.dot(mo_coeff1[:, :ncore] * 2, mo_coeff1[:, :ncore].T)
        ecore = mc.energy_nuc()
        ecore += numpy.einsum('ij,ji', hcore, dm_core)
        h1eff = reduce(numpy.dot, (mocas.T, hcore, mocas))
        if getattr(eris, 'ppaa', None) is not None:
            ecore += eris.vhf_c[:ncore, :ncore].trace()
            h1eff += reduce(numpy.dot,
                            (ucas.T, eris.vhf_c[ncore:nocc, ncore:nocc], ucas))
            aaaa = ao2mo.restore(4, eris.ppaa[ncore:nocc, ncore:nocc, :, :],
                                 ncas)
            aaaa = ao2mo.incore.full(aaaa, ucas)
        else:
            if getattr(mc, 'with_df', None):
                raise NotImplementedError('cas_natorb for DFCASCI/DFCASSCF')
            corevhf = mc.get_veff(mc.mol, dm_core)
            ecore += numpy.einsum('ij,ji', dm_core, corevhf) * .5
            h1eff += reduce(numpy.dot, (mocas.T, corevhf, mocas))
            aaaa = ao2mo.kernel(mc.mol, mocas)

        # See label_symmetry_ function in casci_symm.py which initialize the
        # orbital symmetry information in fcisolver.  This orbital symmetry
        # labels should be reordered to match the sorted active space orbitals.
        if sort and getattr(mo_coeff1, 'orbsym', None) is not None:
            mc.fcisolver.orbsym = mo_coeff1.orbsym[ncore:nocc]

        max_memory = max(400, mc.max_memory - lib.current_memory()[0])
        e, fcivec = mc.fcisolver.kernel(h1eff,
                                        aaaa,
                                        ncas,
                                        nelecas,
                                        ecore=ecore,
                                        max_memory=max_memory,
                                        verbose=log)
        log.debug('In Natural orbital, CASCI energy = %s', e)

    if log.verbose >= logger.INFO:
        ovlp_ao = mc._scf.get_ovlp()
        # where_natorb gives the new locations of the natural orbitals
        where_natorb = mo_1to1map(ucas)
        log.debug('where_natorb %s', str(where_natorb))
        log.info('Natural occ %s', str(occ))
        if with_meta_lowdin:
            log.info(
                'Natural orbital (expansion on meta-Lowdin AOs) in CAS space')
            label = mc.mol.ao_labels()
            orth_coeff = orth.orth_ao(mc.mol, 'meta_lowdin', s=ovlp_ao)
            mo_cas = reduce(numpy.dot,
                            (orth_coeff.T, ovlp_ao, mo_coeff1[:, ncore:nocc]))
        else:
            log.info('Natural orbital (expansion on AOs) in CAS space')
            label = mc.mol.ao_labels()
            mo_cas = mo_coeff1[:, ncore:nocc]
        dump_mat.dump_rec(log.stdout, mo_cas, label, start=1)

        if mc._scf.mo_coeff is not None:
            s = reduce(numpy.dot, (mo_coeff1[:, ncore:nocc].T,
                                   mc._scf.get_ovlp(), mc._scf.mo_coeff))
            idx = numpy.argwhere(abs(s) > .4)
            for i, j in idx:
                log.info('<CAS-nat-orb|mo-hf>  %d  %d  %12.8f', ncore + i + 1,
                         j + 1, s[i, j])
    return mo_coeff1, fcivec, mo_occ
Ejemplo n.º 4
0
def cas_natorb(mc,
               mo_coeff=None,
               ci=None,
               eris=None,
               sort=False,
               casdm1=None,
               verbose=None):
    '''Transform active orbitals to natrual orbitals, and update the CI wfn

    Args:
        mc : a CASSCF/CASCI object or RHF object

    Kwargs:
        sort : bool
            Sort natural orbitals wrt the occupancy.

    Returns:
        A tuple, the first item is natural orbitals, the second is updated CI
        coefficients, the third is the natural occupancy associated to the
        natural orbitals.
    '''
    from pyscf.lo import orth
    from pyscf.tools import dump_mat
    from pyscf.tools.mo_mapping import mo_1to1map
    if isinstance(verbose, logger.Logger):
        log = verbose
    else:
        log = logger.Logger(mc.stdout, mc.verbose)
    if mo_coeff is None: mo_coeff = mc.mo_coeff
    if ci is None: ci = mc.ci
    ncore = mc.ncore
    ncas = mc.ncas
    nocc = ncore + ncas
    nelecas = mc.nelecas
    if casdm1 is None:
        casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas)
    # orbital symmetry is reserved in this _eig call
    occ, ucas = mc._eig(-casdm1, ncore, nocc)
    if sort:
        casorb_idx = numpy.argsort(occ)
        occ = occ[casorb_idx]
        ucas = ucas[:, casorb_idx]


# restore phase
# where_natorb gives the location of the natural orbital for the input cas
# orbitals.  gen_strings4orblist map thes sorted strings (on CAS orbital) to
# the unsorted determinant strings (on natural orbital). e.g.  (3o,2e) system
#       CAS orbital      1  2  3
#       natural orbital  3  1  2        <= by mo_1to1map
#       CASorb-strings   0b011, 0b101, 0b110
#                    ==  (1,2), (1,3), (2,3)
#       natorb-strings   (3,1), (3,2), (1,2)
#                    ==  0B101, 0B110, 0B011    <= by gen_strings4orblist
# then argsort to translate the string representation to the address
#       [2(=0B011), 0(=0B101), 1(=0B110)]
# to indicate which CASorb-strings address to be loaded in each natorb-strings slot
    where_natorb = mo_1to1map(ucas)
    for i, k in enumerate(where_natorb):
        if ucas[i, k] < 0:
            ucas[:, k] *= -1

    occ = -occ
    mo_occ = numpy.zeros(mo_coeff.shape[1])
    mo_occ[:ncore] = 2
    mo_occ[ncore:nocc] = occ

    mo_coeff1 = mo_coeff.copy()
    mo_coeff1[:, ncore:nocc] = numpy.dot(mo_coeff[:, ncore:nocc], ucas)
    if hasattr(mo_coeff, 'orbsym'):
        orbsym = numpy.copy(mo_coeff.orbsym)
        if sort:
            orbsym[ncore:nocc] = orbsym[ncore:nocc][casorb_idx]
        mo_coeff1 = lib.tag_array(mo_coeff1, orbsym=orbsym)

    if isinstance(ci, numpy.ndarray):
        fcivec = fci.addons.transform_ci_for_orbital_rotation(
            ci, ncas, nelecas, ucas)
    elif isinstance(ci, (tuple, list)) and isinstance(ci[0], numpy.ndarray):
        # for state-average eigenfunctions
        fcivec = [
            fci.addons.transform_ci_for_orbital_rotation(
                x, ncas, nelecas, ucas) for x in ci
        ]
    else:
        log.info('FCI vector not available, call CASCI for wavefunction')
        mocas = mo_coeff1[:, ncore:nocc]
        hcore = mc.get_hcore()
        dm_core = numpy.dot(mo_coeff1[:, :ncore] * 2, mo_coeff1[:, :ncore].T)
        ecore = mc._scf.energy_nuc()
        ecore += numpy.einsum('ij,ji', hcore, dm_core)
        h1eff = reduce(numpy.dot, (mocas.T, hcore, mocas))
        if eris is not None and hasattr(eris, 'ppaa'):
            ecore += eris.vhf_c[:ncore, :ncore].trace()
            h1eff += reduce(numpy.dot,
                            (ucas.T, eris.vhf_c[ncore:nocc, ncore:nocc], ucas))
            aaaa = ao2mo.restore(4, eris.ppaa[ncore:nocc, ncore:nocc, :, :],
                                 ncas)
            aaaa = ao2mo.incore.full(aaaa, ucas)
        else:
            corevhf = mc.get_veff(mc.mol, dm_core)
            ecore += numpy.einsum('ij,ji', dm_core, corevhf) * .5
            h1eff += reduce(numpy.dot, (mocas.T, corevhf, mocas))
            aaaa = ao2mo.kernel(mc.mol, mocas)

        # See label_symmetry_ function in casci_symm.py which initialize the
        # orbital symmetry information in fcisolver.  This orbital symmetry
        # labels should be reordered to match the sorted active space orbitals.
        if hasattr(mo_coeff1, 'orbsym') and sort:
            mc.fcisolver.orbsym = mo_coeff1.orbsym[ncore:nocc]

        max_memory = max(400, mc.max_memory - lib.current_memory()[0])
        e, fcivec = mc.fcisolver.kernel(h1eff,
                                        aaaa,
                                        ncas,
                                        nelecas,
                                        ecore=ecore,
                                        max_memory=max_memory,
                                        verbose=log)
        log.debug('In Natural orbital, CASCI energy = %.12g', e)

    if log.verbose >= logger.INFO:
        ovlp_ao = mc._scf.get_ovlp()
        log.debug('where_natorb %s', str(where_natorb))
        log.info('Natural occ %s', str(occ))
        log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space')
        label = mc.mol.ao_labels()
        orth_coeff = orth.orth_ao(mc.mol, 'meta_lowdin', s=ovlp_ao)
        mo_cas = reduce(numpy.dot,
                        (orth_coeff.T, ovlp_ao, mo_coeff1[:, ncore:nocc]))
        dump_mat.dump_rec(log.stdout, mo_cas, label, start=1)

        if mc._scf.mo_coeff is not None:
            s = reduce(numpy.dot, (mo_coeff1[:, ncore:nocc].T,
                                   mc._scf.get_ovlp(), mc._scf.mo_coeff))
            idx = numpy.argwhere(abs(s) > .4)
            for i, j in idx:
                log.info('<CAS-nat-orb|mo-hf>  %d  %d  %12.8f', ncore + i + 1,
                         j + 1, s[i, j])
    return mo_coeff1, fcivec, mo_occ
Ejemplo n.º 5
0
def kernel(localizer, mo_coeff=None, callback=None, verbose=None):
    from pyscf.tools import mo_mapping
    if mo_coeff is not None:
        localizer.mo_coeff = numpy.asarray(mo_coeff, order='C')
    if localizer.mo_coeff.shape[1] <= 1:
        return localizer.mo_coeff

    if localizer.verbose >= logger.WARN:
        localizer.check_sanity()
    localizer.dump_flags()

    cput0 = (time.clock(), time.time())
    log = logger.new_logger(localizer, verbose=verbose)

    if localizer.conv_tol_grad is None:
        conv_tol_grad = numpy.sqrt(localizer.conv_tol * .1)
        log.info('Set conv_tol_grad to %g', conv_tol_grad)
    else:
        conv_tol_grad = localizer.conv_tol_grad

    if mo_coeff is None:
        if getattr(localizer, 'mol', None) and localizer.mol.natm == 0:
            # For customized Hamiltonian
            u0 = localizer.get_init_guess('random')
        else:
            u0 = localizer.get_init_guess(localizer.init_guess)
    else:
        u0 = localizer.get_init_guess(None)

    rotaiter = ciah.rotate_orb_cc(localizer, u0, conv_tol_grad, verbose=log)
    u, g_orb, stat = next(rotaiter)
    cput1 = log.timer('initializing CIAH', *cput0)

    tot_kf = stat.tot_kf
    tot_hop = stat.tot_hop
    conv = False
    e_last = 0
    for imacro in range(localizer.max_cycle):
        norm_gorb = numpy.linalg.norm(g_orb)
        u0 = lib.dot(u0, u)
        e = localizer.cost_function(u0)
        e_last, de = e, e - e_last

        log.info('macro= %d  f(x)= %.14g  delta_f= %g  |g|= %g  %d KF %d Hx',
                 imacro + 1, e, de, norm_gorb, stat.tot_kf + 1, stat.tot_hop)
        cput1 = log.timer('cycle= %d' % (imacro + 1), *cput1)

        if (norm_gorb < conv_tol_grad and abs(de) < localizer.conv_tol):
            conv = True

        if callable(callback):
            callback(locals())

        if conv:
            break

        u, g_orb, stat = rotaiter.send(u0)
        tot_kf += stat.tot_kf
        tot_hop += stat.tot_hop

    rotaiter.close()
    log.info('macro X = %d  f(x)= %.14g  |g|= %g  %d intor %d KF %d Hx',
             imacro + 1, e, norm_gorb, (imacro + 1) * 2, tot_kf + imacro + 1,
             tot_hop)
    # Sort the localized orbitals, to make each localized orbitals as close as
    # possible to the corresponding input orbitals
    sorted_idx = mo_mapping.mo_1to1map(u0)
    localizer.mo_coeff = lib.dot(localizer.mo_coeff, u0[:, sorted_idx])
    return localizer.mo_coeff
Ejemplo n.º 6
0
def cas_natorb(mc, mo_coeff=None, ci=None, eris=None, sort=False,
               casdm1=None, verbose=None):
    '''Transform active orbitals to natrual orbitals, and update the CI wfn

    Args:
        mc : a CASSCF/CASCI object or RHF object

    Kwargs:
        sort : bool
            Sort natural orbitals wrt the occupancy.  Be careful with this
            option since the resultant natural orbitals might have the
            different symmetry to the irreps indicated by CASSCF.orbsym

    Returns:
        A tuple, the first item is natural orbitals, the second is updated CI
        coefficients, the third is the natural occupancy associated to the
        natural orbitals.
    '''
    from pyscf.lo import orth
    from pyscf.tools import dump_mat
    from pyscf.tools.mo_mapping import mo_1to1map
    if isinstance(verbose, logger.Logger):
        log = verbose
    else:
        log = logger.Logger(mc.stdout, mc.verbose)
    if mo_coeff is None: mo_coeff = mc.mo_coeff
    if ci is None: ci = mc.ci
    ncore = mc.ncore
    ncas = mc.ncas
    nocc = ncore + ncas
    nelecas = mc.nelecas
    if casdm1 is None:
        casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas)
    # orbital symmetry is reserved in this _eig call
    occ, ucas = mc._eig(-casdm1, ncore, nocc)
    if sort:
        idx = numpy.argsort(occ)
        occ = occ[idx]
        ucas = ucas[:,idx]
# restore phase
# where_natorb gives the location of the natural orbital for the input cas
# orbitals.  gen_strings4orblist map thes sorted strings (on CAS orbital) to
# the unsorted determinant strings (on natural orbital). e.g.  (3o,2e) system
#       CAS orbital      1  2  3
#       natural orbital  3  1  2        <= by mo_1to1map
#       CASorb-strings   0b011, 0b101, 0b110
#                    ==  (1,2), (1,3), (2,3)
#       natorb-strings   (3,1), (3,2), (1,2)
#                    ==  0B101, 0B110, 0B011    <= by gen_strings4orblist
# then argsort to translate the string representation to the address
#       [2(=0B011), 0(=0B101), 1(=0B110)]
# to indicate which CASorb-strings address to be loaded in each natorb-strings slot
    where_natorb = mo_1to1map(ucas)
    for i, k in enumerate(where_natorb):
        if ucas[i,k] < 0:
            ucas[:,k] *= -1

    occ = -occ
    mo_occ = numpy.zeros(mo_coeff.shape[1])
    mo_occ[:ncore] = 2
    mo_occ[ncore:nocc] = occ

    if isinstance(ci, numpy.ndarray):
        fcivec = fci.addons.transform_ci_for_orbital_rotation(ci, ncas, nelecas, ucas)
    elif isinstance(ci, (tuple, list)) and isinstance(ci[0], numpy.ndarray):
        # for state-average eigenfunctions
        fcivec = [fci.addons.transform_ci_for_orbital_rotation(x, ncas, nelecas, ucas)
                  for x in ci]
    else:
        log.info('FCI vector not available, call CASCI for wavefunction')
        mocas = mo_coeff1[:,ncore:nocc]
        h1eff = reduce(numpy.dot, (mocas.T, mc.get_hcore(), mocas))
        if eris is not None and hasattr(eris, 'ppaa'):
            h1eff += reduce(numpy.dot, (ucas.T, eris.vhf_c[ncore:nocc,ncore:nocc], ucas))
            aaaa = ao2mo.restore(4, eris.ppaa[ncore:nocc,ncore:nocc,:,:], ncas)
            aaaa = ao2mo.incore.full(aaaa, ucas)
        else:
            dm_core = numpy.dot(mo_coeff[:,:ncore]*2, mo_coeff[:,:ncore].T)
            vj, vk = mc._scf.get_jk(mc.mol, dm_core)
            h1eff += reduce(numpy.dot, (mocas.T, vj-vk*.5, mocas))
            aaaa = ao2mo.kernel(mc.mol, mocas)
        max_memory = max(400, mc.max_memory-lib.current_memory()[0])
        e_cas, fcivec = mc.fcisolver.kernel(h1eff, aaaa, ncas, nelecas,
                                            max_memory=max_memory, verbose=log)
        log.debug('In Natural orbital, CI energy = %.12g', e_cas)

    mo_coeff1 = mo_coeff.copy()
    mo_coeff1[:,ncore:nocc] = numpy.dot(mo_coeff[:,ncore:nocc], ucas)
    if log.verbose >= logger.INFO:
        ovlp_ao = mc._scf.get_ovlp()
        log.debug('where_natorb %s', str(where_natorb))
        log.info('Natural occ %s', str(occ))
        log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space')
        label = mc.mol.spheric_labels(True)
        orth_coeff = orth.orth_ao(mc.mol, 'meta_lowdin', s=ovlp_ao)
        mo_cas = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff1[:,ncore:nocc]))
        dump_mat.dump_rec(log.stdout, mo_cas, label, start=1)

        if mc._scf.mo_coeff is not None:
            s = reduce(numpy.dot, (mo_coeff1[:,ncore:nocc].T,
                                   mc._scf.get_ovlp(), mc._scf.mo_coeff))
            idx = numpy.argwhere(abs(s)>.4)
            for i,j in idx:
                log.info('<CAS-nat-orb|mo-hf>  %d  %d  %12.8f',
                         ncore+i+1, j+1, s[i,j])
    return mo_coeff1, fcivec, mo_occ
Ejemplo n.º 7
0
def cas_natorb(mc, mo_coeff=None, ci=None, eris=None, sort=False, casdm1=None, verbose=None):
    """Transform active orbitals to natrual orbitals, and update the CI wfn

    Args:
        mc : a CASSCF/CASCI object or RHF object

    Kwargs:
        sort : bool
            Sort natural orbitals wrt the occupancy.  Be careful with this
            option since the resultant natural orbitals might have the
            different symmetry to the irreps indicated by CASSCF.orbsym

    Returns:
        A tuple, the first item is natural orbitals, the second is updated CI
        coefficients, the third is the natural occupancy associated to the
        natural orbitals.
    """
    from pyscf.mcscf import mc_ao2mo
    from pyscf.tools import dump_mat

    if isinstance(verbose, logger.Logger):
        log = verbose
    else:
        log = logger.Logger(mc.stdout, mc.verbose)
    if mo_coeff is None:
        mo_coeff = mc.mo_coeff
    if ci is None:
        ci = mc.ci
    ncore = mc.ncore
    ncas = mc.ncas
    nocc = ncore + ncas
    nelecas = mc.nelecas
    if casdm1 is None:
        casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas)
    occ, ucas = mc._eig(-casdm1, ncore, nocc)
    if sort:
        idx = numpy.argsort(occ)
        occ = occ[idx]
        ucas = ucas[:, idx]
        if hasattr(mc, "orbsym"):  # for casci_symm
            mc.orbsym[ncore:nocc] = mc.orbsym[ncore:nocc][idx]
            mc.fcisolver.orbsym = mc.orbsym[ncore:nocc]

    occ = -occ

    # where_natorb gives the location of the natural orbital for the input cas
    # orbitals.  gen_strings4orblist map thes sorted strings (on CAS orbital) to
    # the unsorted determinant strings (on natural orbital). e.g.  (3o,2e) system
    #       CAS orbital      1  2  3
    #       natural orbital  3  1  2        <= by mo_1to1map
    #       CASorb-strings   0b011, 0b101, 0b110
    #                    ==  (1,2), (1,3), (2,3)
    #       natorb-strings   (3,1), (3,2), (1,2)
    #                    ==  0B101, 0B110, 0B011    <= by gen_strings4orblist
    # then argsort to translate the string representation to the address
    #       [2(=0B011), 0(=0B101), 1(=0B110)]
    # to indicate which CASorb-strings address to be loaded in each natorb-strings slot
    where_natorb = mo_1to1map(ucas)
    # guide_stringsa = fci.cistring.gen_strings4orblist(where_natorb, nelecas[0])
    # guide_stringsb = fci.cistring.gen_strings4orblist(where_natorb, nelecas[1])
    # old_det_idxa = numpy.argsort(guide_stringsa)
    # old_det_idxb = numpy.argsort(guide_stringsb)
    # ci0 = ci[old_det_idxa[:,None],old_det_idxb]
    if isinstance(ci, numpy.ndarray):
        ci0 = fci.addons.reorder(ci, nelecas, where_natorb)
    elif isinstance(ci, (tuple, list)) and isinstance(ci[0], numpy.ndarray):
        # for state-average eigenfunctions
        ci0 = [fci.addons.reorder(x, nelecas, where_natorb) for x in ci]
    else:
        log.info("FCI vector not available, so not using old wavefunction as initial guess")
        ci0 = None

    # restore phase, to ensure the reordered ci vector is the correct initial guess
    for i, k in enumerate(where_natorb):
        if ucas[i, k] < 0:
            ucas[:, k] *= -1
    mo_coeff1 = mo_coeff.copy()
    mo_coeff1[:, ncore:nocc] = numpy.dot(mo_coeff[:, ncore:nocc], ucas)
    if log.verbose >= logger.INFO:
        log.debug("where_natorb %s", str(where_natorb))
        log.info("Natural occ %s", str(occ))
        log.info("Natural orbital in CAS space")
        label = mc.mol.spheric_labels(True)
        dump_mat.dump_rec(log.stdout, mo_coeff1[:, ncore:nocc], label, start=1)

        if mc._scf.mo_coeff is not None:
            s = reduce(numpy.dot, (mo_coeff1[:, ncore:nocc].T, mc._scf.get_ovlp(), mc._scf.mo_coeff))
            idx = numpy.argwhere(abs(s) > 0.4)
            for i, j in idx:
                log.info("<CAS-nat-orb|mo-hf>  %d  %d  %12.8f", ncore + i + 1, j + 1, s[i, j])

    mocas = mo_coeff1[:, ncore:nocc]
    h1eff = reduce(numpy.dot, (mocas.T, mc.get_hcore(), mocas))
    if eris is not None and hasattr(eris, "ppaa"):
        h1eff += reduce(numpy.dot, (ucas.T, eris.vhf_c[ncore:nocc, ncore:nocc], ucas))
        aaaa = ao2mo.restore(4, eris.ppaa[ncore:nocc, ncore:nocc, :, :], ncas)
        aaaa = ao2mo.incore.full(aaaa, ucas)
    else:
        dm_core = numpy.dot(mo_coeff[:, :ncore] * 2, mo_coeff[:, :ncore].T)
        vj, vk = mc._scf.get_jk(mc.mol, dm_core)
        h1eff += reduce(numpy.dot, (mocas.T, vj - vk * 0.5, mocas))
        aaaa = ao2mo.kernel(mc.mol, mocas)
    e_cas, fcivec = mc.fcisolver.kernel(h1eff, aaaa, ncas, nelecas, ci0=ci0)
    log.debug("In Natural orbital, CI energy = %.12g", e_cas)
    return mo_coeff1, fcivec, occ
Ejemplo n.º 8
0
def cas_natorb(mc, mo_coeff=None, ci=None, eris=None, sort=False,
               casdm1=None, verbose=None, with_meta_lowdin=WITH_META_LOWDIN):
    '''Transform active orbitals to natrual orbitals, and update the CI wfn

    Args:
        mc : a CASSCF/CASCI object or RHF object

    Kwargs:
        sort : bool
            Sort natural orbitals wrt the occupancy.

    Returns:
        A tuple, the first item is natural orbitals, the second is updated CI
        coefficients, the third is the natural occupancy associated to the
        natural orbitals.
    '''
    from pyscf.lo import orth
    from pyscf.tools import dump_mat
    from pyscf.tools.mo_mapping import mo_1to1map
    if mo_coeff is None: mo_coeff = mc.mo_coeff
    if ci is None: ci = mc.ci
    log = logger.new_logger(mc, verbose)
    ncore = mc.ncore
    ncas = mc.ncas
    nocc = ncore + ncas
    nelecas = mc.nelecas
    if casdm1 is None:
        casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas)
    # orbital symmetry is reserved in this _eig call
    occ, ucas = mc._eig(-casdm1, ncore, nocc)
    if sort:
        casorb_idx = numpy.argsort(occ.round(9), kind='mergesort')
        occ = occ[casorb_idx]
        ucas = ucas[:,casorb_idx]

    occ = -occ
    mo_occ = numpy.zeros(mo_coeff.shape[1])
    mo_occ[:ncore] = 2
    mo_occ[ncore:nocc] = occ

    mo_coeff1 = mo_coeff.copy()
    mo_coeff1[:,ncore:nocc] = numpy.dot(mo_coeff[:,ncore:nocc], ucas)
    if getattr(mo_coeff, 'orbsym', None) is not None:
        orbsym = numpy.copy(mo_coeff.orbsym)
        if sort:
            orbsym[ncore:nocc] = orbsym[ncore:nocc][casorb_idx]
        mo_coeff1 = lib.tag_array(mo_coeff1, orbsym=orbsym)

    if isinstance(ci, numpy.ndarray):
        fcivec = fci.addons.transform_ci_for_orbital_rotation(ci, ncas, nelecas, ucas)
    elif isinstance(ci, (tuple, list)) and isinstance(ci[0], numpy.ndarray):
        # for state-average eigenfunctions
        fcivec = [fci.addons.transform_ci_for_orbital_rotation(x, ncas, nelecas, ucas)
                  for x in ci]
    else:
        log.info('FCI vector not available, call CASCI for wavefunction')
        mocas = mo_coeff1[:,ncore:nocc]
        hcore = mc.get_hcore()
        dm_core = numpy.dot(mo_coeff1[:,:ncore]*2, mo_coeff1[:,:ncore].T)
        ecore = mc.energy_nuc()
        ecore+= numpy.einsum('ij,ji', hcore, dm_core)
        h1eff = reduce(numpy.dot, (mocas.T, hcore, mocas))
        if getattr(eris, 'ppaa', None) is not None:
            ecore += eris.vhf_c[:ncore,:ncore].trace()
            h1eff += reduce(numpy.dot, (ucas.T, eris.vhf_c[ncore:nocc,ncore:nocc], ucas))
            aaaa = ao2mo.restore(4, eris.ppaa[ncore:nocc,ncore:nocc,:,:], ncas)
            aaaa = ao2mo.incore.full(aaaa, ucas)
        else:
            if getattr(mc, 'with_df', None):
                raise NotImplementedError('cas_natorb for DFCASCI/DFCASSCF')
            corevhf = mc.get_veff(mc.mol, dm_core)
            ecore += numpy.einsum('ij,ji', dm_core, corevhf) * .5
            h1eff += reduce(numpy.dot, (mocas.T, corevhf, mocas))
            aaaa = ao2mo.kernel(mc.mol, mocas)

        # See label_symmetry_ function in casci_symm.py which initialize the
        # orbital symmetry information in fcisolver.  This orbital symmetry
        # labels should be reordered to match the sorted active space orbitals.
        if sort and getattr(mo_coeff1, 'orbsym', None) is not None:
            mc.fcisolver.orbsym = mo_coeff1.orbsym[ncore:nocc]

        max_memory = max(400, mc.max_memory-lib.current_memory()[0])
        e, fcivec = mc.fcisolver.kernel(h1eff, aaaa, ncas, nelecas, ecore=ecore,
                                        max_memory=max_memory, verbose=log)
        log.debug('In Natural orbital, CASCI energy = %s', e)

    if log.verbose >= logger.INFO:
        ovlp_ao = mc._scf.get_ovlp()
        # where_natorb gives the new locations of the natural orbitals
        where_natorb = mo_1to1map(ucas)
        log.debug('where_natorb %s', str(where_natorb))
        log.info('Natural occ %s', str(occ))
        if with_meta_lowdin:
            log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space')
            label = mc.mol.ao_labels()
            orth_coeff = orth.orth_ao(mc.mol, 'meta_lowdin', s=ovlp_ao)
            mo_cas = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff1[:,ncore:nocc]))
        else:
            log.info('Natural orbital (expansion on AOs) in CAS space')
            label = mc.mol.ao_labels()
            mo_cas = mo_coeff1[:,ncore:nocc]
        dump_mat.dump_rec(log.stdout, mo_cas, label, start=1)

        if mc._scf.mo_coeff is not None:
            s = reduce(numpy.dot, (mo_coeff1[:,ncore:nocc].T,
                                   mc._scf.get_ovlp(), mc._scf.mo_coeff))
            idx = numpy.argwhere(abs(s)>.4)
            for i,j in idx:
                log.info('<CAS-nat-orb|mo-hf>  %d  %d  %12.8f',
                         ncore+i+1, j+1, s[i,j])
    return mo_coeff1, fcivec, mo_occ
Ejemplo n.º 9
0
def symmetrize_space(mol, mo, s=None, check=True):
    '''Symmetrize the given orbital space.

    This function is different to the :func:`symmetrize_orb`:  In this function,
    the given orbitals are mixed to reveal the symmtery; :func:`symmetrize_orb`
    projects out non-symmetric components for each orbital.

    Args:
        mo : 2D float array
            The orbital space to symmetrize

    Kwargs:
        s : 2D float array
            Overlap matrix.  If not given, overlap is computed with the input mol.

    Returns:
        2D orbital coefficients

    Examples:

    >>> from pyscf import gto, symm, scf
    >>> mol = gto.M(atom = 'C  0  0  0; H  1  1  1; H -1 -1  1; H  1 -1 -1; H -1  1 -1',
    ...             basis = 'sto3g')
    >>> mf = scf.RHF(mol).run()
    >>> mol.build(0, 0, symmetry='D2')
    >>> mo = symm.symmetrize_space(mol, mf.mo_coeff)
    >>> print(symm.label_orb_symm(mol, mol.irrep_name, mol.symm_orb, mo))
    ['A', 'A', 'A', 'B1', 'B1', 'B2', 'B2', 'B3', 'B3']
    '''
    from pyscf.tools import mo_mapping
    if s is None:
        s = mol.intor_symmetric('cint1e_ovlp_sph')
    nmo = mo.shape[1]
    mo_s = numpy.dot(mo.T, s)
    if check:
        assert (numpy.allclose(numpy.dot(mo_s, mo), numpy.eye(nmo)))
    mo1 = []
    for i, csym in enumerate(mol.symm_orb):
        moso = numpy.dot(mo_s, csym)
        ovlpso = reduce(numpy.dot, (csym.T, s, csym))

        # excluding orbitals which are already symmetrized
        try:
            diag = numpy.einsum('ik,ki->i', moso,
                                lib.cho_solve(ovlpso, moso.T))
        except:
            ovlpso[numpy.diag_indices(csym.shape[1])] += 1e-12
            diag = numpy.einsum('ik,ki->i', moso,
                                lib.cho_solve(ovlpso, moso.T))
        idx = abs(1 - diag) < 1e-8
        orb_exclude = mo[:, idx]
        mo1.append(orb_exclude)
        moso1 = moso[~idx]
        dm = numpy.dot(moso1.T, moso1)

        if dm.trace() > 1e-8:
            e, u = scipy.linalg.eigh(dm, ovlpso)
            mo1.append(numpy.dot(csym, u[:, abs(1 - e) < 1e-6]))
    mo1 = numpy.hstack(mo1)
    if mo1.shape[1] != nmo:
        raise ValueError('The input orbital space is not symmetrized.\n It is '
                         'probably because the input mol and orbitals are of '
                         'different orientation.')
    snorm = numpy.linalg.norm(
        reduce(numpy.dot, (mo1.T, s, mo1)) - numpy.eye(nmo))
    if check and snorm > 1e-6:
        raise ValueError('Orbitals are not orthogonalized')
    idx = mo_mapping.mo_1to1map(reduce(numpy.dot, (mo.T, s, mo1)))
    return mo1[:, idx]
Ejemplo n.º 10
0
def symmetrize_space(mol,
                     mo,
                     s=None,
                     check=getattr(__config__,
                                   'symm_addons_symmetrize_space_check', True),
                     tol=getattr(__config__,
                                 'symm_addons_symmetrize_space_tol', 1e-7)):
    '''Symmetrize the given orbital space.

    This function is different to the :func:`symmetrize_orb`:  In this function,
    the given orbitals are mixed to reveal the symmtery; :func:`symmetrize_orb`
    projects out non-symmetric components for each orbital.

    Args:
        mo : 2D float array
            The orbital space to symmetrize

    Kwargs:
        s : 2D float array
            Overlap matrix.  If not given, overlap is computed with the input mol.

    Returns:
        2D orbital coefficients

    Examples:

    >>> from pyscf import gto, symm, scf
    >>> mol = gto.M(atom = 'C  0  0  0; H  1  1  1; H -1 -1  1; H  1 -1 -1; H -1  1 -1',
    ...             basis = 'sto3g')
    >>> mf = scf.RHF(mol).run()
    >>> mol.build(0, 0, symmetry='D2')
    >>> mo = symm.symmetrize_space(mol, mf.mo_coeff)
    >>> print(symm.label_orb_symm(mol, mol.irrep_name, mol.symm_orb, mo))
    ['A', 'A', 'A', 'B1', 'B1', 'B2', 'B2', 'B3', 'B3']
    '''
    from pyscf.tools import mo_mapping
    if s is None:
        s = mol.intor_symmetric('int1e_ovlp')
    nmo = mo.shape[1]
    s_mo = numpy.dot(s, mo)
    if check and abs(numpy.dot(mo.conj().T, s_mo) -
                     numpy.eye(nmo)).max() > tol:
        raise ValueError('Orbitals are not orthogonalized')

    mo1 = []
    for i, csym in enumerate(mol.symm_orb):
        moso = numpy.dot(csym.T.conj(), s_mo)
        ovlpso = reduce(numpy.dot, (csym.T.conj(), s, csym))

        # excluding orbitals which are already symmetrized
        try:
            diag = numpy.einsum('ki,ki->i', moso.conj(),
                                lib.cho_solve(ovlpso, moso))
        except numpy.linalg.LinAlgError:
            ovlpso[numpy.diag_indices(csym.shape[1])] += 1e-12
            diag = numpy.einsum('ki,ki->i', moso.conj(),
                                lib.cho_solve(ovlpso, moso))
        idx = abs(1 - diag) < 1e-8
        orb_exclude = mo[:, idx]
        mo1.append(orb_exclude)
        moso1 = moso[:, ~idx]
        dm = numpy.dot(moso1, moso1.T.conj())

        if dm.trace() > 1e-8:
            e, u = scipy.linalg.eigh(dm, ovlpso)
            mo1.append(numpy.dot(csym, u[:, abs(1 - e) < 1e-6]))
    mo1 = numpy.hstack(mo1)
    if mo1.shape[1] != nmo:
        raise ValueError('The input orbital space is not symmetrized.\n One '
                         'possible reason is that the input mol and orbitals '
                         'are of different orientation.')
    if (check and
            abs(reduce(numpy.dot, (mo1.conj().T, s, mo1)) -
                numpy.eye(nmo)).max() > tol):
        raise ValueError('Orbitals are not orthogonalized')
    idx = mo_mapping.mo_1to1map(reduce(numpy.dot, (mo.T.conj(), s, mo1)))
    return mo1[:, idx]
Ejemplo n.º 11
0
def cas_natorb(mc, mo_coeff=None, ci=None, eris=None, sort=False,
               casdm1=None, verbose=None):
    '''Transform active orbitals to natrual orbitals, and update the CI wfn

    Args:
        mc : a CASSCF/CASCI object or RHF object

    Kwargs:
        sort : bool
            Sort natural orbitals wrt the occupancy.  Be careful with this
            option since the resultant natural orbitals might have the
            different symmetry to the irreps indicated by CASSCF.orbsym

    Returns:
        A tuple, the first item is natural orbitals, the second is updated CI
        coefficients, the third is the natural occupancy associated to the
        natural orbitals.
    '''
    from pyscf.lo import orth
    from pyscf.tools import dump_mat
    from pyscf.tools.mo_mapping import mo_1to1map
    if isinstance(verbose, logger.Logger):
        log = verbose
    else:
        log = logger.Logger(mc.stdout, mc.verbose)
    if mo_coeff is None: mo_coeff = mc.mo_coeff
    if ci is None: ci = mc.ci
    ncore = mc.ncore
    ncas = mc.ncas
    nocc = ncore + ncas
    nelecas = mc.nelecas
    if casdm1 is None:
        casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas)
    occ, ucas = mc._eig(-casdm1, ncore, nocc)
    if sort:
        idx = numpy.argsort(occ)
        occ = occ[idx]
        ucas = ucas[:,idx]

    occ = -occ

# where_natorb gives the location of the natural orbital for the input cas
# orbitals.  gen_strings4orblist map thes sorted strings (on CAS orbital) to
# the unsorted determinant strings (on natural orbital). e.g.  (3o,2e) system
#       CAS orbital      1  2  3
#       natural orbital  3  1  2        <= by mo_1to1map
#       CASorb-strings   0b011, 0b101, 0b110
#                    ==  (1,2), (1,3), (2,3)
#       natorb-strings   (3,1), (3,2), (1,2)
#                    ==  0B101, 0B110, 0B011    <= by gen_strings4orblist
# then argsort to translate the string representation to the address
#       [2(=0B011), 0(=0B101), 1(=0B110)]
# to indicate which CASorb-strings address to be loaded in each natorb-strings slot
    where_natorb = mo_1to1map(ucas)
    #guide_stringsa = fci.cistring.gen_strings4orblist(where_natorb, nelecas[0])
    #guide_stringsb = fci.cistring.gen_strings4orblist(where_natorb, nelecas[1])
    #old_det_idxa = numpy.argsort(guide_stringsa)
    #old_det_idxb = numpy.argsort(guide_stringsb)
    #ci0 = ci[old_det_idxa[:,None],old_det_idxb]
    if isinstance(ci, numpy.ndarray):
        ci0 = fci.addons.reorder(ci, nelecas, where_natorb)
    elif isinstance(ci, (tuple, list)) and isinstance(ci[0], numpy.ndarray):
        # for state-average eigenfunctions
        ci0 = [fci.addons.reorder(x, nelecas, where_natorb) for x in ci]
    else:
        log.info('FCI vector not available, so not using old wavefunction as initial guess')
        ci0 = None

# restore phase, to ensure the reordered ci vector is the correct initial guess
    for i, k in enumerate(where_natorb):
        if ucas[i,k] < 0:
            ucas[:,k] *= -1
    mo_coeff1 = mo_coeff.copy()
    mo_coeff1[:,ncore:nocc] = numpy.dot(mo_coeff[:,ncore:nocc], ucas)
    if log.verbose >= logger.INFO:
        log.debug('where_natorb %s', str(where_natorb))
        log.info('Natural occ %s', str(occ))
        log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space')
        label = mc.mol.spheric_labels(True)
        orth_coeff = orth.orth_ao(mc.mol, 'meta_lowdin', s=ovlp_ao)
        mo_cas = reduce(numpy.dot, (orth_coeff.T, ovlp_ao, mo_coeff1[:,ncore:nocc]))
        dump_mat.dump_rec(log.stdout, mo_cas, label, start=1)

        if mc._scf.mo_coeff is not None:
            s = reduce(numpy.dot, (mo_coeff1[:,ncore:nocc].T,
                                   mc._scf.get_ovlp(), mc._scf.mo_coeff))
            idx = numpy.argwhere(abs(s)>.4)
            for i,j in idx:
                log.info('<CAS-nat-orb|mo-hf>  %d  %d  %12.8f',
                         ncore+i+1, j+1, s[i,j])

    mocas = mo_coeff1[:,ncore:nocc]
    h1eff = reduce(numpy.dot, (mocas.T, mc.get_hcore(), mocas))
    if eris is not None and hasattr(eris, 'ppaa'):
        h1eff += reduce(numpy.dot, (ucas.T, eris.vhf_c[ncore:nocc,ncore:nocc], ucas))
        aaaa = ao2mo.restore(4, eris.ppaa[ncore:nocc,ncore:nocc,:,:], ncas)
        aaaa = ao2mo.incore.full(aaaa, ucas)
    else:
        dm_core = numpy.dot(mo_coeff[:,:ncore]*2, mo_coeff[:,:ncore].T)
        vj, vk = mc._scf.get_jk(mc.mol, dm_core)
        h1eff += reduce(numpy.dot, (mocas.T, vj-vk*.5, mocas))
        aaaa = ao2mo.kernel(mc.mol, mocas)
    e_cas, fcivec = mc.fcisolver.kernel(h1eff, aaaa, ncas, nelecas, ci0=ci0)
    log.debug('In Natural orbital, CI energy = %.12g', e_cas)
    return mo_coeff1, fcivec, occ
Ejemplo n.º 12
0
def cas_natorb(mc, mo_coeff=None, ci=None, eris=None, sort=False,
               casdm1=None, verbose=None, with_meta_lowdin=WITH_META_LOWDIN):
    '''Transform active orbitals to natrual orbitals, and update the CI wfn
    accordingly

    Args:
        mc : a CASSCF/CASCI object or RHF object

    Kwargs:
        sort : bool
            Sort natural orbitals wrt the occupancy.

    Returns:
        A tuple, the first item is natural orbitals, the second is updated CI
        coefficients, the third is the natural occupancy associated to the
        natural orbitals.
    '''
    from pyscf.lo import orth
    from pyscf.tools import dump_mat
    from pyscf.tools.mo_mapping import mo_1to1map
    if mo_coeff is None: mo_coeff = mc.mo_coeff
    if ci is None: ci = mc.ci
    log = logger.new_logger(mc, verbose)
    ncore = mc.ncore
    ncas = mc.ncas
    nocc = ncore + ncas
    nelecas = mc.nelecas
    nmo = mo_coeff.shape[1]
    if casdm1 is None:
        casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas)
    # orbital symmetry is reserved in this _eig call
    cas_occ, ucas = mc._eig(-casdm1, ncore, nocc)
    if sort:
        casorb_idx = numpy.argsort(cas_occ.round(9), kind='mergesort')
        cas_occ = cas_occ[casorb_idx]
        ucas = ucas[:,casorb_idx]

    cas_occ = -cas_occ
    mo_occ = numpy.zeros(mo_coeff.shape[1])
    mo_occ[:ncore] = 2
    mo_occ[ncore:nocc] = cas_occ

    mo_coeff1 = mo_coeff.copy()
    mo_coeff1[:,ncore:nocc] = numpy.dot(mo_coeff[:,ncore:nocc], ucas)
    if getattr(mo_coeff, 'orbsym', None) is not None:
        orbsym = numpy.copy(mo_coeff.orbsym)
        if sort:
            orbsym[ncore:nocc] = orbsym[ncore:nocc][casorb_idx]
        mo_coeff1 = lib.tag_array(mo_coeff1, orbsym=orbsym)
    else:
        orbsym = numpy.zeros(nmo, dtype=int)

    # When occupancies of active orbitals equal to 2 or 0, these orbitals
    # need to be canonicalized along with inactive(core or virtual) orbitals
    # using general Fock matrix. Because they are strongly coupled with
    # inactive orbitals, the 0th order Hamiltonian of MRPT methods can be
    # strongly affected. Numerical uncertainty may be found in the perturbed
    # correlation energy.
    # See issue https://github.com/pyscf/pyscf/issues/1041
    occ2_idx = numpy.where(2 - cas_occ < FRAC_OCC_THRESHOLD)[0]
    occ0_idx = numpy.where(cas_occ < FRAC_OCC_THRESHOLD)[0]
    if occ2_idx.size > 0 or occ0_idx.size > 0:
        fock_ao = mc.get_fock(mo_coeff, ci, eris, casdm1, verbose)

        def _diag_subfock_(idx):
            c = mo_coeff1[:,idx]
            fock = reduce(numpy.dot, (c.conj().T, fock_ao, c))
            w, c = mc._eig(fock, None, None, orbsym[idx])
            mo_coeff1[:,idx] = mo_coeff1[:,idx].dot(c)

        if occ2_idx.size > 0:
            log.warn('Active orbitals %s (occs = %s) are canonicalized with core orbitals',
                     occ2_idx, cas_occ[occ2_idx])
            full_occ2_idx = numpy.append(numpy.arange(ncore), ncore + occ2_idx)
            _diag_subfock_(full_occ2_idx)
        if occ0_idx.size > 0:
            log.warn('Active orbitals %s (occs = %s) are canonicalized with external orbitals',
                     occ0_idx, cas_occ[occ0_idx])
            full_occ0_idx = numpy.append(ncore + occ0_idx, numpy.arange(nocc, nmo))
            _diag_subfock_(full_occ0_idx)

    # Rotate CI according to the unitary coefficients ucas if applicable
    fcivec = None
    if getattr(mc.fcisolver, 'transform_ci_for_orbital_rotation', None):
        if isinstance(ci, numpy.ndarray):
            fcivec = mc.fcisolver.transform_ci_for_orbital_rotation(ci, ncas, nelecas, ucas)
        elif (isinstance(ci, (list, tuple)) and
              all(isinstance(x[0], numpy.ndarray) for x in ci)):
            fcivec = [mc.fcisolver.transform_ci_for_orbital_rotation(x, ncas, nelecas, ucas)
                      for x in ci]
    elif getattr(mc.fcisolver, 'states_transform_ci_for_orbital_rotation', None):
        fcivec = mc.fcisolver.states_transform_ci_for_orbital_rotation(ci, ncas, nelecas, ucas)

    # Rerun fcisolver to get wavefunction if it cannot be transformed from
    # existed one.
    if fcivec is None:
        log.info('FCI vector not available, call CASCI to update wavefunction')
        mocas = mo_coeff1[:,ncore:nocc]
        hcore = mc.get_hcore()
        dm_core = numpy.dot(mo_coeff1[:,:ncore]*2, mo_coeff1[:,:ncore].conj().T)
        ecore = mc.energy_nuc()
        ecore+= numpy.einsum('ij,ji', hcore, dm_core)
        h1eff = reduce(numpy.dot, (mocas.conj().T, hcore, mocas))
        if getattr(eris, 'ppaa', None) is not None:
            ecore += eris.vhf_c[:ncore,:ncore].trace()
            h1eff += reduce(numpy.dot, (ucas.conj().T, eris.vhf_c[ncore:nocc,ncore:nocc], ucas))
            aaaa = ao2mo.restore(4, eris.ppaa[ncore:nocc,ncore:nocc,:,:], ncas)
            aaaa = ao2mo.incore.full(aaaa, ucas)
        else:
            if getattr(mc, 'with_df', None):
                aaaa = mc.with_df.ao2mo(mocas)
            else:
                aaaa = ao2mo.kernel(mc.mol, mocas)
            corevhf = mc.get_veff(mc.mol, dm_core)
            ecore += numpy.einsum('ij,ji', dm_core, corevhf) * .5
            h1eff += reduce(numpy.dot, (mocas.conj().T, corevhf, mocas))


        # See label_symmetry_ function in casci_symm.py which initialize the
        # orbital symmetry information in fcisolver.  This orbital symmetry
        # labels should be reordered to match the sorted active space orbitals.
        if sort and getattr(mo_coeff1, 'orbsym', None) is not None:
            mc.fcisolver.orbsym = mo_coeff1.orbsym[ncore:nocc]

        max_memory = max(400, mc.max_memory-lib.current_memory()[0])
        e, fcivec = mc.fcisolver.kernel(h1eff, aaaa, ncas, nelecas, ecore=ecore,
                                        max_memory=max_memory, verbose=log)
        log.debug('In Natural orbital, CASCI energy = %s', e)

    if log.verbose >= logger.INFO:
        ovlp_ao = mc._scf.get_ovlp()
        # where_natorb gives the new locations of the natural orbitals
        where_natorb = mo_1to1map(ucas)
        log.debug('where_natorb %s', str(where_natorb))
        log.info('Natural occ %s', str(cas_occ))
        if with_meta_lowdin:
            log.info('Natural orbital (expansion on meta-Lowdin AOs) in CAS space')
            label = mc.mol.ao_labels()
            orth_coeff = orth.orth_ao(mc.mol, 'meta_lowdin', s=ovlp_ao)
            mo_cas = reduce(numpy.dot, (orth_coeff.conj().T, ovlp_ao, mo_coeff1[:,ncore:nocc]))
        else:
            log.info('Natural orbital (expansion on AOs) in CAS space')
            label = mc.mol.ao_labels()
            mo_cas = mo_coeff1[:,ncore:nocc]
        dump_mat.dump_rec(log.stdout, mo_cas, label, start=1)

        if mc._scf.mo_coeff is not None:
            s = reduce(numpy.dot, (mo_coeff1[:,ncore:nocc].conj().T,
                                   mc._scf.get_ovlp(), mc._scf.mo_coeff))
            idx = numpy.argwhere(abs(s)>.4)
            for i,j in idx:
                log.info('<CAS-nat-orb|mo-hf>  %d  %d  %12.8f',
                         ncore+i+1, j+1, s[i,j])
    return mo_coeff1, fcivec, mo_occ
Ejemplo n.º 13
0
Archivo: boys.py Proyecto: sunqm/pyscf
def kernel(localizer, mo_coeff=None, callback=None, verbose=None):
    from pyscf.tools import mo_mapping
    if mo_coeff is not None:
        localizer.mo_coeff = numpy.asarray(mo_coeff, order='C')
    if localizer.mo_coeff.shape[1] <= 1:
        return localizer.mo_coeff

    if localizer.verbose >= logger.WARN:
        localizer.check_sanity()
    localizer.dump_flags()

    cput0 = (time.clock(), time.time())
    log = logger.new_logger(localizer, verbose=verbose)

    if localizer.conv_tol_grad is None:
        conv_tol_grad = numpy.sqrt(localizer.conv_tol*.1)
        log.info('Set conv_tol_grad to %g', conv_tol_grad)
    else:
        conv_tol_grad = localizer.conv_tol_grad

    if mo_coeff is None:
        if getattr(localizer, 'mol', None) and localizer.mol.natm == 0:
            # For customized Hamiltonian
            u0 = localizer.get_init_guess('random')
        else:
            u0 = localizer.get_init_guess(localizer.init_guess)
    else:
        u0 = localizer.get_init_guess(None)

    rotaiter = ciah.rotate_orb_cc(localizer, u0, conv_tol_grad, verbose=log)
    u, g_orb, stat = next(rotaiter)
    cput1 = log.timer('initializing CIAH', *cput0)

    tot_kf = stat.tot_kf
    tot_hop = stat.tot_hop
    conv = False
    e_last = 0
    for imacro in range(localizer.max_cycle):
        norm_gorb = numpy.linalg.norm(g_orb)
        u0 = lib.dot(u0, u)
        e = localizer.cost_function(u0)
        e_last, de = e, e-e_last

        log.info('macro= %d  f(x)= %.14g  delta_f= %g  |g|= %g  %d KF %d Hx',
                 imacro+1, e, de, norm_gorb, stat.tot_kf+1, stat.tot_hop)
        cput1 = log.timer('cycle= %d'%(imacro+1), *cput1)

        if (norm_gorb < conv_tol_grad and abs(de) < localizer.conv_tol):
            conv = True

        if callable(callback):
            callback(locals())

        if conv:
            break

        u, g_orb, stat = rotaiter.send(u0)
        tot_kf += stat.tot_kf
        tot_hop += stat.tot_hop

    rotaiter.close()
    log.info('macro X = %d  f(x)= %.14g  |g|= %g  %d intor %d KF %d Hx',
             imacro+1, e, norm_gorb,
             (imacro+1)*2, tot_kf+imacro+1, tot_hop)
# Sort the localized orbitals, to make each localized orbitals as close as
# possible to the corresponding input orbitals
    sorted_idx = mo_mapping.mo_1to1map(u0)
    localizer.mo_coeff = lib.dot(localizer.mo_coeff, u0[:,sorted_idx])
    return localizer.mo_coeff
Ejemplo n.º 14
0
def symmetrize_space(mol, mo, s=None, check=True):
    """Symmetrize the given orbital space.

    This function is different to the :func:`symmetrize_orb`:  In this function,
    the given orbitals are mixed to reveal the symmtery; :func:`symmetrize_orb`
    projects out non-symmetric components for each orbital.

    Args:
        mo : 2D float array
            The orbital space to symmetrize

    Kwargs:
        s : 2D float array
            Overlap matrix.  If not given, overlap is computed with the input mol.

    Returns:
        2D orbital coefficients

    Examples:

    >>> from pyscf import gto, symm, scf
    >>> mol = gto.M(atom = 'C  0  0  0; H  1  1  1; H -1 -1  1; H  1 -1 -1; H -1  1 -1',
    ...             basis = 'sto3g')
    >>> mf = scf.RHF(mol).run()
    >>> mol.build(0, 0, symmetry='D2')
    >>> mo = symm.symmetrize_space(mol, mf.mo_coeff)
    >>> print(symm.label_orb_symm(mol, mol.irrep_name, mol.symm_orb, mo))
    ['A', 'A', 'A', 'B1', 'B1', 'B2', 'B2', 'B3', 'B3']
    """
    from pyscf.tools import mo_mapping

    if s is None:
        s = mol.intor_symmetric("cint1e_ovlp_sph")
    nmo = mo.shape[1]
    mo_s = numpy.dot(mo.T, s)
    if check:
        assert numpy.allclose(numpy.dot(mo_s, mo), numpy.eye(nmo))
    mo1 = []
    for i, csym in enumerate(mol.symm_orb):
        moso = numpy.dot(mo_s, csym)
        ovlpso = reduce(numpy.dot, (csym.T, s, csym))

        # excluding orbitals which are already symmetrized
        try:
            diag = numpy.einsum("ik,ki->i", moso, lib.cho_solve(ovlpso, moso.T))
        except:
            ovlpso[numpy.diag_indices(csym.shape[1])] += 1e-12
            diag = numpy.einsum("ik,ki->i", moso, lib.cho_solve(ovlpso, moso.T))
        idx = abs(1 - diag) < 1e-8
        orb_exclude = mo[:, idx]
        mo1.append(orb_exclude)
        moso1 = moso[~idx]
        dm = numpy.dot(moso1.T, moso1)

        if dm.trace() > 1e-8:
            e, u = scipy.linalg.eigh(dm, ovlpso)
            mo1.append(numpy.dot(csym, u[:, abs(1 - e) < 1e-6]))
    mo1 = numpy.hstack(mo1)
    if mo1.shape[1] != nmo:
        raise ValueError(
            "The input orbital space is not symmetrized.\n It is "
            "probably because the input mol and orbitals are of "
            "different orientation."
        )
    snorm = numpy.linalg.norm(reduce(numpy.dot, (mo1.T, s, mo1)) - numpy.eye(nmo))
    if check and snorm > 1e-6:
        raise ValueError("Orbitals are not orthogonalized")
    idx = mo_mapping.mo_1to1map(reduce(numpy.dot, (mo.T, s, mo1)))
    return mo1[:, idx]
Ejemplo n.º 15
0
def cas_natorb(mc, mo_coeff=None, ci=None, eris=None, sort=False,
               verbose=None):
    '''Transform active orbitals to natrual orbitals, and update the CI wfn

    Args:
        mc : a CASSCF/CASCI object or RHF object

        sort : bool
            Sort natural orbitals wrt the occupancy.  Be careful with this
            option since the resultant natural orbitals might have the
            different symmetry to the irreps indicated by CASSCF.orbsym

    Returns:
        A tuple, the first item is natural orbitals, the second is updated CI
        coefficients, the third is the natural occupancy associated to the
        natural orbitals.
    '''
    from pyscf.mcscf import mc_ao2mo
    from pyscf.tools import dump_mat
    if isinstance(verbose, logger.Logger):
        log = verbose
    else:
        log = logger.Logger(mc.stdout, mc.verbose)
    if mo_coeff is None: mo_coeff = mc.mo_coeff
    if ci is None: ci = mc.ci
    if eris is None: eris = mc_ao2mo._ERIS(mc, mo_coeff, approx=2)
    ncore = mc.ncore
    ncas = mc.ncas
    nocc = ncore + ncas
    nelecas = mc.nelecas
    casdm1 = mc.fcisolver.make_rdm1(ci, ncas, nelecas)
    occ, ucas = mc._eig(-casdm1, ncore, nocc)
    if sort:
        idx = numpy.argsort(occ)
        occ = occ[idx]
        ucas = ucas[:,idx]
        if hasattr(mc, 'orbsym'): # for casci_symm
            mc.orbsym[ncore:nocc] = mc.orbsym[ncore:nocc][idx]
            mc.fcisolver.orbsym = mc.orbsym[ncore:nocc]

    occ = -occ

# where_natorb gives the location of the natural orbital for the input cas
# orbitals.  gen_strings4orblist map thes sorted strings (on CAS orbital) to
# the unsorted determinant strings (on natural orbital). e.g.  (3o,2e) system
#       CAS orbital      1  2  3
#       natural orbital  3  1  2        <= by mo_1to1map
#       CASorb-strings   0b011, 0b101, 0b110
#                    ==  (1,2), (1,3), (2,3)
#       natorb-strings   (3,1), (3,2), (1,2)
#                    ==  0B101, 0B110, 0B011    <= by gen_strings4orblist
# then argsort to translate the string representation to the address
#       [2(=0B011), 0(=0B101), 1(=0B110)]
# to indicate which CASorb-strings address to be loaded in each natorb-strings slot
    where_natorb = mo_1to1map(ucas)
    #guide_stringsa = fci.cistring.gen_strings4orblist(where_natorb, nelecas[0])
    #guide_stringsb = fci.cistring.gen_strings4orblist(where_natorb, nelecas[1])
    #old_det_idxa = numpy.argsort(guide_stringsa)
    #old_det_idxb = numpy.argsort(guide_stringsb)
    #ci0 = ci[old_det_idxa[:,None],old_det_idxb]
    ci0 = fci.addons.reorder(ci, nelecas, where_natorb)

# restore phase, to ensure the reordered ci vector is the correct initial guess
    for i, k in enumerate(where_natorb):
        if ucas[i,k] < 0:
            ucas[:,k] *= -1
    mo_coeff1 = mo_coeff.copy()
    mo_coeff1[:,ncore:nocc] = numpy.dot(mo_coeff[:,ncore:nocc], ucas)
    if log.verbose >= logger.INFO:
        log.debug('where_natorb %s', str(where_natorb))
        log.info('Natural occ %s', str(occ))
        log.info('Natural orbital in CAS space')
        label = ['%d%3s %s%-4s' % x for x in mc.mol.spheric_labels()]
        dump_mat.dump_rec(log.stdout, mo_coeff1[:,ncore:nocc], label, start=1)

        s = reduce(numpy.dot, (mo_coeff1[:,ncore:nocc].T, mc._scf.get_ovlp(),
                               mc._scf.mo_coeff))
        idx = numpy.argwhere(abs(s)>.4)
        for i,j in idx:
            log.info('<CAS-nat-orb|mo-hf>  %d  %d  %12.8f',
                     ncore+i+1, j+1, s[i,j])

    h1eff =(reduce(numpy.dot, (mo_coeff[:,ncore:nocc].T, mc.get_hcore(),
                               mo_coeff[:,ncore:nocc]))
          + eris.vhf_c[ncore:nocc,ncore:nocc])
    h1eff = reduce(numpy.dot, (ucas.T, h1eff, ucas))
    aaaa = eris.aapp[:,:,ncore:nocc,ncore:nocc].copy()
    aaaa = ao2mo.incore.full(ao2mo.restore(8, aaaa, ncas), ucas)
    e_cas, fcivec = mc.fcisolver.kernel(h1eff, aaaa, ncas, nelecas, ci0=ci0)
    log.debug('In Natural orbital, CI energy = %.12g', e_cas)
    return mo_coeff1, fcivec, occ
Ejemplo n.º 16
0
def symmetrize_space(mol, mo, s=None,
                     check=getattr(__config__, 'symm_addons_symmetrize_space_check', True),
                     tol=getattr(__config__, 'symm_addons_symmetrize_space_tol', 1e-7)):
    '''Symmetrize the given orbital space.

    This function is different to the :func:`symmetrize_orb`:  In this function,
    the given orbitals are mixed to reveal the symmtery; :func:`symmetrize_orb`
    projects out non-symmetric components for each orbital.

    Args:
        mo : 2D float array
            The orbital space to symmetrize

    Kwargs:
        s : 2D float array
            Overlap matrix.  If not given, overlap is computed with the input mol.

    Returns:
        2D orbital coefficients

    Examples:

    >>> from pyscf import gto, symm, scf
    >>> mol = gto.M(atom = 'C  0  0  0; H  1  1  1; H -1 -1  1; H  1 -1 -1; H -1  1 -1',
    ...             basis = 'sto3g')
    >>> mf = scf.RHF(mol).run()
    >>> mol.build(0, 0, symmetry='D2')
    >>> mo = symm.symmetrize_space(mol, mf.mo_coeff)
    >>> print(symm.label_orb_symm(mol, mol.irrep_name, mol.symm_orb, mo))
    ['A', 'A', 'A', 'B1', 'B1', 'B2', 'B2', 'B3', 'B3']
    '''
    from pyscf.tools import mo_mapping
    if s is None:
        s = mol.intor_symmetric('int1e_ovlp')
    nmo = mo.shape[1]
    s_mo = numpy.dot(s, mo)
    if check and abs(numpy.dot(mo.conj().T, s_mo) - numpy.eye(nmo)).max() > tol:
            raise ValueError('Orbitals are not orthogonalized')

    mo1 = []
    for i, csym in enumerate(mol.symm_orb):
        moso = numpy.dot(csym.T, s_mo)
        ovlpso = reduce(numpy.dot, (csym.T, s, csym))

# excluding orbitals which are already symmetrized
        try:
            diag = numpy.einsum('ki,ki->i', moso.conj(), lib.cho_solve(ovlpso, moso))
        except:
            ovlpso[numpy.diag_indices(csym.shape[1])] += 1e-12
            diag = numpy.einsum('ki,ki->i', moso.conj(), lib.cho_solve(ovlpso, moso))
        idx = abs(1-diag) < 1e-8
        orb_exclude = mo[:,idx]
        mo1.append(orb_exclude)
        moso1 = moso[:,~idx]
        dm = numpy.dot(moso1, moso1.T.conj())

        if dm.trace() > 1e-8:
            e, u = scipy.linalg.eigh(dm, ovlpso)
            mo1.append(numpy.dot(csym, u[:,abs(1-e) < 1e-6]))
    mo1 = numpy.hstack(mo1)
    if mo1.shape[1] != nmo:
        raise ValueError('The input orbital space is not symmetrized.\n One '
                         'possible reason is that the input mol and orbitals '
                         'are of different orientation.')
    if (check and
        abs(reduce(numpy.dot, (mo1.conj().T, s, mo1)) - numpy.eye(nmo)).max() > tol):
        raise ValueError('Orbitals are not orthogonalized')
    idx = mo_mapping.mo_1to1map(reduce(numpy.dot, (mo.T, s, mo1)))
    return mo1[:,idx]