def test_return_stats(): A, _ = diagonal(100) evals, evecs, stats = primme.eigsh(A, 3, tol=1e-6, which='LA', return_stats=True, return_history=True) assert(stats["hist"]["numMatvecs"]) svecs_left, svals, svecs_right, stats = primme.svds(A, 3, tol=1e-6, which='SM', return_stats=True, return_history=True) assert(stats["hist"]["numMatvecs"])
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # PRIMME: https://github.com/primme/primme # Contact: Andreas Stathopoulos, a n d r e a s _at_ c s . w m . e d u import numpy as np from numpy.testing import assert_allclose import scipy.sparse import primme # Sparse diagonal matrix of size 100 A = scipy.sparse.spdiags(np.asarray(range(100), dtype=np.float32), [0], 100, 100) # Compute the three largest eigenvalues of A with a residual norm tolerance of 1e-6 evals, evecs = primme.eigsh(A, 3, tol=1e-6, which='LA') assert_allclose(evals, [99., 98., 97.], atol=1e-6 * 100) print(evals) # [ 99., 98., 97.] # Compute the three largest eigenvalues of A orthogonal to the previous computed # eigenvectors, i.e., the next three eigenvalues evals, evecs = primme.eigsh(A, 3, tol=1e-6, which='LA', lock=evecs) assert_allclose(evals, [96., 95., 94.], atol=1e-6 * 100) print(evals) # [ 96., 95., 94.] # Compute the three closest eigenvalues to 50.1 evals, evecs = primme.eigsh(A, 3, tol=1e-6, which=50.1) assert_allclose(evals, [50., 51., 49.], atol=1e-6 * 100) print(evals) # [ 50., 51., 49.]
def main(total, cmdargs): if total != 2: print(" ".join(str(x) for x in cmdargs)) raise ValueError('Missing arguments') inputname = cmdargs[1] sys.stdout = Logger() # for exporting the logfile # ----------------------- Build and Diagonalize --------------------------------- para = Parameter(inputname) # import parameters from input.inp Lat = Lattice(para) # Build lattice ob = Observ(Lat) # initialize observables tic = time.perf_counter() Hamil = Hamiltonian(Lat) # Build Hamiltonian ham = Hamil.Ham # Hamiltonian as sparse matrix toc = time.perf_counter() print(f"Hamiltonian construction time = {toc - tic:0.4f} sec") tic = time.perf_counter() evals, evecs = primme.eigsh(ham, para.Nstates, tol=1e-12, which='SA') evals, evecs = sort(evals, evecs) toc = time.perf_counter() evals = np.round(evals, 10) print("\n-----------Beg of Eigen Values-----------\n", *evals, sep='\n') print("\n-----------End of Eigen Values-----------") print(f"Diagonalization time = {toc - tic:0.4f} sec \n\n") # -------------- Entanglement properties of GS ----------- if para.Option is not None: if "EE" in para.Option: EntS, Entvec = ob.EntSpec( evecs[:, 0]) # Entanglement spectrum and vector EntS = np.round(EntS, decimals=10) EntS_log = np.zeros(len(EntS)) for i in range(len(EntS)): if EntS[i] <= 0: EntS_log[i] = 0 else: EntS_log[i] = math.log(EntS[i]) EE = -np.around(np.dot(EntS, EntS_log), decimals=8) print("Entanglement Spectrum=\n", EntS) print("Entanglement Entropy=", EE) # ------------------------ Ascii Ostream for EE-------- file = open("entspec.dat", "w") # For a U-scan of Bose-Hubbard model if para.Model == "Bose_Hubbard": for i in range(EntS.size): file.write(str(abs(para.U)) + " " + str(EntS[i])) file.write("\n") else: for i in range(EntS.size): file.write(str(abs(para.Hx)) + " " + str(EntS[i])) file.write("\n") # ------------------------ Hdf5 Ostream (Big Data Storage) ------------------------------ # ------------------------ Hdf5 Ostream (Big Data Storage) ------------------------------ # ------------------------ Hdf5 Ostream (Big Data Storage) ------------------------------ tic = time.perf_counter() file = h5py.File('dataSpec.hdf5', 'w') file.attrs["LLX"] = para.LLX file.attrs["LLY"] = para.LLY file.attrs["IsPeriodicX"] = para.IsPeriodicX file.attrs["IsPeriodicY"] = para.IsPeriodicY file.attrs["Kx"] = para.Kxx file.attrs["Ky"] = para.Kyy file.attrs["Kz"] = para.Kzz file.attrs["Hx"] = para.Hz file.attrs["Hy"] = para.Hy file.attrs["Hz"] = para.Hz file.attrs["#States2Keep"] = para.Nstates file.attrs["Model"] = para.Model file.attrs["Nsites"] = Lat.Nsite LatGrp = file.create_group("1.Lattice") LatGrp.create_dataset("Mesh", data=Lat.mesh_) LatGrp.create_dataset("Nearest Neighbors", data=Lat.nn_) ConnGrp = file.create_group("2.Connectors") if para.Model == "Heisenberg_Square" or para.Model == "Kitaev": ConnGrp.create_dataset("KxxGraph", data=Hamil.KxxGraph_) ConnGrp.create_dataset("KyyGraph", data=Hamil.KyyGraph_) ConnGrp.create_dataset("KzzGraph", data=Hamil.KzzGraph_) elif para.Model == "AKLT": ConnGrp.create_dataset("Kxx1Graph", data=Hamil.Kxx1Graph_) ConnGrp.create_dataset("Kyy1Graph", data=Hamil.Kyy1Graph_) ConnGrp.create_dataset("Kzz1Graph", data=Hamil.Kzz1Graph_) ConnGrp.create_dataset("Kxx2Graph", data=Hamil.Kxx2Graph_) ConnGrp.create_dataset("Kyy2Graph", data=Hamil.Kyy2Graph_) ConnGrp.create_dataset("Kzz2Graph", data=Hamil.Kzz2Graph_) elif para.Model == "Bose_Hubbard": ConnGrp.create_dataset("tGraph", data=Hamil.tGraph_) EigGrp = file.create_group("3.Eigen") EigGrp.create_dataset("Eigen Values", data=evals) EigGrp.create_dataset("Wavefunctions", data=evecs) if para.Option is not None: if "EE" in para.Option: EigGrp = file.create_group("4.Entanglment") EigGrp.create_dataset("ES", data=EntS) EigGrp.create_dataset("ESlog", data=EntS_log) EigGrp.create_dataset("EE", data=EE) file.close() toc = time.perf_counter() print(f"\nHDF5 time = {toc - tic:0.4f} sec")
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # PRIMME: https://github.com/primme/primme # Contact: Andreas Stathopoulos, a n d r e a s _at_ c s . w m . e d u import numpy as np from numpy.testing import assert_allclose import scipy.sparse import primme # Sparse diagonal matrix of size 100 A = scipy.sparse.spdiags(np.asarray(range(100), dtype=np.float32), [0], 100, 100) # Compute the three largest eigenvalues of A with a residual norm tolerance of 1e-6 evals, evecs = primme.eigsh(A, 3, tol=1e-6, which='LA') assert_allclose(evals, [99., 98., 97.], atol=1e-6 * 100) print(evals) # [ 99., 98., 97.] # Compute the three largest eigenvalues of A orthogonal to the previous computed # eigenvectors, i.e., the next three eigenvalues evals, evecs = primme.eigsh(A, 3, tol=1e-6, which='LA', lock=evecs) assert_allclose(evals, [96., 95., 94.], atol=1e-6 * 100) print(evals) # [ 96., 95., 94.] # Sparse rectangular matrix 100x10 with non-zeros on the main diagonal A = scipy.sparse.spdiags(range(10), [0], 100, 10) # Compute the three closest to 4.1 singular values and the left and right corresponding # singular vectors svecs_left, svals, svecs_right = primme.svds(A, 3, tol=1e-6, which=4.1)
def kernel(self, restart=False, include_psi0=False): r"""calculate the roots Parameters ---------- restart: bool, optional if restart from the former converged root. Default is ``False``. If ``restart = True``, ``include_psi0`` must be the same as the former calculation. include_psi0: bool, optional if the basis of Hamiltonian includes the ground state :math:`\Psi_0`. Default is ``False``. Returns ------- e: np.ndarray the energy of the states, if ``include_psi0 = True``, the first element is the ground state energy, otherwise, it is the energy of the first excited state. """ # right canonical mps mpo = self.hmpo nroots = self.nroots algo = self.algo site_num = mpo.site_num if not restart: # make sure that M is not redundant near the edge mps = self.mps.ensure_right_canon().canonicalise().normalize().canonicalise() logger.debug(f"reference mps shape, {mps}") mps_r_cano = mps.copy() assert mps.to_right tangent_u = [] for ims, ms in enumerate(mps): shape = list(ms.shape) u, s, vt = scipy.linalg.svd(ms.l_combine(), full_matrices=True) rank = len(s) if include_psi0 and ims == site_num-1: tangent_u.append(u.reshape(shape[:-1]+[-1])) else: if rank < u.shape[1]: tangent_u.append(u[:,rank:].reshape(shape[:-1]+[-1])) else: tangent_u.append(None) # the tangent space is None mps[ims] = u[:,:rank].reshape(shape[:-1]+[-1]) vt = xp.einsum("i, ij -> ij", asxp(s), asxp(vt)) if ims == site_num-1: assert vt.size == 1 and xp.allclose(vt, 1) else: mps[ims+1] = asnumpy(tensordot(vt, mps[ims+1], ([-1],[0]))) mps_l_cano = mps.copy() mps_l_cano.to_right = False mps_l_cano.qnidx = site_num-1 else: mps_l_cano, mps_r_cano, tangent_u, tda_coeff_list = self.wfn cguess = [] for iroot in range(len(tda_coeff_list)): tda_coeff = tda_coeff_list[iroot] x = [c.flatten() for c in tda_coeff if c is not None] x = np.concatenate(x,axis=None) cguess.append(x) cguess = np.stack(cguess, axis=1) xshape = [] xsize = 0 for ims in range(site_num): if tangent_u[ims] is None: xshape.append((0,0)) else: if ims == site_num-1: xshape.append((tangent_u[ims].shape[-1], 1)) else: xshape.append((tangent_u[ims].shape[-1], mps_r_cano[ims+1].shape[0])) xsize += np.prod(xshape[-1]) logger.debug(f"DMRG-TDA H dimension: {xsize}") if USE_GPU: oe_backend = "cupy" else: oe_backend = "numpy" mps_tangent = mps_r_cano.copy() environ = Environ(mps_tangent, mpo, "R") hdiag = [] for ims in range(site_num): ltensor = environ.GetLR( "L", ims-1, mps_tangent, mpo, itensor=None, method="System" ) rtensor = environ.GetLR( "R", ims+1, mps_tangent, mpo, itensor=None, method="Enviro" ) if tangent_u[ims] is not None: u = asxp(tangent_u[ims]) tmp = oe.contract("abc, ded, bghe, agl, chl -> ld", ltensor, rtensor, asxp(mpo[ims]), u, u, backend=oe_backend) hdiag.append(asnumpy(tmp)) mps_tangent[ims] = mps_l_cano[ims] hdiag = np.concatenate(hdiag, axis=None) count = 0 # recover the vector-like x back to the ndarray tda_coeff def reshape_x(x): tda_coeff = [] offset = 0 for shape in xshape: if shape == (0,0): tda_coeff.append(None) else: size = np.prod(shape) tda_coeff.append(x[offset:size+offset].reshape(shape)) offset += size assert offset == xsize return tda_coeff def hop(x): # H*X nonlocal count count += 1 assert len(x) == xsize tda_coeff = reshape_x(x) res = [np.zeros_like(coeff) if coeff is not None else None for coeff in tda_coeff] # fix ket and sweep bra and accumulate into res for ims in range(site_num): if tda_coeff[ims] is None: assert tangent_u[ims] is None continue # mix-canonical mps mps_tangent = merge(mps_l_cano, mps_r_cano, ims+1) mps_tangent[ims] = tensordot(tangent_u[ims], tda_coeff[ims], (-1, 0)) mps_tangent_conj = mps_r_cano.copy() environ = Environ(mps_tangent, mpo, "R", mps_conj=mps_tangent_conj) for ims_conj in range(site_num): ltensor = environ.GetLR( "L", ims_conj-1, mps_tangent, mpo, itensor=None, mps_conj=mps_tangent_conj, method="System" ) rtensor = environ.GetLR( "R", ims_conj+1, mps_tangent, mpo, itensor=None, mps_conj=mps_tangent_conj, method="Enviro" ) if tda_coeff[ims_conj] is not None: # S-a l-S # d # O-b-O-f-O # e # S-c k-S path = [ ([0, 1], "abc, cek -> abek"), ([2, 0], "abek, bdef -> akdf"), ([1, 0], "akdf, lfk -> adl"), ] out = multi_tensor_contract( path, ltensor, asxp(mps_tangent[ims_conj]), asxp(mpo[ims_conj]), rtensor ) res[ims_conj] += asnumpy(tensordot(tangent_u[ims_conj], out, ([0,1], [0,1]))) # mps_conj combine mps_tangent_conj[ims_conj] = mps_l_cano[ims_conj] res = [mat for mat in res if mat is not None] return np.concatenate(res, axis=None) if algo == "davidson": if restart: cguess = [cguess[:,i] for i in range(cguess.shape[1])] else: cguess = [np.random.random(xsize) - 0.5] precond = lambda x, e, *args: x / (hdiag - e + 1e-4) e, c = davidson( hop, cguess, precond, max_cycle=100, nroots=nroots, max_memory=64000 ) if nroots == 1: c = [c] c = np.stack(c, axis=1) elif algo == "primme": if not restart: cguess = None def multi_hop(x): if x.ndim == 1: return hop(x) elif x.ndim == 2: return np.stack([hop(x[:,i]) for i in range(x.shape[1])],axis=1) else: assert False def precond(x): if x.ndim == 1: return np.einsum("i, i -> i", 1/(hdiag+1e-4), x) elif x.ndim ==2: return np.einsum("i, ij -> ij", 1/(hdiag+1e-4), x) else: assert False A = scipy.sparse.linalg.LinearOperator((xsize,xsize), matvec=multi_hop, matmat=multi_hop) M = scipy.sparse.linalg.LinearOperator((xsize,xsize), matvec=precond, matmat=precond) e, c = primme.eigsh(A, k=min(nroots,xsize), which="SA", v0=cguess, OPinv=M, method="PRIMME_DYNAMIC", tol=1e-6) else: assert False logger.debug(f"H*C times: {count}") tda_coeff_list = [] for iroot in range(nroots): tda_coeff_list.append(reshape_x(c[:,iroot])) self.e = np.array(e) self.wfn = [mps_l_cano, mps_r_cano, tangent_u, tda_coeff_list] return self.e
def optimize_mps(mps: Mps, mpo: Mpo, omega: float = None) -> Tuple[List, Mps]: r""" DMRG ground state algorithm and state-averaged excited states algorithm Parameters ---------- mps : renormalizer.mps.Mps initial guess of mps mpo : renormalizer.mps.Mpo mpo of Hamiltonian omega: float, optional target the eigenpair near omega with special variational function :math:(\hat{H}-\omega)^2. Default is `None`. Returns ------- energy : list list of energy of each marco sweep. :math:`[e_0, e_0, \cdots, e_0]` if ``nroots=1``. :math:`[[e_0, \cdots, e_n], \dots, [e_0, \cdots, e_n]]` if ``nroots=n``. mps : renormalizer.mps.Mps optimized ground state mps. The input mps is overwritten and could not be used anymore. See Also -------- renormalizer.utils.configs.OptimizeConfig : The optimization configuration. """ algo = mps.optimize_config.algo method = mps.optimize_config.method procedure = mps.optimize_config.procedure inverse = mps.optimize_config.inverse nroots = mps.optimize_config.nroots assert method in ["2site", "1site"] logger.info(f"optimization method: {method}") logger.info(f"e_rtol: {mps.optimize_config.e_rtol}") logger.info(f"e_atol: {mps.optimize_config.e_atol}") if USE_GPU: oe_backend = "cupy" else: oe_backend = "numpy" # ensure that mps is left or right-canonical # TODO: start from a mix-canonical MPS if mps.is_left_canon: mps.ensure_right_canon() env = "R" else: mps.ensure_left_canon() env = "L" # in state-averged calculation, contains C of each state for better initial # guess averaged_ms = None # the index of active site of the returned mps res_mps_idx = None # target eigenstate close to omega with (H-omega)^2 # construct the environment matrix if omega is not None: identity = Mpo.identity(mpo.model) mpo = mpo.add(identity.scale(-omega)) environ = Environ(mps, [mpo, mpo], env) else: environ = Environ(mps, mpo, env) macro_iteration_result = [] converged = False for isweep, (mmax, percent) in enumerate(procedure): logger.debug(f"isweep: {isweep}") logger.debug(f"mmax, percent: {mmax}, {percent}") logger.debug(f"{mps}") micro_iteration_result = [] for imps in mps.iter_idx_list(full=True): if method == "2site" and \ ((mps.to_right and imps == mps.site_num-1) or ((not mps.to_right) and imps == 0)): break if mps.to_right: lmethod, rmethod = "System", "Enviro" else: lmethod, rmethod = "Enviro", "System" if method == "1site": lidx = imps - 1 cidx = [imps] ridx = imps + 1 elif method == "2site": if mps.to_right: lidx = imps - 1 cidx = [imps, imps + 1] ridx = imps + 2 else: lidx = imps - 2 cidx = [imps - 1, imps] # center site ridx = imps + 1 else: assert False logger.debug(f"optimize site: {cidx}") if omega is None: operator = mpo else: operator = [mpo, mpo] ltensor = environ.GetLR("L", lidx, mps, operator, itensor=None, method=lmethod) rtensor = environ.GetLR("R", ridx, mps, operator, itensor=None, method=rmethod) # get the quantum number pattern qnbigl, qnbigr, qnmat = mps._get_big_qn(cidx) cshape = qnmat.shape nonzeros = np.sum(qnmat == mps.qntot) logger.debug(f"Hmat dim: {nonzeros}") # center mo cmo = [asxp(mpo[idx]) for idx in cidx] if qnmat.size > 1000 and algo != "direct": # iterative algorithm # diagonal elements of H if omega is None: tmp_ltensor = xp.einsum("aba -> ba", ltensor) tmp_cmo0 = xp.einsum("abbc -> abc", cmo[0]) tmp_rtensor = xp.einsum("aba -> ba", rtensor) if method == "1site": # S-a c f-S # O-b-O-g-O # S-a c f-S path = [([0, 1], "ba, bcg -> acg"), ([1, 0], "acg, gf -> acf")] hdiag = multi_tensor_contract( path, tmp_ltensor, tmp_cmo0, tmp_rtensor)[(qnmat == mps.qntot)] else: # S-a c d f-S # O-b-O-e-O-g-O # S-a c d f-S tmp_cmo1 = xp.einsum("abbc -> abc", cmo[1]) path = [ ([0, 1], "ba, bce -> ace"), ([0, 1], "edg, gf -> edf"), ([0, 1], "ace, edf -> acdf"), ] hdiag = multi_tensor_contract( path, tmp_ltensor, tmp_cmo0, tmp_cmo1, tmp_rtensor)[(qnmat == mps.qntot)] else: if method == "1site": # S-a d h-S # O-b-O-f-O # | e | # O-c-O-g-O # S-a d h-S hdiag = oe.contract( "abca, bdef, cedg, hfgh -> adh", ltensor, cmo[0], cmo[0], rtensor, backend=oe_backend)[(qnmat == mps.qntot)] else: # S-a d h l-S # O-b-O-f-O-j-O # | e i | # O-c-O-g-O-k-O # S-a d h l-S hdiag = oe.contract( "abca, bdef, cedg, fhij, gihk, ljkl -> adhl", ltensor, cmo[0], cmo[0], cmo[1], cmo[1], rtensor, backend=oe_backend)[(qnmat == mps.qntot)] hdiag = asnumpy(hdiag * inverse) # initial guess if method == "1site": # initial guess b-S-c # a if nroots == 1: cguess = [asnumpy(mps[cidx[0]])[qnmat == mps.qntot]] else: cguess = [] if averaged_ms is not None: for ms in averaged_ms: cguess.append(asnumpy(ms)[qnmat == mps.qntot]) else: # initial guess b-S-c-S-e # a d if nroots == 1: cguess = [ asnumpy( tensordot(mps[cidx[0]], mps[cidx[1]], axes=1)[qnmat == mps.qntot]) ] else: cguess = [] if averaged_ms is not None: for ms in averaged_ms: if mps.to_right: cguess.append( asnumpy( tensordot( ms, mps[cidx[1]], axes=1)[qnmat == mps.qntot])) else: cguess.append( asnumpy( tensordot( mps[cidx[0]], ms, axes=1)[qnmat == mps.qntot])) if omega is not None: if method == "1site": # S-a e j-S # O-b-O-g-O # | f | # O-c-O-i-O # S-d h k-S expr = oe.contract_expression( "abcd, befg, cfhi, jgik, aej -> dhk", ltensor, cmo[0], cmo[0], rtensor, cshape, constants=[0, 1, 2, 3]) else: # S-a e j o-S # O-b-O-g-O-l-O # | f k | # O-c-O-i-O-n-O # S-d h m p-S expr = oe.contract_expression( "abcd, befg, cfhi, gjkl, ikmn, olnp, aejo -> dhmp", ltensor, cmo[0], cmo[0], cmo[1], cmo[1], rtensor, cshape, constants=[0, 1, 2, 3, 4, 5]) count = 0 def hop(x): nonlocal count count += 1 clist = [] if x.ndim == 1: clist.append(x) else: for icol in range(x.shape[1]): clist.append(x[:, icol]) res = [] for c in clist: # convert c to initial structure according to qn pattern cstruct = asxp(cvec2cmat(cshape, c, qnmat, mps.qntot)) if omega is None: if method == "1site": # S-a l-S # d # O-b-O-f-O # e # S-c k-S path = [ ([0, 1], "abc, adl -> bcdl"), ([2, 0], "bcdl, bdef -> clef"), ([1, 0], "clef, lfk -> cek"), ] cout = multi_tensor_contract( path, ltensor, cstruct, cmo[0], rtensor) else: # S-a l-S # d g # O-b-O-f-O-j-O # e h # S-c k-S path = [ ([0, 1], "abc, adgl -> bcdgl"), ([3, 0], "bcdgl, bdef -> cglef"), ([2, 0], "cglef, fghj -> clehj"), ([1, 0], "clehj, ljk -> cehk"), ] cout = multi_tensor_contract( path, ltensor, cstruct, cmo[0], cmo[1], rtensor, ) else: cout = expr(cstruct, backend=oe_backend) # convert structure c to 1d according to qn res.append(asnumpy(cout)[qnmat == mps.qntot]) if len(res) == 1: return inverse * res[0] else: return inverse * np.stack(res, axis=1) if len(cguess) < nroots: cguess.extend([ np.random.random([nonzeros]) - 0.5 for i in range(len(cguess), nroots) ]) if algo == "davidson": precond = lambda x, e, *args: x / (hdiag - e + 1e-4) e, c = davidson(hop, cguess, precond, max_cycle=100, nroots=nroots, max_memory=64000) # if one root, davidson return e as np.float #elif algo == "arpack": # # scipy arpack solver : much slower than pyscf/davidson # A = scipy.sparse.linalg.LinearOperator((nonzeros,nonzeros), matvec=hop) # e, c = scipy.sparse.linalg.eigsh(A, k=nroots, which="SA", v0=cguess) # # scipy return numpy.array # if nroots == 1: # e = e[0] #elif algo == "lobpcg": # precond = lambda x: scipy.sparse.diags(1/(hdiag+1e-4)) @ x # A = scipy.sparse.linalg.LinearOperator((nonzeros,nonzeros), # matvec=hop, matmat=hop) # M = scipy.sparse.linalg.LinearOperator((nonzeros,nonzeros), # matvec=precond, matmat=hop) # e, c = scipy.sparse.linalg.lobpcg(A, np.array(cguess).T, # M=M, largest=False) elif algo == "primme": precond = lambda x: scipy.sparse.diags(1 / (hdiag + 1e-4)) @ x A = scipy.sparse.linalg.LinearOperator( (nonzeros, nonzeros), matvec=hop, matmat=hop) M = scipy.sparse.linalg.LinearOperator( (nonzeros, nonzeros), matvec=precond, matmat=hop) e, c = primme.eigsh(A, k=min(nroots, nonzeros), which="SA", v0=np.array(cguess).T, OPinv=M, method="PRIMME_DYNAMIC", tol=1e-6) else: assert False logger.debug(f"use {algo}, HC hops: {count}") else: logger.debug(f"use direct eigensolver") # direct algorithm if omega is None: if method == "1site": # S-a l-S # d # O-b-O-f-O # e # S-c k-S ham = oe.contract("abc,bdef,lfk->adlcek", ltensor, cmo[0], rtensor, backend=oe_backend) ham = ham[:, :, :, qnmat == mps.qntot][ qnmat == mps.qntot, :] * inverse else: # S-a l-S # d g # O-b-O-f-O-j-O # e h # S-c k-S ham = oe.contract("abc,bdef,fghj,ljk->adglcehk", ltensor, cmo[0], cmo[1], rtensor) ham = ham[:, :, :, :, qnmat == mps.qntot][ qnmat == mps.qntot, :] * inverse else: if method == "1site": # S-a e j-S # O-b-O-g-O # | f | # O-c-O-i-O # S-d h k-S ham = oe.contract("abcd, befg, cfhi, jgik -> aejdhk", ltensor, cmo[0], cmo[0], rtensor) ham = ham[:, :, :, qnmat == mps.qntot][ qnmat == mps.qntot, :] * inverse else: # S-a e j o-S # O-b-O-g-O-l-O # | f k | # O-c-O-i-O-n-O # S-d h m p-S ham = oe.contract( "abcd, befg, cfhi, gjkl, ikmn, olnp -> aejodhmp", ltensor, cmo[0], cmo[0], cmo[1], cmo[1], rtensor) ham = ham[:, :, :, :, qnmat == mps.qntot][ qnmat == mps.qntot, :] * inverse w, v = scipy.linalg.eigh(asnumpy(ham)) if nroots == 1: e = w[0] c = v[:, 0] else: e = w[:nroots] c = [ v[:, iroot] for iroot in range(min(nroots, v.shape[1])) ] # if multi roots, both davidson and primme return np.ndarray if nroots > 1: e = e.tolist() logger.debug(f"energy: {e}") micro_iteration_result.append(e) cstruct = cvec2cmat(cshape, c, qnmat, mps.qntot, nroots=nroots) # store the "optimal" mps (usually in the middle of each sweep) if res_mps_idx is not None and res_mps_idx == imps: if nroots == 1: res_mps = mps.copy() res_mps._update_mps(cstruct, cidx, qnbigl, qnbigr, mmax, percent) else: res_mps = [mps.copy() for i in range(len(cstruct))] for iroot in range(len(cstruct)): res_mps[iroot]._update_mps(cstruct[iroot], cidx, qnbigl, qnbigr, mmax, percent) averaged_ms = mps._update_mps(cstruct, cidx, qnbigl, qnbigr, mmax, percent) mps._switch_direction() res_mps_idx = micro_iteration_result.index(min(micro_iteration_result)) macro_iteration_result.append(micro_iteration_result[res_mps_idx]) # check if convergence if isweep > 0 and percent == 0: v1, v2 = sorted(macro_iteration_result)[:2] if np.allclose(v1, v2, rtol=mps.optimize_config.e_rtol, atol=mps.optimize_config.e_atol): converged = True break logger.debug( f"{isweep+1} sweeps are finished, lowest energy = {sorted(macro_iteration_result)[0]}" ) if converged: logger.info("DMRG is converged!") else: logger.warning("DMRG is not converged! Please increase the procedure!") logger.info( f"The lowest two energies: {sorted(macro_iteration_result)[:2]}.") # remove the redundant basis near the edge if nroots == 1: res_mps = res_mps.normalize().ensure_left_canon().canonicalise() logger.info(f"{res_mps}") else: res_mps = [ mp.normalize().ensure_left_canon().canonicalise() for mp in res_mps ] logger.info(f"{res_mps[0]}") return macro_iteration_result, res_mps