Esempio n. 1
0
def renormalization_ddm(cstruct, qnbigl, qnbigr, domain, nexciton, Mmax, percent=0):
    """
        get the new mps, mpsdim, mpdqn, complementary mps to get the next guess
        with diagonalize reduced density matrix method (> 1 root)
    """
    nroots = len(cstruct)
    ddm = 0.0
    for iroot in range(nroots):
        if domain == "R":
            ddm += np.tensordot(
                cstruct[iroot],
                cstruct[iroot],
                axes=(range(qnbigl.ndim), range(qnbigl.ndim)),
            )
        else:
            ddm += np.tensordot(
                cstruct[iroot],
                cstruct[iroot],
                axes=(
                    range(qnbigl.ndim, cstruct[0].ndim),
                    range(qnbigl.ndim, cstruct[0].ndim),
                ),
            )
    ddm /= float(nroots)
    if domain == "L":
        Uset, Sset, qnnew = svd_qn.Csvd(ddm, qnbigl, qnbigl, nexciton, ddm=True)
    else:
        Uset, Sset, qnnew = svd_qn.Csvd(ddm, qnbigr, qnbigr, nexciton, ddm=True)
    mps, mpsdim, mpsqn, compmps = updatemps(
        Uset, Sset, qnnew, None, nexciton, Mmax, percent=percent
    )

    if domain == "R":
        return (
            xp.moveaxis(mps.reshape(list(qnbigr.shape) + [mpsdim]), -1, 0),
            mpsdim,
            mpsqn,
            tensordot(
                asxp(cstruct[0]),
                mps.reshape(list(qnbigr.shape) + [mpsdim]),
                axes=(range(qnbigl.ndim, cstruct[0].ndim), range(qnbigr.ndim)),
            ),
        )
    else:
        return (
            mps.reshape(list(qnbigl.shape) + [mpsdim]),
            mpsdim,
            mpsqn,
            tensordot(
                mps.reshape(list(qnbigl.shape) + [mpsdim]),
                asxp(cstruct[0]),
                axes=(range(qnbigl.ndim), range(qnbigl.ndim)),
            ),
        )
Esempio n. 2
0
 def canonicalise(self, stop_idx: int=None):
     for idx in self.iter_idx_list(full=False):
         self.qnidx = idx
         if stop_idx is not None and idx == stop_idx:
             break
         mt: Matrix = self[idx]
         assert mt.any()
         if self.left:
             mt = mt.l_combine()
         else:
             mt = mt.r_combine()
         qnbigl, qnbigr = self._get_big_qn(idx)
         system = "L" if self.left else "R"
         u, qnlset, v, qnrset = svd_qn.Csvd(
             mt.asnumpy(),
             qnbigl,
             qnbigr,
             self.qntot,
             QR=True,
             system=system,
             full_matrices=False,
         )
         self._update_ms(
             idx, Matrix(u), Matrix(v.T), sigma=None, qnlset=qnlset, qnrset=qnrset
         )
     self._switch_direction()
     return self
Esempio n. 3
0
    def canonicalise(self, stop_idx: int = None, normalize=False):
        # stop_idx: mix canonical site at `stop_idx`
        if self.to_right:
            assert self.qnidx == 0
        else:
            assert self.qnidx == self.site_num - 1

        for idx in self.iter_idx_list(full=False, stop_idx=stop_idx):
            mt: Matrix = self[idx]
            assert mt.any()
            qnbigl, qnbigr, _ = self._get_big_qn([idx])
            system = "L" if self.to_right else "R"
            u, qnlset, v, qnrset = svd_qn.Csvd(
                mt.array,
                qnbigl,
                qnbigr,
                self.qntot,
                QR=True,
                system=system,
                full_matrices=False,
            )
            if normalize:
                # roughly normalize. Used when the each site of the mps is scaled such as in exact thermal prop
                v /= np.linalg.norm(v[:, 0])
            self._update_ms(idx,
                            u,
                            v.T,
                            sigma=None,
                            qnlset=qnlset,
                            qnrset=qnrset)
        # can't iter to idx == 0 or idx == self.site_num - 1
        if (not self.to_right and idx == 1) or (self.to_right
                                                and idx == self.site_num - 2):
            self._switch_direction()
        return self
