コード例 #1
0
 def hop(c):
     nonlocal count
     count += 1
     xstruct = asxp(svd_qn.cvec2cmat(xshape, c, qnmat, constrain_qn))
     if self.method == "1site":
         path_a = [([0, 1], "abcd, aef->bcdef"),
                   ([3, 0], "bcdef, begh->cdfgh"),
                   ([2, 0], "cdfgh, cgij->dfhij"),
                   ([1, 0], "dfhij, fhjk->dik")]
         ax1 = multi_tensor_contract(path_a, first_L, xstruct,
                                    a_oper_isite1, a_oper_isite1, first_R)
         ax2 = xstruct
         ax = ax1 + ax2 * self.eta**2
     else:
         path_a = [([0, 1], "abcd, aefg->bcdefg"),
                   ([5, 0], "bcdefg, behi->cdfghi"),
                   ([4, 0], "cdfghi, ifjk->cdghjk"),
                   ([3, 0], "cdghjk, chlm->dgjklm"),
                   ([2, 0], "dgjklm, mjno->dgklno"),
                   ([1, 0], "dgklno, gkop->dlnp")]
         ax1 = multi_tensor_contract(path_a, first_L, xstruct,
                                    a_oper_isite2, a_oper_isite1,
                                    a_oper_isite2, a_oper_isite1,
                                    first_R)
         ax2 = xstruct
         ax = ax1 + ax2 * self.eta**2
     cout = ax[qnmat == constrain_qn].reshape(nonzeros, 1)
     return asnumpy(cout)
コード例 #2
0
ファイル: gs.py プロジェクト: sailfish009/Renormalizer
                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)
コード例 #3
0
    def update_LR(self, lr_group, direction, isite):
        first_LR = lr_group[0]
        second_LR = lr_group[1]
        # use the updated local site of cv_mps to update LR
        if self.method == "1site":
            if direction == "left":
                path1 = [([0, 1], "abcd, efa->bcdef"),
                         ([3, 0], "bcdef, gfhb->cdegh"),
                         ([2, 0], "cdegh, ihjc->degij"),
                         ([1, 0], "degij, kjd->egik")]
                path2 = [([0, 1], "ab, cda->bcd"),
                         ([1, 0], "bcd, edb->ce")]
                first_LR[isite - 1] = multi_tensor_contract(
                    path1, first_LR[isite], self.cv_mps[isite - 1],
                    self.a_oper[isite - 1], self.a_oper[isite - 1], self.cv_mps[isite - 1])
                second_LR[isite - 1] = multi_tensor_contract(
                    path2, second_LR[isite], self.b_oper[isite - 1], self.cv_mps[isite - 1])

            else:
                path1 = [([0, 1], "abcd, aef->bcdef"),
                         ([3, 0], "bcdef, begh->cdfgh"),
                         ([2, 0], "cdfgh, cgij->dfhij"),
                         ([1, 0], "dfhij, dik->fhjk")]
                path2 = [([0, 1], "ab, acd->bcd"),
                         ([1, 0], "bcd, bce->de")]
                first_LR[isite] = multi_tensor_contract(
                    path1, first_LR[isite - 1], self.cv_mps[isite - 1],
                    self.a_oper[isite - 1], self.a_oper[isite - 1], self.cv_mps[isite - 1])
                second_LR[isite] = multi_tensor_contract(
                    path2, second_LR[isite - 1], self.b_oper[isite - 1], self.cv_mps[isite - 1])

        else:
            if direction == "left":
                path1 = [([0, 1], "abc, efa->bcdef"),
                         ([3, 0], "bcdef, gfhb->cdegh"),
                         ([2, 0], "cdegh, ihgc->degij"),
                         ([1, 0], "degij, kjd->egik")]
                path2 = [([0, 1], "ab, cda->bcd"),
                         ([1, 0], "bcd, edb->ce")]
                first_LR[isite - 1] = multi_tensor_contract(
                    path1, first_LR[isite], self.cv_mps[isite - 1],
                    self.a_oper[isite - 1], self.a_oper[isite - 1], self.cv_mps[isite - 1])
                second_LR[isite - 1] = multi_tensor_contract(
                    path2, second_LR[isite], self.b_oper[isite - 1], self.cv_mps[isite - 1])

            else:
                path1 = [([0, 1], "abc, aef->bcdef"),
                         ([3, 0], "bcdef, begh->cdfgh"),
                         ([2, 0], "cdfgh, cgij->dfhij"),
                         ([1, 0], "dfhij, dik->fhjk")]
                path2 = [([0, 1], "ab, acd->bcd"),
                         ([1, 0], "bcd, bce->de")]
                first_LR[isite - 1] = multi_tensor_contract(
                    path1, first_LR[isite - 2], self.cv_mps[isite - 2],
                    self.a_oper[isite - 2], self.a_oper[isite - 2], self.cv_mps[isite - 2])
                second_LR[isite - 1] = multi_tensor_contract(
                    path2, second_LR[isite - 2], self.b_oper[isite - 2], self.cv_mps[isite - 2])

        return [first_LR, second_LR]
コード例 #4
0
ファイル: mps.py プロジェクト: jiangtong1000/Renormalizer
 def expectations(self, mpos) -> np.ndarray:
     if len(mpos) < 3:
         return np.array([self.expectation(mpo) for mpo in mpos])
     assert 2 < len(mpos)
     # id can be used as efficient hash because of `Matrix` implementation
     mpo_ids = np.array([[id(m) for m in mpo] for mpo in mpos])
     common_mpo_ids = mpo_ids[0].copy()
     mpo0_unique_idx = np.where(np.sum(mpo_ids == common_mpo_ids, axis=0) == 1)[0][0]
     common_mpo_ids[mpo0_unique_idx] = mpo_ids[1][mpo0_unique_idx]
     x, unique_idx = np.where(mpo_ids != common_mpo_ids)
     # should find one at each line
     assert np.allclose(x, np.arange(len(mpos)))
     common_mpo = list(mpos[0])
     common_mpo[mpo0_unique_idx] = mpos[1][mpo0_unique_idx]
     self_conj = self._expectation_conj()
     environ = Environ()
     environ.construct(self, self_conj, common_mpo, "l")
     environ.construct(self, self_conj, common_mpo, "r")
     res_list = []
     for idx, mpo in zip(unique_idx, mpos):
         l = environ.read("l", idx - 1)
         r = environ.read("r", idx + 1)
         path = self._expectation_path()
         res = multi_tensor_contract(path, l, self[idx], mpo[idx], self_conj[idx], r)
         res_list.append(float(res.real))
     return np.array(res_list)
コード例 #5
0
     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)
コード例 #6
0
ファイル: solver.py プロジェクト: jiangtong1000/Renormalizer
            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]
コード例 #7
0
ファイル: finitet.py プロジェクト: sailfish009/Renormalizer
        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)
