Beispiel #1
0
 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
Beispiel #2
0
 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
Beispiel #7
0
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
Beispiel #8
0
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
Beispiel #9
0
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
Beispiel #10
0
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
Beispiel #11
0
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
Beispiel #12
0
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
Beispiel #13
0
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
Beispiel #14
0
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
Beispiel #15
0
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
Beispiel #16
0
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
Beispiel #17
0
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
Beispiel #18
0
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)]
Beispiel #19
0
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