Esempio n. 4
0
def renormalization_svd(cstruct, qnbigl, qnbigr, domain, nexciton, Mmax, percent=0):
    """
        get the new mps, mpsdim, mpdqn, complementary mps to get the next guess
        with singular value decomposition method (1 root)
    """
    assert domain in ["R", "L"]

    Uset, SUset, qnlnew, Vset, SVset, qnrnew = svd_qn.Csvd(
        cstruct, qnbigl, qnbigr, nexciton, system=domain
    )
    if domain == "R":
        mps, mpsdim, mpsqn, compmps = updatemps(
            Vset, SVset, qnrnew, Uset, nexciton, Mmax, percent=percent
        )
        return (
            xp.moveaxis(mps.reshape(list(qnbigr.shape) + [mpsdim]), -1, 0),
            mpsdim,
            mpsqn,
            compmps.reshape(list(qnbigl.shape) + [mpsdim]),
        )
    else:
        mps, mpsdim, mpsqn, compmps = updatemps(
            Uset, SUset, qnlnew, Vset, nexciton, Mmax, percent=percent
        )
        return (
            mps.reshape(list(qnbigl.shape) + [mpsdim]),
            mpsdim,
            mpsqn,
            xp.moveaxis(compmps.reshape(list(qnbigr.shape) + [mpsdim]), -1, 0),
        )
Esempio n. 5
0
    def compress(self):
        """
        inp: canonicalise MPS (or MPO)

        side='l': compress LEFT-canonicalised MPS
                  by sweeping from RIGHT to LEFT
                  output MPS is right canonicalised i.e. CRRR

        side='r': reverse of 'l'

        returns:
             truncated MPS
        """
        # used for logging at exit
        sz_before = self.total_bytes
        if not self.is_mpo:
            # ensure mps is canonicalised. This is time consuming.
            # to disable this, run python as `python -O`
            if self.is_left_canon:
                assert self.check_left_canonical()
            else:
                assert self.check_right_canonical()
        system = "L" if self.left else "R"

        for idx in self.iter_idx_list(full=False):
            mt: Matrix = self[idx]
            if self.left:
                mt = mt.l_combine()
            else:
                mt = mt.r_combine()
            qnbigl, qnbigr = self._get_big_qn(idx)
            u, sigma, qnlset, v, sigma, qnrset = svd_qn.Csvd(
                mt.asnumpy(),
                qnbigl,
                qnbigr,
                self.qntot,
                system=system,
                full_matrices=False,
            )
            vt = v.T
            m_trunc = self.compress_config.compute_m_trunc(
                sigma, idx, self.left
            )
            self._update_ms(
                idx, Matrix(u), Matrix(vt), Matrix(sigma), qnlset, qnrset, m_trunc
            )

        self._switch_direction()
        compress_ratio = sz_before / self.total_bytes
        logger.debug(f"size before/after compress: {sizeof_fmt(sz_before)}/{sizeof_fmt(self.total_bytes)}, ratio: {compress_ratio}")
        return self