コード例 #8
0
ファイル: mps.py プロジェクト: jiangtong1000/Renormalizer
 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)
コード例 #9
0
ファイル: mps.py プロジェクト: jiangtong1000/Renormalizer
                def hop_svt(ms):
                    # S-a   l-S
                    #
                    # O-b - b-O
                    #
                    # S-c   k-S

                    path = [([0, 1], "abc, ck -> abk"), ([1, 0], "abk, lbk -> al")]
                    HC = multi_tensor_contract(path, l_array, ms, r_array)
                    return HC
コード例 #10
0
ファイル: zerot.py プロジェクト: sailfish009/Renormalizer
 def initialize_LR(self):
     # initialize the Lpart and Rpart
     first_LR = []
     first_LR.append(np.ones((1, 1, 1, 1)))
     second_LR = []
     second_LR.append(np.ones((1, 1)))
     for isite in range(1, len(self.cv_mps)):
         first_LR.append(None)
         second_LR.append(None)
     first_LR.append(np.ones((1, 1, 1, 1)))
     second_LR.append(np.ones((1, 1)))
     if self.cv_mps.to_right:
         path1 = [([0, 1], "abcd, efa->bcdef"),
                  ([3, 0], "bcdef, gfhb->cdegh"),
                  ([2, 0], "cdegh, ihjc->degij"),
                  ([1, 0], "degij, kjd->egik")]
         path2 = [([0, 1], "ab, cda->bcd"),
                  ([1, 0], "bcd, edb->ce")]
         for isite in range(len(self.cv_mps), 1, -1):
             first_LR[isite - 1] = asnumpy(multi_tensor_contract(
                 path1, first_LR[isite], self.cv_mps[isite - 1],
                 self.a_oper[isite - 1], self.a_oper[isite - 1],
                 self.cv_mps[isite - 1]))
             second_LR[isite - 1] = asnumpy(multi_tensor_contract(
                 path2, second_LR[isite], self.b_mps[isite - 1],
                 self.cv_mps[isite - 1]))
     else:
         path1 = [([0, 1], "abcd, aef->bcdef"),
                  ([3, 0], "bcdef, begh->cdfgh"),
                  ([2, 0], "cdfgh, cgij->dfhij"),
                  ([1, 0], "dfhij, dik->fhjk")]
         path2 = [([0, 1], "ab, acd->bcd"),
                  ([1, 0], "bcd, bce->de")]
         for isite in range(1, len(self.cv_mps)):
             mps_isite = asxp(self.cv_mps[isite - 1])
             first_LR[isite] = asnumpy(multi_tensor_contract(
                 path1, first_LR[isite - 1], mps_isite,
                 self.a_oper[isite - 1], self.a_oper[isite - 1],
                 self.cv_mps[isite - 1]))
             second_LR[isite] = asnumpy(multi_tensor_contract(
                 path2, second_LR[isite - 1], self.b_mps[isite - 1],
                 mps_isite))
     return [first_LR, second_LR]
コード例 #11
0
ファイル: finitet.py プロジェクト: sailfish009/Renormalizer
    def update_LR(self, lr_group, isite):
        first_LR, second_LR, third_LR, forth_LR = lr_group
        cv_isite = self.cv_mpo[isite - 1]
        dag_cv_isite = moveaxis(cv_isite, (1, 2), (2, 1))
        if self.method == "1site":
            if not self.cv_mpo.to_right:
                path1 = [([0, 1], "abcd, efga -> bcdefg"),
                         ([3, 0], "bcdefg, hgib -> cdefhi"),
                         ([2, 0], "cdefhi, jikc -> defhjk"),
                         ([1, 0], "defhjk, lkfd -> ehjl")]
                path2 = [([0, 1], "ab, cdea->bcde"), ([1,
                                                       0], "bcde, fedb->cf")]
                first_LR[isite - 1] = multi_tensor_contract(
                    path1, first_LR[isite], dag_cv_isite,
                    self.a_oper[isite - 1], self.a_oper[isite - 1], cv_isite)
                second_LR[isite - 1] = multi_tensor_contract(
                    path1, second_LR[isite], dag_cv_isite,
                    self.a_oper[isite - 1], cv_isite, self.h_mpo[isite - 1])
                third_LR[isite - 1] = multi_tensor_contract(
                    path1, third_LR[isite], self.h_mpo[isite - 1],
                    dag_cv_isite, cv_isite, self.h_mpo[isite - 1])
                forth_LR[isite - 1] = multi_tensor_contract(
                    path2, forth_LR[isite],
                    moveaxis(self.b_mpo[isite - 1], (1, 2), (2, 1)), cv_isite)

            else:
                path1 = [([0, 1], "abcd, aefg -> bcdefg"),
                         ([3, 0], "bcdefg, bfhi -> cdeghi"),
                         ([2, 0], "cdeghi, chjk -> degijk"),
                         ([1, 0], "degijk, djel -> gikl")]
                path2 = [([0, 1], "ab, acde->bcde"), ([1,
                                                       0], "bcde, bdcf->ef")]

                first_LR[isite] = multi_tensor_contract(
                    path1, first_LR[isite - 1], dag_cv_isite,
                    self.a_oper[isite - 1], self.a_oper[isite - 1], cv_isite)

                second_LR[isite] = multi_tensor_contract(
                    path1, second_LR[isite - 1], dag_cv_isite,
                    self.a_oper[isite - 1], cv_isite, self.h_mpo[isite - 1])
                third_LR[isite] = multi_tensor_contract(
                    path1, third_LR[isite - 1], self.h_mpo[isite - 1],
                    dag_cv_isite, cv_isite, self.h_mpo[isite - 1])
                forth_LR[isite] = multi_tensor_contract(
                    path2, forth_LR[isite - 1],
                    moveaxis(self.b_mpo[isite - 1], (1, 2), (2, 1)), cv_isite)
        else:
            # 2site for finite temperature is too expensive, so I drop it
            # (at least for now)
            raise NotImplementedError

        return first_LR, second_LR, third_LR, forth_LR
