def expectation(self, mpo, self_conj=None) -> float: if self_conj is None: self_conj = self._expectation_conj() environ = Environ() environ.construct(self, self_conj, mpo, "r") l = ones((1, 1, 1)) r = environ.read("r", 1) path = self._expectation_path() return float(multi_tensor_contract(path, l, self[0], mpo[0], self_conj[0], r).real)
def full_wfn(self): dim = np.prod(self.pbond_list) if 20000 < dim: raise ValueError("wavefunction too large") res = ones((1, 1, 1)) for mt in self: dim1 = res.shape[1] * mt.shape[1] dim2 = mt.shape[-1] res = tensordot(res, mt, axes=1).reshape(1, dim1, dim2) return res[0, :, 0]
def full_operator(self): dim = np.prod(self.pbond_list) if 20000 < dim: raise ValueError("operator too large") res = ones((1, 1, 1, 1)) for mt in self: dim1 = res.shape[1] * mt.shape[1] dim2 = res.shape[2] * mt.shape[2] dim3 = mt.shape[-1] res = tensordot(res, mt, axes=1).transpose((0, 1, 3, 2, 4, 5)).reshape(1, dim1, dim2, dim3) return res[0, :, :, 0]
def construct(self, mps, mps_conj, mpo, domain): tensor = ones((1, 1, 1), mps.dtype) assert domain in ["L", "R", "l", "r"] if domain == "L" or domain == "l": start, end, inc = 0, len(mps) - 1, 1 self.write(domain, -1, tensor) else: start, end, inc = len(mps) - 1, 0, -1 self.write(domain, len(mps), tensor) for idx in range(start, end, inc): tensor = self.addone(tensor, mps, mps_conj, mpo, idx, domain) self.write(domain, idx, tensor)
def transferMat(mps, mpsconj, domain, siteidx): """ calculate the transfer matrix from the left hand or the right hand """ val = ones([1, 1]) if domain == "R": for imps in range(len(mps) - 1, siteidx - 1, -1): val = tensordot(mpsconj[imps], val, axes=(2, 0)) val = tensordot(val, mps[imps], axes=([1, 2], [1, 2])) elif domain == "L": for imps in range(0, siteidx + 1, 1): val = tensordot(mpsconj[imps], val, axes=(0, 0)) val = tensordot(val, mps[imps], axes=([0, 2], [1, 0])) return val
def _calc_reduced_density_matrix(self, mp1, mp2): # further optimization is difficult. There are totally N^2 intermediate results to remember. reduced_density_matrix = np.zeros( (self.mol_list.mol_num, self.mol_list.mol_num), dtype=backend.complex_dtype ) for i in range(self.mol_list.mol_num): for j in range(self.mol_list.mol_num): elem = ones((1, 1)) e_idx = -1 for mt_idx, (mt1, mt2) in enumerate(zip(mp1, mp2)): if self.ephtable.is_electron(mt_idx): e_idx += 1 axis_idx1 = int(e_idx == i) axis_idx2 = int(e_idx == j) sub_mt1 = mt1[:, axis_idx1, :, :] sub_mt2 = mt2[:, :, axis_idx2, :] elem = tensordot(elem, sub_mt1, axes=(0, 0)) elem = tensordot(elem, sub_mt2, axes=[(0, 1), (0, 1)]) else: elem = tensordot(elem, mt1, axes=(0, 0)) elem = tensordot(elem, mt2, axes=[(0, 1, 2), (0, 2, 1)]) reduced_density_matrix[i][j] = elem.flatten()[0] return reduced_density_matrix
def optimize_mps_dmrg(mps, mpo): """ 1 or 2 site optimization procedure """ 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"] # print("optimization method", method) nexciton = mps.nexciton # construct the environment matrix environ = Environ() environ.construct(mps, mps, mpo, "L") nMPS = len(mps) # construct each sweep cycle scheme if method == "1site": loop = [["R", i] for i in range(nMPS - 1, -1, -1)] + [["L", i] for i in range(0, nMPS)] else: loop = [["R", i] for i in range(nMPS - 1, 0, -1)] + [["L", i] for i in range(1, nMPS)] # initial matrix ltensor = ones((1, 1, 1)) rtensor = ones((1, 1, 1)) energies = [] for isweep, (mmax, percent) in enumerate(procedure): logger.debug(f"mmax, percent: {mmax}, {percent}") logger.debug(f"energy: {mps.expectation(mpo)}") logger.debug(f"{mps}") for system, imps in loop: if system == "R": lmethod, rmethod = "Enviro", "System" else: lmethod, rmethod = "System", "Enviro" if method == "1site": lsite = imps - 1 addlist = [imps] else: lsite = imps - 2 addlist = [imps - 1, imps] ltensor = environ.GetLR("L", lsite, mps, mps, mpo, itensor=ltensor, method=lmethod) rtensor = environ.GetLR("R", imps + 1, mps, mps, mpo, itensor=rtensor, method=rmethod) # get the quantum number pattern qnmat, qnbigl, qnbigr = construct_qnmat(mps, mpo.ephtable, mpo.pbond_list, addlist, method, system) cshape = qnmat.shape # hdiag tmp_ltensor = einsum("aba -> ba", ltensor) tmp_MPOimps = einsum("abbc -> abc", mpo[imps]) tmp_rtensor = 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_MPOimps, tmp_rtensor)[(qnmat == nexciton)] # initial guess b-S-c # a cguess = mps[imps][qnmat == nexciton] else: # S-a c d f-S # O-b-O-e-O-g-O # S-a c d f-S tmp_MPOimpsm1 = einsum("abbc -> abc", mpo[imps - 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_MPOimpsm1, tmp_MPOimps, tmp_rtensor)[(qnmat == nexciton)] # initial guess b-S-c-S-e # a d cguess = tensordot(mps[imps - 1], mps[imps], axes=1)[qnmat == nexciton] cguess = cguess.asnumpy() hdiag *= inverse nonzeros = np.sum(qnmat == nexciton) # print("Hmat dim", nonzeros) def hop(c): # convert c to initial structure according to qn pattern cstruct = cvec2cmat(cshape, c, qnmat, nexciton) 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, Matrix(cstruct), mpo[imps], rtensor) # for small matrices, check hermite: # a=tensordot(ltensor, mpo[imps], ((1), (0))) # b=tensordot(a, rtensor, ((4), (1))) # c=b.transpose((0, 2, 4, 1, 3, 5)) # d=c.reshape(16, 16) 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, Matrix(cstruct), mpo[imps - 1], mpo[imps], rtensor, ) # convert structure c to 1d according to qn return inverse * cout.asnumpy()[qnmat == nexciton] if nroots != 1: cguess = [cguess] for iroot in range(nroots - 1): cguess.append(np.random.random([nonzeros]) - 0.5) precond = lambda x, e, *args: x / (hdiag.asnumpy() - e + 1e-4) e, c = davidson(hop, cguess, precond, max_cycle=100, nroots=nroots, max_memory=64000) # scipy arpack solver : much slower than davidson # A = spslinalg.LinearOperator((nonzeros,nonzeros), matvec=hop) # e, c = spslinalg.eigsh(A,k=1, which="SA",v0=cguess) # print("HC loops:", count[0]) # logger.debug(f"isweep: {isweep}, e: {e}") energies.append(e) cstruct = cvec2cmat(cshape, c, qnmat, nexciton, nroots=nroots) if nroots == 1: # direct svd the coefficient matrix mt, mpsdim, mpsqn, compmps = renormalization_svd( cstruct, qnbigl, qnbigr, system, nexciton, Mmax=mmax, percent=percent, ) else: # diagonalize the reduced density matrix mt, mpsdim, mpsqn, compmps = renormalization_ddm( cstruct, qnbigl, qnbigr, system, nexciton, Mmax=mmax, percent=percent, ) if method == "1site": mps[imps] = mt if system == "L": if imps != len(mps) - 1: mps[imps + 1] = tensordot(compmps, mps[imps + 1], axes=1) mps.qn[imps + 1] = mpsqn else: mps[imps] = tensordot(mps[imps], compmps, axes=1) mps.qn[imps + 1] = [0] else: if imps != 0: mps[imps - 1] = tensordot(mps[imps - 1], compmps, axes=1) mps.qn[imps] = mpsqn else: mps[imps] = tensordot(compmps, mps[imps], axes=1) mps.qn[imps] = [0] else: if system == "L": mps[imps - 1] = mt mps[imps] = compmps else: mps[imps] = mt mps[imps - 1] = compmps # mps.dim_list[imps] = mpsdim mps.qn[imps] = mpsqn energies = np.array(energies) if nroots == 1: logger.debug("Optimization complete, lowest energy = %g", energies.min()) return energies
# -*- coding: utf-8 -*- # Author: Jiajun Ren <*****@*****.**> """ construct the operator matrix in the MPS sweep procedure """ from functools import reduce from collections import deque import numpy as np from renormalizer.mps.matrix import Matrix, multi_tensor_contract, ones, EmptyMatrixError sentinel = ones((1, 1, 1)) class Environ: def __init__(self): # todo: real disk and other backend self.virtual_disk = {} def construct(self, mps, mps_conj, mpo, domain): tensor = ones((1, 1, 1), mps.dtype) assert domain in ["L", "R", "l", "r"] if domain == "L" or domain == "l": start, end, inc = 0, len(mps) - 1, 1 self.write(domain, -1, tensor) else: start, end, inc = len(mps) - 1, 0, -1 self.write(domain, len(mps), tensor) for idx in range(start, end, inc):
def _evolve_dmrg_tdvp_mctdhnew(self, mpo, evolve_dt) -> "Mps": # new regularization scheme # JCP 148, 124105 (2018) # JCP 149, 044119 (2018) # a workaround for https://github.com/scipy/scipy/issues/10164 imag_time = np.iscomplex(evolve_dt) if imag_time: evolve_dt = -evolve_dt.imag # used in calculating derivatives coef = -1 else: coef = 1j if self.is_left_canon: assert self.check_left_canonical() self.canonicalise() mps = self.to_complex(inplace=True) # construct the environment matrix environ = Environ() environ.construct(mps, mps.conj(), mpo, "R") # initial matrix ltensor = ones((1, 1, 1)) rtensor = ones((1, 1, 1)) new_mps = mps.metacopy() # statistics for debug output cmf_rk_steps = [] for imps in range(len(mps)): shape = list(mps[imps].shape) system = "L" if mps.left else "R" qnbigl, qnbigr = mps._get_big_qn(imps) u, s, qnlset, v, s, qnrset = svd_qn.Csvd( mps[imps].asnumpy(), qnbigl, qnbigr, mps.qntot, system=system, full_matrices=False, ) vt = v.T mps[imps] = u.reshape(shape[:-1] + [-1]) ltensor = environ.GetLR( "L", imps - 1, mps, mps.conj(), mpo, itensor=ltensor, method="System" ) rtensor = environ.GetLR( "R", imps + 1, mps, mps.conj(), mpo, itensor=rtensor, method="Enviro" ) epsilon = 1e-10 epsilon = np.sqrt(epsilon) s = s + epsilon * np.exp(-s / epsilon) svt = Matrix(np.diag(s).dot(vt)) rtensor = tensordot(rtensor, svt, axes=(2, 1)) rtensor = tensordot(Matrix(vt).conj(), rtensor, axes=(1, 0)) if imps != len(mps) - 1: mps[imps + 1] = tensordot(svt, mps[imps + 1], axes=(-1, 0)) mps.qn[imps + 1] = qnlset new_mps.qn[imps + 1] = qnlset.copy() S_inv = xp.diag(1.0 / s) hop = hop_factory(ltensor, rtensor, mpo[imps], len(shape)) func = integrand_func_factory(shape, hop, imps == len(mps) - 1, S_inv, coef) sol = solve_ivp( func, (0, evolve_dt), mps[imps].ravel().array, method="RK45" ) cmf_rk_steps.append(len(sol.t)) ms = sol.y[:, -1].reshape(shape) if imps == len(mps) - 1: new_mps[imps] = ms * s[0] else: new_mps[imps] = ms mps._switch_direction() new_mps._switch_direction() new_mps.canonicalise() steps_stat = stats.describe(cmf_rk_steps) logger.debug(f"TDVP-MCTDH CMF steps: {steps_stat}") # new_mps.evolve_config.stat = steps_stat return new_mps