Esempio n. 6
0
    def _evolve_dmrg_tdvp_ps(self, mpo, evolve_dt) -> "Mps":
        # PhysRevB.94.165116
        # TDVP projector splitting
        imag_time = np.iscomplex(evolve_dt)
        if imag_time:
            mps = self.copy()
            mps_conj = mps
        else:
            mps = self.to_complex()
            mps_conj = mps.conj()  # another copy, so 3x memory is used.

        # construct the environment matrix
        environ = Environ()
        # almost half is not used. Not a big deal.
        environ.construct(mps, mps_conj, mpo, "L")
        environ.construct(mps, mps_conj, mpo, "R")

        # a workaround for https://github.com/scipy/scipy/issues/10164
        if imag_time:
            evolve_dt = -evolve_dt.imag
            # used in calculating derivatives
            coef = -1
        else:
            coef = 1j

        # statistics for debug output
        cmf_rk_steps = []
        USE_RK = self.evolve_config.tdvp_ps_rk4
        # sweep for 2 rounds
        for i in range(2):
            for imps in mps.iter_idx_list(full=True):
                system = "L" if mps.left else "R"
                ltensor = environ.read("L", imps - 1)
                rtensor = environ.read("R", imps + 1)

                shape = list(mps[imps].shape)
                l_array = ltensor.array
                r_array = rtensor.array

                hop = hop_factory(l_array, r_array, mpo[imps].array, len(shape))

                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

                if USE_RK:
                    def func(t, y):
                        return hop(y.reshape(shape)).ravel() / coef
                    sol = solve_ivp(
                        func, (0, evolve_dt / 2.0), mps[imps].ravel().array, method="RK45"
                    )
                    cmf_rk_steps.append(len(sol.t))
                    mps_t = sol.y[:, -1]
                else:
                    # Can't use the same func because here H should be Hermitian
                    def func(y):
                        return hop(y.reshape(shape)).ravel()
                    mps_t = expm_krylov(func, (evolve_dt / 2) / coef, mps[imps].ravel().array)
                mps_t = mps_t.reshape(shape)

                qnbigl, qnbigr = mps._get_big_qn(imps)
                u, qnlset, v, qnrset = svd_qn.Csvd(
                    asnumpy(mps_t),
                    qnbigl,
                    qnbigr,
                    mps.qntot,
                    QR=True,
                    system=system,
                    full_matrices=False,
                )
                vt = v.T

                if mps.is_left_canon and imps != 0:
                    mps[imps] = vt.reshape([-1] + shape[1:])
                    mps_conj[imps] = mps[imps].conj()
                    mps.qn[imps] = qnrset

                    rtensor = environ.GetLR(
                        "R", imps, mps, mps_conj, mpo, itensor=rtensor, method="System"
                    )
                    r_array = rtensor.array

                    # reverse update u site
                    shape_u = u.shape

                    if USE_RK:
                        def func_u(t, y):
                            return hop_svt(y.reshape(shape_u)).ravel() / coef
                        sol_u = solve_ivp(
                            func_u, (0, -evolve_dt / 2), u.ravel(), method="RK45"
                        )
                        cmf_rk_steps.append(len(sol_u.t))
                        mps_t = sol_u.y[:, -1]
                    else:
                        def func_u(y):
                            return hop_svt(y.reshape(shape_u)).ravel()
                        mps_t = expm_krylov(func_u, (-evolve_dt / 2) / coef, u.ravel())
                    mps_t = mps_t.reshape(shape_u)
                    mps[imps - 1] = tensordot(
                        mps[imps - 1].array,
                        mps_t,
                        axes=(-1, 0),
                    )
                    mps_conj[imps - 1] = mps[imps - 1].conj()

                elif mps.is_right_canon and imps != len(mps) - 1:
                    mps[imps] = u.reshape(shape[:-1] + [-1])
                    mps_conj[imps] = mps[imps].conj()
                    mps.qn[imps + 1] = qnlset

                    ltensor = environ.GetLR(
                        "L", imps, mps, mps_conj, mpo, itensor=ltensor, method="System"
                    )
                    l_array = ltensor.array

                    # reverse update svt site
                    shape_svt = vt.shape

                    if USE_RK:
                        def func_svt(t, y):
                            return hop_svt(y.reshape(shape_svt)).ravel() / coef
                        sol_svt = solve_ivp(
                            func_svt, (0, -evolve_dt / 2), vt.ravel(), method="RK45"
                        )
                        cmf_rk_steps.append(len(sol_svt.t))
                        mps_t = sol_svt.y[:, -1]
                    else:
                        def func_svt(y):
                            return hop_svt(y.reshape(shape_svt)).ravel()
                        mps_t = expm_krylov(func_svt, (-evolve_dt / 2) / coef, vt.ravel())
                    mps_t = mps_t.reshape(shape_svt)
                    mps[imps + 1] = tensordot(
                        mps_t,
                        mps[imps + 1].array,
                        axes=(1, 0),
                    )
                    mps_conj[imps + 1] = mps[imps + 1].conj()

                else:
                    mps[imps] = mps_t
                    mps_conj[imps] = mps[imps].conj()
            mps._switch_direction()

        if USE_RK:
            steps_stat = stats.describe(cmf_rk_steps)
            logger.debug(f"TDVP-PS CMF steps: {steps_stat}")
            mps.evolve_config.stat = steps_stat

        return mps
