Пример #1
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
Пример #2
0
def update_cv(vset,
              sset,
              qnset,
              compset,
              nexciton,
              Mmax,
              spectratype,
              percent=0):
    sidx = select_Xbasis(qnset,
                         sset,
                         range(nexciton + 1),
                         Mmax,
                         spectratype,
                         percent=percent)
    xdim = len(sidx)
    x = np.zeros((vset.shape[0], xdim), dtype=vset.dtype)
    xqn = []
    if compset is not None:
        compx = np.zeros((compset.shape[0], xdim), dtype=compset.dtype)
    else:
        compx = None

    for idim in range(xdim):
        x[:, idim] = vset[:, sidx[idim]].copy()
        if (compset is not None) and (sidx[idim] < compset.shape[1]):
            compx[:, idim] = compset[:, sidx[idim]].copy() * sset[sidx[idim]]
        xqn.append(qnset[sidx[idim]])
    if compx is not None:
        compx = Matrix(compx)
    return Matrix(x), xdim, xqn, compx
Пример #3
0
def updatemps(vset, sset, qnset, compset, nexciton, Mmax, percent=0):
    """
    select basis to construct new mps, and complementary mps
    vset, compset is the column vector
    """
    sidx = select_basis(qnset,
                        sset,
                        range(nexciton + 1),
                        Mmax,
                        percent=percent)
    mpsdim = len(sidx)
    # need to set value column by column. better in CPU
    ms = np.zeros((vset.shape[0], mpsdim), dtype=vset.dtype)

    if compset is not None:
        compmps = np.zeros((compset.shape[0], mpsdim), dtype=compset.dtype)
    else:
        compmps = None

    mpsqn = []
    stot = 0.0
    for idim in range(mpsdim):
        ms[:, idim] = vset[:, sidx[idim]].copy()
        if (compset is not None) and sidx[idim] < compset.shape[1]:
            compmps[:, idim] = compset[:, sidx[idim]].copy() * sset[sidx[idim]]
        mpsqn.append(qnset[sidx[idim]])
        stot += sset[sidx[idim]]**2

    # print("discard:", 1.0 - stot)
    if compmps is not None:
        compmps = Matrix(compmps)

    return Matrix(ms), mpsdim, mpsqn, compmps
Пример #4
0
 def _array2mt(self, array, idx):
     if isinstance(array, Matrix):
         mt = array.astype(self.dtype)
     else:
         mt = Matrix(array, dtype=self.dtype)
     if self.use_dummy_qn:
         mt.sigmaqn = np.zeros(mt.pdim_prod, dtype=np.int)
     else:
         mt.sigmaqn = self._get_sigmaqn(idx)
     return mt
Пример #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
Пример #6
0
            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
    def _array2mt(self, array, idx, allow_dump=True):
        # convert dtype
        if isinstance(array, Matrix):
            mt = array.astype(self.dtype)
        else:
            mt = Matrix(array, dtype=self.dtype)
        if mt.pdim[0] != self.pbond_list[idx]:
            raise ValueError(
                "Matrix physical bond dimension does not match system information"
            )
        # setup the matrix
        mt.sigmaqn = self._get_sigmaqn(idx)

        # array too large. Should be stored in disk
        # use ``while`` to handle the multiple-exit logic
        while allow_dump and self.compress_config.dump_matrix_size < mt.array.nbytes:
            dir_with_id = os.path.join(self.compress_config.dump_matrix_dir,
                                       str(id(self)))
            if not os.path.exists(dir_with_id):
                try:
                    os.mkdir(dir_with_id)
                except:
                    logger.exception(
                        "Creating dump dir failed. Working with the matrix in memory."
                    )
                    break
            dump_name = os.path.join(dir_with_id, f"{idx}.npy")
            try:
                array = mt.array
                if not array.flags.c_contiguous and not array.flags.f_contiguous:
                    # for faster dump (3x). Costs more memory.
                    array = np.ascontiguousarray(array)
                np.save(dump_name, array)
            except:
                logger.exception(
                    "Save matrix to disk failed. Working with the matrix in memory."
                )
                break
            return dump_name

        return mt
Пример #8
0
 def __getitem__(self, item):
     mt_or_str_or_list = self._mp[item]
     if isinstance(mt_or_str_or_list, list):
         assert isinstance(item, slice)
         for elem in mt_or_str_or_list:
             if isinstance(elem, str):
                 # load all matrices to memory will make
                 # the dump mechanism pointless
                 raise IndexError("Can't slice on dump matrices.")
     if isinstance(mt_or_str_or_list, str):
         try:
             mt = Matrix(np.load(mt_or_str_or_list), dtype=self.dtype)
             mt.sigmaqn = self._get_sigmaqn(item)
         except:
             logger.exception(f"Can't load matrix from {mt_or_str_or_list}")
             raise RuntimeError("MPS internal structure corrupted.")
     else:
         if not isinstance(mt_or_str_or_list, (Matrix, type(None))):
             raise RuntimeError(
                 f"Unknown matrix type: {type(mt_or_str_or_list)}")
         mt = mt_or_str_or_list
     return mt
Пример #9
0
 def _update_ms(
     self, idx: int, u: Matrix, vt: Matrix, sigma=None, qnlset=None, qnrset=None, m_trunc=None
 ):
     if m_trunc is None:
         m_trunc = u.shape[1]
     u = u[:, :m_trunc]
     vt = vt[:m_trunc, :]
     if sigma is None:
         # canonicalise, vt is not unitary
         if self.is_mpo:
             if self.left:
                 norm = vt.norm()
                 u = Matrix(u.array * norm)
                 vt = Matrix(vt.array / norm)
             else:
                 norm = u.norm()
                 u = Matrix(u.array / norm)
                 vt = Matrix(vt.array * norm)
     else:
         sigma = sigma[:m_trunc]
         if (not self.is_mpo and self.left) or (self.is_mpo and not self.left):
             vt = einsum("i, ij -> ij", sigma, vt)
         else:
             u = einsum("ji, i -> ji", u, sigma)
     if self.left:
         self[idx + 1] = tensordot(vt, self[idx + 1], axes=1)
         ret_mpsi = u.reshape(
             [u.shape[0] // self[idx].pdim_prod] + list(self[idx].pdim) + [m_trunc]
         )
         if qnlset is not None:
             self.qn[idx + 1] = qnlset[:m_trunc]
     else:
         self[idx - 1] = tensordot(self[idx - 1], u, axes=1)
         ret_mpsi = vt.reshape(
             [m_trunc] + list(self[idx].pdim) + [vt.shape[1] // self[idx].pdim_prod]
         )
         if qnrset is not None:
             self.qn[idx] = qnrset[:m_trunc]
     if ret_mpsi.nbytes < ret_mpsi.base.nbytes * 0.8:
         # do copy here to discard unnecessary data. Note that in NumPy common slicing returns
         # a `view` containing the original data. If `ret_mpsi` is used directly the original
         # `u` or `vt` is not garbage collected.
         ret_mpsi = ret_mpsi.copy()
     assert ret_mpsi.any()
     self[idx] = ret_mpsi
Пример #10
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 (
            moveaxis(mps.reshape(list(qnbigr.shape) + [mpsdim]), -1, 0),
            mpsdim,
            mpsqn,
            tensordot(
                Matrix(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]),
                Matrix(cstruct[0]),
                axes=(range(qnbigl.ndim), range(qnbigl.ndim)),
            ),
        )
Пример #11
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