コード例 #12
0
ファイル: zerot.py プロジェクト: sailfish009/Renormalizer
        def hop(c):
            nonlocal count
            count += 1
            xstruct = asxp(cvec2cmat(xshape, c, qnmat, constrain_qn))
            if self.method == "1site":
                path_a = [([0, 1], "abcd, aef->bcdef"),
                          ([3, 0], "bcdef, begh->cdfgh"),
                          ([2, 0], "cdfgh, cgij->dfhij"),
                          ([1, 0], "dfhij, fhjk->dik")]
                ax1 = multi_tensor_contract(path_a, first_L, xstruct,
                                            a_oper_isite1, a_oper_isite1, first_R)
            else:
                # opt_einsum v3.2.1 is not bad, ~10% faster than the hand-design
                # contraction path for this complicated cases and consumes a little bit less memory
                # this is the only place in renormalizer we use opt_einsum now.
                # we keep it here just for a demo.
                # ax1 = oe.contract("abcd, befh, cfgi, hjkn, iklo, mnop, dglp -> aejm",
                #        first_L, a_oper_isite2, a_oper_isite2, a_oper_isite1,
                #        a_oper_isite1, first_R, xstruct)
                if USE_GPU:
                    oe_backend = "cupy"
                else:
                    oe_backend = "numpy"
                ax1 = expr(xstruct, backend=oe_backend)   
                #print(oe.contract_path("abcd, befh, cfgi, hjkn, iklo, mnop, dglp -> aejm",
                #        first_L, a_oper_isite2, a_oper_isite2, a_oper_isite1,
                #        a_oper_isite1, first_R, xstruct))

                #path_a = [([0, 1], "abcd, aefg->bcdefg"),
                #          ([5, 0], "bcdefg, behi->cdfghi"),
                #          ([4, 0], "cdfghi, ifjk->cdghjk"),
                #          ([3, 0], "cdghjk, chlm->dgjklm"),
                #          ([2, 0], "dgjklm, mjno->dgklno"),
                #          ([1, 0], "dgklno, gkop->dlnp")]
                #ax1 = multi_tensor_contract(path_a, first_L, xstruct,
                #                           a_oper_isite2, a_oper_isite1,
                #                           a_oper_isite2, a_oper_isite1,
                #                           first_R)
            ax = ax1 + xstruct * self.eta**2
            cout = ax[qnmat == constrain_qn]
            return asnumpy(cout)
コード例 #13
0
ファイル: solver.py プロジェクト: jiangtong1000/Renormalizer
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
コード例 #14
0
ファイル: finitet.py プロジェクト: sailfish009/Renormalizer
    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)
コード例 #15
0
ファイル: finitet.py プロジェクト: sailfish009/Renormalizer
    def initialize_LR(self):

        first_LR = [np.ones((1, 1, 1, 1))]
        forth_LR = [np.ones((1, 1))]
        for isite in range(1, len(self.cv_mpo)):
            first_LR.append(None)
            forth_LR.append(None)
        first_LR.append(np.ones((1, 1, 1, 1)))
        second_LR = copy.deepcopy(first_LR)
        third_LR = copy.deepcopy(first_LR)
        forth_LR.append(np.ones((1, 1)))

        if self.cv_mpo.to_right:
            for isite in range(len(self.cv_mpo), 1, -1):
                cv_isite = self.cv_mpo[isite - 1]
                dag_cv_isite = moveaxis(cv_isite, (1, 2), (2, 1))
                path1 = [([0, 1], "abcd, efga -> bcdefg"),
                         ([3, 0], "bcdefg, hgib -> cdefhi"),
                         ([2, 0], "cdefhi, jikc -> defhjk"),
                         ([1, 0], "defhjk, lkfd -> ehjl")]
                path2 = [([0, 1], "ab, cdea->bcde"), ([1,
                                                       0], "bcde, fedb->cf")]
                first_LR[isite - 1] = asnumpy(
                    multi_tensor_contract(path1, first_LR[isite], dag_cv_isite,
                                          self.a_oper[isite - 1],
                                          self.a_oper[isite - 1], cv_isite))

                second_LR[isite - 1] = asnumpy(
                    multi_tensor_contract(path1, second_LR[isite],
                                          dag_cv_isite, self.a_oper[isite - 1],
                                          cv_isite, self.h_mpo[isite - 1]))
                third_LR[isite - 1] = asnumpy(
                    multi_tensor_contract(path1, third_LR[isite],
                                          self.h_mpo[isite - 1], dag_cv_isite,
                                          cv_isite, self.h_mpo[isite - 1]))
                forth_LR[isite - 1] = asnumpy(
                    multi_tensor_contract(
                        path2, forth_LR[isite],
                        moveaxis(self.b_mpo[isite - 1], (1, 2), (2, 1)),
                        cv_isite))

        else:
            for isite in range(1, len(self.cv_mpo)):
                cv_isite = self.cv_mpo[isite - 1]
                dag_cv_isite = moveaxis(cv_isite, (1, 2), (2, 1))
                path1 = [([0, 1], "abcd, aefg -> bcdefg"),
                         ([3, 0], "bcdefg, bfhi -> cdeghi"),
                         ([2, 0], "cdeghi, chjk -> degijk"),
                         ([1, 0], "degijk, djel -> gikl")]
                path2 = [([0, 1], "ab, acde->bcde"), ([1,
                                                       0], "bcde, bdcf->ef")]
                first_LR[isite] = asnumpy(
                    multi_tensor_contract(path1, first_LR[isite - 1],
                                          dag_cv_isite, self.a_oper[isite - 1],
                                          self.a_oper[isite - 1], cv_isite))
                second_LR[isite] = asnumpy(
                    multi_tensor_contract(path1, second_LR[isite - 1],
                                          dag_cv_isite, self.a_oper[isite - 1],
                                          cv_isite, self.h_mpo[isite - 1]))
                third_LR[isite] = asnumpy(
                    multi_tensor_contract(path1, third_LR[isite - 1],
                                          self.h_mpo[isite - 1], dag_cv_isite,
                                          cv_isite, self.h_mpo[isite - 1]))
                forth_LR[isite] = asnumpy(
                    multi_tensor_contract(
                        path2, forth_LR[isite - 1],
                        moveaxis(self.b_mpo[isite - 1], (1, 2), (2, 1)),
                        cv_isite))
        return [first_LR, second_LR, third_LR, forth_LR]