Esempio n. 7
0
    def _evolve_dmrg_tdvp_mctdhnew(self, mpo, evolve_dt) -> "Mps":
        # new regularization scheme
        # JCP 148, 124105 (2018)
        # JCP 149, 044119 (2018)

        # a workaround for https://github.com/scipy/scipy/issues/10164
        imag_time = np.iscomplex(evolve_dt)
        if imag_time:
            evolve_dt = -evolve_dt.imag
            # used in calculating derivatives
            coef = -1
        else:
            coef = 1j

        if self.is_left_canon:
            assert self.check_left_canonical()
            self.canonicalise()

        mps = self.to_complex(inplace=True)

        # construct the environment matrix
        environ = Environ()
        environ.construct(mps, mps.conj(), mpo, "R")

        # initial matrix
        ltensor = ones((1, 1, 1))
        rtensor = ones((1, 1, 1))

        new_mps = mps.metacopy()

        # statistics for debug output
        cmf_rk_steps = []

        for imps in range(len(mps)):
            shape = list(mps[imps].shape)

            system = "L" if mps.left else "R"
            qnbigl, qnbigr = mps._get_big_qn(imps)
            u, s, qnlset, v, s, qnrset = svd_qn.Csvd(
                mps[imps].asnumpy(),
                qnbigl,
                qnbigr,
                mps.qntot,
                system=system,
                full_matrices=False,
            )
            vt = v.T

            mps[imps] = u.reshape(shape[:-1] + [-1])

            ltensor = environ.GetLR(
                "L", imps - 1, mps, mps.conj(), mpo, itensor=ltensor, method="System"
            )
            rtensor = environ.GetLR(
                "R", imps + 1, mps, mps.conj(), mpo, itensor=rtensor, method="Enviro"
            )

            epsilon = 1e-10
            epsilon = np.sqrt(epsilon)
            s = s + epsilon * np.exp(-s / epsilon)

            svt = Matrix(np.diag(s).dot(vt))

            rtensor = tensordot(rtensor, svt, axes=(2, 1))
            rtensor = tensordot(Matrix(vt).conj(), rtensor, axes=(1, 0))

            if imps != len(mps) - 1:
                mps[imps + 1] = tensordot(svt, mps[imps + 1], axes=(-1, 0))
                mps.qn[imps + 1] = qnlset
                new_mps.qn[imps + 1] = qnlset.copy()

            S_inv = xp.diag(1.0 / s)

            hop = hop_factory(ltensor, rtensor, mpo[imps], len(shape))

            func = integrand_func_factory(shape, hop, imps == len(mps) - 1, S_inv, coef)

            sol = solve_ivp(
                func, (0, evolve_dt), mps[imps].ravel().array, method="RK45"
            )
            cmf_rk_steps.append(len(sol.t))
            ms = sol.y[:, -1].reshape(shape)

            if imps == len(mps) - 1:
                new_mps[imps] = ms * s[0]
            else:
                new_mps[imps] = ms
        mps._switch_direction()
        new_mps._switch_direction()
        new_mps.canonicalise()

        steps_stat = stats.describe(cmf_rk_steps)
        logger.debug(f"TDVP-MCTDH CMF steps: {steps_stat}")
        # new_mps.evolve_config.stat = steps_stat

        return new_mps
