Source code for ctm.one_site_c4v_abelian.env_c4v_abelian

import config as cfg
import numpy as np
import yast.yast as yast

[docs]class ENV_C4V_ABELIAN(): def __init__(self, chi=1, state=None, settings=None, init=False, init_method=None, ctm_args=cfg.ctm_args, global_args=cfg.global_args): r""" :param chi: environment bond dimension :math:`\chi` :param state: wavefunction :param settings: YAST configuration :type settings: NamedTuple or SimpleNamespace (TODO link to definition) :param init: initialize environment tensors :type init: bool :param init_method: choice of environment initialization. See :meth:`init_env`. :type init_method: str :param ctm_args: CTM algorithm configuration :param global_args: global configuration :type chi: int :type state: IPEPS_ABELIAN_C4V :type ctm_args: CTMARGS :type global_args: GLOBALARGS Assuming C4v symmetric single-site ``state`` create corresponding half-row(column) tensor T and corner tensor C. The corner tensor has dimensions :math:`\chi \times \chi` and the half-row(column) tensor has dimensions :math:`\chi \times \chi \times D^2` with :math:`D` given by ``state`` or ``bond_dim``:: y\x -1 0 1 -1 C T C 0 T A T 1 C T C If both ``state`` and ``bond_dim`` are supplied, the ``bond_dim`` parameter is ignored. The environment tensors of an ENV object ``e`` are accesed through members ``C`` and ``T`` The index-position convention is as follows: For upper left C and left T start from the index in the **direction "up"** <=> (-1,0) and continue **anti-clockwise**:: C--1 0--T--1 0--C C(-1) (+1)T(+1) | | | (-1) (-1) 0 2 1 0 0 (+1) | | T(-1) T--2 2--T (+1) | | 1 1 0 2 0 | | | C--1 0--T--1 1--C All C's and T's in the above diagram are identical and they are symmetric under the exchange of environment bond indices :math:`C_{ij}=C^*_{ji}` and :math:`T_{ija}=T^*_{jia}`. """ if state: assert len(state.sites)==1, "Not a 1-site ipeps" self.engine= state.engine self.dtype= state.dtype self.device= state.device self.nsym = state.nsym self.sym= state.sym elif settings: self.engine= settings self.dtype= settings.default_dtype self.device= settings.default_device self.nsym = settings.sym.NSYM self.sym= settings.sym.SYM_ID else: raise RuntimeError("Either state or settings must be provided") self.chi= chi # initialize environment tensors # The same structure is preserved as for generic ipeps ``ENV``. We store keys for access # to dummy dicts ``C`` and ``T`` self.keyC= ((0,0),(-1,-1)) self.keyT= ((0,0),(-1,0)) self.C= dict() self.T= dict() if init or init_method: if not init_method: init_method= ctm_args.ctm_env_init_type if state and init_method in ["CTMRG"]: init_env(state, self, init_method) else: raise RuntimeError("Cannot perform initialization for desired"\ +" ctm_env_init_type "+init_method+"."\ +" Missing state.") def __str__(self): s=f"ENV_C4V_abelian chi={self.chi}\n" s+=f"dtype {self.dtype}\n" s+=f"device {self.device}\n" s+=f"nsym {self.nsym}\n" s+=f"sym {self.sym}\n" if len(self.C)==0: s+="C is empty\n" for cr,t in self.C.items(): s+=f"C({cr[0]} {cr[1]}): {t}\n" if len(self.T)==0: s+="T is empty\n" for cr,t in self.T.items(): s+=f"T({cr[0]} {cr[1]}): {t}\n" return s
[docs] def get_C(self): r""" :return: get corner tensor :rtype: yast.Tensor """ return self.C[self.keyC]
[docs] def get_T(self): r""" :return: get half-row/-column tensor :rtype: yast.Tensor """ return self.T[self.keyT]
# TODO add virtual spaces from on-site tensor as in ENV_ABELIAN?
[docs] def to_dense(self, ctm_args=cfg.ctm_args, global_args=cfg.global_args): r""" :return: returns a copy of the environment with all C,T tensors in their dense representation. If the environment already has just dense C,T tensors returns ``self``. :rtype: ENV_C4V_ABELIAN Create a copy of environment with all on-site tensors as dense possesing no explicit block structure (symmetry). This operations preserves gradients on returned dense environment. """ if self.nsym==0: return self C_dense= {cid: c.to_nonsymmetric() for cid,c in self.C.items()} T_dense= {tid: t.to_nonsymmetric() for tid,t in self.T.items()} env_dense= ENV_C4V_ABELIAN(self.chi, settings=next(iter(C_dense.values())).conf, \ ctm_args=ctm_args, global_args=global_args) env_dense.C= C_dense env_dense.T= T_dense return env_dense
[docs] def detach(self): r""" :return: returns a view of the environment with all C,T tensors detached from computational graph. :rtype: ENV_C4V_ABELIAN In case of using PyTorch backend, get a detached "view" of the environment. See `torch.Tensor.detach <https://pytorch.org/docs/stable/generated/torch.Tensor.detach.html>`_. .. note:: This operation does not preserve gradient tracking. """ e= ENV_C4V_ABELIAN(self.chi, settings=self.engine) e.C= {cid: c.detach() for cid,c in self.C.items()} e.T= {tid: t.detach() for tid,t in self.T.items()} return e
[docs] def clone(self): r""" :return: returns a clone of the environment :rtype: ENV_C4V_ABELIAN Create a clone of environment with all tensors (their blocks) attached to the computational graph. .. note:: This operation preserves gradient tracking. """ e= ENV_C4V_ABELIAN(self.chi, settings=self.engine) e.C= {cid: c.clone() for cid,c in self.C.items()} e.T= {tid: t.clone() for tid,t in self.T.items()} return e
def detach_(self): self.get_C().detach() self.get_T().detach() def compute_multiplets(self, eps_multiplet_gap=1.0e-10): return compute_multiplets(self.get_C(), eps_multiplet_gap=eps_multiplet_gap)
[docs]def init_env(state, env, init_method=None, ctm_args=cfg.ctm_args): """ :param state: wavefunction :param env: C4v symmetric CTM environment :param init_method: choice of environment initialization :type str: :param ctm_args: CTM algorithm configuration :type state: IPEPS_ABELIAN_C4V :type env: ENV_C4V_ABELIAN :type ctm_args: CTMARGS Initializes the environment `env` according to one of the supported options specified inside :class:`CTMARGS.ctm_env_init_type <config.CTMARGS>` * ``'CTMRG'`` - tensors C and T are built from the on-site tensor of `state` """ if not init_method: init_method= ctm_args.ctm_env_init_type if init_method=='CONST': init_const(env, ctm_args.verbosity_initialization) elif init_method=='RANDOM': init_random(env, ctm_args.verbosity_initialization) elif init_method=='CTMRG': init_from_ipeps_pbc(state, env, ctm_args.verbosity_initialization) else: raise ValueError("Invalid environment initialization: "+str(ctm_args.ctm_env_init_type))
def init_const(env, verbosity=0): raise NotImplementedError # env.C[env.keyC]= TA.ones(settings= env.engine, s=[-1,-1], n=0, # t=([0],[0]), D=([1],[1])) # TODO what about T ? def init_random(env, verbosity=0): raise NotImplementedError
[docs]def init_from_ipeps_pbc(state, env, verbosity=0): r""" :param state: wavefunction :param env: C4v symmetric CTM environment :type state: IPEPS_ABELIAN_C4V :type env: ENV_C4V_ABELIAN Technically, we decorate the lattice with a single C4v symmetric tensor and in order to define consistent network - change signature on every sublattice B-site:: V ^ -> A <- B -> ^ V <- B -> A <- V ^ where the ingoing arrow signifies +1 and outgoing arrow signifies -1 signature. Tensors A and B are identical in the content & symmetry sectors (thus they have exactly opposite total charge). The C tensor of tensor A is formed from A, while T tensors are formed by B:: C--T-- A--B-- T--A-- <=> B--A-- | | | | """ if verbosity>0: print("ENV: init_from_ipeps_pbc") # Left-upper corner # # i(-1) = C--2(+1),3(-1)->1(-1) # (-1)j--A*--4(-1) 0(+1),1(-1)->0(-1) # /\(-1) # (-1)3 m # \(+1) i(+1) # (+1)j--A--4(+1) # / # (+1)3 vec = (-1,-1) A = state.site() a= A.tensordot(A, ((0,1,2), (0,1,2)), conj=(0,1)) # mijef,mijab->efab a= a.transpose((0,2,1,3)) # efab->eafb ## here we need to group-legs / reshape a= a.fuse_legs( axes=((0,1),(2,3)) ) a= a/a.norm(p='inf') env.C[env.keyC]= a # left transfer matrix # # (+1)1 A 0(-1),1(+1)->0(+1) # (+1)i--A*--4(+1) = | T--4(-1),5(+1)->2(-),3(+1) # /\(+1) | 2(-1),3(+1)->1(+1) # (+1)3 m # \(-1) 1(-1) # (-1)i--A--4(-1) # / # (-1)3 vec = (-1,0) B= A.flip_signature() a= B.tensordot(B, ((0,2), (0,2)), conj=(0,1)) # meifg,maibc->efgabc a= a.transpose((0,3,1,4,2,5)) # efgabc->eafbgc a= a.fuse_legs( axes=((0,1),(2,3),4,5) ) a= a/a.norm(p='inf') env.T[env.keyT]=a
def compute_multiplets(C, eps_multiplet_gap=1.0e-10): U,S,V= yast.linalg.svd(C, (0,1), sU=1) S_dense= np.diag(S.to_numpy()) chi= len(S_dense) D= np.zeros(chi+1, dtype=S_dense.dtype) # numpy sorts in ascending fashion D[:chi]= np.sort(S_dense)[::-1] m=[] l=0 for i in range(chi): l+=1 g=D[i]-D[i+1] #print(f"{i} {D[i]} {g}", end=" ") if g>eps_multiplet_gap: #print(f"{l}", end=" ") m.append(l) l=0 D=D/max(D) return D[:chi], m