コード例 #16
0
    def optimize_cv(self, lr_group, direction, isite, num, percent=0.0):
        # depending on the spectratype, to restrict the exction
        first_LR = lr_group[0]
        second_LR = lr_group[1]
        if self.spectratype == "abs":
            constrain_qn = 1
        else:
            constrain_qn = 0
        # this function aims at solving the work equation of ZT CV-DMRG
        # L = <CV|op_a|CV>+2\eta<op_b|CV>, take a derivative to local CV
        # S-a-S-e-S                          S-a-S-d-S
        # |   d   |                          |   |   |
        # O-b-O-g-O  * CV[isite-1]  = -\eta  |   c   |
        # |   f   |                          |   |   |
        # S-c- -h-S                          S-b- -e-S

        # note to be a_mat * x = vec_b
        # the environment matrix

        if self.method == "1site":
            addlist = [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])
        else:
            addlist = [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])

        if direction == 'left':
            system = 'R'
        else:
            system = 'L'

        # this part just be similar with ground state calculation
        qnmat, qnbigl, qnbigr = svd_qn.construct_qnmat(
            self.cv_mps, self.mpo.pbond_list,
            addlist, self.method, system)
        xshape = qnmat.shape
        nonzeros = np.sum(qnmat == constrain_qn)

        if self.method == '1site':
            guess = self.cv_mps[isite - 1][qnmat == constrain_qn].reshape(nonzeros, 1)
            path_b = [([0, 1], "ab, acd->bcd"),
                      ([1, 0], "bcd, de->bce")]
            vec_b = multi_tensor_contract(
                path_b, second_L, self.b_oper[isite - 1], second_R
            )[qnmat == constrain_qn].reshape(nonzeros, 1)
        else:
            guess = tensordot(
                self.cv_mps[isite - 2], self.cv_mps[isite - 1], axes=(-1, 0)
            )
            guess = guess[qnmat == constrain_qn].reshape(nonzeros, 1)
            path_b = [([0, 1], "ab, acd->bcd"),
                      ([2, 0], "bcd, def->bcef"),
                      ([1, 0], "bcef, fg->bceg")]
            vec_b = multi_tensor_contract(
                path_b, second_L, self.b_oper[isite - 2],
                self.b_oper[isite - 1], second_R
            )[qnmat == constrain_qn].reshape(nonzeros, 1)

        if self.method == "2site":
            a_oper_isite2 = asxp(self.a_oper[isite - 2])
        else:
            a_oper_isite2 = None
        a_oper_isite1 = asxp(self.a_oper[isite - 1])

        # use the diagonal part of mat_a to construct the preconditinoner for linear solver
        if self.method == "1site":
            part_l = xp.einsum('abca->abc', first_L)
            part_r = xp.einsum('hfgh->hfg', first_R)
            path_pre = [([0, 1], "abc, bdef->acdef"),
                        ([1, 0], "acdef, hfg->acdehg")]
            pre_a_mat1 = multi_tensor_contract(path_pre, part_l, a_oper_isite1,
                                               part_r)
            path_pre2 = [([0, 1], "acdehg, ceig->adhi")]
            pre_a_mat1 = multi_tensor_contract(path_pre2, pre_a_mat1, a_oper_isite1)
            pre_a_mat1 = xp.einsum('adhd->adh', pre_a_mat1)[qnmat == constrain_qn]
            # pre_a_mat1 = xp.einsum('abca, bdef, cedg, hfgh->adh', first_L, a_oper_isite1,
            #                        a_oper_isite1, first_R)[qnmat == constrain_qn]
            cv_shape = self.cv_mps[isite - 1].shape
            pre_a_mat2 = xp.ones(cv_shape)[qnmat == constrain_qn]
            pre_a_mat = pre_a_mat1 + pre_a_mat2 * self.eta**2
        else:
            pre_a_mat1 = xp.einsum(
                'abca, bdef, cedg, fhij, gihk, ljkl->adhl', first_L, a_oper_isite2, a_oper_isite2,
                a_oper_isite1, a_oper_isite1, first_R)[qnmat == constrain_qn]
            cv_shape1 = self.cv_mps[isite - 2].shape
            cv_shape2 = self.cv_mps[isite - 1].shape
            new_shape = [cv_shape1[0], cv_shape1[1], cv_shape2[1], cv_shape2[2]]
            pre_a_mat2 = xp.ones(new_shape)[qnmat == constrain_qn]
            pre_a_mat = pre_a_mat1 + pre_a_mat2 * self.eta**2

        pre_a_mat = np.diag(1./asnumpy(pre_a_mat))

        count = 0

        def hop(c):
            nonlocal count
            count += 1
            xstruct = asxp(svd_qn.cvec2cmat(xshape, c, qnmat, constrain_qn))
            if self.method == "1site":
                path_a = [([0, 1], "abcd, aef->bcdef"),
                          ([3, 0], "bcdef, begh->cdfgh"),
                          ([2, 0], "cdfgh, cgij->dfhij"),
                          ([1, 0], "dfhij, fhjk->dik")]
                ax1 = multi_tensor_contract(path_a, first_L, xstruct,
                                           a_oper_isite1, a_oper_isite1, first_R)
                ax2 = xstruct
                ax = ax1 + ax2 * self.eta**2
            else:
                path_a = [([0, 1], "abcd, aefg->bcdefg"),
                          ([5, 0], "bcdefg, behi->cdfghi"),
                          ([4, 0], "cdfghi, ifjk->cdghjk"),
                          ([3, 0], "cdghjk, chlm->dgjklm"),
                          ([2, 0], "dgjklm, mjno->dgklno"),
                          ([1, 0], "dgklno, gkop->dlnp")]
                ax1 = multi_tensor_contract(path_a, first_L, xstruct,
                                           a_oper_isite2, a_oper_isite1,
                                           a_oper_isite2, a_oper_isite1,
                                           first_R)
                ax2 = xstruct
                ax = ax1 + ax2 * self.eta**2
            cout = ax[qnmat == constrain_qn].reshape(nonzeros, 1)
            return asnumpy(cout)

        mat_a = scipy.sparse.linalg.LinearOperator((nonzeros, nonzeros), matvec=hop)
        # for the first two sweep, not use the previous matrix as initial guess
        # at the inital stage, they are far from from the optimized one
        if num in [1, 2]:
            x, info = scipy.sparse.linalg.cg(mat_a, asnumpy(vec_b), atol=0)
        else:
            x, info = scipy.sparse.linalg.cg(mat_a, asnumpy(vec_b), tol=1.e-5,
                                             x0=guess, M=pre_a_mat, atol=0)
        # logger.info(f'hop times:{count}')
        self.hop_time.append(count)
        if info != 0:
            logger.info(f"iteration solver not converged")

        # the value of the functional L
        l_value = np.inner(hop(x).reshape(1, nonzeros), x.reshape(1, nonzeros)
                     ) - 2 * np.inner(
                         asnumpy(vec_b).reshape(1, nonzeros), x.reshape(1, nonzeros))
        xstruct = svd_qn.cvec2cmat(xshape, x, qnmat, constrain_qn)
        x, xdim, xqn, compx = \
            solver.renormalization_svd(xstruct, qnbigl, qnbigr, system,
                                       constrain_qn, self.m_max, percent)
        if self.method == "1site":
            self.cv_mps[isite - 1] = x
            if direction == "left":
                if isite != 1:
                    self.cv_mps[isite - 2] = tensordot(
                        self.cv_mps[isite - 2], compx, axes=(-1, 0))
                    self.cv_mps.qn[isite - 1] = xqn
                else:
                    self.cv_mps[isite - 1] = tensordot(
                        compx, self.cv_mps[isite - 1], axes=(-1, 0))
                    self.cv_mps.qn[isite - 1] = [0]
            elif direction == "right":
                if isite != len(self.cv_mps):
                    self.cv_mps[isite] = tensordot(
                        compx, self.cv_mps[isite], axes=(-1, 0))
                    self.cv_mps.qn[isite] = xqn
                else:
                    self.cv_mps[isite - 1] = tensordot(
                        self.cv_mps[isite - 1], compx, axes=(-1, 0))
                    self.cv_mps.qn[isite] = [0]
        else:
            if direction == "left":
                self.cv_mps[isite - 1] = x
                self.cv_mps[isite - 2] = compx
            else:
                self.cv_mps[isite - 2] = x
                self.cv_mps[isite - 1] = compx
            self.cv_mps.qn[isite - 1] = xqn
        return l_value[0][0]