Esempio n. 8
0
    def _update_mps(self, cstruct, cidx, qnbigl, qnbigr, Mmax, percent=0):
        r"""update mps with basis selection algorithm of J. Chem. Phys. 120,
        3172 (2004).
        
        Parameters
        ---------
        cstruct : ndarray, List[ndarray]
            The active site coefficient.
        cidx : list
            The List of active site index.
        qnbigl : ndarray
            The super-L-block quantum number.
        qnbigr : ndarray
            The super-R-block quantum number.
        Mmax : int
            The maximal bond dimension.
        percent : float, int
            The percentage of renormalized basis which is equally selected from
            each quantum number section rather than according to singular
            values. ``percent`` is defined in ``procedure`` of 
            `renormalizer.utils.configs.OptimizeConfig` and ``vprocedure`` of
            `renormalizer.utils.configs.CompressConfig`.

        Returns
        -------
        averaged_ms : 
            if ``cstruct`` is a list, ``averaged_ms`` is a list of rotated ms of
                each element in ``cstruct`` as a single site calculation. It is
                used for better initial guess in SA-DMRG algorithm. Otherwise,
                ``None`` is returned.
                ``self`` is overwritten inplace. 
        
        """

        system = "L" if self.to_right else "R"

        # step 1: get the selected U, S, V
        if type(cstruct) is not list:
            # SVD method
            # full_matrices = True here to enable increase the bond dimension
            Uset, SUset, qnlnew, Vset, SVset, qnrnew = svd_qn.Csvd(
                asnumpy(cstruct), qnbigl, qnbigr, self.qntot, system=system)

            if self.to_right:
                ms, msdim, msqn, compms = select_basis(Uset,
                                                       SUset,
                                                       qnlnew,
                                                       Vset,
                                                       Mmax,
                                                       percent=percent)
                ms = ms.reshape(list(qnbigl.shape) + [msdim])
                compms = xp.moveaxis(
                    compms.reshape(list(qnbigr.shape) + [msdim]), -1, 0)

            else:
                ms, msdim, msqn, compms = select_basis(Vset,
                                                       SVset,
                                                       qnrnew,
                                                       Uset,
                                                       Mmax,
                                                       percent=percent)
                ms = xp.moveaxis(ms.reshape(list(qnbigr.shape) + [msdim]), -1,
                                 0)
                compms = compms.reshape(list(qnbigl.shape) + [msdim])

        else:
            # state-averaged method
            ddm = 0.0
            for iroot in range(len(cstruct)):
                if self.to_right:
                    ddm += tensordot(
                        cstruct[iroot],
                        cstruct[iroot],
                        axes=(
                            range(qnbigl.ndim, cstruct[iroot].ndim),
                            range(qnbigl.ndim, cstruct[iroot].ndim),
                        ),
                    )
                else:
                    ddm += tensordot(
                        cstruct[iroot],
                        cstruct[iroot],
                        axes=(range(qnbigl.ndim), range(qnbigl.ndim)),
                    )
            ddm /= len(cstruct)
            Uset, Sset, qnnew = svd_qn.Csvd(asnumpy(ddm),
                                            qnbigl,
                                            qnbigr,
                                            self.qntot,
                                            system=system,
                                            ddm=True)
            ms, msdim, msqn, compms = select_basis(Uset,
                                                   Sset,
                                                   qnnew,
                                                   None,
                                                   Mmax,
                                                   percent=percent)
            rotated_c = []
            averaged_ms = []
            if self.to_right:
                ms = ms.reshape(list(qnbigl.shape) + [msdim])
                for c in cstruct:
                    compms = tensordot(
                        ms,
                        c,
                        axes=(range(qnbigl.ndim), range(qnbigl.ndim)),
                    )
                    rotated_c.append(compms)
                compms = rotated_c[0]
            else:
                ms = ms.reshape(list(qnbigr.shape) + [msdim])
                for c in cstruct:
                    compms = tensordot(
                        c,
                        ms,
                        axes=(range(qnbigl.ndim,
                                    cstruct[0].ndim), range(qnbigr.ndim)),
                    )
                    rotated_c.append(compms)
                compms = rotated_c[0]
                ms = xp.moveaxis(ms, -1, 0)

        # step 2, put updated U, S, V back to self
        if len(cidx) == 1:
            # 1site method
            self[cidx[0]] = ms
            if self.to_right:
                if cidx[0] != self.site_num - 1:
                    if type(cstruct) is list:
                        for c in rotated_c:
                            averaged_ms.append(
                                tensordot(c, self[cidx[0] + 1], axes=1))
                    self[cidx[0] + 1] = tensordot(compms,
                                                  self[cidx[0] + 1],
                                                  axes=1)
                    self.qn[cidx[0] + 1] = msqn
                    self.qnidx = cidx[0] + 1
                else:
                    if type(cstruct) is list:
                        for c in rotated_c:
                            averaged_ms.append(
                                tensordot(self[cidx[0]], c, axes=1))
                    self[cidx[0]] = tensordot(self[cidx[0]], compms, axes=1)
                    self.qnidx = self.site_num - 1
            else:
                if cidx[0] != 0:
                    if type(cstruct) is list:
                        for c in rotated_c:
                            averaged_ms.append(
                                tensordot(self[cidx[0] - 1], c, axes=1))
                    self[cidx[0] - 1] = tensordot(self[cidx[0] - 1],
                                                  compms,
                                                  axes=1)
                    self.qn[cidx[0]] = msqn
                    self.qnidx = cidx[0] - 1
                else:
                    if type(cstruct) is list:
                        for c in rotated_c:
                            averaged_ms.append(
                                tensordot(c, self[cidx[0]], axes=1))
                    self[cidx[0]] = tensordot(compms, self[cidx[0]], axes=1)
                    self.qnidx = 0
        else:
            if self.to_right:
                self[cidx[0]] = ms
                self[cidx[1]] = compms
                self.qnidx = cidx[1]
            else:
                self[cidx[1]] = ms
                self[cidx[0]] = compms
                self.qnidx = cidx[0]
            if type(cstruct) is list:
                averaged_ms = rotated_c
            self.qn[cidx[1]] = msqn
        if type(cstruct) is list:
            return averaged_ms
        else:
            return None
