def renormalization_svd(cstruct, qnbigl, qnbigr, domain, nexciton, Mmax, percent=0): """ get the new mps, mpsdim, mpdqn, complementary mps to get the next guess with singular value decomposition method (1 root) """ assert domain in ["R", "L"] Uset, SUset, qnlnew, Vset, SVset, qnrnew = svd_qn.Csvd( cstruct, qnbigl, qnbigr, nexciton, system=domain ) if domain == "R": mps, mpsdim, mpsqn, compmps = updatemps( Vset, SVset, qnrnew, Uset, nexciton, Mmax, percent=percent ) return ( xp.moveaxis(mps.reshape(list(qnbigr.shape) + [mpsdim]), -1, 0), mpsdim, mpsqn, compmps.reshape(list(qnbigl.shape) + [mpsdim]), ) else: mps, mpsdim, mpsqn, compmps = updatemps( Uset, SUset, qnlnew, Vset, nexciton, Mmax, percent=percent ) return ( mps.reshape(list(qnbigl.shape) + [mpsdim]), mpsdim, mpsqn, xp.moveaxis(compmps.reshape(list(qnbigr.shape) + [mpsdim]), -1, 0), )
def apply(self, mp: MatrixProduct, canonicalise: bool=False) -> MatrixProduct: # todo: use meta copy to save time, could be subtle when complex type is involved # todo: inplace version (saved memory and can be used in `hybrid_exact_propagator`) new_mps = self.promote_mt_type(mp.copy()) if mp.is_mps: # mpo x mps for i, (mt_self, mt_other) in enumerate(zip(self, mp)): assert mt_self.shape[2] == mt_other.shape[1] # mt=np.einsum("apqb,cqd->acpbd",mpo[i],mps[i]) mt = xp.moveaxis( tensordot(mt_self.array, mt_other.array, axes=([2], [1])), 3, 1 ) mt = mt.reshape( ( mt_self.shape[0] * mt_other.shape[0], mt_self.shape[1], mt_self.shape[-1] * mt_other.shape[-1], ) ) new_mps[i] = mt elif mp.is_mpo or mp.is_mpdm: # mpo x mpo for i, (mt_self, mt_other) in enumerate(zip(self, mp)): assert mt_self.shape[2] == mt_other.shape[1] # mt=np.einsum("apqb,cqrd->acprbd",mt_s,mt_o) mt = xp.moveaxis( tensordot(mt_self.array, mt_other.array, axes=([2], [1])), [-3, -2], [1, 3], ) mt = mt.reshape( ( mt_self.shape[0] * mt_other.shape[0], mt_self.shape[1], mt_other.shape[2], mt_self.shape[-1] * mt_other.shape[-1], ) ) new_mps[i] = mt else: assert False orig_idx = new_mps.qnidx new_mps.move_qnidx(self.qnidx) qn = self.qn if not mp.use_dummy_qn else self.dummy_qn new_mps.qn = [ np.add.outer(np.array(qn_o), np.array(qn_m)).ravel().tolist() for qn_o, qn_m in zip(qn, new_mps.qn) ] new_mps.qntot += self.qntot new_mps.move_qnidx(orig_idx) # concerns about whether to canonicalise: # * canonicalise helps to keep mps in a truly canonicalised state # * canonicalise comes with a cost. Unnecessary canonicalise (for example in P&C evolution and # expectation calculation) hampers performance. if canonicalise: new_mps.canonicalise() return new_mps
def apply(self, mp, canonicalise=False) -> "MpDmBase": # Note usually mp is an mpo assert not mp.is_mps new_mpdm = self.metacopy() if mp.is_complex: new_mpdm.to_complex(inplace=True) # todo: also duplicate with MPO apply. What to do??? for i, (mt_self, mt_other) in enumerate(zip(self, mp)): assert mt_self.shape[2] == mt_other.shape[1] # mt=np.einsum("apqb,cqrd->acprbd",mt_s,mt_o) mt = xp.moveaxis( tensordot(mt_self.array, mt_other.array, axes=([2], [1])), [-3, -2], [1, 3], ) mt = mt.reshape(( mt_self.shape[0] * mt_other.shape[0], mt_self.shape[1], mt_other.shape[2], mt_self.shape[-1] * mt_other.shape[-1], )) new_mpdm[i] = mt qn = mp.dummy_qn new_mpdm.qn = [ np.add.outer(np.array(qn_o), np.array(qn_m)).ravel().tolist() for qn_o, qn_m in zip(self.qn, qn) ] if canonicalise: new_mpdm.canonicalise() return new_mpdm
def hop(x): nonlocal count count += 1 dag_struct = asxp(self.dag2mat(xshape, x, dag_qnmat)) if self.method == "1site": M1 = multi_tensor_contract(path_1, first_L, dag_struct, a_oper_isite, a_oper_isite, first_R) M2 = multi_tensor_contract(path_2, second_L, dag_struct, a_oper_isite, h_mpo_isite, second_R) M2 = xp.moveaxis(M2, (1, 2), (2, 1)) M3 = multi_tensor_contract(path_2, third_L, h_mpo_isite, dag_struct, h_mpo_isite, third_R) M3 = xp.moveaxis(M3, (1, 2), (2, 1)) cout = M1 + 2 * M2 + M3 + dag_struct * self.eta**2 cout = cout[self.condition(dag_qnmat, [down_exciton, up_exciton])] return asnumpy(cout)
def renormalization_ddm(cstruct, qnbigl, qnbigr, domain, nexciton, Mmax, percent=0): """ get the new mps, mpsdim, mpdqn, complementary mps to get the next guess with diagonalize reduced density matrix method (> 1 root) """ nroots = len(cstruct) ddm = 0.0 for iroot in range(nroots): if domain == "R": ddm += np.tensordot( cstruct[iroot], cstruct[iroot], axes=(range(qnbigl.ndim), range(qnbigl.ndim)), ) else: ddm += np.tensordot( cstruct[iroot], cstruct[iroot], axes=( range(qnbigl.ndim, cstruct[0].ndim), range(qnbigl.ndim, cstruct[0].ndim), ), ) ddm /= float(nroots) if domain == "L": Uset, Sset, qnnew = svd_qn.Csvd(ddm, qnbigl, qnbigl, nexciton, ddm=True) else: Uset, Sset, qnnew = svd_qn.Csvd(ddm, qnbigr, qnbigr, nexciton, ddm=True) mps, mpsdim, mpsqn, compmps = updatemps( Uset, Sset, qnnew, None, nexciton, Mmax, percent=percent ) if domain == "R": return ( xp.moveaxis(mps.reshape(list(qnbigr.shape) + [mpsdim]), -1, 0), mpsdim, mpsqn, tensordot( asxp(cstruct[0]), mps.reshape(list(qnbigr.shape) + [mpsdim]), axes=(range(qnbigl.ndim, cstruct[0].ndim), range(qnbigr.ndim)), ), ) else: return ( mps.reshape(list(qnbigl.shape) + [mpsdim]), mpsdim, mpsqn, tensordot( mps.reshape(list(qnbigl.shape) + [mpsdim]), asxp(cstruct[0]), axes=(range(qnbigl.ndim), range(qnbigl.ndim)), ), )
def moveaxis(a: Matrix, source, destination): return Matrix(xp.moveaxis(a.array, source, destination))
def optimize_cv(self, lr_group, direction, isite, num, percent=0): if self.spectratype == "abs": # quantum number restriction, |1><0| up_exciton, down_exciton = 1, 0 elif self.spectratype == "emi": # quantum number restriction, |0><1| up_exciton, down_exciton = 0, 1 nexciton = 1 first_LR, second_LR, third_LR, forth_LR = lr_group if self.method == "1site": add_list = [isite - 1] first_L = asxp(first_LR[isite - 1]) first_R = asxp(first_LR[isite]) second_L = asxp(second_LR[isite - 1]) second_R = asxp(second_LR[isite]) third_L = asxp(third_LR[isite - 1]) third_R = asxp(third_LR[isite]) forth_L = asxp(forth_LR[isite - 1]) forth_R = asxp(forth_LR[isite]) else: add_list = [isite - 2, isite - 1] first_L = asxp(first_LR[isite - 2]) first_R = asxp(first_LR[isite]) second_L = asxp(second_LR[isite - 2]) second_R = asxp(second_LR[isite]) third_L = asxp(third_LR[isite - 2]) third_R = asxp(third_LR[isite]) forth_L = asxp(forth_LR[isite - 2]) forth_R = asxp(forth_LR[isite]) xqnmat, xqnbigl, xqnbigr, xshape = \ self.construct_X_qnmat(add_list, direction) dag_qnmat, dag_qnbigl, dag_qnbigr = self.swap(xqnmat, xqnbigl, xqnbigr, direction) nonzeros = np.sum(self.condition(dag_qnmat, [down_exciton, up_exciton])) if self.method == "1site": guess = moveaxis(self.cv_mpo[isite - 1], (1, 2), (2, 1)) else: guess = tensordot(moveaxis(self.cv_mpo[isite - 2], (1, 2), (2, 1)), moveaxis(self.cv_mpo[isite - 1]), axes=(-1, 0)) guess = guess[self.condition(dag_qnmat, [down_exciton, up_exciton])].reshape( nonzeros, 1) if self.method == "1site": # define dot path path_1 = [([0, 1], "abc, adef -> bcdef"), ([2, 0], "bcdef, begh -> cdfgh"), ([1, 0], "cdfgh, fhi -> cdgi")] path_2 = [([0, 1], "abcd, aefg -> bcdefg"), ([3, 0], "bcdefg, bfhi -> cdeghi"), ([2, 0], "cdeghi, djek -> cghijk"), ([1, 0], "cghijk, gilk -> chjl")] path_4 = [([0, 1], "ab, acde -> bcde"), ([1, 0], "bcde, ef -> bcdf")] vecb = multi_tensor_contract( path_4, forth_L, moveaxis(self.a_ket_mpo[isite - 1], (1, 2), (2, 1)), forth_R) vecb = -self.eta * vecb a_oper_isite = asxp(self.a_oper[isite - 1]) b_oper_isite = asxp(self.b_oper[isite - 1]) h_mpo_isite = asxp(self.h_mpo[isite - 1]) # construct preconditioner Idt = xp.identity(h_mpo_isite.shape[1]) M1_1 = xp.einsum('aea->ae', first_L) M1_2 = xp.einsum('eccf->ecf', a_oper_isite) M1_3 = xp.einsum('dfd->df', first_R) M1_4 = xp.einsum('bb->b', Idt) path_m1 = [([0, 1], "ae,b->aeb"), ([2, 0], "aeb,ecf->abcf"), ([1, 0], "abcf, df->abcd")] pre_M1 = multi_tensor_contract(path_m1, M1_1, M1_4, M1_2, M1_3) pre_M1 = pre_M1[self.condition(dag_qnmat, [down_exciton, up_exciton])] M2_1 = xp.einsum('aeag->aeg', second_L) M2_2 = xp.einsum('eccf->ecf', b_oper_isite) M2_3 = xp.einsum('gbbh->gbh', h_mpo_isite) M2_4 = xp.einsum('dfdh->dfh', second_R) path_m2 = [([0, 1], "aeg,gbh->aebh"), ([2, 0], "aebh,ecf->abchf"), ([1, 0], "abhcf,dfh->abcd")] pre_M2 = multi_tensor_contract(path_m2, M2_1, M2_3, M2_2, M2_4) pre_M2 = pre_M2[self.condition(dag_qnmat, [down_exciton, up_exciton])] M4_1 = xp.einsum('faah->fah', third_L) M4_4 = xp.einsum('gddi->gdi', third_R) M4_5 = xp.einsum('cc->c', Idt) M4_path = [([0, 1], "fah,febg->ahebg"), ([2, 0], "ahebg,hjei->abgji"), ([1, 0], "abgji,gdi->abjd")] pre_M4 = multi_tensor_contract(M4_path, M4_1, h_mpo_isite, h_mpo_isite, M4_4) pre_M4 = xp.einsum('abbd->abd', pre_M4) pre_M4 = xp.tensordot(pre_M4, M4_5, axes=0) pre_M4 = xp.moveaxis(pre_M4, [2, 3], [3, 2])[self.condition( dag_qnmat, [down_exciton, up_exciton])] pre_M = (pre_M1 + 2 * pre_M2 + pre_M4) indices = np.array(range(nonzeros)) indptr = np.array(range(nonzeros + 1)) pre_M = scipy.sparse.csc_matrix((asnumpy(pre_M), indices, indptr), shape=(nonzeros, nonzeros)) M_x = lambda x: scipy.sparse.linalg.spsolve(pre_M, x) M = scipy.sparse.linalg.LinearOperator((nonzeros, nonzeros), M_x) count = 0 def hop(x): nonlocal count count += 1 dag_struct = asxp(self.dag2mat(xshape, x, dag_qnmat, direction)) if self.method == "1site": M1 = multi_tensor_contract(path_1, first_L, dag_struct, a_oper_isite, first_R) M2 = multi_tensor_contract(path_2, second_L, dag_struct, b_oper_isite, h_mpo_isite, second_R) M2 = xp.moveaxis(M2, (1, 2), (2, 1)) M3 = multi_tensor_contract(path_2, third_L, h_mpo_isite, dag_struct, h_mpo_isite, third_R) M3 = xp.moveaxis(M3, (1, 2), (2, 1)) cout = M1 + 2 * M2 + M3 cout = cout[self.condition(dag_qnmat, [down_exciton, up_exciton])].reshape( nonzeros, 1) return asnumpy(cout) # Matrix A and Vector b vecb = asnumpy(vecb)[self.condition( dag_qnmat, [down_exciton, up_exciton])].reshape(nonzeros, 1) mata = scipy.sparse.linalg.LinearOperator((nonzeros, nonzeros), matvec=hop) # conjugate gradient method # x, info = scipy.sparse.linalg.cg(MatA, VecB, atol=0) if num == 1: x, info = scipy.sparse.linalg.cg(mata, vecb, tol=1.e-5, maxiter=500, M=M, atol=0) else: x, info = scipy.sparse.linalg.cg(mata, vecb, tol=1.e-5, x0=guess, maxiter=500, M=M, atol=0) # logger.info(f"linear eq dim: {nonzeros}") # logger.info(f'times for hop:{count}') self.hop_time.append(count) if info != 0: logger.warning( f"cg not converged, vecb.norm:{np.linalg.norm(vecb)}") l_value = np.inner( hop(x).reshape(1, nonzeros), x.reshape(1, nonzeros)) - \ 2 * np.inner(vecb.reshape(1, nonzeros), x.reshape(1, nonzeros)) x = self.dag2mat(xshape, x, dag_qnmat, direction) if self.method == "1site": x = np.moveaxis(x, [1, 2], [2, 1]) x, xdim, xqn, compx = self.x_svd(x, xqnbigl, xqnbigr, nexciton, direction, percent=percent) if self.method == "1site": self.cv_mpo[isite - 1] = x if direction == "left": if isite != 1: self.cv_mpo[isite - 2] = \ tensordot(self.cv_mpo[isite - 2], compx, axes=(-1, 0)) self.cv_mpo.qn[isite - 1] = xqn else: self.cv_mpo[isite - 1] = \ tensordot(compx, self.cv_mpo[isite - 1], axes=(-1, 0)) elif direction == "right": if isite != len(self.cv_mpo): self.cv_mpo[isite] = \ tensordot(compx, self.cv_mpo[isite], axes=(-1, 0)) self.cv_mpo.qn[isite] = xqn else: self.cv_mpo[isite - 1] = \ tensordot(self.cv_mpo[isite - 1], compx, axes=(-1, 0)) else: if direction == "left": self.cv_mpo[isite - 2] = compx self.cv_mpo[isite - 1] = x else: self.cv_mpo[isite - 2] = x self.cv_mpo[isite - 1] = compx self.cv_mpo.qn[isite - 1] = xqn return l_value[0][0]
def _update_mps(self, cstruct, cidx, qnbigl, qnbigr, Mmax, percent=0): r"""update mps with basis selection algorithm of J. Chem. Phys. 120, 3172 (2004). Parameters --------- cstruct : ndarray, List[ndarray] The active site coefficient. cidx : list The List of active site index. qnbigl : ndarray The super-L-block quantum number. qnbigr : ndarray The super-R-block quantum number. Mmax : int The maximal bond dimension. percent : float, int The percentage of renormalized basis which is equally selected from each quantum number section rather than according to singular values. ``percent`` is defined in ``procedure`` of `renormalizer.utils.configs.OptimizeConfig` and ``vprocedure`` of `renormalizer.utils.configs.CompressConfig`. Returns ------- averaged_ms : if ``cstruct`` is a list, ``averaged_ms`` is a list of rotated ms of each element in ``cstruct`` as a single site calculation. It is used for better initial guess in SA-DMRG algorithm. Otherwise, ``None`` is returned. ``self`` is overwritten inplace. """ system = "L" if self.to_right else "R" # step 1: get the selected U, S, V if type(cstruct) is not list: # SVD method # full_matrices = True here to enable increase the bond dimension Uset, SUset, qnlnew, Vset, SVset, qnrnew = svd_qn.Csvd( asnumpy(cstruct), qnbigl, qnbigr, self.qntot, system=system) if self.to_right: ms, msdim, msqn, compms = select_basis(Uset, SUset, qnlnew, Vset, Mmax, percent=percent) ms = ms.reshape(list(qnbigl.shape) + [msdim]) compms = xp.moveaxis( compms.reshape(list(qnbigr.shape) + [msdim]), -1, 0) else: ms, msdim, msqn, compms = select_basis(Vset, SVset, qnrnew, Uset, Mmax, percent=percent) ms = xp.moveaxis(ms.reshape(list(qnbigr.shape) + [msdim]), -1, 0) compms = compms.reshape(list(qnbigl.shape) + [msdim]) else: # state-averaged method ddm = 0.0 for iroot in range(len(cstruct)): if self.to_right: ddm += tensordot( cstruct[iroot], cstruct[iroot], axes=( range(qnbigl.ndim, cstruct[iroot].ndim), range(qnbigl.ndim, cstruct[iroot].ndim), ), ) else: ddm += tensordot( cstruct[iroot], cstruct[iroot], axes=(range(qnbigl.ndim), range(qnbigl.ndim)), ) ddm /= len(cstruct) Uset, Sset, qnnew = svd_qn.Csvd(asnumpy(ddm), qnbigl, qnbigr, self.qntot, system=system, ddm=True) ms, msdim, msqn, compms = select_basis(Uset, Sset, qnnew, None, Mmax, percent=percent) rotated_c = [] averaged_ms = [] if self.to_right: ms = ms.reshape(list(qnbigl.shape) + [msdim]) for c in cstruct: compms = tensordot( ms, c, axes=(range(qnbigl.ndim), range(qnbigl.ndim)), ) rotated_c.append(compms) compms = rotated_c[0] else: ms = ms.reshape(list(qnbigr.shape) + [msdim]) for c in cstruct: compms = tensordot( c, ms, axes=(range(qnbigl.ndim, cstruct[0].ndim), range(qnbigr.ndim)), ) rotated_c.append(compms) compms = rotated_c[0] ms = xp.moveaxis(ms, -1, 0) # step 2, put updated U, S, V back to self if len(cidx) == 1: # 1site method self[cidx[0]] = ms if self.to_right: if cidx[0] != self.site_num - 1: if type(cstruct) is list: for c in rotated_c: averaged_ms.append( tensordot(c, self[cidx[0] + 1], axes=1)) self[cidx[0] + 1] = tensordot(compms, self[cidx[0] + 1], axes=1) self.qn[cidx[0] + 1] = msqn self.qnidx = cidx[0] + 1 else: if type(cstruct) is list: for c in rotated_c: averaged_ms.append( tensordot(self[cidx[0]], c, axes=1)) self[cidx[0]] = tensordot(self[cidx[0]], compms, axes=1) self.qnidx = self.site_num - 1 else: if cidx[0] != 0: if type(cstruct) is list: for c in rotated_c: averaged_ms.append( tensordot(self[cidx[0] - 1], c, axes=1)) self[cidx[0] - 1] = tensordot(self[cidx[0] - 1], compms, axes=1) self.qn[cidx[0]] = msqn self.qnidx = cidx[0] - 1 else: if type(cstruct) is list: for c in rotated_c: averaged_ms.append( tensordot(c, self[cidx[0]], axes=1)) self[cidx[0]] = tensordot(compms, self[cidx[0]], axes=1) self.qnidx = 0 else: if self.to_right: self[cidx[0]] = ms self[cidx[1]] = compms self.qnidx = cidx[1] else: self[cidx[1]] = ms self[cidx[0]] = compms self.qnidx = cidx[0] if type(cstruct) is list: averaged_ms = rotated_c self.qn[cidx[1]] = msqn if type(cstruct) is list: return averaged_ms else: return None
def optimize_cv(self, lr_group, isite, percent=0): if self.spectratype == "abs": # quantum number restriction, |1><0| up_exciton, down_exciton = 1, 0 elif self.spectratype == "emi": # quantum number restriction, |0><1| up_exciton, down_exciton = 0, 1 nexciton = 1 first_LR, second_LR, third_LR, forth_LR = lr_group if self.method == "1site": add_list = [isite - 1] first_L = asxp(first_LR[isite - 1]) first_R = asxp(first_LR[isite]) second_L = asxp(second_LR[isite - 1]) second_R = asxp(second_LR[isite]) third_L = asxp(third_LR[isite - 1]) third_R = asxp(third_LR[isite]) forth_L = asxp(forth_LR[isite - 1]) forth_R = asxp(forth_LR[isite]) else: add_list = [isite - 2, isite - 1] first_L = asxp(first_LR[isite - 2]) first_R = asxp(first_LR[isite]) second_L = asxp(second_LR[isite - 2]) second_R = asxp(second_LR[isite]) third_L = asxp(third_LR[isite - 2]) third_R = asxp(third_LR[isite]) forth_L = asxp(forth_LR[isite - 2]) forth_R = asxp(forth_LR[isite]) xqnmat, xqnbigl, xqnbigr, xshape = \ self.construct_X_qnmat(add_list) dag_qnmat, dag_qnbigl, dag_qnbigr = self.swap(xqnmat, xqnbigl, xqnbigr) nonzeros = int( np.sum(self.condition(dag_qnmat, [down_exciton, up_exciton]))) if self.method == "1site": guess = moveaxis(self.cv_mpo[isite - 1], (1, 2), (2, 1)) else: guess = tensordot(moveaxis(self.cv_mpo[isite - 2], (1, 2), (2, 1)), moveaxis(self.cv_mpo[isite - 1]), axes=(-1, 0)) guess = guess[self.condition(dag_qnmat, [down_exciton, up_exciton])] if self.method == "1site": # define dot path path_1 = [([0, 1], "abcd, aefg -> bcdefg"), ([3, 0], "bcdefg, bfhi -> cdeghi"), ([2, 0], "cdeghi, chjk -> degijk"), ([1, 0], "degijk, gikl -> dejl")] path_2 = [([0, 1], "abcd, aefg -> bcdefg"), ([3, 0], "bcdefg, bfhi -> cdeghi"), ([2, 0], "cdeghi, djek -> cghijk"), ([1, 0], "cghijk, gilk -> chjl")] path_3 = [([0, 1], "ab, acde -> bcde"), ([1, 0], "bcde, ef -> bcdf")] vecb = multi_tensor_contract( path_3, forth_L, moveaxis(self.b_mpo[isite - 1], (1, 2), (2, 1)), forth_R)[self.condition(dag_qnmat, [down_exciton, up_exciton])] a_oper_isite = asxp(self.a_oper[isite - 1]) h_mpo_isite = asxp(self.h_mpo[isite - 1]) # construct preconditioner Idt = xp.identity(h_mpo_isite.shape[1]) M1_1 = xp.einsum('abca->abc', first_L) path_m1 = [([0, 1], "abc, bdef->acdef"), ([1, 0], "acdef, cegh->adfgh")] M1_2 = multi_tensor_contract(path_m1, M1_1, a_oper_isite, a_oper_isite) M1_2 = xp.einsum("abcbd->abcd", M1_2) M1_3 = xp.einsum('ecde->ecd', first_R) M1_4 = xp.einsum('ff->f', Idt) path_m1 = [([0, 1], "abcd,ecd->abe"), ([1, 0], "abe,f->abef")] pre_M1 = multi_tensor_contract(path_m1, M1_2, M1_3, M1_4) pre_M1 = xp.moveaxis(pre_M1, [-2, -1], [-1, -2])[self.condition( dag_qnmat, [down_exciton, up_exciton])] M2_1 = xp.einsum('aeag->aeg', second_L) M2_2 = xp.einsum('eccf->ecf', a_oper_isite) M2_3 = xp.einsum('gbbh->gbh', h_mpo_isite) M2_4 = xp.einsum('dfdh->dfh', second_R) path_m2 = [([0, 1], "aeg,gbh->aebh"), ([2, 0], "aebh,ecf->abchf"), ([1, 0], "abhcf,dfh->abcd")] pre_M2 = multi_tensor_contract(path_m2, M2_1, M2_3, M2_2, M2_4) pre_M2 = pre_M2[self.condition(dag_qnmat, [down_exciton, up_exciton])] M4_1 = xp.einsum('faah->fah', third_L) M4_4 = xp.einsum('gddi->gdi', third_R) M4_5 = xp.einsum('cc->c', Idt) M4_path = [([0, 1], "fah,febg->ahebg"), ([2, 0], "ahebg,hjei->abgji"), ([1, 0], "abgji,gdi->abjd")] pre_M4 = multi_tensor_contract(M4_path, M4_1, h_mpo_isite, h_mpo_isite, M4_4) pre_M4 = xp.einsum('abbd->abd', pre_M4) pre_M4 = xp.tensordot(pre_M4, M4_5, axes=0) pre_M4 = xp.moveaxis(pre_M4, [2, 3], [3, 2])[self.condition( dag_qnmat, [down_exciton, up_exciton])] M_x = lambda x: asnumpy( asxp(x) / (pre_M1 + 2 * pre_M2 + pre_M4 + xp.ones(nonzeros) * self.eta**2)) pre_M = scipy.sparse.linalg.LinearOperator((nonzeros, nonzeros), M_x) count = 0 def hop(x): nonlocal count count += 1 dag_struct = asxp(self.dag2mat(xshape, x, dag_qnmat)) if self.method == "1site": M1 = multi_tensor_contract(path_1, first_L, dag_struct, a_oper_isite, a_oper_isite, first_R) M2 = multi_tensor_contract(path_2, second_L, dag_struct, a_oper_isite, h_mpo_isite, second_R) M2 = xp.moveaxis(M2, (1, 2), (2, 1)) M3 = multi_tensor_contract(path_2, third_L, h_mpo_isite, dag_struct, h_mpo_isite, third_R) M3 = xp.moveaxis(M3, (1, 2), (2, 1)) cout = M1 + 2 * M2 + M3 + dag_struct * self.eta**2 cout = cout[self.condition(dag_qnmat, [down_exciton, up_exciton])] return asnumpy(cout) # Matrix A mat_a = scipy.sparse.linalg.LinearOperator((nonzeros, nonzeros), matvec=hop) x, info = scipy.sparse.linalg.cg(mat_a, asnumpy(vecb), tol=1.e-5, x0=asnumpy(guess), maxiter=500, M=pre_M, atol=0) # logger.info(f"linear eq dim: {nonzeros}") # logger.info(f'times for hop:{count}') self.hop_time.append(count) if info != 0: logger.warning( f"cg not converged, vecb.norm:{xp.linalg.norm(vecb)}") l_value = xp.dot(asxp(hop(x)), asxp(x)) - 2 * xp.dot(vecb, asxp(x)) x = self.dag2mat(xshape, x, dag_qnmat) if self.method == "1site": x = np.moveaxis(x, [1, 2], [2, 1]) x, xdim, xqn, compx = self.x_svd(x, xqnbigl, xqnbigr, nexciton, percent=percent) if self.method == "1site": self.cv_mpo[isite - 1] = x if not self.cv_mpo.to_right: if isite != 1: self.cv_mpo[isite - 2] = \ tensordot(self.cv_mpo[isite - 2], compx, axes=(-1, 0)) self.cv_mpo.qn[isite - 1] = xqn self.cv_mpo.qnidx = isite - 2 else: self.cv_mpo[isite - 1] = \ tensordot(compx, self.cv_mpo[isite - 1], axes=(-1, 0)) self.cv_mpo.qnidx = 0 else: if isite != len(self.cv_mpo): self.cv_mpo[isite] = \ tensordot(compx, self.cv_mpo[isite], axes=(-1, 0)) self.cv_mpo.qn[isite] = xqn self.cv_mpo.qnidx = isite else: self.cv_mpo[isite - 1] = \ tensordot(self.cv_mpo[isite - 1], compx, axes=(-1, 0)) self.cv_mpo.qnidx = self.cv_mpo.site_num - 1 else: if not self.cv_mpo.to_right: self.cv_mpo[isite - 2] = compx self.cv_mpo[isite - 1] = x self.cv_mpo.qnidx = isite - 2 else: self.cv_mpo[isite - 2] = x self.cv_mpo[isite - 1] = compx self.cv_mpo.qnidx = isite - 1 self.cv_mpo.qn[isite - 1] = xqn return float(l_value)