コード例 #17
0
    def update_LR(self, lr_group, direction, isite):
        first_LR, second_LR, third_LR, forth_LR = lr_group
        assert direction in ["left", "right"]
        if self.method == "1site":
            if direction == "left":
                path1 = [([0, 1], "abc, defa -> bcdef"),
                         ([2, 0], "bcdef, gfhb -> cdegh"),
                         ([1, 0], "cdegh, ihec -> dgi")]
                first_LR[isite - 1] = multi_tensor_contract(
                    path1, first_LR[isite],
                    moveaxis(self.cv_mpo[isite - 1], (1, 2), (2, 1)),
                    self.a_oper[isite - 1], self.cv_mpo[isite - 1])
                path2 = [([0, 1], "abcd, efga -> bcdefg"),
                         ([3, 0], "bcdefg, hgib -> cdefhi"),
                         ([2, 0], "cdefhi, jikc -> defhjk"),
                         ([1, 0], "defhjk, lkfd -> ehjl")]
                path4 = [([0, 1], "ab, cdea->bcde"), ([1,
                                                       0], "bcde, fedb->cf")]
                second_LR[isite - 1] = multi_tensor_contract(
                    path2, second_LR[isite],
                    moveaxis(self.cv_mpo[isite - 1], (1, 2),
                             (2, 1)), self.b_oper[isite - 1],
                    self.cv_mpo[isite - 1], self.h_mpo[isite - 1])
                third_LR[isite - 1] = multi_tensor_contract(
                    path2, third_LR[isite], self.h_mpo[isite - 1],
                    moveaxis(self.cv_mpo[isite - 1], (1, 2), (2, 1)),
                    self.cv_mpo[isite - 1], self.h_mpo[isite - 1])
                forth_LR[isite - 1] = multi_tensor_contract(
                    path4, forth_LR[isite],
                    moveaxis(self.a_ket_mpo[isite - 1], (1, 2), (2, 1)),
                    self.cv_mpo[isite - 1])

            elif direction == "right":
                path1 = [([0, 1], "abc, adef -> bcdef"),
                         ([2, 0], "bcdef, begh -> cdfgh"),
                         ([1, 0], "cdfgh, cgdi -> fhi")]
                first_LR[isite] = multi_tensor_contract(
                    path1, first_LR[isite - 1],
                    moveaxis(self.cv_mpo[isite - 1], (1, 2), (2, 1)),
                    self.a_oper[isite - 1], self.cv_mpo[isite - 1])
                path2 = [([0, 1], "abcd, aefg -> bcdefg"),
                         ([3, 0], "bcdefg, bfhi -> cdeghi"),
                         ([2, 0], "cdeghi, chjk -> degijk"),
                         ([1, 0], "degijk, djel -> gikl")]
                path4 = [([0, 1], "ab, acde->bcde"), ([1,
                                                       0], "bcde, bdcf->ef")]
                second_LR[isite] = multi_tensor_contract(
                    path2, second_LR[isite - 1],
                    moveaxis(self.cv_mpo[isite - 1], (1, 2),
                             (2, 1)), self.b_oper[isite - 1],
                    self.cv_mpo[isite - 1], self.h_mpo[isite - 1])
                third_LR[isite] = multi_tensor_contract(
                    path2, third_LR[isite - 1], self.h_mpo[isite - 1],
                    moveaxis(self.cv_mpo[isite - 1], (1, 2), (2, 1)),
                    self.cv_mpo[isite - 1], self.h_mpo[isite - 1])
                forth_LR[isite] = multi_tensor_contract(
                    path4, forth_LR[isite - 1],
                    moveaxis(self.a_ket_mpo[isite - 1], (1, 2), (2, 1)),
                    self.cv_mpo[isite - 1])
        else:
            # 2site for finite temperature is too expensive, so I drop it
            # (at least for now)
            raise NotImplementedError

        return first_LR, second_LR, third_LR, forth_LR
コード例 #18
0
    def initialize_LR(self, direction):

        first_LR = [np.ones((1, 1, 1))]
        second_LR = [np.ones((1, 1, 1, 1))]
        forth_LR = [np.ones((1, 1))]
        for isite in range(1, len(self.cv_mpo)):
            first_LR.append(None)
            second_LR.append(None)
            forth_LR.append(None)
        first_LR.append(np.ones((1, 1, 1)))
        second_LR.append(np.ones((1, 1, 1, 1)))
        third_LR = copy.deepcopy(second_LR)
        forth_LR.append(np.ones((1, 1)))

        if direction == "right":
            for isite in range(len(self.cv_mpo), 1, -1):
                path1 = [([0, 1], "abc, defa -> bcdef"),
                         ([2, 0], "bcdef, gfhb -> cdegh"),
                         ([1, 0], "cdegh, ihec -> dgi")]
                first_LR[isite - 1] = asnumpy(
                    multi_tensor_contract(
                        path1, first_LR[isite],
                        moveaxis(self.cv_mpo[isite - 1], (1, 2), (2, 1)),
                        self.a_oper[isite - 1], self.cv_mpo[isite - 1]))
                path2 = [([0, 1], "abcd, efga -> bcdefg"),
                         ([3, 0], "bcdefg, hgib -> cdefhi"),
                         ([2, 0], "cdefhi, jikc -> defhjk"),
                         ([1, 0], "defhjk, lkfd -> ehjl")]
                path4 = [([0, 1], "ab, cdea->bcde"), ([1,
                                                       0], "bcde, fedb->cf")]
                second_LR[isite - 1] = asnumpy(
                    multi_tensor_contract(
                        path2, second_LR[isite],
                        moveaxis(self.cv_mpo[isite - 1], (1, 2),
                                 (2, 1)), self.b_oper[isite - 1],
                        self.cv_mpo[isite - 1], self.h_mpo[isite - 1]))
                third_LR[isite - 1] = asnumpy(
                    multi_tensor_contract(
                        path2, third_LR[isite], self.h_mpo[isite - 1],
                        moveaxis(self.cv_mpo[isite - 1], (1, 2), (2, 1)),
                        self.cv_mpo[isite - 1], self.h_mpo[isite - 1]))
                forth_LR[isite - 1] = asnumpy(
                    multi_tensor_contract(
                        path4, forth_LR[isite],
                        moveaxis(self.a_ket_mpo[isite - 1], (1, 2), (2, 1)),
                        self.cv_mpo[isite - 1]))

        if direction == "left":

            for isite in range(1, len(self.cv_mpo)):
                path1 = [([0, 1], "abc, adef -> bcdef"),
                         ([2, 0], "bcdef, begh -> cdfgh"),
                         ([1, 0], "cdfgh, cgdi -> fhi")]
                first_LR[isite] = asnumpy(
                    multi_tensor_contract(
                        path1, first_LR[isite - 1],
                        moveaxis(self.cv_mpo[isite - 1], (1, 2), (2, 1)),
                        self.a_oper[isite - 1], self.cv_mpo[isite - 1]))
                path2 = [([0, 1], "abcd, aefg -> bcdefg"),
                         ([3, 0], "bcdefg, bfhi -> cdeghi"),
                         ([2, 0], "cdeghi, chjk -> degijk"),
                         ([1, 0], "degijk, djel -> gikl")]
                path4 = [([0, 1], "ab, acde->bcde"), ([1,
                                                       0], "bcde, bdcf->ef")]
                second_LR[isite] = asnumpy(
                    multi_tensor_contract(
                        path2, second_LR[isite - 1],
                        moveaxis(self.cv_mpo[isite - 1], (1, 2),
                                 (2, 1)), self.b_oper[isite - 1],
                        self.cv_mpo[isite - 1], self.h_mpo[isite - 1]))
                third_LR[isite] = asnumpy(
                    multi_tensor_contract(
                        path2, third_LR[isite - 1], self.h_mpo[isite - 1],
                        moveaxis(self.cv_mpo[isite - 1], (1, 2), (2, 1)),
                        self.cv_mpo[isite - 1], self.h_mpo[isite - 1]))
                forth_LR[isite] = asnumpy(
                    multi_tensor_contract(
                        path4, forth_LR[isite - 1],
                        moveaxis(self.a_ket_mpo[isite - 1], (1, 2), (2, 1)),
                        self.cv_mpo[isite - 1]))
        return [first_LR, second_LR, third_LR, forth_LR]
コード例 #19
0
    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]
コード例 #20
0
    def addone(self, intensor, MPS, MPSconj, MPO, isite, domain):
        """
        add one MPO/MPS(MPO) site
                 _   _
                | | | |
        S-S-    | S-|-S-
        O-O- or | O-|-O- (the ancillary bond is traced)
        S-S-    | S-|-S-
                |_| |_|
        """
        assert domain in ["L", "R", "l", "r"]

        if domain == "L" or domain == "l":
            assert intensor.shape[0] == MPSconj[isite].shape[0]
            assert intensor.shape[1] == MPO[isite].shape[0]
            assert intensor.shape[2] == MPS[isite].shape[0]
            """
                           l 
            S-a-S-f    O-a-O-f
                d          d
            O-b-O-g or O-b-O-g  
                e          e
            S-c-S-h    O-c-O-h
                           l  
            """

            if MPS[isite].ndim == 3:
                path = [
                    ([0, 1], "abc, adf -> bcdf"),
                    ([2, 0], "bcdf, bdeg -> cfeg"),
                    ([1, 0], "cfeg, ceh -> fgh"),
                ]
            elif MPS[isite].ndim == 4:
                path = [
                    ([0, 1], "abc, adlf -> bcdlf"),
                    ([2, 0], "bcdlf, bdeg -> clfeg"),
                    ([1, 0], "clfeg, celh -> fgh"),
                ]
            else:
                raise ValueError("MPS ndim at %d is not 3 or 4, got %s" %
                                 (isite, MPS[isite].ndim))
            outtensor = multi_tensor_contract(path, intensor, MPSconj[isite],
                                              MPO[isite], MPS[isite])

        else:
            assert intensor.shape[0] == MPSconj[isite].shape[-1]
            assert intensor.shape[1] == MPO[isite].shape[-1]
            assert intensor.shape[2] == MPS[isite].shape[-1]
            """
                           l
            -f-S-a-S    -f-S-a-S
               d           d
            -g-O-b-O or -g-O-b-O
               e           e
            -h-S-c-S    -h-S-c-S
                           l
            """

            if MPS[isite].ndim == 3:
                path = [
                    ([0, 1], "fda, abc -> fdbc"),
                    ([2, 0], "fdbc, gdeb -> fcge"),
                    ([1, 0], "fcge, hec -> fgh"),
                ]
            elif MPS[isite].ndim == 4:
                path = [
                    ([0, 1], "fdla, abc -> fdlbc"),
                    ([2, 0], "fdlbc, gdeb -> flcge"),
                    ([1, 0], "flcge, helc -> fgh"),
                ]
            else:
                raise ValueError("MPS ndim at %d is not 3 or 4, got %s" %
                                 (isite, MPS[isite].ndim))
            outtensor = multi_tensor_contract(path, MPSconj[isite], intensor,
                                              MPO[isite], MPS[isite])

        return outtensor
