-
Dear Huanchen, thank you very much for building up such a nice code and for providing very detailed documentation. I am trying to make use of the CSF coefficients that can be calculating from the spin-adapted MPS state. Could you explain, what kind of spin-adaptation scheme is used, and how to read the CSF labels from the printout. As a test system I chose N2 at some distance in using localized orbitals, see the attached input.
and
|
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 3 replies
-
Hi Nikolay, Thanks for the good question. This part is indeed not clear in the documentation.
|
Beta Was this translation helpful? Give feedback.
-
The CSF output from idx = np.array([0,3,1,4,2,5])
h1e = h1e[idx][:, idx]
g2e = g2e[idx][:, idx][:, :, idx][:, :, :, idx]
I agree that an arbitrary reordering of orbitals in the CSF is hard to implement. But if you simply reverse the orbital order and flip the coupling signs, it will not generate any CG factors. The resulting CSF string will match that from UGA-FCI, as long as you also use a reversed orbital order in your UGA-FCI inputs. The following is a script (tested on your example system) to show that the CSF strings generated by the "reverse and exchange +/-" trick for CSFs from the singlet embedding MPS, can produce the correct energy expectation when interpreted as CSF strings in DRT/UGA. So it is ok to do this. from pyblock2.driver.core import DMRGDriver, SymmetryTypes
from pyblock2._pyscf import mcscf as b2mcscf
from pyscf import gto, scf, mcscf, ao2mo
import numpy as np
mol = gto.M(atom="N 0 0 -3; N 0 0 3", basis="sto3g", symmetry="c1", verbose=0, spin=6)
mf = scf.RHF(mol).run(conv_tol=1E-14)
# get localized orbitals
lo_coeff, lo_occ, lo_energy = b2mcscf.get_uno(mf.to_uhf())
selected = b2mcscf.select_active_space(mol, lo_coeff, lo_occ, ao_labels=["N-2p"], atom_order=[0, 1])
lo_coeff[:, np.array(sorted(selected))] = lo_coeff[:, np.array(selected)]
# get active space integrals
ncas, n_elec, spin = 6, 6, 2
mc = mcscf.CASCI(mf, ncas, n_elec)
mc.mo_coeff = lo_coeff
h1e, ecore = mc.get_h1eff()
g2e = ao2mo.restore(1, mc.get_h2eff(), ncas)
g2e[np.abs(g2e) < 1E-12] = 0
h1e[np.abs(h1e) < 1E-12] = 0
# manual orbital reordering (optional)
# idx = np.argsort(np.array([0,2,5,1,3,4]))
# h1e = h1e[idx][:, idx]
# g2e = g2e[idx][:, idx][:, :, idx][:, :, :, idx]
bond_dims = [250] * 4 + [500] * 5
noises = [1e-4] * 4 + [1e-5] * 5 + [0]
thrds = [1e-20] * 9
driver = DMRGDriver(scratch="./tmp", symm_type=SymmetryTypes.SU2, n_threads=4)
driver.initialize_system(n_sites=ncas, n_elec=n_elec, spin=spin)
mpo = driver.get_qc_mpo(h1e=h1e, g2e=g2e, ecore=ecore, iprint=1)
ket = driver.get_random_mps(tag="GS", bond_dim=250, nroots=1)
energy = driver.dmrg(mpo, ket, n_sweeps=20, bond_dims=bond_dims, noises=noises,
thrds=thrds, cutoff=0, iprint=1)
print('DMRG energy = %20.15f' % energy)
csfs, coeffs = driver.get_csf_coefficients(ket, cutoff=0.05, iprint=1)
# DRT-FCI
fd = driver.bw.b.FCIDUMP()
fd.initialize_su2(ncas, n_elec, spin, 0, ecore, h1e, g2e)
iqs = driver.bw.VectorSX([driver.bw.SX(n_elec, spin, 0)])
# Here 'True' indicates a reserved ordering of orbitals inside DRT
big = driver.bw.b.su2.DRTBigSite(iqs, True, driver.n_sites, driver.orb_sym, fd, 0)
big.drt = driver.bw.b.su2.DRT(big.drt.n_sites, big.drt.get_init_qs(), big.drt.orb_sym)
print('\n%r' % big.drt)
hamil_op = driver.bw.b.su2.OpElement(driver.bw.b.OpNames.H, driver.bw.b.SiteIndex(), driver.vacuum)
hmat = big.get_site_op(0, hamil_op)[0]
dci = np.zeros((hmat.n, ), dtype=float)
hmat.diag(driver.bw.b.Matrix(dci.reshape(-1, 1)))
def hop(kci):
bci = np.zeros((hmat.n, ), dtype=kci.dtype)
driver.bw.b.CSRMatrixFunctions.multiply(hmat, 0, driver.bw.b.Matrix(kci.reshape(-1, 1)), 0,
driver.bw.b.Matrix(bci.reshape(-1, 1)), 1.0, 0.0)
return bci
kci = np.zeros((hmat.m, ))
kci[big.drt >> (big.drt ^ 0)] = 1
eners = driver.bw.b.IterativeMatrixFunctions.davidson(hop, dci, [kci], True, conv_thrd=1E-20)
print('E(%10s) = %15.10f' % ("DRT-FCI", eners[0] + ecore))
for ix, (csf, val) in enumerate([(c, v) for c, v in sorted(zip(big.drt, kci), key=lambda x: -abs(x[1])) if abs(v) >= 0.05]):
print('CSF', "%10d" % ix, csf, " = %20.15f" % val)
# Energy expectation from CSFs generated from MPS
mpsci = np.zeros((hmat.m, ))
for occ, val in zip(csfs, coeffs):
csf = ''.join("0-+2"[x] for x in occ[::-1])
mpsci[big.drt.index(csf)] = val
print('E(%10s) = %15.10f' % ("MPS-CSF", np.dot(mpsci, hop(mpsci)) + ecore)) A few notes:
|
Beta Was this translation helpful? Give feedback.
The CSF output from
block2
will not make sense if you usereorder=np.array([0,3,1,4,2,5])
inget_qc_mpo
. This orbital reordering feature is not compatible with the CSF because as you know adjusting the CSF orbital order is a non-trivial thing. If you really need this orbital reordering, you can do that on your integrals before you put them intoblock2
, as the following: