def energy(h1e, eri, fcivec, norb, nelec, link_index=None): '''Compute the FCI electronic energy for given Hamiltonian and FCI vector. ''' from pyscf.fci import direct_spin1 h2e = direct_spin1.absorb_h1e(h1e, eri, norb, nelec, .5) ci1 = direct_spin1.contract_2e(h2e, fcivec, norb, nelec, link_index) return numpy.dot(fcivec.reshape(-1), ci1.reshape(-1))
def test_lasuccsd_total_energy (self): mc = mcscf.CASCI (mf, 4, 4) mc.mo_coeff = las.mo_coeff mc.fcisolver = lasuccsd.FCISolver (mol) mc.fcisolver.norb_f = [2,2] mc.kernel () with self.subTest (from_='reported'): self.assertAlmostEqual (mc.e_tot, ref.e_tot, 6) psi = mc.fcisolver.psi x = psi.x h1, h0 = mc.get_h1eff () h2 = mc.get_h2eff () h = [h0, h1, h2] energy, gradient = psi.e_de (x, h) with self.subTest (from_='obj fn'): self.assertAlmostEqual (energy, mc.e_tot, 9) c = np.squeeze (fockspace.fock2hilbert (psi.get_fcivec (x), 4, (2,2))) h2eff = direct_spin1.absorb_h1e (h1, h2, 4, (2,2), 0.5) hc = direct_spin1.contract_2e (h2eff, c, 4, (2,2)) chc = c.conj ().ravel ().dot (hc.ravel ()) + h0 with self.subTest (from_='h2 contraction'): self.assertAlmostEqual (chc, mc.e_tot, 9) c_f = psi.ci_f for ifrag, c in enumerate (c_f): heff = psi.get_dense_heff (x, h, ifrag) hc = np.dot (heff.full, c.ravel ()) chc = np.dot (c.conj ().ravel (), hc) with self.subTest (from_='frag {} Fock-space dense effective Hamiltonian'.format (ifrag)): self.assertAlmostEqual (chc, mc.e_tot, 9) heff, _ = heff.get_number_block ((1,1),(1,1)) c = np.squeeze (fockspace.fock2hilbert (c, 2, (1,1))).ravel () hc = np.dot (heff, c) chc = np.dot (c.conj (), hc) with self.subTest (from_='frag {} Hilbert-space dense effective Hamiltonian'.format (ifrag)): self.assertAlmostEqual (chc, mc.e_tot, 9)
def contract_2e(eri, fcivec, norb, nelec, link_index=None, orbsym=None): fcivec = numpy.asarray(fcivec, order='C') if orbsym is None: return direct_spin1.contract_2e(eri, fcivec, norb, nelec, link_index) eri = pyscf.ao2mo.restore(4, eri, norb) link_indexa, link_indexb = direct_spin1._unpack(norb, nelec, link_index) na, nlinka = link_indexa.shape[:2] nb, nlinkb = link_indexb.shape[:2] assert(fcivec.size == na*nb) ci1 = numpy.empty_like(fcivec) eri, link_indexa, dimirrep = reorder4irrep(eri, norb, link_indexa, orbsym) link_indexb = reorder4irrep(eri, norb, link_indexb, orbsym)[1] dimirrep = numpy.array(dimirrep, dtype=numpy.int32) libfci.FCIcontract_2e_spin1_symm(eri.ctypes.data_as(ctypes.c_void_p), fcivec.ctypes.data_as(ctypes.c_void_p), ci1.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(norb), ctypes.c_int(na), ctypes.c_int(nb), ctypes.c_int(nlinka), ctypes.c_int(nlinkb), link_indexa.ctypes.data_as(ctypes.c_void_p), link_indexb.ctypes.data_as(ctypes.c_void_p), dimirrep.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(len(dimirrep))) return ci1
def contract_2e(eri, fcivec, norb, nelec, link_index=None, orbsym=[]): if not orbsym: return direct_spin1.contract_2e(eri, fcivec, norb, nelec, link_index) eri = pyscf.ao2mo.restore(4, eri, norb) if link_index is None: if isinstance(nelec, (int, numpy.integer)): nelecb = nelec//2 neleca = nelec - nelecb else: neleca, nelecb = nelec link_indexa = cistring.gen_linkstr_index_trilidx(range(norb), neleca) link_indexb = cistring.gen_linkstr_index_trilidx(range(norb), nelecb) else: link_indexa, link_indexb = link_index na, nlinka = link_indexa.shape[:2] nb, nlinkb = link_indexb.shape[:2] fcivec = fcivec.reshape(na,nb) ci1 = numpy.empty_like(fcivec) eri, link_indexa, dimirrep = reorder4irrep(eri, norb, link_indexa, orbsym) link_indexb = reorder4irrep(eri, norb, link_indexb, orbsym)[1] dimirrep = numpy.array(dimirrep, dtype=numpy.int32) libfci.FCIcontract_2e_spin1_symm(eri.ctypes.data_as(ctypes.c_void_p), fcivec.ctypes.data_as(ctypes.c_void_p), ci1.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(norb), ctypes.c_int(na), ctypes.c_int(nb), ctypes.c_int(nlinka), ctypes.c_int(nlinkb), link_indexa.ctypes.data_as(ctypes.c_void_p), link_indexb.ctypes.data_as(ctypes.c_void_p), dimirrep.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(len(dimirrep))) return ci1
def contract_h2 (self, h, ci, norb=None): if norb is None: norb = self.norb hci = h[0] * ci for neleca, nelecb in product (range (norb+1), repeat=2): nelec = (neleca, nelecb) h2eff = self.fcisolver.absorb_h1e (h[1], h[2], norb, nelec, 0.5) ci_h = np.squeeze (fockspace.fock2hilbert (ci, norb, nelec)) hc = direct_spin1.contract_2e (h2eff, ci_h, norb, nelec) hci += np.squeeze (fockspace.hilbert2fock (hc, norb, nelec)) return hci
def test_spin0_contract(self): myci = selected_ci_spin0.SCI() civec_strs = selected_ci._as_SCIvector(spin0_ci_coeff, spin0_ci_strs) ci0 = selected_ci.to_fci(civec_strs, norb, nelec) e1 = numpy.dot(civec_strs.ravel(), myci.contract_2e(eri, civec_strs, norb, nelec).ravel()) eref = numpy.dot(ci0.ravel(), direct_spin1.contract_2e(eri, ci0, norb, nelec).ravel()) self.assertAlmostEqual(e1, eref, 9) e2 = numpy.dot(civec_strs.ravel(), myci.contract_2e(eri, spin0_ci_coeff, norb, nelec).ravel()) self.assertAlmostEqual(e2, eref, 9)
def print_line (c, ne): try: ss, smult = spin_square0 (c, norb, ne) hc = contract_2e (eri, c, norb, ne) chc = c.conj ().ravel ().dot (hc.ravel ()) except AssertionError as e: assert (any ([n<0 for n in ne])) ss, smult, chc = (0.0, 1.0, 0.0) cc = linalg.norm (c) ndeta, ndetb = c.shape print (" {:>6d} {:>6d} {:>5d} {:>5d} {:5.3f} {:13.9f} {:5.3f} {:5.3f}".format (ne[0], ne[1], ndeta, ndetb, cc, chc, ss, smult))
def test_contract(self): myci = select_ci.SelectCI() ci0 = select_ci.to_fci(civec_strs, norb, nelec) e1 = numpy.dot(civec_strs.ravel(), select_ci.contract_2e(eri, civec_strs, norb, nelec).ravel()) e2 = numpy.dot(ci0.ravel(), direct_spin1.contract_2e(eri, ci0, norb, nelec).ravel()) self.assertAlmostEqual(e1, e2, 9) dm1 = myci.make_rdm1(civec_strs, norb, nelec) self.assertAlmostEqual(finger(dm1), 0.70181046385686563, 9) dm1 = myci.trans_rdm1(civec_strs, civec_strs, norb, nelec) self.assertAlmostEqual(finger(dm1), 0.70181046385686563, 9) dm2 = myci.make_rdm2(civec_strs, norb, nelec) self.assertAlmostEqual(finger(dm2), -3.8397469683353962, 9)
def test_contract(self): myci = select_ci.SCI() ci0 = select_ci.to_fci(civec_strs, norb, nelec) e1 = numpy.dot( civec_strs.ravel(), select_ci.contract_2e(eri, civec_strs, norb, nelec).ravel()) e2 = numpy.dot(ci0.ravel(), direct_spin1.contract_2e(eri, ci0, norb, nelec).ravel()) self.assertAlmostEqual(e1, e2, 9) dm1 = myci.make_rdm1(civec_strs, norb, nelec) self.assertAlmostEqual(finger(dm1), 0.70181046385686563, 9) dm1 = myci.trans_rdm1(civec_strs, civec_strs, norb, nelec) self.assertAlmostEqual(finger(dm1), 0.70181046385686563, 9) dm2 = myci.make_rdm2(civec_strs, norb, nelec) self.assertAlmostEqual(finger(dm2), -3.8397469683353962, 9)
def test_contract_2e_1(self): myci = selected_ci.SCI() nelec = (4,3) strsa = cistring.make_strings(range(norb), nelec[0]) strsb = cistring.make_strings(range(norb), nelec[1]) ci0 = selected_ci._as_SCIvector(numpy.random.random((len(strsa),len(strsb))), (strsa,strsb)) h2 = ao2mo.restore(1, eri, norb) c1 = myci.contract_2e(h2, ci0, norb, nelec) c2 = direct_spin1.contract_2e(h2, ci0, norb, nelec) self.assertAlmostEqual(float(abs(c1-c2).sum()), 0, 9) dm1_1 = myci.make_rdm1(c1, norb, nelec) dm1_2 = direct_spin1.make_rdm1(c2, norb, nelec) self.assertAlmostEqual(abs(dm1_1 - dm1_2).sum(), 0, 9) dm2_1 = myci.make_rdm12(c1, norb, nelec)[1] dm2_2 = direct_spin1.make_rdm12(c2, norb, nelec)[1] self.assertAlmostEqual(abs(dm2_1 - dm2_2).sum(), 0, 9)
def test_ham(self): for norb in range(2, 5): npair = norb * (norb + 1) // 2 c = np.random.rand(2**(2 * norb)) c /= linalg.norm(c) h0 = np.random.rand(1)[0] h1 = np.random.rand(norb, norb) h2 = np.random.rand(npair, npair) h1 += h1.T h2 += h2.T hc_ref = np.zeros_like(c) for nelec in product(range(norb + 1), repeat=2): c_h = np.squeeze(fockspace.fock2hilbert(c, norb, nelec)) h2eff = direct_spin1.absorb_h1e(h1, h2, norb, nelec, 0.5) hc_h = h0 * c_h + direct_spin1.contract_2e( h2eff, c_h, norb, nelec) hc_ref += fockspace.hilbert2fock(hc_h, norb, nelec).ravel() h1 = h1[np.tril_indices(norb)] hc_test = _op1h_spinsym(norb, [h0, h1, h2], c) with self.subTest(norb=norb): self.assertAlmostEqual(lib.fp(hc_test), lib.fp(hc_ref), 6)
jk = jk.ravel() jk_sorted = abs(jk).argsort()[::-1] ci1 = [as_SCIvector(numpy.ones(1), hf_str)] myci = SelectedCI() myci.select_cutoff = .001 myci.ci_coeff_cutoff = .001 ci2 = enlarge_space(myci, ci1, h1, eri, jk, eri_sorted, jk_sorted, norb, nelec) print(len(ci2[0])) ci2 = enlarge_space(myci, ci1, h1, eri, jk, eri_sorted, jk_sorted, norb, nelec) numpy.random.seed(1) ci3 = numpy.random.random(ci2[0].size) ci3 *= 1./numpy.linalg.norm(ci3) ci3 = [ci3] ci3 = enlarge_space(myci, ci2, h1, eri, jk, eri_sorted, jk_sorted, norb, nelec) efci = direct_spin1.kernel(h1, eri, norb, nelec, verbose=5)[0] ci4 = contract_2e_ctypes((h1, eri), ci3[0], norb, nelec) fci3 = to_fci(ci3, norb, nelec) h2e = direct_spin1.absorb_h1e(h1, eri, norb, nelec, .5) fci4 = direct_spin1.contract_2e(h2e, fci3, norb, nelec) fci4 = from_fci(fci4, ci3[0]._strs, norb, nelec) print(abs(ci4-fci4).sum()) e = myci.kernel(h1, eri, norb, nelec, verbose=5)[0] print(e, efci)
def contract_2e(eri, fcivec, norb, nelec, link_index=None, orbsym=None, wfnsym=0): if orbsym is None: return direct_spin1.contract_2e(eri, fcivec, norb, nelec, link_index) eri = ao2mo.restore(4, eri, norb) neleca, nelecb = _unpack_nelec(nelec) link_indexa, link_indexb = direct_spin1._unpack(norb, nelec, link_index) na, nlinka = link_indexa.shape[:2] nb, nlinkb = link_indexb.shape[:2] eri_irs, rank_eri, irrep_eri = reorder_eri(eri, norb, orbsym) strsa = cistring.gen_strings4orblist(range(norb), neleca) aidx, link_indexa = gen_str_irrep(strsa, orbsym, link_indexa, rank_eri, irrep_eri) if neleca == nelecb: bidx, link_indexb = aidx, link_indexa else: strsb = cistring.gen_strings4orblist(range(norb), nelecb) bidx, link_indexb = gen_str_irrep(strsb, orbsym, link_indexb, rank_eri, irrep_eri) Tirrep = ctypes.c_void_p * TOTIRREPS linka_ptr = Tirrep( *[x.ctypes.data_as(ctypes.c_void_p) for x in link_indexa]) linkb_ptr = Tirrep( *[x.ctypes.data_as(ctypes.c_void_p) for x in link_indexb]) eri_ptrs = Tirrep(*[x.ctypes.data_as(ctypes.c_void_p) for x in eri_irs]) dimirrep = (ctypes.c_int * TOTIRREPS)(*[x.shape[0] for x in eri_irs]) fcivec_shape = fcivec.shape fcivec = fcivec.reshape((na, nb), order='C') ci1new = numpy.zeros_like(fcivec) nas = (ctypes.c_int * TOTIRREPS)(*[x.size for x in aidx]) nbs = (ctypes.c_int * TOTIRREPS)(*[x.size for x in bidx]) # aa, ab ci0 = [] ci1 = [] for ir in range(TOTIRREPS): ma, mb = aidx[ir].size, bidx[wfnsym ^ ir].size ci0.append(numpy.zeros((ma, mb))) ci1.append(numpy.zeros((ma, mb))) if ma > 0 and mb > 0: lib.take_2d(fcivec, aidx[ir], bidx[wfnsym ^ ir], out=ci0[ir]) ci0_ptrs = Tirrep(*[x.ctypes.data_as(ctypes.c_void_p) for x in ci0]) ci1_ptrs = Tirrep(*[x.ctypes.data_as(ctypes.c_void_p) for x in ci1]) libfci.FCIcontract_2e_symm1(eri_ptrs, ci0_ptrs, ci1_ptrs, ctypes.c_int(norb), nas, nbs, ctypes.c_int(nlinka), ctypes.c_int(nlinkb), linka_ptr, linkb_ptr, dimirrep, ctypes.c_int(wfnsym)) for ir in range(TOTIRREPS): if ci0[ir].size > 0: lib.takebak_2d(ci1new, ci1[ir], aidx[ir], bidx[wfnsym ^ ir]) # bb, ba ci0T = [] for ir in range(TOTIRREPS): mb, ma = bidx[ir].size, aidx[wfnsym ^ ir].size ci0T.append(numpy.zeros((mb, ma))) if ma > 0 and mb > 0: lib.transpose(ci0[wfnsym ^ ir], out=ci0T[ir]) ci0, ci0T = ci0T, None ci1 = [numpy.zeros_like(x) for x in ci0] ci0_ptrs = Tirrep(*[x.ctypes.data_as(ctypes.c_void_p) for x in ci0]) ci1_ptrs = Tirrep(*[x.ctypes.data_as(ctypes.c_void_p) for x in ci1]) libfci.FCIcontract_2e_symm1(eri_ptrs, ci0_ptrs, ci1_ptrs, ctypes.c_int(norb), nbs, nas, ctypes.c_int(nlinkb), ctypes.c_int(nlinka), linkb_ptr, linka_ptr, dimirrep, ctypes.c_int(wfnsym)) for ir in range(TOTIRREPS): if ci0[ir].size > 0: lib.takebak_2d(ci1new, lib.transpose(ci1[ir]), aidx[wfnsym ^ ir], bidx[ir]) return ci1new.reshape(fcivec_shape)
na = cistring.num_strings(norb, nelec // 2 + 1) nb = cistring.num_strings(norb, nelec // 2) fcivec = numpy.random.random((na, nb)) orbsym = symm.label_orb_symm(mol, mol.irrep_id, mol.symm_orb, m.mo_coeff) cis = FCISolver(mol) cis.orbsym = orbsym fcivec = addons.symmetrize_wfn(fcivec, norb, nelec, cis.orbsym, wfnsym=0) ci1 = cis.contract_2e(eri, fcivec, norb, nelec, orbsym=cis.orbsym, wfnsym=0) ci1ref = direct_spin1.contract_2e(eri, fcivec, norb, nelec) print(numpy.allclose(ci1ref, ci1)) ci1 = contract_2e(eri, fcivec, norb, nelec, orbsym=orbsym) ci1ref = direct_spin1.contract_2e(eri, fcivec, norb, nelec) print(numpy.allclose(ci1ref, ci1)) cis.wfnsym = 3 e = cis.kernel(h1e, eri, norb, nelec, ecore=m.energy_nuc(), davidson_only=True)[0] print(e, e - -74.695029029452357) mol.atom = [['H', (0, 0, i)] for i in range(8)]
def hop(c): hc = fcisolver.contract_2e(h2e, c, norb, nelec) return hc.reshape(-1)
def contract_2e_ref(eri, fcivec, norb, nelec, *args, **kwargs): hc = direct_spin1.contract_2e(eri, numpy.diag(fcivec), norb, nelec) return hc.diagonal()
def hop(c): hc = direct_spin1.contract_2e(h2e, c, norb, nelec) return hc.reshape(-1)
def get_cc_chc_smult (eri, c, norb, ne): cc = c.conj ().ravel ().dot (c.ravel ()) chc = c.conj ().ravel ().dot (contract_2e (eri, c, norb, ne).ravel ()) ss, smult = spin_square0 (c, norb, ne) return cc, chc, smult
def contract_2e(eri, fcivec, norb, nelec, link_index=None, orbsym=None, wfnsym=0): if orbsym is None: return direct_spin1.contract_2e(eri, fcivec, norb, nelec, link_index) eri = ao2mo.restore(4, eri, norb) neleca, nelecb = _unpack_nelec(nelec) link_indexa, link_indexb = direct_spin1._unpack(norb, nelec, link_index) na, nlinka = link_indexa.shape[:2] nb, nlinkb = link_indexb.shape[:2] eri_irs, rank_eri, irrep_eri = reorder_eri(eri, norb, orbsym) strsa = cistring.gen_strings4orblist(range(norb), neleca) aidx, link_indexa = gen_str_irrep(strsa, orbsym, link_indexa, rank_eri, irrep_eri) if neleca == nelecb: bidx, link_indexb = aidx, link_indexa else: strsb = cistring.gen_strings4orblist(range(norb), nelecb) bidx, link_indexb = gen_str_irrep(strsb, orbsym, link_indexb, rank_eri, irrep_eri) Tirrep = ctypes.c_void_p*TOTIRREPS linka_ptr = Tirrep(*[x.ctypes.data_as(ctypes.c_void_p) for x in link_indexa]) linkb_ptr = Tirrep(*[x.ctypes.data_as(ctypes.c_void_p) for x in link_indexb]) eri_ptrs = Tirrep(*[x.ctypes.data_as(ctypes.c_void_p) for x in eri_irs]) dimirrep = (ctypes.c_int*TOTIRREPS)(*[x.shape[0] for x in eri_irs]) fcivec_shape = fcivec.shape fcivec = fcivec.reshape((na,nb), order='C') ci1new = numpy.zeros_like(fcivec) nas = (ctypes.c_int*TOTIRREPS)(*[x.size for x in aidx]) nbs = (ctypes.c_int*TOTIRREPS)(*[x.size for x in bidx]) # aa, ab ci0 = [] ci1 = [] for ir in range(TOTIRREPS): ma, mb = aidx[ir].size, bidx[wfnsym^ir].size ci0.append(numpy.zeros((ma,mb))) ci1.append(numpy.zeros((ma,mb))) if ma > 0 and mb > 0: lib.take_2d(fcivec, aidx[ir], bidx[wfnsym^ir], out=ci0[ir]) ci0_ptrs = Tirrep(*[x.ctypes.data_as(ctypes.c_void_p) for x in ci0]) ci1_ptrs = Tirrep(*[x.ctypes.data_as(ctypes.c_void_p) for x in ci1]) libfci.FCIcontract_2e_symm1(eri_ptrs, ci0_ptrs, ci1_ptrs, ctypes.c_int(norb), nas, nbs, ctypes.c_int(nlinka), ctypes.c_int(nlinkb), linka_ptr, linkb_ptr, dimirrep, ctypes.c_int(wfnsym)) for ir in range(TOTIRREPS): if ci0[ir].size > 0: lib.takebak_2d(ci1new, ci1[ir], aidx[ir], bidx[wfnsym^ir]) # bb, ba ci0T = [] for ir in range(TOTIRREPS): mb, ma = bidx[ir].size, aidx[wfnsym^ir].size ci0T.append(numpy.zeros((mb,ma))) if ma > 0 and mb > 0: lib.transpose(ci0[wfnsym^ir], out=ci0T[ir]) ci0, ci0T = ci0T, None ci1 = [numpy.zeros_like(x) for x in ci0] ci0_ptrs = Tirrep(*[x.ctypes.data_as(ctypes.c_void_p) for x in ci0]) ci1_ptrs = Tirrep(*[x.ctypes.data_as(ctypes.c_void_p) for x in ci1]) libfci.FCIcontract_2e_symm1(eri_ptrs, ci0_ptrs, ci1_ptrs, ctypes.c_int(norb), nbs, nas, ctypes.c_int(nlinkb), ctypes.c_int(nlinka), linkb_ptr, linka_ptr, dimirrep, ctypes.c_int(wfnsym)) for ir in range(TOTIRREPS): if ci0[ir].size > 0: lib.takebak_2d(ci1new, lib.transpose(ci1[ir]), aidx[wfnsym^ir], bidx[ir]) return ci1new.reshape(fcivec_shape)
norb = m.mo_coeff.shape[1] nelec = mol.nelectron-1 h1e = reduce(numpy.dot, (m.mo_coeff.T, scf.hf.get_hcore(mol), m.mo_coeff)) eri = ao2mo.incore.full(m._eri, m.mo_coeff) numpy.random.seed(1) na = cistring.num_strings(norb, nelec//2+1) nb = cistring.num_strings(norb, nelec//2) fcivec = numpy.random.random((na,nb)) orbsym = symm.label_orb_symm(mol, mol.irrep_id, mol.symm_orb, m.mo_coeff) cis = FCISolver(mol) cis.orbsym = orbsym fcivec = addons.symmetrize_wfn(fcivec, norb, nelec, cis.orbsym, wfnsym=0) ci1 = cis.contract_2e(eri, fcivec, norb, nelec, orbsym=cis.orbsym, wfnsym=0) ci1ref = direct_spin1.contract_2e(eri, fcivec, norb, nelec) print(numpy.allclose(ci1ref, ci1)) ci1 = contract_2e(eri, fcivec, norb, nelec, orbsym=orbsym) ci1ref = direct_spin1.contract_2e(eri, fcivec, norb, nelec) print(numpy.allclose(ci1ref, ci1)) cis.wfnsym = 3 e = cis.kernel(h1e, eri, norb, nelec, ecore=m.energy_nuc(), davidson_only=True)[0] print(e, e - -74.695029029452357) mol.atom = [['H', (0, 0, i)] for i in range(8)] mol.basis = {'H': 'sto-3g'} mol.symmetry = True mol.build() m = scf.RHF(mol) ehf = m.scf()
def energy(h1e, eri, fcivec, norb, nelec, link_index=None): from pyscf.fci import direct_spin1 h2e = direct_spin1.absorb_h1e(h1e, eri, norb, nelec, .5) ci1 = direct_spin1.contract_2e(h2e, fcivec, norb, nelec, link_index) return numpy.dot(fcivec.reshape(-1), ci1.reshape(-1))
def kernel(mc, nroots): mc_1root = mc mc_1root = mcscf.CASCI(mc._scf, mc.ncas, mc.nelecas) mc_1root.fcisolver = fci.solver(mc._scf.mol, singlet=False, symm=False) mc_1root.mo_coeff = mc.mo_coeff nao, nmo = mc.mo_coeff.shape ncas, ncore = mc.ncas, mc.ncore nocc = ncas + ncore mo_cas = mc.mo_coeff[:, ncore:nocc] casdm1 = mc.fcisolver.states_make_rdm1(mc.ci, mc_1root.ncas, mc_1root.nelecas) dm1 = np.dot(casdm1, mo_cas.T) dm1 = np.dot(mo_cas, dm1).transpose(1, 0, 2) aeri = ao2mo.restore(1, mc.get_h2eff(mc.mo_coeff), mc.ncas) rows, col = np.tril_indices(nroots, k=-1) pairs = len(rows) ci_array = np.array(mc.ci) u = np.identity(nroots) t = np.zeros((nroots, nroots)) t_old = np.zeros((nroots, nroots)) trans12_tdm1, trans12_tdm2 = mc.fcisolver.states_trans_rdm12( ci_array[col], ci_array[rows], mc_1root.ncas, mc_1root.nelecas) trans12_tdm1_array = np.array(trans12_tdm1) tdm1 = np.dot(trans12_tdm1_array, mo_cas.T) tdm1 = np.dot(mo_cas, tdm1).transpose(1, 0, 2) log = lib.logger.new_logger(mc, mc.verbose) log.info("Entering cmspdft3.kernel") # MRH: PySCF convention is never to use the "print" function in method code. # All I/O in published PySCF code goes through the pyscf.lib.logger module. # This prints data to an output file specified by the user like # mol = gto.M (atom = ..., verbose = lib.logger.INFO, output = 'fname.log') # If no output file is specified, it goes to the STDOUT as if it were a print # command. The different pyscf.lib.logger functions, "note", "info", "debug", # and some others, correspond to different levels of verbosity. In the above # example, log.note and log.info commands would print information to # 'fname.log', but "debug" commands, which are a higher level, are skipped. # I changed all the print commands in this function to log.debug or log.info # commands. #Print the First Coulomb Energy j = mc_1root._scf.get_j(dm=dm1) e_coul = (j * dm1).sum((1, 2)) / 2 log.debug("Reference state e_coul {}".format(e_coul)) log.info("Reference state e_coul sum = %f", e_coul.sum()) # MRH: There are a couple of things going on here. First of all, the two # statements share different levels of detail, so I use different verbosities: # "debug" (only printed if the user has verbose=DEBUG or higher) for the list # of all Coulomb energies and "info" (verbose=INFO is the default if an output # file is specified) for the sum. Secondly, I am showing two separate ways of # formatting strings in Python. log.xxx () knows how to parse the "old" Python # string-formatting rules, which is what I've used in the log.info command, but # it doesn't know how to interpret a list of arguments the way "print" does, so # you have to pass it only one single string. The other way to do this is with # "new" python formatting, which I think is simpler: # print ("{} and {} and {}".format (a, b, c)) # is basically identical to # print (a, "and", b, "and", c) # but you can only use the former in log.xxx: # log.xxx ("{} and {} and {}".format (a, b, c)) #Hessian Functions # MRH: Here's a way do not do those nested loops and conditionals and therefore # have less to worry about re indentation. Print out "rowscol2ind" and it should # be clear how this works. rowscol2ind = np.zeros((nroots, nroots), dtype=np.integer) rowscol2ind[(rows, col)] = list(range(pairs)) # 0,1,2,3,... rowscol2ind += rowscol2ind.T # Now it can handle both k>l and l>k rowscol2ind[np.diag_indices( nroots)] = -1 # Makes sure it crashes if you look # for k==l, since that's the density # matrix and we compute that with a # different function. def w_klmn(k, l, m, n, dm, tdm): # casdm1 = mc.fcisolver.states_make_rdm1 (ci,mc_1root.ncas,mc_1root.nelecas) # MRH: don't you also need to put casdm1 into the AO basis/recompute dm1? # dm1 = np.dot(casdm1,mo_cas.T) # dm1 = np.dot(mo_cas,dm1).transpose(1,0,2) # MRH: IMO it's more elegant to rewrite these functions to take the density # matrices dm1_cirot and tdm1 as you compute them for the gradient downstairs, # since it's the same density matrices for both derivatives. Then this whole # function could be like 5 lines long: d = dm[k] if k == l else tdm[rowscol2ind[k, l]] dm1_g = mc_1root._scf.get_j(dm=d) d = dm[m] if m == n else tdm[rowscol2ind[m, n]] w = (dm1_g * d).sum((0, 1)) return w # But I'll leave it like this so you can see what's going on more clearly in # the github "files changed" tab. # trans12_tdm1, trans12_tdm2 = mc.fcisolver.states_trans_rdm12(ci[col],ci[rows],mc_1root.ncas,mc_1root.nelecas) # if k==l: # dm1_g = mc_1root._scf.get_j (dm=dm1[k]) # else: # ind = rowscol2ind[k,l] # tdm1_2 = np.dot(trans12_tdm1[ind],mo_cas.T) # tdm1_2 = np.dot(mo_cas,tdm1_2).transpose(1,0) # dm1_g = mc_1root._scf.get_j(dm=tdm1_2) # if m==n: # w = (dm1_g*dm1[n]).sum((0,1)) # else: # ind2 = rowscol2ind[m,n] # tdm1_2 = np.dot(trans12_tdm1_array[ind2],mo_cas.T) # tdm1_2 = np.dot(mo_cas,tdm1_2).transpose(1,0) # w = (dm1_g*tdm1_2).sum((0,1)) def v_klmn(k, l, m, n, dm, tdm): if l == m: v = w_klmn(k, n, k, k, dm, tdm) - w_klmn( k, n, l, l, dm, tdm) + w_klmn(n, k, n, n, dm, tdm) - w_klmn( k, n, m, m, dm, tdm) - 4 * w_klmn(k, l, m, n, dm, tdm) #Compute Classical Couloumb Energy casdm1 = mc.fcisolver.states_make_rdm1(mc.ci, mc_1root.ncas, mc_1root.nelecas) dm1 = np.dot(casdm1, mo_cas.T) dm1 = np.dot(mo_cas, dm1).transpose(1, 0, 2) print("dm1", np.shape(dm1)) # print("dm1",dm1) j = mc_1root._scf.get_j(dm=dm1) # print("j",j) e_coul = (j * dm1).sum((1, 2)) / 2 print("e_coul_1", e_coul) #Transition Density for i in range(0, nroots): ci_coeff = mc.ci[i] print("ci", i, ci_coeff) print("mc.ci", type(mc.ci)) rows, col = np.tril_indices(nroots, k=-1) pairs = len(rows) print("rows", rows, "col", col, "pairs", pairs) print("mc.ci shape", np.shape(mc.ci)) mc.ci_array = np.array(mc.ci) row_ci = mc.ci_array[rows] # print ("mc.ci[rows]",row_ci) col_ci = mc.ci_array[col] trans12_tdm1, trans12_tdm2 = mc.fcisolver.states_trans_rdm12( col_ci, row_ci, mc_1root.ncas, mc_1root.nelecas) print("trans12_tdm1", np.shape(trans12_tdm1)) #Load in the two-electron integrals aeri = ao2mo.restore(1, mc.get_h2eff(mc.mo_coeff), mc.ncas) # print("aeri", aeri) # print("eri shape", np.shape(aeri)) #Initialize rotation matrix u = np.identity(mc.fcisolver.nroots) print("U :", u) t = np.zeros((nroots, nroots)) u_lt = linalg.expm(t) print("u_lt", u_lt) ###################################################### #Begin Loop ###################################################### #Gradients trans12_tdm1_array = np.array(trans12_tdm1) grad = np.zeros(pairs) for i in range(pairs): ind = rows[i] grad[i] = (casdm1[ind] * trans12_tdm1_array[i] * aeri).sum( (0, 1, 2, 3)) print('ind,i', ind, ',', i) grad = 4 * grad print('grad', grad) # gradnorm = np.linalg.norm(grad) # print ("grad norm", gradnorm) print("grad shape", np.shape(grad)) #Try 2 # j1 = mc_1root._scf.get_jk(mc._scf.mol, dm1, 'ijkl,lk->ij', intor='int2e_ip1_sph', aosym='s2kl', comp=3) # print("j1",j1) #Gradient try 3 # grad3 = trans12_tdm1_array*casdm1 * aeri # print ("grad3",grad3) # print("dm1",np.shape(dm1)) grad3 = np.zeros(pairs) # for i in range (pairs): # dg = mc_1root._scf.get_j (dm=dm1[col]) # print("dg shape",np.shape(dg)) tdm1 = np.dot(trans12_tdm1_array, mo_cas.T) tdm1 = np.dot(mo_cas, tdm1).transpose(1, 0, 2) dg = mc_1root._scf.get_j(dm=tdm1) print("tdm1_array", trans12_tdm1) # tdm1_1 = tdm1[i] # grad3 = (dg*tdm1).sum((1,2)) grad3 = 4 * (dg * dm1[rows]).sum((1, 2)) print("grad 3 shape", np.shape(grad3)) # grad3=grad3.sum((1,2)) print("grad3 rows", grad3) # print("grad3*4", grad3*4) gradnorm3 = np.linalg.norm(grad3) print("grad norm", gradnorm3) grad4 = 4 * (dg * dm1[col]).sum((1, 2)) print("grad3 col", grad4) gradnorm4 = np.linalg.norm(grad4) print("grad norm", gradnorm4) print("grad norm 3+4", gradnorm4 + gradnorm3) gradsum = grad4 + grad3 print("grad sum", gradsum) print("grad sum norm", np.linalg.norm(gradsum)) #GRADIENT TRY 5 # grad5 = np.zeros((nroots,pairs)) # for i in range(nroots): # dg = mc_1root._scf.get_j(dm=dm1[i]) # for j in range(pairs): # if rows[j]==i or col[j]==i: # grad5[i,j]=(dg*tdm1[j]).sum((0,1)) # print("grad5",grad5) #Hessian def w_klmn(k, l, m, n): if k == l: dm1_g = mc_1root._scf.get_j(dm=dm1[k]) else: for i in range(pairs): if rows[i] == k: if col[i] == l: ind = i # print("ind",ind) if col[i] == k: if rows[i] == l: ind = i # print("ind",ind) tdm1_2 = np.dot(trans12_tdm1[ind], mo_cas.T) # print("tdm1",np.shape(tdm1_2)) tdm1_2 = np.dot(mo_cas, tdm1_2).transpose(1, 0) # print("tdm1", np.shape(tdm1_2)) dm1_g = mc_1root._scf.get_j(dm=tdm1_2) if m == n: w = (dm1_g * dm1[n]).sum((0, 1)) else: for i in range(pairs): if rows[i] == m: if col[i] == n: ind2 = i # print("ind2",ind2) if col[i] == m: if rows[i] == n: ind2 = i # print("ind2",ind2) tdm1_2 = np.dot(trans12_tdm1_array[ind2], mo_cas.T) tdm1_2 = np.dot(mo_cas, tdm1_2).transpose(1, 0) w = (dm1_g * tdm1_2).sum((0, 1)) return w # for k in range (nroots): # for l in range(nroots): # for m in range(nroots): # for n in range nroots: def v_klmn(k, l, m, n): if l == m: v = w_klmn(k, n, k, k) - w_klmn(k, n, l, l) + w_klmn( n, k, n, n) - w_klmn(k, n, m, m) - 4 * w_klmn(k, l, m, n) else: v = 0 return v #Rotate to XMS States h1, h0 = mc.get_h1eff() # print("fvecs_nstates",fvecs_nstates) # ci_array = np.tensordot(fvecs_nstates, ci_array, 1) casdm1 = mc.fcisolver.states_make_rdm1(ci_array, mc_1root.ncas, mc_1root.nelecas) dm1 = np.dot(casdm1, mo_cas.T) dm1 = np.dot(mo_cas, dm1).transpose(1, 0, 2) #Loop Initializations dm1_old = dm1 maxiter = 150 ci_old = ci_array thrs = 1.0e-06 e_coul_old = e_coul conv = False ################# #Begin Loop ################# for it in range(maxiter): log.info("****iter {} ***********".format(it)) # Form U U = linalg.expm(t) # Rotate T ci_rot = np.tensordot(U, ci_old, 1) # Form New DM1s casdm1_rot = mc.fcisolver.states_make_rdm1(ci_rot, mc_1root.ncas, mc_1root.nelecas) dm1_cirot = np.dot(casdm1_rot, mo_cas.T) dm1_cirot = np.dot(mo_cas, dm1_cirot).transpose(1, 0, 2) dm1_cirot = np.array(dm1_cirot) # Form New TDM trans12_tdm1_rot, trans12_tdm2 = mc.fcisolver.states_trans_rdm12( ci_rot[col], ci_rot[rows], mc_1root.ncas, mc_1root.nelecas) trans12_tdm1_array = np.array(trans12_tdm1_rot) tdm1 = np.dot(trans12_tdm1_array, mo_cas.T) tdm1 = np.dot(mo_cas, tdm1).transpose(1, 0, 2) # Print New E coul and difference j = mc_1root._scf.get_j(dm=dm1_cirot) e_coul_new = (j * dm1_cirot).sum((1, 2)) / 2 log.info("Sum e_coul = {} ; difference = {}".format( e_coul_new.sum(), e_coul_new.sum() - e_coul_old.sum())) # Compute Gradient dg = mc_1root._scf.get_j(dm=tdm1) grad1 = (dg * dm1_cirot[rows]).sum((1, 2)) grad2 = (dg * dm1_cirot[col]).sum((1, 2)) grad = 2 * (grad1 - grad2) grad_norm = np.linalg.norm(grad) log.debug("grad: {}".format(grad)) log.info("grad norm = %f", grad_norm) # if grad_norm < thrs: # conv = True # ci_final = ci_rot # best just to use ci_rot # break # print("hello") # Hessian hess = np.zeros((pairs, pairs)) # MRH: you can do this whole nested loop, defining all six indices, in one (1) line: for (i, (k, l)), (j, (m, n)) in product(enumerate(zip(rows, col)), repeat=2): # To explain: # for k,l in zip (rows, col) # ^ Puts elements of "rows" in "k" and elements of "col" in "l" # Advances through "rows" and "col" simultaneously, so it's always # the nth element of "rows" and the nth element of "col" # So obviously, "rows" and "col" have to be the same size. # You don't need parentheses on the right-hand side at this point # for i, (k, l) in enumerate (zip (rows,col)): # ^ Iterates over the zip and puts its elements in (k, l), and also # counts upwards from zero and puts the count in "i". Here, you do # need parentheses so that the interpreter understands that "k" # and "l" are grouped together, separate from "i". # the uncommented line # ^ "Product" is like "zip", except instead of advancing through the # arguments simultaneously, it gives you all combinations of all # elements. Also, I've only entered one argument and asked it to # repeat it twice. I would have gotten the same result with, i.e., # product (enumerate, enumerate). Note that I had to import the # function "product" from the built-in Python module "itertools", # which happens on line 3. Again, you need parentheses to show # which indices are grouped together. # Using stuff like this really helps keep the indentations under # control. Nested loops and conditionals are really slow in Python, # so it's a good idea to combine them using tools like this whenever # possible. hess[i, j] = v_klmn(k, l, m, n, dm1_cirot, tdm1) + v_klmn( l, k, n, m, dm1_cirot, tdm1) - v_klmn( k, l, n, m, dm1_cirot, tdm1) - v_klmn( l, k, m, n, dm1_cirot, tdm1) # MRH: print some diagnostic stuff, but only do the potentially-expensive # diagonalization if the user-specified verbosity is high enough # if log.verbose >= lib.logger.DEBUG: evals, evecs = linalg.eigh(hess) evecs = np.array(evecs) log.info("Hessian eigenvalues: {}".format(evals)) hess1 = hess # print("hess1",hess) # print("evals",evals) for i in range(pairs): if 1E-09 > evals[i] and evals[i] > -1E-09: log.info("Hess is singular!") if evals[i] > 0: neg = False break neg = True log.info("Hess diag is neg? {}".format(neg)) # If the diagonals are positive make them negative: if neg == False: for i in range(pairs): if evals[i] > 0: evals[i] = -evals[i] # Remake the Hessian diag = np.identity(pairs) * evals hess = np.dot(np.dot(evecs, diag), evecs.T) hess2 = hess # print("hess2", hess) # print("difference between hessian", hess2-hess1) # Make T t_add = linalg.solve(hess, -grad) t[:] = 0 t[np.tril_indices(t.shape[0], k=-1)] = t_add t = t - t.T # t = t + t_old # MRH: I don't think you add them. They're expressed in different # bases, and anyway ci_rot already has the effect of t_old in it. On # iteration zero, say ci_rot is called ci0. Then on iteration 1, it's # ci1 = expm (t1) ci0 # Then on iteration 2, it's # ci2 = expm (t2) ci1 = expm (t2) expm (t1) ci0 # and so forth. So you don't need to keep the running sum. t_old = t.copy() # Reset Old Values ci_old = ci_rot e_coul_old = e_coul_new if grad_norm < thrs and neg == True: conv = True break ######################### # End Loop if conv: log.note("CMS-PDFT intermediate state determination CONVERGED") else: log.note(("CMS-PDFT intermediate state determination did not converge" " after {} cycles").format(it)) # Intermediate Energies # Run MC-PDFT #mc.ci = ci_final #E_int = np.zeros((nroots)) #with lib.temporary_env (mc, ci=ci_rot): # # This ^ ~temporarily sets mc.ci to ci_rot # # As soon as you leave this indent block, it returns to # # whatever it was before. Convenient! Of course, it would # # be WAY BETTER for me to just implement ci as a kwarg # # in mcpdft.kernel. # for i in range(nroots): # E_int [i]= mcpdft.mcpdft.kernel(mc,mc.otfnal,root=i)[0] E_int = np.asarray( [mcpdft.mcpdft.kernel(mc, ot=mc.otfnal, ci=c)[0] for c in ci_rot]) log.info("CMS-PDFT intermediate state energies: {}".format(E_int)) #Compute the full Hamiltonian h1, h0 = mc.get_h1eff() h2 = mc.get_h2eff() h2eff = direct_spin1.absorb_h1e(h1, h2, mc.ncas, mc.nelecas, 0.5) hc_all = [ direct_spin1.contract_2e(h2eff, c, mc.ncas, mc.nelecas) for c in ci_rot ] Ham = np.tensordot(ci_rot, hc_all, axes=((1, 2), (1, 2))) # print ("ham", Ham) for i in range(nroots): Ham[i, i] = E_int[i] # print("ham",Ham) log.info("Effective hamiltonian: {}".format(Ham)) e_cms, e_vecs = linalg.eigh(Ham) # print("e_cms", e_cms) log.info("CMS-PDFT final state energies: {}".format(e_cms)) return conv, E_int, ci_rot