コード例 #21
0
ファイル: gs.py プロジェクト: sailfish009/Renormalizer
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
コード例 #22
0
ファイル: mp.py プロジェクト: sailfish009/Renormalizer
    def variational_compress(self, mpo=None, guess=None):
        r"""Variational compress an mps/mpdm/mpo
        
        Parameters
        ----------
        mpo : renormalizer.mps.Mpo, optional 
            Default is ``None``. if mpo is not ``None``, the returned mps is
            an approximation of ``mpo @ self``
        guess : renormalizer.mps.MatrixProduct, optional
            Initial guess of compressed mps/mpdm/mpo. Default is ``None``. 
        
        Note
        ----
        the variational compress related configurations is defined in
        ``self`` if ``guess=None``, otherwise is defined in ``guess``

        Returns
        -------
        mp : renormalizer.mps.MatrixProduct
            a new compressed mps/mpdm/mpo, ``self`` is not overwritten.
            ``guess`` is overwritten.
        
        """

        if mpo is None:
            logger.info(
                "Recommend to use svd to compress a single mps/mpo/mpdm.")
            raise NotImplementedError

        if guess is None:
            # a minimal representation of self and mpo
            compressed_mpo = mpo.copy().canonicalise().compress(
                temp_m_trunc=self.compress_config.vguess_m[0])
            compressed_mps = self.copy().canonicalise().compress(
                temp_m_trunc=self.compress_config.vguess_m[1])
            # the attributes of guess would be the same as self
            guess = compressed_mpo.apply(compressed_mps)
        mps = guess
        mps.ensure_left_canon()
        logger.info(f"initial guess bond dims: {mps.bond_dims}")

        procedure = mps.compress_config.vprocedure
        method = mps.compress_config.vmethod

        environ = Environ(self, mpo, "L", mps_conj=mps.conj())

        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 bond dims: {mps.bond_dims}")

            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}")

                ltensor = environ.GetLR("L",
                                        lidx,
                                        self,
                                        mpo,
                                        itensor=None,
                                        method=lmethod,
                                        mps_conj=mps.conj())
                rtensor = environ.GetLR("R",
                                        ridx,
                                        self,
                                        mpo,
                                        itensor=None,
                                        method=rmethod,
                                        mps_conj=mps.conj())

                # get the quantum number pattern
                qnbigl, qnbigr, qnmat = mps._get_big_qn(cidx)

                # center mo
                cmo = [asxp(mpo[idx]) for idx in cidx]
                cms = [asxp(self[idx]) for idx in cidx]
                if method == "1site":
                    if cms[0].ndim == 3:
                        # 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"),
                        ]
                    elif cms[0].ndim == 4:
                        # S-a   l-S
                        #     d
                        # O-b-O-f-O
                        #     e
                        # S-c   k-S
                        #     g
                        path = [
                            ([0, 2], "abc, bdef -> acdef"),
                            ([2, 0], "acdef, cegk -> adfgk"),
                            ([1, 0], "adfgk, lfk -> adgl"),
                        ]
                    cout = multi_tensor_contract(path, ltensor, cms[0], cmo[0],
                                                 rtensor)
                else:
                    if USE_GPU:
                        oe_backend = "cupy"
                    else:
                        oe_backend = "numpy"
                    if cms[0].ndim == 3:
                        # S-a       l-S
                        #     d   g
                        # O-b-O-f-O-j-O
                        #     e   h
                        # S-c   m   k-S

                        cout = oe.contract(
                            "abc, bdef, fghj, ljk, cem, mhk -> adgl",
                            ltensor,
                            cmo[0],
                            cmo[1],
                            rtensor,
                            cms[0],
                            cms[1],
                            backend=oe_backend)
                    elif cms[0].ndim == 4:
                        # S-a       l-S
                        #     d   g
                        # O-b-O-f-O-j-O
                        #     e   h
                        # S-c   m   k-S
                        #     n   p
                        cout = oe.contract(
                            "abc, bdef, fghj, ljk, cenm, mhpk -> adngpl",
                            ltensor,
                            cmo[0],
                            cmo[1],
                            rtensor,
                            cms[0],
                            cms[1],
                            backend=oe_backend)
                # clean up the elements which do not meet the qn requirements
                cout[qnmat != mps.qntot] = 0
                mps._update_mps(cout, cidx, qnbigl, qnbigr, mmax, percent)

            mps._switch_direction()

            # check if convergence
            if isweep > 0 and percent == 0 and \
                    mps.distance(mps_old) / np.sqrt(mps.dot(mps.conj()).real) < mps.compress_config.vrtol:
                converged = True
                break

            mps_old = mps.copy()

        if converged:
            logger.info("Variational compress is converged!")
        else:
            logger.warning(
                "Variational compress is not converged! Please increase the procedure!"
            )

        # remove the redundant bond dimension near the boundary of the MPS
        mps.canonicalise()
        logger.info(f"{mps}")

        return mps
コード例 #23
0
ファイル: mps.py プロジェクト: jiangtong1000/Renormalizer
 def hop(ms: xp.ndarray):
     return multi_tensor_contract(path, ltensor, mo, ms, rtensor)
コード例 #24
0
ファイル: lib.py プロジェクト: sailfish009/Renormalizer
def contract_one_site(environ, ms, mo, domain, ms_conj=None):
    """
    contract one mpo/mps(mpdm) site
             _   _
            | | | |
    S-S-    | S-|-S-
    O-O- or | O-|-O- (the ancillary bond is traced)
    S-S-    | S-|-S-
            |_| |_|
    """
    assert domain in ["L", "R"]
    if isinstance(ms, Matrix):
        ms = ms.array
    if isinstance(mo, Matrix):
        mo = mo.array
    if ms_conj is None:
        ms_conj = ms.conj()
    if domain == "L":
        assert environ.shape[0] == ms_conj.shape[0]
        assert environ.shape[1] == mo.shape[0]
        assert environ.shape[2] == ms.shape[0]
        """
                       l
        S-a-S-f    O-a-O-f
            d          d
        O-b-O-g or O-b-O-g
            e          e
        S-c-S-h    O-c-O-h
                       l
        """

        if ms.ndim == 3:
            path = [
                ([0, 1], "abc, adf -> bcdf"),
                ([2, 0], "bcdf, bdeg -> cfeg"),
                ([1, 0], "cfeg, ceh -> fgh"),
            ]
        elif ms.ndim == 4:
            path = [
                ([0, 1], "abc, adlf -> bcdlf"),
                ([2, 0], "bcdlf, bdeg -> clfeg"),
                ([1, 0], "clfeg, celh -> fgh"),
            ]
        else:
            raise ValueError(f"MPS ndim is not 3 or 4, got {ms.ndim}")
        outtensor = multi_tensor_contract(path, environ, ms_conj, mo, ms)

    else:
        assert environ.shape[0] == ms_conj.shape[-1]
        assert environ.shape[1] == mo.shape[-1]
        assert environ.shape[2] == ms.shape[-1]
        """
                       l
        -f-S-a-S    -f-S-a-S
           d           d
        -g-O-b-O or -g-O-b-O
           e           e
        -h-S-c-S    -h-S-c-S
                       l
        """

        if ms.ndim == 3:
            path = [
                ([0, 1], "fda, abc -> fdbc"),
                ([2, 0], "fdbc, gdeb -> fcge"),
                ([1, 0], "fcge, hec -> fgh"),
            ]
        elif ms.ndim == 4:
            path = [
                ([0, 1], "fdla, abc -> fdlbc"),
                ([2, 0], "fdlbc, gdeb -> flcge"),
                ([1, 0], "flcge, helc -> fgh"),
            ]
        else:
            raise ValueError(f"MPS ndim is not 3 or 4, got {ms.ndim}")
        outtensor = multi_tensor_contract(path, ms_conj, environ, mo, ms)

    return outtensor
