def test_sub_addrs(self): addrs = cistring.sub_addrs(6, 3, (0, 2, 3, 5)) self.assertEqual([bin(x) for x in cistring.addrs2str(6, 3, addrs)], ['0b1101', '0b100101', '0b101001', '0b101100']) addrs = cistring.sub_addrs(6, 3, (3, 0, 5, 2)) self.assertEqual([bin(x) for x in cistring.addrs2str(6, 3, addrs)], ['0b101001', '0b1101', '0b101100', '0b100101']) addrs = cistring.sub_addrs(6, 3, (3, 0, 5, 2), 2) self.assertEqual([bin(x) for x in cistring.addrs2str(6, 3, addrs)], [ '0b111', '0b1011', '0b1110', '0b10101', '0b11001', '0b11100', '0b100011', '0b100110', '0b101010', '0b110001', '0b110100', '0b111000' ]) addrs = cistring.sub_addrs(6, 3, (0, 2, 3, 5), 2) self.assertEqual([bin(x) for x in cistring.addrs2str(6, 3, addrs)], [ '0b111', '0b1011', '0b1110', '0b10101', '0b11001', '0b11100', '0b100011', '0b100110', '0b101010', '0b110001', '0b110100', '0b111000' ]) addrs = cistring.sub_addrs(6, 3, (0, 2, 3, 5), 1) self.assertEqual([bin(x) for x in cistring.addrs2str(6, 3, addrs)], ['0b10011', '0b10110', '0b11010', '0b110010'])
def pspace(h1e, eri, norb, nelec, hdiag=None, np=400): '''pspace Hamiltonian to improve Davidson preconditioner. See, CPL, 169, 463 ''' if norb > 63: raise NotImplementedError('norb > 63') neleca, nelecb = _unpack_nelec(nelec) h1e = numpy.ascontiguousarray(h1e) eri = ao2mo.restore(1, eri, norb) nb = cistring.num_strings(norb, nelecb) if hdiag is None: hdiag = make_hdiag(h1e, eri, norb, nelec) if hdiag.size < np: addr = numpy.arange(hdiag.size) else: try: addr = numpy.argpartition(hdiag, np-1)[:np] except AttributeError: addr = numpy.argsort(hdiag)[:np] addra, addrb = divmod(addr, nb) stra = cistring.addrs2str(norb, neleca, addra) strb = cistring.addrs2str(norb, nelecb, addrb) np = len(addr) h0 = numpy.zeros((np,np)) libfci.FCIpspace_h0tril(h0.ctypes.data_as(ctypes.c_void_p), h1e.ctypes.data_as(ctypes.c_void_p), eri.ctypes.data_as(ctypes.c_void_p), stra.ctypes.data_as(ctypes.c_void_p), strb.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(norb), ctypes.c_int(np)) for i in range(np): h0[i,i] = hdiag[addr[i]] h0 = lib.hermi_triu(h0) return addr, h0
def ddaddrs2csdaddrs(norb, neleca, nelecb, ddaddrs): ''' Convert double-determinant (DD) CI vector element addresses, [deta, detb], into configuration-spin-determinant (CSD) vector element addresses, [npair, dconf, sconf, spinstate], to facilitate a later transformation into CSFs. In the CSD format, max (0, neleca + nelecb - norb) <= npair <= nelecb is the number of electron pairs dconf is the address for a particular configuration of npair pairs' use cistring for npair 'electrons' in norb orbitals sconf is the address for a particular configuration of the neleca + nelecb - 2*npair (= nunp) unpaired electrons given dconf; use cistring for nunp electrons in norb - npair orbitals spinstate is the state of (nunp + neleca - nelecb)/2 alpha and (nunp - neleca + nelecb)/2 beta spins; use cistring for (nunp + neleca - nelecb)/2 'electrons' in nunp 'orbitals' Args: norb, neleca, nelecb are integers ddaddrs is an array that specifies double determinant CI vector element address If 1d, interpreted as deta_addr*ndetb + detb_addr If 2d, interpreted as row/column i is interpreted as the ith index pair [deta, detb], if there are 2 columns/rows Returns: csdaddrs, list of integers for the CI vector in the form of a 1d array ''' ddaddrs = format_ddaddrs(norb, neleca, nelecb, ddaddrs) ddstrs = np.asarray([ cistring.addrs2str(norb, neleca, ddaddrs[0]), cistring.addrs2str(norb, nelecb, ddaddrs[1]) ], dtype=np.int64) csdstrs = ddstrs2csdstrs(norb, neleca, nelecb, ddstrs) csdaddrs = csdstrs2csdaddrs(norb, neleca, nelecb, csdstrs) return csdaddrs
def make_hdiag_csf (h1e, eri, norb, nelec, transformer, hdiag_det=None): smult = transformer.smult if hdiag_det is None: hdiag_det = make_hdiag_det (None, h1e, eri, norb, nelec) eri = ao2mo.restore(1, eri, norb) tlib = wlib = 0 neleca, nelecb = _unpack_nelec (nelec) min_npair, npair_csd_offset, npair_dconf_size, npair_sconf_size, npair_sdet_size = get_csdaddrs_shape (norb, neleca, nelecb) _, npair_csf_offset, _, _, npair_csf_size = get_csfvec_shape (norb, neleca, nelecb, smult) npair_econf_size = npair_dconf_size * npair_sconf_size max_npair = min (neleca, nelecb) ncsf_all = count_all_csfs (norb, neleca, nelecb, smult) ndeta_all = cistring.num_strings(norb, neleca) ndetb_all = cistring.num_strings(norb, nelecb) ndet_all = ndeta_all * ndetb_all hdiag_csf = np.ascontiguousarray (np.zeros (ncsf_all, dtype=np.float64)) hdiag_csf_check = np.ones (ncsf_all, dtype=np.bool) for npair in range (min_npair, max_npair+1): ipair = npair - min_npair nconf = npair_econf_size[ipair] ndet = npair_sdet_size[ipair] ncsf = npair_csf_size[ipair] if ncsf == 0: continue nspin = neleca + nelecb - 2*npair csd_offset = npair_csd_offset[ipair] csf_offset = npair_csf_offset[ipair] hdiag_conf = np.ascontiguousarray (np.zeros ((nconf, ndet, ndet), dtype=np.float64)) det_addr = transformer.csd_mask[csd_offset:][:nconf*ndet] if ndet == 1: # Closed-shell singlets assert (ncsf == 1) hdiag_csf[csf_offset:][:nconf] = hdiag_det[det_addr.flat] hdiag_csf_check[csf_offset:][:nconf] = False continue det_addra, det_addrb = divmod (det_addr, ndetb_all) det_stra = np.ascontiguousarray (cistring.addrs2str (norb, neleca, det_addra).reshape (nconf, ndet, order='C')) det_strb = np.ascontiguousarray (cistring.addrs2str (norb, nelecb, det_addrb).reshape (nconf, ndet, order='C')) det_addr = det_addr.reshape (nconf, ndet, order='C') hdiag_conf = np.ascontiguousarray (np.zeros ((nconf, ndet, ndet), dtype=np.float64)) hdiag_conf_det = np.ascontiguousarray (hdiag_det[det_addr], dtype=np.float64) t1 = time.process_time () w1 = time.time () libcsf.FCICSFhdiag (hdiag_conf.ctypes.data_as (ctypes.c_void_p), hdiag_conf_det.ctypes.data_as (ctypes.c_void_p), eri.ctypes.data_as (ctypes.c_void_p), det_stra.ctypes.data_as (ctypes.c_void_p), det_strb.ctypes.data_as (ctypes.c_void_p), ctypes.c_uint (norb), ctypes.c_uint (nconf), ctypes.c_uint (ndet)) tlib += time.process_time () - t1 wlib += time.time () - w1 umat = get_spin_evecs (nspin, neleca, nelecb, smult) hdiag_conf = np.tensordot (hdiag_conf, umat, axes=1) hdiag_conf *= umat[np.newaxis,:,:] hdiag_csf[csf_offset:][:nconf*ncsf] = hdiag_conf.sum (1).ravel (order='C') hdiag_csf_check[csf_offset:][:nconf*ncsf] = False assert (np.count_nonzero (hdiag_csf_check) == 0), np.count_nonzero (hdiag_csf_check) #print ("Time in hdiag_csf library: {}, {}".format (tlib, wlib)) return hdiag_csf
def pspace(h1e, eri, norb, nelec, hdiag=None, np=400): '''pspace Hamiltonian to improve Davidson preconditioner. See, CPL, 169, 463 ''' if norb > 63: raise NotImplementedError('norb > 63') if h1e.dtype == numpy.complex or eri.dtype == numpy.complex: raise NotImplementedError('Complex Hamiltonian') neleca, nelecb = _unpack_nelec(nelec) h1e = numpy.ascontiguousarray(h1e) eri = ao2mo.restore(1, eri, norb) nb = cistring.num_strings(norb, nelecb) if hdiag is None: hdiag = make_hdiag(h1e, eri, norb, nelec) if hdiag.size < np: addr = numpy.arange(hdiag.size) else: try: addr = numpy.argpartition(hdiag, np - 1)[:np].copy() except AttributeError: addr = numpy.argsort(hdiag)[:np].copy() addra, addrb = divmod(addr, nb) stra = cistring.addrs2str(norb, neleca, addra) strb = cistring.addrs2str(norb, nelecb, addrb) np = len(addr) h0 = numpy.zeros((np, np)) libfci.FCIpspace_h0tril(h0.ctypes.data_as(ctypes.c_void_p), h1e.ctypes.data_as(ctypes.c_void_p), eri.ctypes.data_as(ctypes.c_void_p), stra.ctypes.data_as(ctypes.c_void_p), strb.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(norb), ctypes.c_int(np)) HERMITIAN_THRESHOLD = 1e-10 if (abs(h1e - h1e.T).max() < HERMITIAN_THRESHOLD and abs(eri - eri.transpose(1, 0, 3, 2)).max() < HERMITIAN_THRESHOLD): # symmetric Hamiltonian h0 = lib.hermi_triu(h0) else: # Fill the upper triangular part h0 = numpy.asarray(h0, order='F') h1e = numpy.asarray(h1e.T, order='C') eri = numpy.asarray(eri.transpose(1, 0, 3, 2), order='C') libfci.FCIpspace_h0tril(h0.ctypes.data_as(ctypes.c_void_p), h1e.ctypes.data_as(ctypes.c_void_p), eri.ctypes.data_as(ctypes.c_void_p), stra.ctypes.data_as(ctypes.c_void_p), strb.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(norb), ctypes.c_int(np)) idx = numpy.arange(np) h0[idx, idx] = hdiag[addr] return addr, h0
def fock2hilbert(ci, norb, nelec): assert (norb <= MAX_NORB) nelec = _unpack_nelec(nelec) ci = np.asarray(ci).reshape(-1, 2**norb, 2**norb) nroots = ci.shape[0] ndeta = cistring.num_strings(norb, nelec[0]) ndetb = cistring.num_strings(norb, nelec[1]) ci1 = np.empty((nroots, ndeta, ndetb), dtype=ci.dtype) strsa = cistring.addrs2str(norb, nelec[0], list(range(ndeta))) strsb = cistring.addrs2str(norb, nelec[1], list(range(ndetb))) ci1[:, :, :] = ci[:, strsa[:, None], strsb] return ci1
def csfaddrs2str (norb, neleca, nelecb, smult, addrs): npair, domo_addrs, somo_addrs, spincpl_addrs = unpack_csfaddrs (norb, neleca, nelecb, smult, addrs) domo_str = np.zeros (len (addrs), dtype=np.int64) somo_str = np.zeros (len (addrs), dtype=np.int64) spincpl_str = np.zeros (len (addrs), dtype=np.int64) for uniq_npair in np.unique (npair): idx_uniq = (npair == uniq_npair) notdomo = norb - uniq_npair nspin = neleca + nelecb - 2*uniq_npair domo_str[idx_uniq] = cistring.addrs2str (norb, uniq_npair, domo_addrs[idx_uniq]) if uniq_npair > 0 else -1 somo_str[idx_uniq] = cistring.addrs2str (notdomo, nspin, somo_addrs[idx_uniq]) spincpl_str[idx_uniq] = addrs2str (nspin, smult, spincpl_addrs[idx_uniq]) return domo_str, somo_str, spincpl_str
def pspace(h1e, eri, norb, nelec, hdiag=None, np=400): '''pspace Hamiltonian to improve Davidson preconditioner. See, CPL, 169, 463 ''' if norb > 63: raise NotImplementedError('norb > 63') neleca, nelecb = _unpack_nelec(nelec) h1e = numpy.ascontiguousarray(h1e) eri = ao2mo.restore(1, eri, norb) nb = cistring.num_strings(norb, nelecb) if hdiag is None: hdiag = make_hdiag(h1e, eri, norb, nelec) if hdiag.size < np: addr = numpy.arange(hdiag.size) else: try: addr = numpy.argpartition(hdiag, np-1)[:np] except AttributeError: addr = numpy.argsort(hdiag)[:np] addra, addrb = divmod(addr, nb) stra = cistring.addrs2str(norb, neleca, addra) strb = cistring.addrs2str(norb, nelecb, addrb) np = len(addr) h0 = numpy.zeros((np,np)) libfci.FCIpspace_h0tril(h0.ctypes.data_as(ctypes.c_void_p), h1e.ctypes.data_as(ctypes.c_void_p), eri.ctypes.data_as(ctypes.c_void_p), stra.ctypes.data_as(ctypes.c_void_p), strb.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(norb), ctypes.c_int(np)) HERMITIAN_THRESHOLD = 1e-10 if (abs(h1e - h1e.T).max() < HERMITIAN_THRESHOLD and abs(eri - eri.transpose(1,0,3,2)).max() < HERMITIAN_THRESHOLD): # symmetric Hamiltonian h0 = lib.hermi_triu(h0) else: # Fill the upper triangular part h0 = numpy.asarray(h0, order='F') h1e = numpy.asarray(h1e.T, order='C') eri = numpy.asarray(eri.transpose(1,0,3,2), order='C') libfci.FCIpspace_h0tril(h0.ctypes.data_as(ctypes.c_void_p), h1e.ctypes.data_as(ctypes.c_void_p), eri.ctypes.data_as(ctypes.c_void_p), stra.ctypes.data_as(ctypes.c_void_p), strb.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(norb), ctypes.c_int(np)) idx = numpy.arange(np) h0[idx,idx] = hdiag[addr] return addr, h0
def pretty_ddaddrs (norb, neleca, nelecb, ddaddrs): ''' Printable string for dd strings based on their addresses ''' ddaddrs = format_ddaddrs (norb, neleca, nelecb, ddaddrs) ddstrs = np.asarray ([cistring.addrs2str (norb, neleca, ddaddrs[0]), cistring.addrs2str (norb, nelecb, ddaddrs[1])], dtype=np.int64) output = [] for i in range (len (ddstrs[0])): dstra = bin (ddstrs[0,i])[2:] dstrb = bin (ddstrs[1,i])[2:] while len (dstra) < norb: dstra = '0' + dstra while len (dstrb) < norb: dstrb = '0' + dstrb out = dstra + ' ' + dstrb output.append (out) return output
def large_ci(ci, norb, nelec, tol=.1, return_strs=True): '''Search for the largest CI coefficients ''' neleca, nelecb = _unpack(nelec) addra, addrb = numpy.where(abs(ci) > tol) strsa = cistring.addrs2str(norb, neleca, addra) strsb = cistring.addrs2str(norb, nelecb, addrb) if return_strs: strsa = [bin(x) for x in strsa] strsb = [bin(x) for x in strsb] return list(zip(ci[addra, addrb], strsa, strsb)) else: occslsta = cistring._strs2occslst(strsa, norb) occslstb = cistring._strs2occslst(strsb, norb) return list(zip(ci[addra, addrb], occslsta, occslstb))
def test_spin_evecs (nspin, neleca, nelecb, smult, S2mat=None): s = (smult - 1) / 2 ms = (neleca - nelecb) / 2 assert (ms <= s) assert (smult-1 <= nspin) assert (nspin >= neleca + nelecb) na = (nspin + neleca - nelecb) // 2 ndet = special.comb (nspin, na, exact=True) ncsf = count_csfs (nspin, smult) spinstrs = cistring.addrs2str (nspin, na, list (range (ndet))) if S2mat is None: S2mat = np.zeros ((ndet, ndet), dtype=np.float_) twoS = smult - 1 twoMS = int (round (2 * ms)) t_start = time.time () libcsf.FCICSFmakeS2mat (S2mat.ctypes.data_as (ctypes.c_void_p), spinstrs.ctypes.data_as (ctypes.c_void_p), ctypes.c_int (ndet), ctypes.c_int (nspin), ctypes.c_int (twoS), ctypes.c_int (twoMS)) print ("TIME: {} seconds to make S2mat for {} spins with s={}, ms={}".format ( time.time() - t_start, nspin, (smult-1)/2, ms)) print ("MEMORY: {} MB for {}-spin S2 matrix with s={}, ms={}".format (S2mat.nbytes / 1e6, nspin, (smult-1)/2, ms)) umat = get_spin_evecs (nspin, neleca, nelecb, smult) print ("MEMORY: {} MB for {}-spin csfs with s={}, ms={}".format (umat.nbytes / 1e6, nspin, (smult-1)/2, ms)) assert (umat.shape == tuple((ndet, ncsf))), "umat shape should be ({},{}); is {}".format (ndet, ncsf, umat.shape) s = (smult-1)/2 t_start = time.time () isorth = np.allclose (np.dot (umat.T, umat), np.eye (umat.shape[1])) ortherr = linalg.norm (np.dot (umat.T, umat) - np.eye (umat.shape[1])) S2mat_csf = reduce (np.dot, (umat.T, S2mat, umat)) S2mat_csf_comp = s * (s+1) * np.eye (umat.shape[1]) S2mat_csf_err = linalg.norm (S2mat_csf - S2mat_csf_comp) diagsS2 = np.allclose (S2mat_csf, S2mat_csf_comp) passed = isorth and diagsS2 print ("TIME: {} seconds to analyze umat for {} spins with s={}, ms={}".format ( time.time() - t_start, nspin, s, ms)) print (('For a system of {} spins with total spin {} and spin projection {}' ', {} CSFs found from {} determinants by Clebsch-Gordan algorithm').format ( nspin, s, ms, umat.shape[1], len (spinstrs))) print ('Did the Clebsch-Gordan algorithm give orthonormal eigenvectors? {}'.format ( ('NO (err = {})'.format (ortherr), 'Yes')[isorth])) print ('Did the Clebsch-Gordan algorithm diagonalize the S2 matrix with the correct eigenvalues? {}'.format ( ('NO (err = {})'.format (S2mat_csf_err), 'Yes')[diagsS2])) print ('nspin = {}, S = {}, MS = {}: {}'.format (nspin, s, ms, ('FAILED','Passed')[passed])) sys.stdout.flush () return umat, S2mat
def large_ci(ci, norb, nelec, tol=.1, return_strs=True): '''Search for the largest CI coefficients ''' neleca, nelecb = _unpack(nelec) addra, addrb = numpy.where(abs(ci) > tol) if addra.size == 0: # No large CI coefficient > tol addra, addrb = numpy.unravel_index(numpy.argmax(abs(ci)), ci.shape) addra = numpy.asarray([addra]) addrb = numpy.asarray([addrb]) strsa = cistring.addrs2str(norb, neleca, addra) strsb = cistring.addrs2str(norb, nelecb, addrb) if return_strs: strsa = [bin(x) for x in strsa] strsb = [bin(x) for x in strsb] return list(zip(ci[addra, addrb], strsa, strsb)) else: occslsta = cistring._strs2occslst(strsa, norb) occslstb = cistring._strs2occslst(strsb, norb) return list(zip(ci[addra, addrb], occslsta, occslstb))
def test_str2addr(self): self.assertEqual(cistring.str2addr(6, 3, int('0b11001' ,2)), 7) self.assertEqual(cistring.str2addr(6, 3, int('0b11010' ,2)), 8) self.assertEqual(cistring.str2addr(7, 4, int('0b110011',2)), 9) self.assertEqual(cistring.str2addr(6, 3, cistring.addr2str(6, 3, 7)), 7) self.assertEqual(cistring.str2addr(6, 3, cistring.addr2str(6, 3, 8)), 8) self.assertEqual(cistring.str2addr(7, 4, cistring.addr2str(7, 4, 9)), 9) self.assertTrue(all(numpy.arange(20) == cistring.strs2addr(6, 3, cistring.addrs2str(6, 3, range(20)))))
def large_ci(ci, norb, nelec, tol=LARGE_CI_TOL, return_strs=RETURN_STRS): '''Search for the largest CI coefficients ''' neleca, nelecb = _unpack(nelec) addra, addrb = numpy.where(abs(ci) > tol) if addra.size == 0: # No large CI coefficient > tol, search for the largest coefficient addra, addrb = numpy.unravel_index(numpy.argmax(abs(ci)), ci.shape) addra = numpy.asarray([addra]) addrb = numpy.asarray([addrb]) strsa = cistring.addrs2str(norb, neleca, addra) strsb = cistring.addrs2str(norb, nelecb, addrb) if return_strs: strsa = [bin(x) for x in strsa] strsb = [bin(x) for x in strsb] return list(zip(ci[addra,addrb], strsa, strsb)) else: occslsta = cistring._strs2occslst(strsa, norb) occslstb = cistring._strs2occslst(strsb, norb) return list(zip(ci[addra,addrb], occslsta, occslstb))
def test_sub_addrs(self): addrs = cistring.sub_addrs(6, 3, (0,2,3,5)) self.assertEqual([bin(x) for x in cistring.addrs2str(6, 3, addrs)], ['0b1101', '0b100101', '0b101001', '0b101100']) addrs = cistring.sub_addrs(6, 3, (3,0,5,2)) self.assertEqual([bin(x) for x in cistring.addrs2str(6, 3, addrs)], ['0b101001', '0b1101', '0b101100', '0b100101']) addrs = cistring.sub_addrs(6, 3, (3,0,5,2), 2) self.assertEqual([bin(x) for x in cistring.addrs2str(6, 3, addrs)], ['0b111', '0b1011', '0b1110', '0b10101', '0b11001', '0b11100', '0b100011', '0b100110', '0b101010', '0b110001', '0b110100', '0b111000']) addrs = cistring.sub_addrs(6, 3, (0,2,3,5), 2) self.assertEqual([bin(x) for x in cistring.addrs2str(6, 3, addrs)], ['0b111', '0b1011', '0b1110', '0b10101', '0b11001', '0b11100', '0b100011', '0b100110', '0b101010', '0b110001', '0b110100', '0b111000']) addrs = cistring.sub_addrs(6, 3, (0,2,3,5), 1) self.assertEqual([bin(x) for x in cistring.addrs2str(6, 3, addrs)], ['0b10011', '0b10110', '0b11010', '0b110010'])
def pspace(h1e, eri, norb, nelec, hdiag=None, np=400): neleca, nelecb = direct_spin1._unpack_nelec(nelec) h1e_a = numpy.ascontiguousarray(h1e[0]) h1e_b = numpy.ascontiguousarray(h1e[1]) g2e_aa = ao2mo.restore(1, eri[0], norb) g2e_ab = ao2mo.restore(1, eri[1], norb) g2e_bb = ao2mo.restore(1, eri[2], norb) link_indexa = cistring.gen_linkstr_index_trilidx(range(norb), neleca) link_indexb = cistring.gen_linkstr_index_trilidx(range(norb), nelecb) nb = link_indexb.shape[0] if hdiag is None: hdiag = make_hdiag(h1e, eri, norb, nelec) if hdiag.size < np: addr = numpy.arange(hdiag.size) else: try: addr = numpy.argpartition(hdiag, np - 1)[:np] except AttributeError: addr = numpy.argsort(hdiag)[:np] addra = addr // nb addrb = addr % nb stra = cistring.addrs2str(norb, neleca, addra) strb = cistring.addrs2str(norb, nelecb, addrb) np = len(addr) h0 = numpy.zeros((np, np)) libfci.FCIpspace_h0tril_uhf(h0.ctypes.data_as(ctypes.c_void_p), h1e_a.ctypes.data_as(ctypes.c_void_p), h1e_b.ctypes.data_as(ctypes.c_void_p), g2e_aa.ctypes.data_as(ctypes.c_void_p), g2e_ab.ctypes.data_as(ctypes.c_void_p), g2e_bb.ctypes.data_as(ctypes.c_void_p), stra.ctypes.data_as(ctypes.c_void_p), strb.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(norb), ctypes.c_int(np)) for i in range(np): h0[i, i] = hdiag[addr[i]] h0 = lib.hermi_triu(h0) return addr, h0
def large_ci(ci, norb, nelec, tol=LARGE_CI_TOL, return_strs=RETURN_STRS): '''Search for the largest CI coefficients ''' neleca, nelecb = _unpack_nelec(nelec) na = cistring.num_strings(norb, neleca) nb = cistring.num_strings(norb, nelecb) assert (ci.shape == (na, nb)) addra, addrb = numpy.where(abs(ci) > tol) if addra.size == 0: # No large CI coefficient > tol, search for the largest coefficient addra, addrb = numpy.unravel_index(numpy.argmax(abs(ci)), ci.shape) addra = numpy.asarray([addra]) addrb = numpy.asarray([addrb]) strsa = cistring.addrs2str(norb, neleca, addra) strsb = cistring.addrs2str(norb, nelecb, addrb) if return_strs: strsa = [bin(x) for x in strsa] strsb = [bin(x) for x in strsb] return list(zip(ci[addra, addrb], strsa, strsb)) else: occslsta = cistring._strs2occslst(strsa, norb) occslstb = cistring._strs2occslst(strsb, norb) return list(zip(ci[addra, addrb], occslsta, occslstb))
def test_str2addr(self): self.assertEqual(cistring.str2addr(6, 3, int('0b11001', 2)), 7) self.assertEqual(cistring.str2addr(6, 3, int('0b11010', 2)), 8) self.assertEqual(cistring.str2addr(7, 4, int('0b110011', 2)), 9) self.assertEqual(cistring.str2addr(6, 3, cistring.addr2str(6, 3, 7)), 7) self.assertEqual(cistring.str2addr(6, 3, cistring.addr2str(6, 3, 8)), 8) self.assertEqual(cistring.str2addr(7, 4, cistring.addr2str(7, 4, 9)), 9) self.assertTrue( all( numpy.arange(20) == cistring.strs2addr( 6, 3, cistring.addrs2str(6, 3, range(20)))))
def get_scstrs (nspin, smult): ''' This is not a great way to do this, but I seriously can't think of any straightforward way to put the coupling strings in order... ''' if (smult >= nspin): return np.ones ((0), dtype=np.int64) elif (nspin == 0): return np.zeros ((1), dtype=np.int64) assert (int (round (smult + nspin)) % 2 == 1), "npsin = {}; 2S+1 = {}".format (nspin, smult) nup = (nspin + smult - 1) // 2 ndet = int (special.comb (nspin, nup)) scstrs = cistring.addrs2str (nspin, nup, list (range (ndet))) mask = np.ones (len (scstrs), dtype=np.bool_) libcsf.FCICSFgetscstrs (scstrs.ctypes.data_as (ctypes.c_void_p), mask.ctypes.data_as (ctypes.c_void_p), ctypes.c_int (len (scstrs)), ctypes.c_int (nspin)) return np.ascontiguousarray (scstrs[mask], dtype=np.int64)
def get_spin_evecs(nspin, neleca, nelecb, smult): ms = (neleca - nelecb) / 2 s = (smult - 1) / 2 assert (neleca >= nelecb) assert (neleca - nelecb <= smult - 1) assert (neleca - nelecb <= nspin) assert ((neleca - nelecb) % 2 == (smult - 1) % 2) assert ((neleca - nelecb) % 2 == nspin % 2) na = (nspin + neleca - nelecb) // 2 ndet = special.comb(nspin, na, exact=True) ncsf = count_csfs(nspin, smult) t_start = time.time() spinstrs = cistring.addrs2str(nspin, na, list(range(ndet))) assert (len(spinstrs) == ndet ), "should have {} spin strings; have {} (nspin={}, ms={}".format( ndet, len(spinstrs), nspin, ms) t_start = time.time() scstrs = addrs2str(nspin, smult, list(range(ncsf))) assert ( len(scstrs) == ncsf ), "should have {} coupling strings; have {} (nspin={}, s={})".format( ncsf, len(scstrs), nspin, s) umat = np.ones((ndet, ncsf), dtype=np.float_) twoS = smult - 1 twoMS = neleca - nelecb t_start = time.time() libcsf.FCICSFmakecsf(umat.ctypes.data_as(ctypes.c_void_p), spinstrs.ctypes.data_as(ctypes.c_void_p), scstrs.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(nspin), ctypes.c_int(ndet), ctypes.c_int(ncsf), ctypes.c_int(twoS), ctypes.c_int(twoMS)) return umat
def make_hdiag_csf_slower (h1e, eri, norb, nelec, transformer, hdiag_det=None): ''' This is tricky because I need the diagonal blocks for each configuration in order to get the correct csf hdiag values, not just the diagonal elements for each determinant. ''' smult = transformer.smult t0, w0 = time.process_time (), time.time () tstr = tlib = tloop = wstr = wlib = wloop = 0 if hdiag_det is None: hdiag_det = make_hdiag_det (None, h1e, eri, norb, nelec) eri = ao2mo.restore(1, eri, norb) neleca, nelecb = _unpack_nelec (nelec) min_npair, npair_csd_offset, npair_dconf_size, npair_sconf_size, npair_sdet_size = get_csdaddrs_shape (norb, neleca, nelecb) _, npair_csf_offset, _, _, npair_csf_size = get_csfvec_shape (norb, neleca, nelecb, smult) npair_econf_size = npair_dconf_size * npair_sconf_size max_npair = min (neleca, nelecb) ncsf_all = count_all_csfs (norb, neleca, nelecb, smult) ndeta_all = cistring.num_strings(norb, neleca) ndetb_all = cistring.num_strings(norb, nelecb) ndet_all = ndeta_all * ndetb_all hdiag_csf = np.ascontiguousarray (np.zeros (ncsf_all, dtype=np.float64)) hdiag_csf_check = np.ones (ncsf_all, dtype=np.bool) for npair in range (min_npair, max_npair+1): ipair = npair - min_npair nconf = npair_econf_size[ipair] ndet = npair_sdet_size[ipair] ncsf = npair_csf_size[ipair] if ncsf == 0: continue nspin = neleca + nelecb - 2*npair csd_offset = npair_csd_offset[ipair] csf_offset = npair_csf_offset[ipair] hdiag_conf = np.ascontiguousarray (np.zeros ((nconf, ndet, ndet), dtype=np.float64)) det_addr = transformer.csd_mask[csd_offset:][:nconf*ndet] if ndet == 1: # Closed-shell singlets assert (ncsf == 1) hdiag_csf[csf_offset:][:nconf] = hdiag_det[det_addr.flat] hdiag_csf_check[csf_offset:][:nconf] = False continue umat = get_spin_evecs (nspin, neleca, nelecb, smult) det_addra, det_addrb = divmod (det_addr, ndetb_all) t1, w1 = time.process_time (), time.time () det_stra = cistring.addrs2str (norb, neleca, det_addra).reshape (nconf, ndet, order='C') det_strb = cistring.addrs2str (norb, nelecb, det_addrb).reshape (nconf, ndet, order='C') tstr += time.process_time () - t1 wstr += time.time () - w1 det_addr = det_addr.reshape (nconf, ndet, order='C') diag_idx = np.diag_indices (ndet) triu_idx = np.triu_indices (ndet) ipair_check = 0 # It looks like the library call below is, itself, usually responsible for about 50% of the # clock and wall time that this function consumes. t1, w1 = time.process_time (), time.time () for iconf in range (nconf): addr = det_addr[iconf] assert (len (addr) == ndet) stra = det_stra[iconf] strb = det_strb[iconf] t2, w2 = time.process_time (), time.time () libfci.FCIpspace_h0tril(hdiag_conf[iconf].ctypes.data_as(ctypes.c_void_p), h1e.ctypes.data_as(ctypes.c_void_p), eri.ctypes.data_as(ctypes.c_void_p), stra.ctypes.data_as(ctypes.c_void_p), strb.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(norb), ctypes.c_int(ndet)) tlib += time.process_time () - t2 wlib += time.time () - w2 #hdiag_conf[iconf][diag_idx] = hdiag_det[addr] #hdiag_conf[iconf] = lib.hermi_triu(hdiag_conf[iconf]) for iconf in range (nconf): hdiag_conf[iconf] = lib.hermi_triu (hdiag_conf[iconf]) for iconf in range (nconf): hdiag_conf[iconf][diag_idx] = hdiag_det[det_addr[iconf]] tloop += time.process_time () - t1 wloop += time.time () - w1 hdiag_conf = np.tensordot (hdiag_conf, umat, axes=1) hdiag_conf = (hdiag_conf * umat[np.newaxis,:,:]).sum (1) hdiag_csf[csf_offset:][:nconf*ncsf] = hdiag_conf.ravel (order='C') hdiag_csf_check[csf_offset:][:nconf*ncsf] = False assert (np.count_nonzero (hdiag_csf_check) == 0), np.count_nonzero (hdiag_csf_check) #print ("Total time in hdiag_csf: {}, {}".format (time.process_time () - t0, time.time () - w0)) #print (" Loop: {}, {}".format (tloop, wloop)) #print (" Library: {}, {}".format (tlib, wlib)) #print (" Cistring: {}, {}".format (tstr, wstr)) return hdiag_csf
dtype=np.bool_) # Boolean index array for CI vector ref = np.array([0]) # HF determinant is usually addr = 0 addr[ref[0], ref[0]] = True sing_addrs = [] for links, nelec, sp in zip(linkstr, mc.nelecas, ('up', 'down')): redun = ref sing = np.setdiff1d(links[ref, :, 2], redun) # E_pq|ref> = |single> (unique only) redun = np.append(ref, sing) doub = np.setdiff1d(links[sing, :, 2], redun) # E_pq|single> = |double> (unique only) print( "Reference, single excitation, and double excitation addresses for {}-spin electrons:" .format(sp), ref, sing, doub) print("Reference string for {}-spin:".format(sp), bin(cistring.addrs2str(mc.ncas, nelec, ref)[0])) print("Single-excitation strings for {}-spin:".format(sp), [bin(x) for x in cistring.addrs2str(mc.ncas, nelec, sing)]) print("Double-excitation strings for {}-spin:".format(sp), [bin(x) for x in cistring.addrs2str(mc.ncas, nelec, doub)]) if sp == 'up': # Same-spin single and double excitations addr[sing, 0] = True addr[doub, 0] = True elif sp == 'down': addr[0, sing] = True addr[0, doub] = True sing_addrs.append(sing) addr[np.ix_( *sing_addrs)] = True # Combine the two spin singles to get ab-> doubles cisd = mc.ci[addr] print("Norm of ref+singles+doubles part of FCI vector:", linalg.norm(cisd))
def pspace (fci, h1e, eri, norb, nelec, transformer, hdiag_det=None, hdiag_csf=None, npsp=200): ''' Note that getting pspace for npsp CSFs is substantially more costly than getting it for npsp determinants, until I write code than can evaluate Hamiltonian matrix elements of CSFs directly. On the other hand a pspace of determinants contains many redundant degrees of freedom for the same reason. Therefore I have reduced the default pspace size by a factor of 2.''' if norb > 63: raise NotImplementedError('norb > 63') t0 = (time.process_time (), time.time ()) neleca, nelecb = _unpack_nelec(nelec) h1e = np.ascontiguousarray(h1e) eri = ao2mo.restore(1, eri, norb) nb = cistring.num_strings(norb, nelecb) if hdiag_det is None: hdiag_det = fci.make_hdiag(h1e, eri, norb, nelec) if hdiag_csf is None: hdiag_csf = fci.make_hdiag_csf(h1e, eri, norb, nelec, hdiag_det=hdiag_det) csf_addr = np.arange (hdiag_csf.size, dtype=np.int) if transformer.wfnsym is None: ncsf_sym = hdiag_csf.size else: idx_sym = transformer.confsym[transformer.econf_csf_mask] == transformer.wfnsym ncsf_sym = np.count_nonzero (idx_sym) csf_addr = csf_addr[idx_sym] if ncsf_sym > npsp: try: csf_addr = csf_addr[np.argpartition(hdiag_csf[csf_addr], npsp-1)[:npsp]] except AttributeError: csf_addr = csf_addr[np.argsort(hdiag_csf[csf_addr])[:npsp]] # To build econf_addr = np.unique (transformer.econf_csf_mask[csf_addr]) det_addr = np.concatenate ([np.nonzero (transformer.econf_det_mask == conf)[0] for conf in econf_addr]) lib.logger.debug (fci, ("csf.pspace: Lowest-energy %s CSFs correspond to %s configurations" " which are spanned by %s determinants"), npsp, econf_addr.size, det_addr.size) addra, addrb = divmod(det_addr, nb) stra = cistring.addrs2str(norb, neleca, addra) strb = cistring.addrs2str(norb, nelecb, addrb) npsp_det = len(det_addr) h0 = np.zeros((npsp_det,npsp_det)) h1e_ab = unpack_h1e_ab (h1e) h1e_a = np.ascontiguousarray(h1e_ab[0]) h1e_b = np.ascontiguousarray(h1e_ab[1]) g2e = ao2mo.restore(1, eri, norb) g2e_ab = g2e_bb = g2e_aa = g2e _debug_g2e (fci, g2e, eri, norb) # Exploring g2e nan bug; remove later? t0 = lib.logger.timer (fci, "csf.pspace: index manipulation", *t0) libfci.FCIpspace_h0tril_uhf(h0.ctypes.data_as(ctypes.c_void_p), h1e_a.ctypes.data_as(ctypes.c_void_p), h1e_b.ctypes.data_as(ctypes.c_void_p), g2e_aa.ctypes.data_as(ctypes.c_void_p), g2e_ab.ctypes.data_as(ctypes.c_void_p), g2e_bb.ctypes.data_as(ctypes.c_void_p), stra.ctypes.data_as(ctypes.c_void_p), strb.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(norb), ctypes.c_int(npsp_det)) t0 = lib.logger.timer (fci, "csf.pspace: pspace Hamiltonian in determinant basis", *t0) for i in range(npsp_det): h0[i,i] = hdiag_det[det_addr[i]] h0 = lib.hermi_triu(h0) try: if fci.verbose >= lib.logger.DEBUG: evals_before = scipy.linalg.eigh (h0)[0] except ValueError as e: lib.logger.debug (fci, ("ERROR: h0 has {} infs, {} nans; h1e_a has {} infs, {} nans; " "h1e_b has {} infs, {} nans; g2e has {} infs, {} nans, norb = {}, npsp_det = {}").format ( np.count_nonzero (np.isinf (h0)), np.count_nonzero (np.isnan (h0)), np.count_nonzero (np.isinf (h1e_a)), np.count_nonzero (np.isnan (h1e_a)), np.count_nonzero (np.isinf (h1e_b)), np.count_nonzero (np.isnan (h1e_b)), np.count_nonzero (np.isinf (g2e)), np.count_nonzero (np.isnan (g2e)), norb, npsp_det)) evals_before = np.zeros (npsp_det) h0, csf_addr = transformer.mat_det2csf_confspace (h0, econf_addr) t0 = lib.logger.timer (fci, "csf.pspace: transform pspace Hamiltonian into CSF basis", *t0) if fci.verbose >= lib.logger.DEBUG: lib.logger.debug2 (fci, "csf.pspace: eigenvalues of h0 before transformation %s", evals_before) evals_after = scipy.linalg.eigh (h0)[0] lib.logger.debug2 (fci, "csf.pspace: eigenvalues of h0 after transformation %s", evals_after) idx = [np.argmin (np.abs (evals_before - ev)) for ev in evals_after] resid = evals_after - evals_before[idx] lib.logger.debug2 (fci, "csf.pspace: best h0 eigenvalue matching differences after transformation: %s", resid) lib.logger.debug (fci, "csf.pspace: if the transformation of h0 worked the following number will be zero: %s", np.max (np.abs(resid))) # We got extra CSFs from building the configurations most of the time. if csf_addr.size > npsp: try: csf_addr_2 = np.argpartition(np.diag (h0), npsp-1)[:npsp] except AttributeError: csf_addr_2 = np.argsort(np.diag (h0))[:npsp] csf_addr = csf_addr[csf_addr_2] h0 = h0[np.ix_(csf_addr_2,csf_addr_2)] npsp_csf = csf_addr.size lib.logger.debug (fci, "csf_solver.pspace: asked for %s-CSF pspace; found %s CSFs", npsp, npsp_csf) t0 = lib.logger.timer (fci, "csf.pspace wrapup", *t0) return csf_addr, h0
def csdaddrs2csdstrs(norb, neleca, nelecb, csdaddrs): ''' This is extremely slow because of the amount of repetition in dconf_addr, sconf_addr, and spins_addr! ''' t_start = time.time() min_npair, npair_offset, npair_dconf_size, npair_sconf_size, npair_spins_size = get_csdaddrs_shape( norb, neleca, nelecb) csdstrs = np.empty((4, len(csdaddrs)), dtype=np.int64) for npair, offset, dconf_size, sconf_size, spins_size in zip( range(min_npair, min(neleca, nelecb) + 1), npair_offset, npair_dconf_size, npair_sconf_size, npair_spins_size): nspins = neleca + nelecb - 2 * npair nup = (nspins + neleca - nelecb) // 2 assert ((nspins + neleca - nelecb) % 2 == 0) next_offset = offset + (dconf_size * sconf_size * spins_size) idx = (csdaddrs >= offset) & (csdaddrs < next_offset) if len(idx) == 1: if not idx[0]: continue else: idx = 0 try: dconf_addr = (csdaddrs[idx] - offset) // (sconf_size * spins_size) except: print(idx) assert (False) dconf_rem = (csdaddrs[idx] - offset) % (sconf_size * spins_size) sconf_addr = dconf_rem // spins_size spins_addr = dconf_rem % spins_size csdstrs[0, idx] = npair t_ref = time.time() dconf_addr_uniq, dconf_addr_uniq2full = np.unique(dconf_addr, return_inverse=True) try: csdstrs[1, idx] = cistring.addrs2str( norb, npair, dconf_addr_uniq)[dconf_addr_uniq2full] except TypeError: csdstrs[1, idx] = cistring.addr2str(norb, npair, dconf_addr_uniq) sconf_addr_uniq, sconf_addr_uniq2full = np.unique(sconf_addr, return_inverse=True) try: csdstrs[2, idx] = cistring.addrs2str( norb - npair, nspins, sconf_addr_uniq)[sconf_addr_uniq2full] except TypeError: csdstrs[2, idx] = cistring.addr2str(norb - npair, nspins, sconf_addr_uniq) spins_addr_uniq, spins_addr_uniq2full = np.unique(spins_addr, return_inverse=True) try: csdstrs[3, idx] = cistring.addrs2str( nspins, nup, spins_addr_uniq)[spins_addr_uniq2full] except TypeError: csdstrs[3, idx] = cistring.addr2str(nspins, nup, spins_addr_uniq) #print ("{:.2f} seconds in cistring".format (time.time () - t_ref)) ''' t_ref = time.time () try: csdstrs[1,idx] = cistring.addrs2str (norb, npair, dconf_addr) csdstrs[2,idx] = cistring.addrs2str (norb - npair, nspins, sconf_addr) csdstrs[3,idx] = cistring.addrs2str (nspins, nup, spins_addr) except TypeError: csdstrs[1,idx] = cistring.addr2str (norb, npair, dconf_addr) csdstrs[2,idx] = cistring.addr2str (norb - npair, nspins, sconf_addr) csdstrs[3,idx] = cistring.addr2str (nspins, nup, spins_addr) print ("{:.2f} seconds in cistring".format (time.time () - t_ref)) ''' #print ("{:.2f} seconds spent in csdaddrs2csdstrs".format (time.time () - t_start)) return csdstrs