Source code for renormalizer.mps.mpdm

# -*- coding: utf-8 -*-

import logging

import numpy as np

from renormalizer.mps.backend import xp
from renormalizer.mps.matrix import tensordot
from renormalizer.mps.svd_qn import add_outer
from renormalizer.mps import Mpo, Mps

logger = logging.getLogger(__name__)

# MPS first. `digest`, `metacopy`
[docs]class MpDm(Mps, Mpo):
[docs] @classmethod def random(cls, mpo, qntot, m_max, percent=0): # avoid misuse to produce mps raise ValueError("MpDm don't have to produce random state")
[docs] @classmethod def ground_state(cls, model, max_entangled): raise ValueError( "Use max_entangled_ex or max_entangled_gs for matrix product density matrix" )
[docs] @classmethod def from_mps(cls, mps: Mps): mpo = cls() mpo.model = mps.model for ms in mps: mo = np.zeros(tuple([ms.shape[0]] + [ms.shape[1]] * 2 + [ms.shape[2]])) for iaxis in range(ms.shape[1]): mo[:, iaxis, iaxis, :] = ms[:, iaxis, :].array mpo.append(mo) mpo.coeff = mps.coeff mpo.optimize_config = mps.optimize_config mpo.evolve_config = mps.evolve_config mpo.qn = [qn.copy() for qn in mps.qn] mpo.qntot = mps.qntot mpo.qnidx = mps.qnidx mpo.to_right = mps.to_right mpo.compress_config = mps.compress_config.copy() return mpo
[docs] @classmethod def from_dense(cls, model, wfn: np.ndarray): raise NotImplementedError
[docs] @classmethod def max_entangled_ex(cls, model, normalize=True): """ T = \\infty locally maximal entangled EX state """ mps = Mps.ground_state(model, max_entangled=True) # the creation operator \\sum_i a^\\dagger_i ex_mpo = Mpo.onsite(model, r"a^\dagger") ex_mps = ex_mpo @ mps if normalize: ex_mps.normalize("mps_and_coeff") return cls.from_mps(ex_mps)
[docs] @classmethod def max_entangled_gs(cls, model) -> "MpDm": return cls.from_mps(Mps.ground_state(model, max_entangled=True))
def _get_sigmaqn(self, idx): array_up = self.model.basis[idx].sigmaqn array_down = np.zeros_like(array_up) return add_outer(array_up, array_down)
[docs] def evolve_exact(self, h_mpo, evolve_dt, space): MPOprop = Mpo.exact_propagator( self.model, -1.0j * evolve_dt, space=space, shift=-h_mpo.offset ) # Mpdm is applied on the propagator, different from base method new_mpdm = self.apply(MPOprop, canonicalise=True) new_mpdm.coeff *= np.exp(-1.0j * h_mpo.offset * evolve_dt) return new_mpdm
[docs] def todense(self): # explicitly call to MPO because MPS is firstly inherited return Mpo.todense(self)
@property def is_mps(self): return False @property def is_mpo(self): return False @property def is_mpdm(self): return True def _expectation_path(self): # e # | # S--a--S--f--S # | | | # | d | # | | | # O--b--O--h--O # | | | # | g | # | | | # S--c--S--j--S # | # e path = [ ([0, 1], "abc, cgej -> abgej"), ([3, 0], "abgej, bdgh -> aejdh"), ([2, 0], "aejdh, adef -> jhf"), ([1, 0], "jhf, fhj -> "), ] return path
[docs] def conj_trans(self): raise NotImplementedError logger.warning("using conj_trans on mpdm leads to dummy qn") new_mpdm: "MpDmBase" = super().conj_trans() new_mpdm.coeff = new_mpdm.coeff.conjugate() return new_mpdm
[docs] def apply(self, mp, canonicalise=False) -> "MpDmBase": # Note usually mp is an mpo assert not mp.is_mps new_mpdm = self.metacopy() if mp.is_complex: new_mpdm.to_complex(inplace=True) # todo: also duplicate with MPO apply. What to do??? for i, (mt_self, mt_other) in enumerate(zip(self, mp)): assert mt_self.shape[2] == mt_other.shape[1] # mt=np.einsum("apqb,cqrd->acprbd",mt_s,mt_o) mt = xp.moveaxis( tensordot(mt_self.array, mt_other.array, axes=([2], [1])), [-3, -2], [1, 3], ) mt = mt.reshape( ( mt_self.shape[0] * mt_other.shape[0], mt_self.shape[1], mt_other.shape[2], mt_self.shape[-1] * mt_other.shape[-1], ) ) new_mpdm[i] = mt qn = mp.dummy_qn new_mpdm.qn = [ add_outer(np.array(qn_o), np.array(qn_m)).reshape(-1, qn_o.shape[1]) for qn_o, qn_m in zip(self.qn, qn) ] if canonicalise: new_mpdm.canonicalise() return new_mpdm