コード例 #25
0
ファイル: zerot.py プロジェクト: sailfish009/Renormalizer
    def optimize_cv(self, lr_group, isite, percent=0.0):
        # depending on the spectratype, to restrict the exction
        first_LR = lr_group[0]
        second_LR = lr_group[1]
        constrain_qn = self.cv_mps.qntot
        # this function aims at solving the work equation of ZT CV-DMRG
        # L = <CV|op_a|CV>+2\eta<op_b|CV>, take a derivative to local CV
        # S-a-S-e-S                          S-a-S-d-S
        # |   d   |                          |   |   |
        # O-b-O-g-O  * CV[isite-1]  = -\eta  |   c   |
        # |   f   |                          |   |   |
        # S-c- -h-S                          S-b- -e-S

        # note to be a_mat * x = vec_b
        # the environment matrix

        if self.method == "1site":
            cidx = [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])
        else:
            cidx = [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])

        # this part just be similar with ground state calculation
        qnbigl, qnbigr, qnmat = self.cv_mps._get_big_qn(cidx)
        xshape = qnmat.shape
        nonzeros = int(np.sum(qnmat == constrain_qn))
        if self.method == '1site':
            guess = self.cv_mps[isite - 1][qnmat == constrain_qn]
            path_b = [([0, 1], "ab, acd->bcd"),
                      ([1, 0], "bcd, de->bce")]
            vec_b = multi_tensor_contract(
                path_b, second_L, self.b_mps[isite - 1], second_R
            )[qnmat == constrain_qn]
        else:
            guess = tensordot(
                self.cv_mps[isite - 2], self.cv_mps[isite - 1], axes=(-1, 0)
            )[qnmat == constrain_qn]
            path_b = [([0, 1], "ab, acd->bcd"),
                      ([2, 0], "bcd, def->bcef"),
                      ([1, 0], "bcef, fg->bceg")]
            vec_b = multi_tensor_contract(
                path_b, second_L, self.b_mps[isite - 2],
                self.b_mps[isite - 1], second_R
            )[qnmat == constrain_qn]

        if self.method == "2site":
            a_oper_isite2 = asxp(self.a_oper[isite - 2])
        else:
            a_oper_isite2 = None
        a_oper_isite1 = asxp(self.a_oper[isite - 1])

        # use the diagonal part of mat_a to construct the preconditinoner
        # for linear solver
        part_l = xp.einsum('abca->abc', first_L)
        part_r = xp.einsum('hfgh->hfg', first_R)
        if self.method == "1site":
            #  S-a   d    h-S
            #  O-b  -O-   f-O
            #  |     e      |
            #  O-c  -O-   g-O
            #  S-a   i    h-S
            path_pre = [([0, 1], "abc, bdef -> acdef"),
                        ([1, 0], "acdef, ceig -> adfig")]
            a_diag = multi_tensor_contract(path_pre, part_l, a_oper_isite1,
                                           a_oper_isite1)
            a_diag = xp.einsum("adfdg -> adfg", a_diag)
            a_diag = xp.tensordot(a_diag, part_r,
                                  axes=([2, 3], [1, 2]))[qnmat == constrain_qn]
        else:
            #  S-a   d     k   h-S
            #  O-b  -O- j -O-  f-O
            #  |     e     l   |
            #  O-c  -O- m -O-  g-O
            #  S-a   i     n   h-S
            # first left half, second right half, last contraction

            path_pre = [([0, 1], "abc, bdej -> acdej"),
                        ([1, 0], "acdej, ceim -> adjim")]
            a_diagl = multi_tensor_contract(path_pre, part_l, a_oper_isite2,
                                            a_oper_isite2)
            a_diagl = xp.einsum("adjdm -> adjm", a_diagl)

            path_pre = [([0, 1], "hfg, jklf -> hgjkl"),
                        ([1, 0], "hgjkl, mlng -> hjkmn")]
            a_diagr = multi_tensor_contract(path_pre, part_r, a_oper_isite1,
                                            a_oper_isite1)
            a_diagr = xp.einsum("hjkmk -> khjm", a_diagr)

            a_diag = xp.tensordot(
                a_diagl, a_diagr, axes=([2, 3], [2, 3]))[qnmat == constrain_qn]

        a_diag = asnumpy(a_diag + xp.ones(nonzeros) * self.eta**2)
        M_x = lambda x: x / a_diag
        pre_M = scipy.sparse.linalg.LinearOperator((nonzeros, nonzeros), M_x)

        count = 0

        # cache oe path
        if self.method == "2site":
            expr = oe.contract_expression(
                "abcd, befh, cfgi, hjkn, iklo, mnop, dglp -> aejm",
                first_L, a_oper_isite2, a_oper_isite2, a_oper_isite1,
                a_oper_isite1, first_R, xshape,
                constants=[0, 1, 2, 3, 4, 5])

        def hop(c):
            nonlocal count
            count += 1
            xstruct = asxp(cvec2cmat(xshape, c, qnmat, constrain_qn))
            if self.method == "1site":
                path_a = [([0, 1], "abcd, aef->bcdef"),
                          ([3, 0], "bcdef, begh->cdfgh"),
                          ([2, 0], "cdfgh, cgij->dfhij"),
                          ([1, 0], "dfhij, fhjk->dik")]
                ax1 = multi_tensor_contract(path_a, first_L, xstruct,
                                            a_oper_isite1, a_oper_isite1, first_R)
            else:
                # opt_einsum v3.2.1 is not bad, ~10% faster than the hand-design
                # contraction path for this complicated cases and consumes a little bit less memory
                # this is the only place in renormalizer we use opt_einsum now.
                # we keep it here just for a demo.
                # ax1 = oe.contract("abcd, befh, cfgi, hjkn, iklo, mnop, dglp -> aejm",
                #        first_L, a_oper_isite2, a_oper_isite2, a_oper_isite1,
                #        a_oper_isite1, first_R, xstruct)
                if USE_GPU:
                    oe_backend = "cupy"
                else:
                    oe_backend = "numpy"
                ax1 = expr(xstruct, backend=oe_backend)   
                #print(oe.contract_path("abcd, befh, cfgi, hjkn, iklo, mnop, dglp -> aejm",
                #        first_L, a_oper_isite2, a_oper_isite2, a_oper_isite1,
                #        a_oper_isite1, first_R, xstruct))

                #path_a = [([0, 1], "abcd, aefg->bcdefg"),
                #          ([5, 0], "bcdefg, behi->cdfghi"),
                #          ([4, 0], "cdfghi, ifjk->cdghjk"),
                #          ([3, 0], "cdghjk, chlm->dgjklm"),
                #          ([2, 0], "dgjklm, mjno->dgklno"),
                #          ([1, 0], "dgklno, gkop->dlnp")]
                #ax1 = multi_tensor_contract(path_a, first_L, xstruct,
                #                           a_oper_isite2, a_oper_isite1,
                #                           a_oper_isite2, a_oper_isite1,
                #                           first_R)
            ax = ax1 + xstruct * self.eta**2
            cout = ax[qnmat == constrain_qn]
            return asnumpy(cout)

        mat_a = scipy.sparse.linalg.LinearOperator((nonzeros, nonzeros),
                                                   matvec=hop)

        x, info = scipy.sparse.linalg.cg(mat_a, asnumpy(vec_b), tol=1.e-5,
                                         x0=asnumpy(guess),
                                         M=pre_M, atol=0)

        self.hop_time.append(count)
        if info != 0:
            logger.info(f"iteration solver not converged")
        # the value of the functional L
        l_value = xp.dot(asxp(hop(x)), asxp(x)) - 2 * xp.dot(vec_b, asxp(x))
        xstruct = cvec2cmat(xshape, x, qnmat, constrain_qn)
        self.cv_mps._update_mps(xstruct, cidx, qnbigl, qnbigr, self.m_max, percent)
        
        return float(l_value)