def get_aXa(a, op): # a - on-site tensor # op - operator dims_a = a.size() dims_op = None if op is None else op.size() if op is None: # identity A = einsum('nefgh,nabcd->eafbgchd', a, conj(a)) A = view(contiguous(A), (dims_a[1]**2, dims_a[2]**2, dims_a[3]**2, dims_a[4]**2)) elif len(dims_op) == 2: # one-site operator A = einsum('nefgh,nabcd->eafbgchd', einsum('mefgh,mn->nefgh', a, op), conj(a)) A= view(contiguous(A), \ (dims_a[1]**2, dims_a[2]**2, dims_a[3]**2, dims_a[4]**2)) elif len(dims_op) == 3: # edge operators of some MPO within the transfer matrix # # 0 0 # | | # op--2 ... or ... 2--op # | | # 1 1 # # assume the last index of the op is the MPO dimension. # It will become the last index of the resulting edge A = einsum('nefghl,nabcd->eafbgchdl', einsum('mefgh,mnl->nefghl', a, op), conj(a)) A= view(contiguous(A), \ (dims_a[1]**2, dims_a[2]**2, dims_a[3]**2, dims_a[4]**2, -1)) if verbosity > 0: print(f"aXa {A.size()}") return A
def get_aXa(a, op): # a - on-site tensor # op - operator dims_a = a.size() dims_op = None if op is None else op.size() if op is None: # identity A = einsum('nefgh,nabcd->eafbgchd', a, conj(a)) A= view(contiguous(A), \ (dims_a[1]**2, dims_a[2]**2, dims_a[3]**2, dims_a[4]**2)) elif len(dims_op) == 2: # one-site operator A = einsum('nefgh,nabcd->eafbgchd', einsum('mefgh,mn->nefgh', a, op), conj(a)) A= view(contiguous(A), \ (dims_a[1]**2, dims_a[2]**2, dims_a[3]**2, dims_a[4]**2)) elif len(dims_op) == 3: # edge operators of some MPO # # 0 0 # | | # op--2 ... or ... 2--op # | | # 1 1 # # assume the last index of the op is the MPO dimension. # It will become the last index of the resulting edge A = einsum('nefghl,nabcd->eafbgchdl', einsum('mefgh,mnl->nefghl', a, op), conj(a)) A= view(contiguous(A), \ (dims_a[1]**2, dims_a[2]**2, dims_a[3]**2, dims_a[4]**2, -1)) elif len(dims_op) == 4: # intermediate op of some MPO # # 0 # | # ... 2--op--3 ... # | # 1 # # assume the index 2 is to be contracted with extra (MPO) index # of edge. The remaining index 3 becomes last index resulting edge A = einsum('nefghlk,nabcd->eafbgchdlk', einsum('mefgh,mnlk->nefghlk', a, op), conj(a)) A= view(contiguous(A), \ (dims_a[1]**2, dims_a[2]**2, dims_a[3]**2, dims_a[4]**2, dims_op[2], dims_op[3])) if verbosity > 0: print(f"aXa {A.size()}") return A
def c2x2_RD_c(*tensors): C, T1, T2, A = tensors # 1<-0 0 # 2<-1--T1--2 1--C C2x2 = contract(C, T1, ([1], [2])) # 2<-0 # 3<-1--T2 # 2 # 0<-1 0 # 1<-2--T1---C C2x2 = contract(C2x2, T2, ([0], [2])) # 2<-0 1<-2 # 3<-1--A--3 3--T2 # 2 | # 0 | # 0<-1--T1------C C2x2 = contract(C2x2, A, ([0, 3], [2, 3])) # permute 0123->1203 # reshape (12)(03)->01 C2x2 = contiguous(permute(C2x2, (1, 2, 0, 3))) C2x2 = view(C2x2, (T2.size(0) * A.size(0), T1.size(1) * A.size(1))) # 0 # | # 1--C2x2 return C2x2
def c2x2_LU_c(*tensors): C, T1, T2, A = tensors # C--10--T1--2 # 0 1 C2x2 = contract(C, T1, ([1], [0])) # C------T1--2->1 # 0 1->0 # 0 # T2--2->3 # 1->2 C2x2 = contract(C2x2, T2, ([0], [0])) # C-------T1--1->0 # | 0 # | 0 # T2--3 1 A--3 # 2->1 2 C2x2 = contract(C2x2, A, ([0, 3], [0, 1])) # permute 0123->1203 # reshape (12)(03)->01 C2x2 = contiguous(permute(C2x2, (1, 2, 0, 3))) C2x2 = view(C2x2, (T2.size(1) * A.size(2), T1.size(2) * A.size(3))) # C2x2--1 # | # 0 return C2x2
def c2x2_RU_c(*tensors): C, T1, T2, A = tensors # 0--C # 1 # 0 # 1--T1 # 2 C2x2 = contract(C, T1, ([1], [0])) # 2<-0--T2--2 0--C # 3<-1 | # 0<-1--T1 # 1<-2 C2x2 = contract(C2x2, T2, ([0], [2])) # 1<-2--T2------C # 3 | # 0 | # 2<-1--A--3 0--T1 # 3<-2 0<-1 C2x2 = contract(C2x2, A, ([0, 3], [3, 0])) # permute 0123->1203 # reshape (12)(03)->01 C2x2 = contiguous(permute(C2x2, (1, 2, 0, 3))) C2x2 = view(C2x2, (T2.size(0) * A.size(1), T1.size(2) * A.size(2))) # 0--C2x2 # | # 1 return C2x2
def c2x2_LD_c(*tensors): C, T1, T2, A = tensors # 0->1 # T1--2 # 1 # 0 # C--1->0 C2x2 = contract(C, T1, ([0], [1])) # 1->0 # T1--2->1 # | # | 0->2 # C--0 1--T1--2->3 C2x2 = contract(C2x2, T2, ([0], [1])) # 0 0->2 # T1--1 1--A--3 # | 2 # | 2 # C--------T2--3->1 C2x2 = contract(C2x2, A, ([1, 2], [1, 2])) # permute 0123->0213 # reshape (02)(13)->01 C2x2 = contiguous(permute(C2x2, (0, 2, 1, 3))) C2x2 = view(C2x2, (T1.size(0) * A.size(0), T2.size(2) * A.size(3))) # 0 # | # C2x2--1 return C2x2
def rdm2x1(coord, state, env, sym_pos_def=False, verbosity=0): r""" :param coord: vertex (x,y) specifies position of 2x1 subsystem :param state: underlying wavefunction :param env: environment corresponding to ``state`` :param verbosity: logging verbosity :type coord: tuple(int,int) :type state: IPEPS :type env: ENV :type verbosity: int :return: 2-site reduced density matrix with indices :math:`s_0s_1;s'_0s'_1` :rtype: torch.tensor Computes 2-site reduced density matrix :math:`\rho_{2x1}` of a horizontal 2x1 subsystem using following strategy: 1. compute four individual corners 2. construct right and left half of the network 3. contract right and left halt to obtain final reduced density matrix :: C--T------------T------------------C = C2x2_LU(coord)--C2x2(coord+(1,0)) | | | | | | T--A^+A(coord)--A^+A(coord+(1,0))--T C2x1_LD(coord)--C2x1(coord+(1,0)) | | | | C--T------------T------------------C The physical indices `s` and `s'` of on-sites tensors :math:`A` (and :math:`A^\dagger`) at vertices ``coord``, ``coord+(1,0)`` are left uncontracted """ who="rdm2x1" #----- building C2x2_LU ---------------------------------------------------- C = env.C[(state.vertexToSite(coord),(-1,-1))] T1 = env.T[(state.vertexToSite(coord),(0,-1))] T2 = env.T[(state.vertexToSite(coord),(-1,0))] dimsA = state.site(coord).size() a = einsum('mefgh,nabcd->eafbgchdmn',state.site(coord),conj(state.site(coord))) a = view(contiguous(a),\ (dimsA[1]**2, dimsA[2]**2, dimsA[3]**2, dimsA[4]**2, dimsA[0], dimsA[0])) # C--10--T1--2 # 0 1 C2x2_LU =contract(C, T1, ([1],[0])) # C------T1--2->1 # 0 1->0 # 0 # T2--2->3 # 1->2 C2x2_LU =contract(C2x2_LU, T2, ([0],[0])) # C-------T1--1->0 # | 0 # | 0 # T2--3 1 a--3 # 2->1 2\45 C2x2_LU =contract(C2x2_LU, a, ([0,3],[0,1])) # permute 012345->120345 # reshape (12)(03)45->0123 # C2x2--1 # |\23 # 0 C2x2_LU= permute(C2x2_LU, (1,2,0,3,4,5)) C2x2_LU= view(contiguous(C2x2_LU), \ (T2.size(1)*a.size(2),T1.size(2)*a.size(3),dimsA[0],dimsA[0])) if verbosity>0: print("C2X2 LU "+str(coord)+"->"+str(state.vertexToSite(coord))+" (-1,-1): "+str(C2x2_LU.size())) #----- building C2x1_LD ---------------------------------------------------- C = env.C[(state.vertexToSite(coord),(-1,1))] T2 = env.T[(state.vertexToSite(coord),(0,1))] # 0 0->1 # C--1 1--T2--2 C2x1_LD=contract(C, T2, ([1],[1])) # reshape (01)2->(0)1 # 0 # | # C2x1--1 C2x1_LD= view(contiguous(C2x1_LD), (C.size(0)*T2.size(0),T2.size(2))) if verbosity>0: print("C2X1 LD "+str(coord)+"->"+str(state.vertexToSite(coord))+" (-1,1): "+str(C2x1_LD.size())) #----- build left part C2x2_LU--C2x1_LD ------------------------------------ # C2x2_LU--1 # |\23 # 0 # 0 # C2x1_LD--1->0 # TODO is it worthy(performance-wise) to instead overwrite one of C2x2_LU,C2x2_RU ? left_half= contract(C2x1_LD, C2x2_LU, ([0],[0])) #----- building C2x2_RU ---------------------------------------------------- vec = (1,0) shitf_coord = state.vertexToSite((coord[0]+vec[0],coord[1]+vec[1])) C = env.C[(shitf_coord,(1,-1))] T1 = env.T[(shitf_coord,(1,0))] T2 = env.T[(shitf_coord,(0,-1))] dimsA = state.site(shitf_coord).size() a= einsum('mefgh,nabcd->eafbgchdmn',state.site(shitf_coord),conj(state.site(shitf_coord))) a= view(contiguous(a), \ (dimsA[1]**2, dimsA[2]**2, dimsA[3]**2, dimsA[4]**2, dimsA[0], dimsA[0])) # 0--C # 1 # 0 # 1--T1 # 2 C2x2_RU =contract(C, T1, ([1],[0])) # 2<-0--T2--2 0--C # 3<-1 | # 0<-1--T1 # 1<-2 C2x2_RU =contract(C2x2_RU, T2, ([0],[2])) # 1<-2--T2------C # 3 | # 45\0 | # 2<-1--a--3 0--T1 # 3<-2 0<-1 C2x2_RU =contract(C2x2_RU, a, ([0,3],[3,0])) # permute 012334->120345 # reshape (12)(03)45->0123 # 0--C2x2 # 23/| # 1 C2x2_RU= permute(C2x2_RU, (1,2,0,3,4,5)) C2x2_RU= view(contiguous(C2x2_RU), \ (T2.size(0)*a.size(1),T1.size(2)*a.size(2), dimsA[0], dimsA[0])) if verbosity>0: print("C2X2 RU "+str((coord[0]+vec[0],coord[1]+vec[1]))+"->"+str(shitf_coord)+" (1,-1): "+str(C2x2_RU.size())) #----- building C2x1_RD ---------------------------------------------------- C = env.C[(shitf_coord,(1,1))] T1 = env.T[(shitf_coord,(0,1))] # 1<-0 0 # 2<-1--T1--2 1--C C2x1_RD =contract(C, T1, ([1],[2])) # reshape (01)2->(0)1 C2x1_RD = view(contiguous(C2x1_RD), (C.size(0)*T1.size(0),T1.size(1))) # 0 # | # 1--C2x1 if verbosity>0: print("C2X1 RD "+str((coord[0]+vec[0],coord[1]+vec[1]))+"->"+str(shitf_coord)+" (1,1): "+str(C2x1_RD.size())) #----- build right part C2x2_RU--C2x1_RD ----------------------------------- # 1<-0--C2x2_RU # |\23 # 1 # 0 # 0<-1--C2x1_RD right_half =contract(C2x1_RD, C2x2_RU, ([0],[1])) # construct reduced density matrix by contracting left and right halfs # C2x2_LU--1 1----C2x2_RU # |\23->01 |\23 # | | # C2x1_LD--0 0----C2x1_RD rdm =contract(left_half,right_half,([0,1],[0,1])) # permute into order of s0,s1;s0',s1' where primed indices # represent "ket" # 0123->0213 # symmetrize and normalize rdm = contiguous(permute(rdm, (0,2,1,3))) rdm= _sym_pos_def_rdm(rdm, sym_pos_def=sym_pos_def, verbosity=verbosity, who=who) return rdm
def apply_TM_2sO_1sChannel(coord, direction, state, env, edge, op=None, verbosity=0): r""" :param coord: tuple (x,y) specifying vertex on a square lattice :param direction: direction in which the transfer operator is applied :param state: underlying wavefunction :param env: environment corresponding to ``state`` :param edge: tensor of dimensions :math:`\chi \times D^2 \times \chi` :param op: two-site operator to be inserted into the two consecutive transfer matrices :param verbosity: logging verbosity :type coord: tuple(int,int) :type direction: tuple(int,int) :type state: IPEPS :type env: ENV :type edge: torch.tensor :type op: torch.tensor :type verbosity: int :return: ``edge`` with two transfer matrices (and operator ``op``, if any) applied. The resulting tensor has an identical index structure as the original ``edge`` :rtype: torch.tensor Applies two transfer matrices to the ``edge`` tensor, including the two-site operator ``op``. The applied transfer matrices depend on the selected site r=(x,y) and the ``direction`` of the growth. The following network is contracted:: -----T-------------------T-------------------------- | | | edge--(a(r)^+ op_l a(r))==(a(r+dir)^+ op_r a(r+dir))-- | | | -----T-------------------T-------------------------- where the physical indices `s` and `s'` of the on-site tensor :math:`a` and it's hermitian conjugate :math:`a^\dagger` are contracted with identity :math:`\delta_{s,s'}` or ``op_l`` and ``op_r`` if ``op`` is supplied. The ``op_l`` and ``op_r`` are given by the SVD decomposition of the two-site operator ``op``:: 0 1 0 1 0 1->0 | | SVD | | | | | op | = |op_l|--(S--|op^~_r|) = |op_l|--2 2--|op_r| | | | | | | 2 3 2 3 2->1 3->1 """ # TODO stronger verification if op is not None: assert (len(op.size()) == 4) # pre-process ``op`` # TODO possibly truncate/compress according to the vanishingly small singular values dims_op = op.size() op_mat = view(contiguous(permute(op, (0, 2, 1, 3))), (dims_op[0]**2, dims_op[0]**2)) op_l, s, op_r = truncated_svd_gesdd(op_mat, op_mat.size(0)) op_l = view(op_l, (dims_op[0], dims_op[0], s.size()[0])) op_r = conj(transpose(op_r)) * s[:, None] op_r = contiguous( permute(view(op_r, (s.size()[0], dims_op[0], dims_op[0])), (1, 2, 0))) else: op_l = None op_r = None E = apply_TM_1sO(coord, direction, state, env, edge, op=op_l, verbosity=verbosity) c1 = (coord[0] + direction[0], coord[1] + direction[1]) E = apply_TM_1sO(c1, direction, state, env, E, op=op_r, verbosity=verbosity) return E
def apply_TM_2sO_2sChannel(coord, direction, state, env, edge, op=None, verbosity=0): r""" :param coord: tuple (x,y) specifying vertex on a square lattice :param direction: direction in which the transfer operator is applied :param state: underlying wavefunction :param env: environment corresponding to ``state`` :param edge: tensor of dimensions :math:`\chi \times (D^2)^2 \times \chi` :param op: two-site operator to be inserted within the two-site transfer matrix :param verbosity: logging verbosity :type coord: tuple(int,int) :type direction: tuple(int,int) :type state: IPEPS :type env: ENV :type edge: torch.tensor :type op: torch.tensor :type verbosity: int :return: ``edge`` with a single instance of the transfer matrix applied The resulting tensor has an identical index structure as the original ``edge`` :rtype: torch.tensor Applies a single instance of the two-site "transfer matrix" with site r=(x,y) to the ``edge`` tensor by contracting the following network, or its corresponding rotation depending on the ``direction``:: direction: right=(1,0) down=(0,1) -----T-------------------- --------edge------------- | | | | | | edge--(a(r)^+ o1 a(r))------ T--(a(r)^+)---(a(r+x)^+)--T | | \ |\ o1---------o2 | |----(a(r+y)^+) o2 a(r+y)-- | --a(r)-------a(r+x)-----T | | | | | | -----T-------------------- The two-site operator is first decomposed into a simple MPO o1--o2 (TODO case where op comes with extra MPO index):: s1' s2' s1' s2' | op | = |o1|-----|o2| s1 s2 s1 s2 where the physical indices `s` and `s'` of the on-site tensor :math:`a` and it's hermitian conjugate :math:`a^\dagger` are contracted with identity :math:`\delta_{s,s'}` or ``o1``, ``o2``. The transfer matrix is always grown from left to right or from top to bottom. """ # TODO stronger verification op_1, op_2 = None, None if op is not None: if len(op.size()) == 4: # pre-process ``op`` # TODO possibly truncate/compress according to the vanishingly small singular values dims_op = op.size() op_mat = view(contiguous(permute(op, (0, 2, 1, 3))), (dims_op[0]**2, dims_op[0]**2)) op_1, s, op_2 = truncated_svd_gesdd(op_mat, op_mat.size(0)) op_1 = view(op_1, (dims_op[0], dims_op[0], s.size()[0])) op_2 = conj(transpose(op_2)) op_2 = contiguous( permute(view(op_2, (s.size()[0], dims_op[0], dims_op[0])), (1, 2, 0))) else: raise ValueError(f"Invalid op: rank {op.size()}") def shift_coord(c, d=(0, 0)): c0 = (c[0] + d[0], c[1] + d[1]) return c0, state.vertexToSite(c0) # Four basic cases of passed op def get_aXa(a, op): # a - on-site tensor # op - operator dims_a = a.size() dims_op = None if op is None else op.size() if op is None: # identity A = einsum('nefgh,nabcd->eafbgchd', a, conj(a)) A = view(contiguous(A), (dims_a[1]**2, dims_a[2]**2, dims_a[3]**2, dims_a[4]**2)) elif len(dims_op) == 2: # one-site operator A = einsum('nefgh,nabcd->eafbgchd', einsum('mefgh,mn->nefgh', a, op), conj(a)) A= view(contiguous(A), \ (dims_a[1]**2, dims_a[2]**2, dims_a[3]**2, dims_a[4]**2)) elif len(dims_op) == 3: # edge operators of some MPO within the transfer matrix # # 0 0 # | | # op--2 ... or ... 2--op # | | # 1 1 # # assume the last index of the op is the MPO dimension. # It will become the last index of the resulting edge A = einsum('nefghl,nabcd->eafbgchdl', einsum('mefgh,mnl->nefghl', a, op), conj(a)) A= view(contiguous(A), \ (dims_a[1]**2, dims_a[2]**2, dims_a[3]**2, dims_a[4]**2, -1)) if verbosity > 0: print(f"aXa {A.size()}") return A dir_r = (1, 0) dir_b = (0, 1) c0, c = shift_coord(coord) if direction == (0, -1): #up raise ValueError("Direction: " + str(direction) + "not implemented") elif direction == (-1, 0): #left raise ValueError("Direction: " + str(direction) + "not implemented") elif direction == (0, 1): #down T1 = env.T[(c, (-1, 0))] # Assume index structure of ``edge`` tensor to be as follows # # --edge-- ------ # 0 1->2 2->3 3->4 # 0 # T1--2->1 # 1->0 E = contract(T1, edge, ([0], [0])) if verbosity > 0: print("E=edgeT " + str(E.size())) # TODO - more efficent contraction with uncontracted-double-layer on-site tensor # Possibly reshape indices 1,2 of E, which are to be contracted with # on-site tensor and contract bra,ket in two steps instead of creating # double layer tensor # / # --A-- # /|s # X # s'|/ # --A-- # / # # where X is Id or op a = state.site(c) A = get_aXa(a, op_1) # -------edge---- ------ # | 2 3->1 4->2 # | 0 # T1--1 1--A--3->4 # | |\ # 0 3<-2 (4->5) E = contract(E, A, ([1, 2], [1, 0])) if verbosity > 0: print("E=edgeTA " + str(E.size())) c0, c = shift_coord(c0, dir_r) a = state.site(c) A = get_aXa(a, op_2) # ----edge------ ------ # | | 1 2->1 # | | 0 # T1----A--4 1---A--3->4 # | |\ /| # 0 2<-3 (5)(4) 2->3 E = contract(E,A,([1,4],[0,1])) if op is None else \ contract(E,A,([1,4,5],[0,1,4])) if verbosity > 0: print("E=edgeTAA " + str(E.size())) T2 = env.T[(c, (1, 0))] # ----edge----- ------- # | | | 1 # | | | 0 # T1----A=======A--4 1--T2 # | | | 2->4 # 0 2->1 3->2 E = contract(E, T2, ([1, 4], [0, 1])) if verbosity > 0: print("E=edgeTAAT " + str(E.size())) elif direction == (1, 0): #right T1 = env.T[(c, (0, -1))] # Assume index structure of ``edge`` tensor to be as follows # # -- 0 # edge |-- 1 # |---2 # -- 3 # # ----0 0--T1--2->1 # | 1->0 # edge--1->2 # | # ----2->3 # | # ----3->4 E = contract(T1, edge, ([0], [0])) if verbosity > 0: print("E=edgeT " + str(E.size())) # TODO - more efficent contraction with uncontracted-double-layer on-site tensor # Possibly reshape indices 1,2 of E, which are to be contracted with # on-site tensor and contract bra,ket in two steps instead of creating # double layer tensor # / # --A-- # /|s # X # s'|/ # --A-- # / # # where X is Id or op a = state.site(c) A = get_aXa(a, op_1) # ---------T1--1->0 # | 0 # | 0 # edge--2 1--A--3->4 # | 3<-2 \ # ----3->1 (4->5) # | # ----4->2 E = contract(E, A, ([0, 2], [0, 1])) if verbosity > 0: print("E=edgeTA " + str(E.size())) c0, c = shift_coord(c0, dir_b) a = state.site(c) A = get_aXa(a, op_2) # ---------T1--0 # | | # edge-------A--4->2 # | | \ # | 3 (5) # | 0 (4) # | | / # ----1 1--A--2->3 # | 3->4 # ----2->1 E = contract(E,A,([1,3],[1,0])) if op is None else \ contract(E,A,([1,3,5],[1,0,4])) if verbosity > 0: print("E=edgeTAA " + str(E.size())) T2 = env.T[(c, (0, 1))] # ---------T1--0 # | | # edge-------A--2->1 # | | # ---------A--3->2 # | 3 # | 0 # ----1 1--T2--2->3 E = contract(E, T2, ([1, 3], [1, 0])) if verbosity > 0: print("E=edgeTAAT " + str(E.size())) else: raise ValueError("Invalid direction: " + str(direction)) return E
def absorb_truncate_CTM_MOVE_UP_c(*tensors): C1, T1, T, T2, C2, A, P2, Pt2, P1, Pt1 = tensors # 0--C1 # 1 # 0 # 1--T1 # 2 nC1 = contract(C1, T1, ([1], [0])) # --0 0--C1 # | | # 0<-2--Pt1 | # | | # --1 1--T1 # 2->1 nC1 = contract(Pt1, nC1, ([0, 1], [0, 1])) # C2--1->0 # 0 # 0 # T2--2 # 1 nC2 = contract(C2, T2, ([0], [0])) # C2--0 0-- # | | # | P2--2->1 # | | # T2--2 1-- # 1->0 nC2 = contract(nC2, P2, ([0, 2], [0, 1])) # --0 0--T--2->3 # | 1->2 # 1<-2--Pt2 # | # --1->0 nT = contract(Pt2, T, ([0], [0])) # -------T--3->1 # | 2 # 0<-1--Pt2 | # | 0 # --0 1--A--3 # 2 nT = contract(nT, A, ([0, 2], [1, 0])) # -------T--1 0-- # | | | # 0--Pt2 | P1--2 # | | | # -------A--3 1-- # 2->1 nT = contract(nT, P1, ([1, 3], [0, 1])) nT = contiguous(nT) # Assign new C,T # # C(coord,(-1,-1))-- --T(coord,(0,-1))-- --C(coord,(1,-1)) # | P2-- --Pt2 | P1-- -Pt1 | # T(coord,(-1,0))--- --A(coord)--------- --T(coord,(1,0)) # | | | # # => # # C^new(coord+(0,1),(-1,-1))-- --T^new(coord+(0,1),(0,-1))-- --C^new(coord+(0,1),(1,-1)) # | | | nC1 = nC1 / nC1.abs().max() nC2 = nC2 / nC2.abs().max() nT = nT / nT.abs().max() return nC1, nC2, nT
def rdm2x2(coord, state, env, sym_pos_def=False, verbosity=0): r""" :param coord: vertex (x,y) specifies upper left site of 2x2 subsystem :param state: underlying wavefunction :param env: environment corresponding to ``state`` :param verbosity: logging verbosity :type coord: tuple(int,int) :type state: IPEPS :type env: ENV :type verbosity: int :return: 4-site reduced density matrix with indices :math:`s_0s_1s_2s_3;s'_0s'_1s'_2s'_3` :rtype: torch.tensor Computes 4-site reduced density matrix :math:`\rho_{2x2}` of 2x2 subsystem specified by the vertex ``coord`` of its upper left corner using strategy: 1. compute four individual corners 2. construct upper and lower half of the network 3. contract upper and lower half to obtain final reduced density matrix :: C--T------------------T------------------C = C2x2_LU(coord)--------C2x2(coord+(1,0)) | | | | | | T--A^+A(coord)--------A^+A(coord+(1,0))--T C2x2_LD(coord+(0,1))--C2x2(coord+(1,1)) | | | | T--A^+A(coord+(0,1))--A^+A(coord+(1,1))--T | | | | C--T------------------T------------------C The physical indices `s` and `s'` of on-sites tensors :math:`A` (and :math:`A^\dagger`) at vertices ``coord``, ``coord+(1,0)``, ``coord+(0,1)``, and ``coord+(1,1)`` are left uncontracted and given in the same order:: s0 s1 s2 s3 """ who= "rdm2x2" #----- building C2x2_LU ---------------------------------------------------- C = env.C[(state.vertexToSite(coord),(-1,-1))] T1 = env.T[(state.vertexToSite(coord),(0,-1))] T2 = env.T[(state.vertexToSite(coord),(-1,0))] dimsA = state.site(coord).size() a = contiguous(einsum('mefgh,nabcd->eafbgchdmn',state.site(coord),conj(state.site(coord)))) a = view(a, (dimsA[1]**2, dimsA[2]**2, dimsA[3]**2, dimsA[4]**2, dimsA[0], dimsA[0])) # C--10--T1--2 # 0 1 C2x2_LU = contract(C, T1, ([1],[0])) # C------T1--2->1 # 0 1->0 # 0 # T2--2->3 # 1->2 C2x2_LU = contract(C2x2_LU, T2, ([0],[0])) # C-------T1--1->0 # | 0 # | 0 # T2--3 1 a--3 # 2->1 2\45 C2x2_LU = contract(C2x2_LU, a, ([0,3],[0,1])) # permute 012345->120345 # reshape (12)(03)45->0123 # C2x2--1 # |\23 # 0 C2x2_LU = contiguous(permute(C2x2_LU,(1,2,0,3,4,5))) C2x2_LU = view(C2x2_LU, (T2.size(1)*a.size(2),T1.size(2)*a.size(3),dimsA[0],dimsA[0])) if verbosity>0: print("C2X2 LU "+str(coord)+"->"+str(state.vertexToSite(coord))+" (-1,-1): "+str(C2x2_LU.size())) #----- building C2x2_RU ---------------------------------------------------- vec = (1,0) shitf_coord = state.vertexToSite((coord[0]+vec[0],coord[1]+vec[1])) C = env.C[(shitf_coord,(1,-1))] T1 = env.T[(shitf_coord,(1,0))] T2 = env.T[(shitf_coord,(0,-1))] dimsA = state.site(shitf_coord).size() a = contiguous(einsum('mefgh,nabcd->eafbgchdmn',state.site(shitf_coord),conj(state.site(shitf_coord)))) a = view(a, (dimsA[1]**2, dimsA[2]**2, dimsA[3]**2, dimsA[4]**2, dimsA[0], dimsA[0])) # 0--C # 1 # 0 # 1--T1 # 2 C2x2_RU = contract(C, T1, ([1],[0])) # 2<-0--T2--2 0--C # 3<-1 | # 0<-1--T1 # 1<-2 C2x2_RU = contract(C2x2_RU, T2, ([0],[2])) # 1<-2--T2------C # 3 | # 45\0 | # 2<-1--a--3 0--T1 # 3<-2 0<-1 C2x2_RU = contract(C2x2_RU, a, ([0,3],[3,0])) # permute 012334->120345 # reshape (12)(03)45->0123 # 0--C2x2 # 23/| # 1 C2x2_RU = contiguous(permute(C2x2_RU, (1,2,0,3,4,5))) C2x2_RU = view(C2x2_RU, (T2.size(0)*a.size(1),T1.size(2)*a.size(2), dimsA[0], dimsA[0])) if verbosity>0: print("C2X2 RU "+str((coord[0]+vec[0],coord[1]+vec[1]))+"->"+str(shitf_coord)+" (1,-1): "+str(C2x2_RU.size())) #----- build upper part C2x2_LU--C2x2_RU ----------------------------------- # C2x2_LU--1 0--C2x2_RU C2x2_LU------C2x2_RU # |\23->12 |\23->45 & permute |\12->23 |\45 # 0 1->3 0 3->1 # TODO is it worthy(performance-wise) to instead overwrite one of C2x2_LU,C2x2_RU ? upper_half = contract(C2x2_LU, C2x2_RU, ([1],[0])) upper_half = permute(upper_half, (0,3,1,2,4,5)) #----- building C2x2_RD ---------------------------------------------------- vec = (1,1) shitf_coord = state.vertexToSite((coord[0]+vec[0],coord[1]+vec[1])) C = env.C[(shitf_coord,(1,1))] T1 = env.T[(shitf_coord,(0,1))] T2 = env.T[(shitf_coord,(1,0))] dimsA = state.site(shitf_coord).size() a = contiguous(einsum('mefgh,nabcd->eafbgchdmn',state.site(shitf_coord),conj(state.site(shitf_coord)))) a = view(a, (dimsA[1]**2, dimsA[2]**2, dimsA[3]**2, dimsA[4]**2, dimsA[0], dimsA[0])) # 1<-0 0 # 2<-1--T1--2 1--C C2x2_RD = contract(C, T1, ([1],[2])) # 2<-0 # 3<-1--T2 # 2 # 0<-1 0 # 1<-2--T1---C C2x2_RD = contract(C2x2_RD, T2, ([0],[2])) # 2<-0 1<-2 # 3<-1--a--3 3--T2 # 2\45 | # 0 | # 0<-1--T1------C C2x2_RD = contract(C2x2_RD, a, ([0,3],[2,3])) # permute 012345->120345 # reshape (12)(03)45->0123 C2x2_RD = contiguous(permute(C2x2_RD, (1,2,0,3,4,5))) C2x2_RD = view(C2x2_RD, (T2.size(0)*a.size(0),T1.size(1)*a.size(1), dimsA[0], dimsA[0])) # 0 # |/23 # 1--C2x2 if verbosity>0: print("C2X2 RD "+str((coord[0]+vec[0],coord[1]+vec[1]))+"->"+str(shitf_coord)+" (1,1): "+str(C2x2_RD.size())) #----- building C2x2_LD ---------------------------------------------------- vec = (0,1) shitf_coord = state.vertexToSite((coord[0]+vec[0],coord[1]+vec[1])) C = env.C[(shitf_coord,(-1,1))] T1 = env.T[(shitf_coord,(-1,0))] T2 = env.T[(shitf_coord,(0,1))] dimsA = state.site(shitf_coord).size() a = contiguous(einsum('mefgh,nabcd->eafbgchdmn',state.site(shitf_coord),conj(state.site(shitf_coord)))) a = view(a, (dimsA[1]**2, dimsA[2]**2, dimsA[3]**2, dimsA[4]**2, dimsA[0], dimsA[0])) # 0->1 # T1--2 # 1 # 0 # C--1->0 C2x2_LD = contract(C, T1, ([0],[1])) # 1->0 # T1--2->1 # | # | 0->2 # C--0 1--T2--2->3 C2x2_LD = contract(C2x2_LD, T2, ([0],[1])) # 0 0->2 # T1--1 1--a--3 # | 2\45 # | 2 # C--------T2--3->1 C2x2_LD = contract(C2x2_LD, a, ([1,2],[1,2])) # permute 012345->021345 # reshape (02)(13)45->0123 # 0 # |/23 # C2x2--1 C2x2_LD = contiguous(permute(C2x2_LD, (0,2,1,3,4,5))) C2x2_LD = view(C2x2_LD, (T1.size(0)*a.size(0),T2.size(2)*a.size(3), dimsA[0], dimsA[0])) if verbosity>0: print("C2X2 LD "+str((coord[0]+vec[0],coord[1]+vec[1]))+"->"+str(shitf_coord)+" (-1,1): "+str(C2x2_LD.size())) #----- build lower part C2x2_LD--C2x2_RD ----------------------------------- # 0 0->3 0 3->1 # |/23->12 |/23->45 & permute |/12->23 |/45 # C2x2_LD--1 1--C2x2_RD C2x2_LD------C2x2_RD # TODO is it worthy(performance-wise) to instead overwrite one of C2x2_LD,C2x2_RD ? lower_half = contract(C2x2_LD, C2x2_RD, ([1],[1])) lower_half = permute(lower_half, (0,3,1,2,4,5)) # construct reduced density matrix by contracting lower and upper halfs # C2x2_LU------C2x2_RU # |\23->01 |\45->23 # 0 1 # 0 1 # |/23->45 |/45->67 # C2x2_LD------C2x2_RD rdm = contract(upper_half,lower_half,([0,1],[0,1])) # permute into order of s0,s1,s2,s3;s0',s1',s2',s3' where primed indices # represent "ket" # 01234567->02461357 # symmetrize and normalize rdm= contiguous(permute(rdm, (0,2,4,6,1,3,5,7))) rdm= _sym_pos_def_rdm(rdm, sym_pos_def=sym_pos_def, verbosity=verbosity, who=who) return rdm
def absorb_truncate_CTM_MOVE_RIGHT_c(*tensors): C1, T1, T, T2, C2, A, P2, Pt2, P1, Pt1 = tensors # 0->1 0 # 2<-1--T1--2 1--C1 nC1 = contract(C1, T1, ([1], [2])) # 2->0 # __Pt1_ # 1 0 # 1 0 # 1<-2--T1----C1 nC1 = contract(Pt1, nC1, ([0, 1], [0, 1])) # 1<-0--T2--2 0--C2 # 2<-1 0<-1 nC2 = contract(C2, T2, ([0], [2])) # 0<-1--T2----C2 # 2 0 # 1 0 # |__P2_| # 2->1 nC2 = contract(nC2, P2, ([0, 2], [0, 1])) # 1<-2 # ___Pt2__ # 0<-1 0 # 0 # 2<-1--T # 3<-2 nT = contract(Pt2, T, ([0], [0])) # 0<-1 # ___Pt2__ # 0 | # 0 | # 2<-1--A--3 2--T # 3<-2 1<-3 nT = contract(nT, A, ([0, 2], [0, 3])) # 0 # ___Pt2__ # | | # | | # 1<-2--A-------T # 3 1 # 1 0 # |___P1__| # 2 nT = contract(nT, P1, ([1, 3], [0, 1])) nT = contiguous(nT) # Assign new C,T # # --T(coord,(0,-1))--C(coord,(1,-1)) =>--C^new(coord+(-1,0),(1,-1)) # |______ ________| | # P2 # | # # | # ______Pt2 # | | | # --A(coord)--T(coord,(1,0)) --T^new(coord+(-1,0),(1,0)) # |______ _| | # P1 # | # # | # ______Pt1______ # | | | # --T(coord,(0,1))--C(coord,(1,1)) --C^new(coord+(-1,0),(1,1)) nC1 = nC1 / nC1.abs().max() nC2 = nC2 / nC2.abs().max() nT = nT / nT.abs().max() return nC1, nC2, nT
def absorb_truncate_CTM_MOVE_DOWN_c(*tensors): C1, T1, T, T2, C2, A, P2, Pt2, P1, Pt1 = tensors # 0->1 # T1--2->2 # 1 # 0 # C1--1->0 nC1 = contract(C1, T1, ([0], [1])) # 1->0 # T1--2 1-- # | | # | Pt1--2->1 # | | # C1--0 0-- nC1 = contract(nC1, Pt1, ([0, 2], [0, 1])) # 1<-0 # 2<-1--T2 # 2 # 0 # 0<-1--C2 nC2 = contract(C2, T2, ([0], [2])) # 0<-1 # --1 2--T2 # | | # 1<-2--P2 | # | | # --0 0--C2 nC2 = contract(nC2, P2, ([0, 2], [0, 1])) # --1->0 # | # 1<-2--P1 # | 0->2 # --0 1--T--2->3 nT = contract(P1, T, ([0], [1])) # 0->2 # --0 1--A--3 # | 2 # 0<-1--P1 | # | 2 # -------T--3->1 nT = contract(nT, A, ([0, 2], [1, 2])) # 2->1 # -------A--3 1-- # | | | # 0--P1 | Pt2--2 # | | | # -------T--1 0-- nT = contract(nT, Pt2, ([1, 3], [0, 1])) nT = contiguous(permute(nT, (1, 0, 2))) # Assign new C,T # # | | | # T(coord,(-1,0))-- --A(coord)-------- --T(coord,(1,0)) # | Pt1-- --P1 | Pt2-- --P2 | # C(coord,(-1,1))-- --T(coord,(0,1))-- --C(coord,(1,1)) # # => # # | | | # C^new(coord+(0,-1),(-1,1))-- --T^new(coord+(0,-1),(0,1))-- --C^new(coord+(0,-1),(1,1)) nC1 = nC1 / nC1.abs().max() nC2 = nC2 / nC2.abs().max() nT = nT / nT.abs().max() return nC1, nC2, nT
def absorb_truncate_CTM_MOVE_LEFT_c(*tensors): C1, T1, T, T2, C2, A, P2, Pt2, P1, Pt1 = tensors # C1--1 0--T1--2 # | | # 0 1 nC1 = contract(C1, T1, ([1], [0])) # C1--1 0--T1--2->1 # | | # 0 1 # 0 1 # |___Pt1__| # 2->0 nC1 = contract(Pt1, nC1, ([0, 1], [0, 1])) # 0 0->1 # C2--1 1--T2--2 nC2 = contract(C2, T2, ([1], [1])) # 2->0 # ___P2___ # 0 1 # 0 1 # C2-----T2--2->1 nC2 = contract(P2, nC2, ([0, 1], [0, 1])) # 2->1 # ___P1__ # 0 1->0 # 0 # T--2->3 # 1->2 nT = contract(P1, T, ([0], [0])) # 1->0 # ___P1____ # | 0 # | 0 # T--3 1--A--3 # 2->1 2 nT = contract(nT, A, ([0, 3], [0, 1])) # 0 # ___P1___ # | | # | | # T-------A--3->1 # 1 2 # 0 1 # |___Pt2_| # 2 nT = contract(nT, Pt2, ([1, 2], [0, 1])) nT = contiguous(permute(nT, (0, 2, 1))) # Assign new C,T # # C(coord,(-1,-1))--T(coord,(0,-1))-- => C^new(coord+(1,0),(-1,-1))-- # |________ ______| | # Pt1 # | # # | # _________P1______ # | | | # T(coord,(-1,0))--A(coord)-- T^new(coord+(1,0),(-1,0))-- # |________ _____| | # Pt2 # | # # | # ________P2_______ # | | | # C(coord,(-1,1))--T(coord,(0,1))-- C^new(coord+(1,0),(-1,1)) nC1 = nC1 / nC1.abs().max() nC2 = nC2 / nC2.abs().max() nT = nT / nT.abs().max() return nC1, nC2, nT
def rdm1x2(coord, state, env, sym_pos_def=False, verbosity=0): r""" :param coord: vertex (x,y) specifies position of 1x2 subsystem :param state: underlying wavefunction :param env: environment corresponding to ``state`` :param verbosity: logging verbosity :type coord: tuple(int,int) :type state: IPEPS :type env: ENV :type verbosity: int :return: 2-site reduced density matrix with indices :math:`s_0s_1;s'_0s'_1` :rtype: torch.tensor Computes 2-site reduced density matrix :math:`\rho_{1x2}` of a vertical 1x2 subsystem using following strategy: 1. compute four individual corners 2. construct upper and lower half of the network 3. contract upper and lower halt to obtain final reduced density matrix :: C--T------------------C = C2x2_LU(coord)--------C1x2(coord) | | | | | T--A^+A(coord)--------T C2x2_LD(coord+(0,1))--C1x2(coord+0,1)) | | | T--A^+A(coord+(0,1))--T | | | C--T------------------C The physical indices `s` and `s'` of on-sites tensors :math:`A` (and :math:`A^\dagger`) at vertices ``coord``, ``coord+(0,1)`` are left uncontracted """ who="rdm1x2" #----- building C2x2_LU ---------------------------------------------------- C = env.C[(state.vertexToSite(coord),(-1,-1))] T1 = env.T[(state.vertexToSite(coord),(0,-1))] T2 = env.T[(state.vertexToSite(coord),(-1,0))] dimsA = state.site(coord).size() a= einsum('mefgh,nabcd->eafbgchdmn',state.site(coord), conj(state.site(coord))) a= view(contiguous(a), \ (dimsA[1]**2, dimsA[2]**2, dimsA[3]**2, dimsA[4]**2, dimsA[0], dimsA[0])) # C--10--T1--2 # 0 1 C2x2_LU =contract(C, T1, ([1],[0])) # C------T1--2->1 # 0 1->0 # 0 # T2--2->3 # 1->2 C2x2_LU =contract(C2x2_LU, T2, ([0],[0])) # C-------T1--1->0 # | 0 # | 0 # T2--3 1 a--3 # 2->1 2\45 C2x2_LU =contract(C2x2_LU, a, ([0,3],[0,1])) # permute 012345->120345 # reshape (12)(03)45->0123 # C2x2--1 # |\23 # 0 C2x2_LU= permute(C2x2_LU, (1,2,0,3,4,5)) C2x2_LU= view(contiguous(C2x2_LU), \ (T2.size(1)*a.size(2),T1.size(2)*a.size(3),dimsA[0],dimsA[0])) if verbosity>0: print("C2X2 LU "+str(coord)+"->"+str(state.vertexToSite(coord))+" (-1,-1): "+str(C2x2_LU.size())) #----- building C1x2_RU ---------------------------------------------------- C = env.C[(state.vertexToSite(coord),(1,-1))] T1 = env.T[(state.vertexToSite(coord),(1,0))] # 0--C # 1 # 0 # 1--T1 # 2 C1x2_RU =contract(C, T1, ([1],[0])) # reshape (01)2->(0)1 # 0--C1x2 # 23/| # 1 C1x2_RU= view(contiguous(C1x2_RU), (C.size(0)*T1.size(1),T1.size(2))) if verbosity>0: print("C1X2 RU "+str(coord)+"->"+str(state.vertexToSite(coord))+" (1,-1): "+str(C1x2_RU.size())) #----- build upper part C2x2_LU--C1x2_RU ----------------------------------- # C2x2_LU--1 0--C1x2_RU # |\23 | # 0->1 1->0 upper_half =contract(C1x2_RU, C2x2_LU, ([0],[1])) #----- building C2x2_LD ---------------------------------------------------- vec = (0,1) shitf_coord = state.vertexToSite((coord[0]+vec[0],coord[1]+vec[1])) C = env.C[(shitf_coord,(-1,1))] T1 = env.T[(shitf_coord,(-1,0))] T2 = env.T[(shitf_coord,(0,1))] dimsA = state.site(shitf_coord).size() a= einsum('mefgh,nabcd->eafbgchdmn',state.site(shitf_coord),conj(state.site(shitf_coord))) a= view(contiguous(a), \ (dimsA[1]**2, dimsA[2]**2, dimsA[3]**2, dimsA[4]**2, dimsA[0], dimsA[0])) # 0->1 # T1--2 # 1 # 0 # C--1->0 C2x2_LD =contract(C, T1, ([0],[1])) # 1->0 # T1--2->1 # | # | 0->2 # C--0 1--T2--2->3 C2x2_LD =contract(C2x2_LD, T2, ([0],[1])) # 0 0->2 # T1--1 1--a--3 # | 2\45 # | 2 # C--------T2--3->1 C2x2_LD =contract(C2x2_LD, a, ([1,2],[1,2])) # permute 012345->021345 # reshape (02)(13)45->0123 # 0 # |/23 # C2x2--1 C2x2_LD= permute(C2x2_LD, (0,2,1,3,4,5)) C2x2_LD= view(contiguous(C2x2_LD), \ (T1.size(0)*a.size(0),T2.size(2)*a.size(3), dimsA[0], dimsA[0])) if verbosity>0: print("C2X2 LD "+str((coord[0]+vec[0],coord[1]+vec[1]))+"->"+str(shitf_coord)+" (-1,1): "+str(C2x2_LD.size())) #----- building C2x2_RD ---------------------------------------------------- C = env.C[(shitf_coord,(1,1))] T2 = env.T[(shitf_coord,(1,0))] # 0 # 1--T2 # 2 # 0 # 2<-1--C C1x2_RD =contract(T2, C, ([2],[0])) # permute 012->021 # reshape 0(12)->0(1) C1x2_RD = view(contiguous(permute(C1x2_RD,(0,2,1))), \ (T2.size()[0],C.size()[1]*T2.size()[1])) # 0 # | # 1--C1x2 if verbosity>0: print("C1X2 RD "+str((coord[0]+vec[0],coord[1]+vec[1]))+"->"+str(shitf_coord)+" (1,1): "+str(C1x2_RD.size())) #----- build lower part C2x2_LD--C1x2_RD ----------------------------------- # 0->1 0 # |/23 | # C2x2_LD--1 1--C1x2_RD lower_half =contract(C1x2_RD, C2x2_LD, ([1],[1])) # construct reduced density matrix by contracting lower and upper halfs # C2x2_LU------C1x2_RU # |\23->01 | # 1 0 # 1 0 # |/23 | # C2x2_LD------C1x2_RD rdm =contract(upper_half,lower_half,([0,1],[0,1])) # permute into order of s0,s1;s0',s1' where primed indices # represent "ket" # 0123->0213 # symmetrize and normalize rdm = contiguous(permute(rdm, (0,2,1,3))) rdm= _sym_pos_def_rdm(rdm, sym_pos_def=sym_pos_def, verbosity=verbosity, who=who) return rdm
def rdm1x1(coord, state, env, sym_pos_def=False, verbosity=0): r""" :param coord: vertex (x,y) for which reduced density matrix is constructed :param state: underlying wavefunction :param env: environment corresponding to ``state`` :param verbosity: logging verbosity :type coord: tuple(int,int) :type state: IPEPS :type env: ENV :type verbosity: int :return: 1-site reduced density matrix with indices :math:`s;s'` :rtype: torch.tensor Computes 1-site reduced density matrix :math:`\rho_{1x1}` centered on vertex ``coord`` by contracting the following tensor network:: C--T-----C | | | T--A^+A--T | | | C--T-----C where the physical indices `s` and `s'` of on-site tensor :math:`A` at vertex ``coord`` and it's hermitian conjugate :math:`A^\dagger` are left uncontracted """ who= "rdm1x1" # C(-1,-1)--1->0 # 0 # 0 # T(-1,0)--2 # 1 rdm = contract(env.C[(coord,(-1,-1))],env.T[(coord,(-1,0))],([0],[0])) if verbosity>0: print("rdm=CT "+str(rdm.size())) # C(-1,-1)--0 # | # T(-1,0)--2->1 # 1 # 0 # C(-1,1)--1->2 rdm = contract(rdm,env.C[(coord,(-1,1))],([1],[0])) if verbosity>0: print("rdm=CTC "+str(rdm.size())) # C(-1,-1)--0 # | # T(-1,0)--1 # | 0->2 # C(-1,1)--2 1--T(0,1)--2->3 rdm = contract(rdm,env.T[(coord,(0,1))],([2],[1])) if verbosity>0: print("rdm=CTCT "+str(rdm.size())) # TODO - more efficent contraction with uncontracted-double-layer on-site tensor # Possibly reshape indices 1,2 of rdm, which are to be contracted with # on-site tensor and contract bra,ket in two steps instead of creating # double layer tensor # / # --A-- # /|s # # s'|/ # --A-- # / # dimsA = state.site(coord).size() a = contiguous(einsum('mefgh,nabcd->eafbgchdmn',state.site(coord),conj(state.site(coord)))) a = view(a, (dimsA[1]**2, dimsA[2]**2, dimsA[3]**2, dimsA[4]**2, dimsA[0], dimsA[0])) # C(-1,-1)--0 # | # | 0->2 # T(-1,0)--1 1--a--3 # | 2\45(s,s') # | 2 # C(-1,1)-------T(0,1)--3->1 rdm = contract(rdm,a,([1,2],[1,2])) if verbosity>0: print("rdm=CTCTa "+str(rdm.size())) # C(-1,-1)--0 0--T(0,-1)--2->0 # | 1 # | 2 # T(-1,0)--------a--3->2 # | |\45->34(s,s') # | | # C(-1,1)--------T(0,1)--1 rdm = contract(env.T[(coord,(0,-1))],rdm,([0,1],[0,2])) if verbosity>0: print("rdm=CTCTaT "+str(rdm.size())) # C(-1,-1)--T(0,-1)--0 0--C(1,-1) # | | 1->0 # | | # T(-1,0)---a--2 # | |\34(s,s') # | | # C(-1,1)---T(0,1)--0->1 rdm = contract(env.C[(coord,(1,-1))],rdm,([0],[0])) if verbosity>0: print("rdm=CTCTaTC "+str(rdm.size())) # C(-1,-1)--T(0,-1)-----C(1,-1) # | | 0 # | | 0 # T(-1,0)---a--2 1------T(1,0) # | |\34->23(s,s') 2->0 # | | # C(-1,1)---T(0,1)--1 rdm = contract(env.T[(coord,(1,0))],rdm,([0,1],[0,2])) if verbosity>0: print("rdm=CTCTaTCT "+str(rdm.size())) # C(-1,-1)--T(0,-1)--------C(1,-1) # | | | # | | | # T(-1,0)---a--------------T(1,0) # | |\23->12(s,s') 0 # | | 0 # C(-1,1)---T(0,1)--1 1----C(1,1) rdm = contract(rdm,env.C[(coord,(1,1))],([0,1],[0,1])) if verbosity>0: print("rdm=CTCTaTCTC "+str(rdm.size())) # symmetrize and normalize rdm= _sym_pos_def_rdm(rdm, sym_pos_def=sym_pos_def, verbosity=verbosity, who=who) return rdm
def apply_TM_1sO(coord, direction, state, env, edge, op=None, verbosity=0): r""" :param coord: tuple (x,y) specifying vertex on a square lattice :param direction: direction in which the transfer operator is applied :param state: underlying wavefunction :param env: environment corresponding to ``state`` :param edge: tensor of dimensions :math:`\chi \times D^2 \times \chi (\times d_{MPO})`, potentially with 4th index beloning to some MPO :param op: operator to be inserted into transfer matrix :param verbosity: logging verbosity :type coord: tuple(int,int) :type direction: tuple(int,int) :type state: IPEPS :type env: ENV :type edge: torch.tensor :type op: torch.tensor :type verbosity: int :return: ``edge`` with a single instance of the transfer matrix applied The resulting tensor has an identical index structure as the original ``edge`` :rtype: torch.tensor Applies a single instance of the "transfer matrix" of site r=(x,y) to the ``edge`` tensor by contracting the following network, or its corresponding rotation depending on the ``direction``:: direction: right=(1,0) down=(0,1) -----T---------------- --------edge-------- | | | | |\ edge--(a(r)^+ op a(r))-- T--(a(r)^+ op a(r))--T \ | | | | | (MPO) -----T---------------- (\-----------------MPO) where the physical indices `s` and `s'` of the on-site tensor :math:`a` and it's hermitian conjugate :math:`a^\dagger` are contracted with identity :math:`\delta_{s,s'}` or ``op`` (if supplied). Potentially, the ``edge`` can carry an extra MPO index. """ # Check if edge has MPO index contains_mpo = len(edge.size()) == 4 # Four basic cases of passed op def get_aXa(a, op): # a - on-site tensor # op - operator dims_a = a.size() dims_op = None if op is None else op.size() if op is None: # identity A = einsum('nefgh,nabcd->eafbgchd', a, conj(a)) A= view(contiguous(A), \ (dims_a[1]**2, dims_a[2]**2, dims_a[3]**2, dims_a[4]**2)) elif len(dims_op) == 2: # one-site operator A = einsum('nefgh,nabcd->eafbgchd', einsum('mefgh,mn->nefgh', a, op), conj(a)) A= view(contiguous(A), \ (dims_a[1]**2, dims_a[2]**2, dims_a[3]**2, dims_a[4]**2)) elif len(dims_op) == 3: # edge operators of some MPO # # 0 0 # | | # op--2 ... or ... 2--op # | | # 1 1 # # assume the last index of the op is the MPO dimension. # It will become the last index of the resulting edge A = einsum('nefghl,nabcd->eafbgchdl', einsum('mefgh,mnl->nefghl', a, op), conj(a)) A= view(contiguous(A), \ (dims_a[1]**2, dims_a[2]**2, dims_a[3]**2, dims_a[4]**2, -1)) elif len(dims_op) == 4: # intermediate op of some MPO # # 0 # | # ... 2--op--3 ... # | # 1 # # assume the index 2 is to be contracted with extra (MPO) index # of edge. The remaining index 3 becomes last index resulting edge A = einsum('nefghlk,nabcd->eafbgchdlk', einsum('mefgh,mnlk->nefghlk', a, op), conj(a)) A= view(contiguous(A), \ (dims_a[1]**2, dims_a[2]**2, dims_a[3]**2, dims_a[4]**2, dims_op[2], dims_op[3])) if verbosity > 0: print(f"aXa {A.size()}") return A c = state.vertexToSite(coord) if direction == (0, -1): #up T1 = env.T[(c, (-1, 0))] # Assume index structure of ``edge`` tensor to be as follows # # 0 # T1--2->1 # 1 # 0 1->2 2->3 (3->4) # --edge-- ----- E = contract(T1, edge, ([1], [0])) if verbosity > 0: print("E=edgeT " + str(E.size())) # TODO - more efficent contraction with uncontracted-double-layer on-site tensor # Possibly reshape indices 1,2 of E, which are to be contracted with # on-site tensor and contract bra,ket in two steps instead of creating # double layer tensor # / # --A-- # /|s # X # s'|/ # --A-- # / # # where X is Id or op a = state.site(c) A = get_aXa(a, op) # edge with no MPO edge with MPO index # # 0 2<-0 (4) 0 2<-0 ---------(4)(5->4) # | |/ | |/ | # T1--1 1--A--3 T1--1 1--A--3 | # | 2 | 2 | # | 2 3->1 | 2 3->1 (4) # --------edge-- --------edge-- ------ E = contract(E,A,([1,2,4],[1,2,4])) if contains_mpo else \ contract(E,A,([1,2],[1,2])) if verbosity > 0: print("E=EA " + str(E.size())) # 0 1<-2 (4->2) 0->2 (0->3) # | |/ | # T1----A--3 1------T2 # | | 2 # | | 1 # -----edge--------- T2 = env.T[(c, (1, 0))] E = contract(E, T2, ([1, 3], [2, 1])) # if we have extra MPO dimension, permute it to the last index if len(E.size()) == 4: E = contiguous(permute(E, (0, 1, 3, 2))) if verbosity > 0: print("E=ET " + str(E.size())) elif direction == (-1, 0): #left T1 = env.T[(c, (0, -1))] # Assume index structure of ``edge`` tensor to be as follows # # 0 -- # 1 --| edge # 2 -- # # 0--T1--2 0---- # 1 | # 2<-1--edge # | # 3<-2----| # (4<-3)--- E = contract(T1, edge, ([2], [0])) if verbosity > 0: print("E=edgeT " + str(E.size())) # TODO - more efficent contraction with uncontracted-double-layer on-site tensor # Possibly reshape indices 1,2 of E, which are to be contracted with # on-site tensor and contract bra,ket in two steps instead of creating # double layer tensor # / # --A-- # /|s # X # s'|/ # --A-- # / # # where X is Id or op a = state.site(c) A = get_aXa(a, op) # 0--T1--------- 0--T1--------- # 1 | 1 | # 0 | 0 | # 2<-1--A----3 2--edge 2<-1--A----3 2--edge # / 2->3 | / 2->3 | # | 1<-3--| (4) 1<-3-- # (4<-5)(4)-------(4)-- E = contract(E,A,([1,2,4],[0,3,4])) if contains_mpo else \ contract(E,A,([1,2],[0,3])) if verbosity > 0: print("E=EA " + str(E.size())) # 0--T1------- # | | # | | # 1<-2--A-------edge # / 3 | # (2<-4) 0 | # (3<-1) 2<-1--T2--2 1-- T2 = env.T[(c, (0, 1))] E = contract(E, T2, ([1, 3], [2, 0])) if len(E.size()) == 4: E = contiguous(permute(E, (0, 1, 3, 2))) if verbosity > 0: print("E=ET " + str(E.size())) elif direction == (0, 1): #down T1 = env.T[(c, (-1, 0))] # Assume index structure of ``edge`` tensor to be as follows # # --edge-- ------ # 0 1->2 2->3 (3->4) # 0 # T1--2->1 # 1->0 E = contract(T1, edge, ([0], [0])) if verbosity > 0: print("E=edgeT " + str(E.size())) # TODO - more efficent contraction with uncontracted-double-layer on-site tensor # Possibly reshape indices 1,2 of E, which are to be contracted with # on-site tensor and contract bra,ket in two steps instead of creating # double layer tensor # / # --A-- # /|s # X # s'|/ # --A-- # / # # where X is Id or op a = state.site(c) A = get_aXa(a, op) # -------edge---- -------edge---- ------ # | 2 3->1 | 2 3->1 (4) # | 0 | 0 | # T1--1 1--A--3 T1--1 1--A--3 | # | |\ | |\ | # 0 2 (4) 0 2 -----------(4)(5->4) E = contract(E,A,([1,2,4],[1,0,4])) if contains_mpo else \ contract(E,A,([1,2],[1,0])) if verbosity > 0: print("E=EA " + str(E.size())) # ----edge------- # | | 1 # | | 0 # T1----A--3 1----T2 # | |\ | # 0 1<-2 (4->2) 2 (2->3) T2 = env.T[(c, (1, 0))] E = contract(E, T2, ([1, 3], [0, 1])) if len(E.size()) == 4: E = contiguous(permute(E, (0, 1, 3, 2))) if verbosity > 0: print("E=ET " + str(E.size())) elif direction == (1, 0): #right T1 = env.T[(c, (0, -1))] # Assume index structure of ``edge`` tensor to be as follows # # -- 0 # edge |-- 1 # -- 2 # # ----0 0--T1--2->1 # | 1->0 # edge--1->2 # | # ----2->3 # | # ----(3->4) E = contract(T1, edge, ([0], [0])) if verbosity > 0: print("E=edgeT " + str(E.size())) # TODO - more efficent contraction with uncontracted-double-layer on-site tensor # Possibly reshape indices 1,2 of E, which are to be contracted with # on-site tensor and contract bra,ket in two steps instead of creating # double layer tensor # / # --A-- # /|s # X # s'|/ # --A-- # / # # where X is Id or op a = state.site(c) A = get_aXa(a, op) # ---------T1--1->0 ---------T1--1->0 # | 0 | 0 # | 0 | 0 # edge--2 1--A--3 edge--2 1--A--3 # | 2 \ | 2 # ----3->1 | ----3->1 # | | # ----4 ---- (4)(5->4) E = contract(E,A,([0,2,4],[0,1,4])) if contains_mpo else \ contract(E,A,([0,2],[0,1])) if verbosity > 0: print("E=EA " + str(E.size())) # -------T1--0 # | | # | | # edge-----A--3->1 # | 2 \(4->2) # | 0 # --1 1--T2--2 (2->3) T2 = env.T[(c, (0, 1))] E = contract(E, T2, ([1, 2], [1, 0])) if len(E.size()) == 4: E = contiguous(permute(E, (0, 1, 3, 2))) if verbosity > 0: print("E=ET " + str(E.size())) else: raise ValueError("Invalid direction: " + str(direction)) return E
def init_from_ipeps_pbc(state, env, verbosity=0): if verbosity > 0: print("ENV: init_from_ipeps") for coord, site in state.sites.items(): for rel_vec in [(-1, -1), (1, -1), (1, 1), (-1, 1)]: env.C[(coord, rel_vec)] = torch.zeros(env.chi, env.chi, dtype=env.dtype, device=env.device) # Left-upper corner # # i = C--1 # j--A--3 0 # /\ # 2 m # \ i # j--A--3 # / # 2 vec = (-1, -1) A = state.site((coord[0] + vec[0], coord[1] + vec[1])) dimsA = A.size() a = contiguous(einsum('mijef,mijab->eafb', A, conj(A))) a = view(a, (dimsA[3]**2, dimsA[4]**2)) a = a / a.abs().max() env.C[(coord,vec)][:min(env.chi,dimsA[3]**2),:min(env.chi,dimsA[4]**2)]=\ a[:min(env.chi,dimsA[3]**2),:min(env.chi,dimsA[4]**2)] # right-upper corner # # i = 0--C # 1--A--j 1 # /\ # 2 m # \ i # 1--A--j # / # 2 vec = (1, -1) A = state.site((coord[0] + vec[0], coord[1] + vec[1])) dimsA = A.size() a = contiguous(einsum('miefj,miabj->eafb', A, conj(A))) a = view(a, (dimsA[2]**2, dimsA[3]**2)) a = a / a.abs().max() env.C[(coord,vec)][:min(env.chi,dimsA[2]**2),:min(env.chi,dimsA[3]**2)]=\ a[:min(env.chi,dimsA[2]**2),:min(env.chi,dimsA[3]**2)] # right-lower corner # # 0 = 0 # 1--A--j 1--C # /\ # i m # \ 0 # 1--A--j # / # i vec = (1, 1) A = state.site((coord[0] + vec[0], coord[1] + vec[1])) dimsA = A.size() a = contiguous(einsum('mefij,mabij->eafb', A, conj(A))) a = view(a, (dimsA[1]**2, dimsA[2]**2)) a = a / a.abs().max() env.C[(coord,vec)][:min(env.chi,dimsA[1]**2),:min(env.chi,dimsA[2]**2)]=\ a[:min(env.chi,dimsA[1]**2),:min(env.chi,dimsA[2]**2)] # left-lower corner # # 0 = 0 # i--A--3 C--1 # /\ # j m # \ 0 # i--A--3 # / # j vec = (-1, 1) A = state.site((coord[0] + vec[0], coord[1] + vec[1])) dimsA = A.size() a = contiguous(einsum('meijf,maijb->eafb', A, conj(A))) a = view(a, (dimsA[1]**2, dimsA[4]**2)) a = a / a.abs().max() env.C[(coord,vec)][:min(env.chi,dimsA[1]**2),:min(env.chi,dimsA[4]**2)]=\ a[:min(env.chi,dimsA[1]**2),:min(env.chi,dimsA[4]**2)] # upper transfer matrix # # i = 0--T--2 # 1--A--3 1 # /\ # 2 m # \ i # 1--A--3 # / # 2 vec = (0, -1) A = state.site((coord[0] + vec[0], coord[1] + vec[1])) dimsA = A.size() a = contiguous(einsum('miefg,miabc->eafbgc', A, conj(A))) a = view(a, (dimsA[2]**2, dimsA[3]**2, dimsA[4]**2)) a = a / a.abs().max() env.T[(coord, vec)] = torch.zeros((env.chi, dimsA[3]**2, env.chi), dtype=env.dtype, device=env.device) env.T[(coord,vec)][:min(env.chi,dimsA[2]**2),:,:min(env.chi,dimsA[4]**2)]=\ a[:min(env.chi,dimsA[2]**2),:,:min(env.chi,dimsA[4]**2)] # left transfer matrix # # 0 = 0 # i--A--3 T--2 # /\ 1 # 2 m # \ 0 # i--A--3 # / # 2 vec = (-1, 0) A = state.site((coord[0] + vec[0], coord[1] + vec[1])) dimsA = A.size() a = contiguous(einsum('meifg,maibc->eafbgc', A, conj(A))) a = view(a, (dimsA[1]**2, dimsA[3]**2, dimsA[4]**2)) a = a / a.abs().max() env.T[(coord, vec)] = torch.zeros((env.chi, env.chi, dimsA[4]**2), dtype=env.dtype, device=env.device) env.T[(coord,vec)][:min(env.chi,dimsA[1]**2),:min(env.chi,dimsA[3]**2),:]=\ a[:min(env.chi,dimsA[1]**2),:min(env.chi,dimsA[3]**2),:] # lower transfer matrix # # 0 = 0 # 1--A--3 1--T--2 # /\ # i m # \ 0 # 1--A--3 # / # i vec = (0, 1) A = state.site((coord[0] + vec[0], coord[1] + vec[1])) dimsA = A.size() a = contiguous(einsum('mefig,mabic->eafbgc', A, conj(A))) a = view(a, (dimsA[1]**2, dimsA[2]**2, dimsA[4]**2)) a = a / a.abs().max() env.T[(coord, vec)] = torch.zeros((dimsA[1]**2, env.chi, env.chi), dtype=env.dtype, device=env.device) env.T[(coord,vec)][:,:min(env.chi,dimsA[2]**2),:min(env.chi,dimsA[4]**2)]=\ a[:,:min(env.chi,dimsA[2]**2),:min(env.chi,dimsA[4]**2)] # right transfer matrix # # 0 = 0 # 1--A--i 1--T # /\ 2 # 2 m # \ 0 # 1--A--i # / # 2 vec = (1, 0) A = state.site((coord[0] + vec[0], coord[1] + vec[1])) dimsA = A.size() a = contiguous(einsum('mefgi,mabci->eafbgc', A, conj(A))) a = view(a, (dimsA[1]**2, dimsA[2]**2, dimsA[3]**2)) a = a / a.abs().max() env.T[(coord, vec)] = torch.zeros((env.chi, dimsA[2]**2, env.chi), dtype=env.dtype, device=env.device) env.T[(coord,vec)][:min(env.chi,dimsA[1]**2),:,:min(env.chi,dimsA[3]**2)]=\ a[:min(env.chi,dimsA[1]**2),:,:min(env.chi,dimsA[3]**2)]
def run(state, env, conv_check=None, ctm_args=cfg.ctm_args, global_args=cfg.global_args): r""" :param state: wavefunction :param env: environment :param conv_check: function which determines the convergence of CTM algorithm. If ``None``, the algorithm performs ``ctm_args.ctm_max_iter`` iterations. :param ctm_args: CTM algorithm configuration :param global_args: global configuration :type state: IPEPS :type env: ENV :type conv_check: function(IPEPS,ENV,list[float],CTMARGS)->bool :type ctm_args: CTMARGS :type global_args: GLOBALARGS Executes directional CTM algorithm for generic iPEPS starting from the intial environment ``env``. TODO add reference """ # 0) Create double-layer (DL) tensors, preserving the same convenction # for order of indices # # / / # --A^dag-- = --a-- # /| / # |/ # --A-- # / # sitesDL = dict() for coord, A in state.sites.items(): dimsA = A.size() a = contiguous(einsum('mefgh,mabcd->eafbgchd', A, conj(A))) a = view(a, (dimsA[1]**2, dimsA[2]**2, dimsA[3]**2, dimsA[4]**2)) sitesDL[coord] = a stateDL = IPEPS(sitesDL, state.vertexToSite) # 1) perform CTMRG t_obs = t_ctm = 0. history = None for i in range(ctm_args.ctm_max_iter): t0_ctm = time.perf_counter() for direction in ctm_args.ctm_move_sequence: ctm_MOVE(direction, stateDL, env, ctm_args=ctm_args, global_args=global_args, \ verbosity=ctm_args.verbosity_ctm_move) t1_ctm = time.perf_counter() t0_obs = time.perf_counter() if conv_check is not None: # evaluate convergence of the CTMRG procedure converged, history = conv_check(state, env, history, ctm_args=ctm_args) if ctm_args.verbosity_ctm_convergence > 1: print(history[-1]) if converged: if ctm_args.verbosity_ctm_convergence > 0: print( f"CTMRG converged at iter= {i}, history= {history[-1]}" ) break t1_obs = time.perf_counter() t_ctm += t1_ctm - t0_ctm t_obs += t1_obs - t0_obs return env, history, t_ctm, t_obs