Esempio n. 9
0
    def compress(self, temp_m_trunc=None, ret_s=False):
        """
        inp: canonicalise MPS (or MPO)

        side='l': compress LEFT-canonicalised MPS
                  by sweeping from RIGHT to LEFT
                  output MPS is right canonicalised i.e. CRRR

        side='r': reverse of 'l'

        Returns
        -------
             truncated MPS
        """
        if self.to_right:
            assert self.qnidx == 0
        else:
            assert self.qnidx == self.site_num - 1

        if self.compress_config.bonddim_should_set:
            self.compress_config.set_bonddim(len(self) + 1)
        # used for logging at exit
        sz_before = self.total_bytes
        if not self.is_mpo:
            # ensure mps is canonicalised. This is time consuming.
            # to disable this, run python as `python -O`
            if self.is_left_canon:
                assert self.check_left_canonical()
            else:
                assert self.check_right_canonical()
        system = "L" if self.to_right else "R"

        s_list = []
        for idx in self.iter_idx_list(full=False):
            mt: Matrix = self[idx]
            qnbigl, qnbigr, _ = self._get_big_qn([idx])
            u, sigma, qnlset, v, sigma, qnrset = svd_qn.Csvd(
                mt.array,
                qnbigl,
                qnbigr,
                self.qntot,
                system=system,
                full_matrices=False,
            )
            vt = v.T
            s_list.append(sigma)
            if temp_m_trunc is None:
                m_trunc = self.compress_config.compute_m_trunc(
                    sigma, idx, self.to_right)
            else:
                m_trunc = min(temp_m_trunc, len(sigma))
            self._update_ms(idx, u, vt, sigma, qnlset, qnrset, m_trunc)

        self._switch_direction()
        compress_ratio = sz_before / self.total_bytes
        logger.debug(
            f"size before/after compress: {sizeof_fmt(sz_before)}/{sizeof_fmt(self.total_bytes)}, ratio: {compress_ratio}"
        )
        if not ret_s:
            # usual exit
            return self
        else:
            # return singular value list
            return self, s_list