예제 #1
0
def move_gauge_right_qr(ten1,ten2):
    """
    Move the gauge via qr decomposition from ten1 to ten2

    Args:
        ten1 : np or ctf array
            The site currently holding the gauge
        ten2 : np or ctf array
            The neighboring right site
    Returns:
        ten1 : np or ctf array
            The now isometrized tensor
        ten2 : np or ctf array
            The tensor now holding the gauge
    """
    if DEBUG:
        res0 = einsum('abc,cde->abde',ten1,ten2).make_sparse()

    # Perform the svd on the tensor
    Q,R = ten1.qr(2)
    ten1 = Q

    # Multiply remainder into neighboring site
    ten2 = einsum('ab,bpc->apc',R,ten2)

    if DEBUG:
        res1 = einsum('abc,cde->abde',ten1,ten2).make_sparse()
        mpiprint(0,'QR (right) Difference = {}'.format((res0-res1).abs().sum()))

    # Return results
    return ten1,ten2
예제 #2
0
    def contract(self,mps2):
        """
        Contract an mps with another mps

        Args:
            self : MPS Object
                The mps which will be contracted
            mps2 : MPS Object
                The second mps which will be contracted

        Returns:
            res : float
                The resulting scalar from the contraction
        """
        # Throw error if either mps is not in memory
        if not self.all_in_mem(): 
            raise ValueError('MPS not in memory for norm calculation')

        if not mps2.all_in_mem():
            raise ValueError('MPS not in memory for norm calculation')

        # Move to the left, contracting env with bra and ket tensors
        for site in range(self.N):

            if site == 0:

                # Form initial norm environment
                norm_env = einsum('apb,ApB->aAbB',self[site],mps2[site])

                # Remove initial empty indices
                norm_env = norm_env.remove_empty_ind(0)
                norm_env = norm_env.remove_empty_ind(0)

            else:

                # Add next mps tensors to norm environment
                tmp1 = einsum('aA,apb->Apb',norm_env,self[site])
                norm_env = einsum('Apb,ApB->bB',tmp1,mps2[site])

        # Extract and return result
        if norm_env.sym is None:
            einstr = 'abcdefghijklmnopqrstuvwxyz'[:len(norm_env.ten.shape)]+'->'
            norm = norm_env.backend.einsum(einstr,norm_env.ten)
        else:
            einstr = 'abcdefghijklmnopqrstuvwxyz'[:len(norm_env.ten.array.shape)]+'->'
            norm = norm_env.backend.einsum(einstr,norm_env.ten.array)

        # Return result
        return norm
예제 #3
0
def quick_op(op1,op2):
    """
    Convert two local operators (2 legs each)
    into an operator between sites (4 legs)
    """
    #return einsum('io,IO->iIoO',op1,op2)
    return einsum('io,IO->oOiI',op1,op2)
예제 #4
0
def op(ops):
    """
    Operator for sites in center of lattice

    Args:
        ops : OPS object
            An object holding required operators
    """
    I = ops.I
    op = einsum('io,IO->iIoO',I,I)
    return op
예제 #5
0
def exp_mpo_nosym(h,a=1.):
    """
    Take the exponential of a three site
    gate stored as an MPO for a non-symmetric tensor
    """
    # Get correct library
    lib = h[0].backend
    # Contract MPO into a single tensor
    ten = einsum('oOa,apPqQ->opqOPQ',h[0],einsum('apPb,bqQ->apPqQ',h[1],h[2]))
    ten = ten.ten
    shape = ten.shape
    # reshape into a matrix
    ten = ten.reshape((np.prod(shape[:3]),np.prod(shape[3:])))
    # Take the exponential of the matrix
    ten *= a
    exph = lib.expm(ten)
    # Do some reshaping and transposing
    res = exph.reshape(shape)
    res = res.transpose([0,3,1,4,2,5])
    shape = res.shape
    # Do SVD on results to get back into an MPO
    res = res.reshape((np.prod(shape[:2]),-1))
    U,S,V = lib.svd(res,full_matrices=False)
    ten1 = lib.einsum('ij,j->ij',U,lib.sqrt(S))
    ten1 = ten1.reshape(shape[:2]+(-1,))
    res = lib.einsum('j,jk->jk',lib.sqrt(S),V)
    res = res.reshape((np.prod([ten1.shape[2],shape[2],shape[3]]),-1))
    U,S,V = lib.svd(res,full_matrices=False)
    ten2 = lib.einsum('ij,j->ij',U,lib.sqrt(S))
    ten2 = ten2.reshape((ten1.shape[2],shape[2],shape[3],-1))
    ten3 = lib.einsum('j,jk->jk',lib.sqrt(S),V)
    ten3 = ten3.reshape((-1,shape[4],shape[5]))
    # Put into gen_tens
    ten1 = GEN_TEN(ten=ten1)
    ten2 = GEN_TEN(ten=ten2)
    ten3 = GEN_TEN(ten=ten3)
    return [ten1,ten2,ten3]
예제 #6
0
def move_gauge_left(mps,site,truncate_mbd=1e100,return_ent=True,return_wgt=True,split_s=False):
    """
    Move the gauge via svd from ten2 to ten1

    Args:
        ten1 : np or ctf array
            The neighboring left site
        ten2 : np or ctf array
            The site currently holding the gauge

    Kwargs:
        return_ent : bool
            Whether or not to return the entanglement 
            entropy and entanglement spectrum
            Default: True
        return_wgt : bool
            Whether or not to return the sum of the 
            discarded weights.
            Default: True
        truncate_mbd : int
            The Maximum retained Bond Dimension

    Returns:
        ten1 : np or ctf array
            The tensor now holding the gauge
        ten2 : np or ctf array
            The now isometrized tensor
        EE : float
            The von neumann entanglement entropy
            Only returned if return_ent == True
        EEs : 1D Array of floats
            The von neumann entanglement spectrum
            Only returned if return_ent == True
        wgt : float
            The sum of the discarded weigths
            Only returned if return_wgt == True
    """
    # Retrieve the relevant tensors
    ten1 = mps[site-1]
    ten2 = mps[site]

    if DEBUG:
        init_cont = einsum('abc,cde->abde',mps[site-1],mps[site])

    # Move the gauge
    out = move_gauge_left_tens(ten1,ten2,
                               truncate_mbd=truncate_mbd,
                               return_ent=return_ent,
                               return_wgt=return_wgt,
                               split_s=split_s)
    ten1,ten2 = out[0],out[1]
    
    # Put back into the mps
    mps[site-1] = ten1
    mps[site] = ten2

    if DEBUG:
        fin_cont = einsum('abc,cde->abde',mps[site-1],mps[site])
        if fin_cont.sym is None:
            diff = init_cont.backend.sum(init_cont.backend.abs(init_cont.ten-fin_cont.ten))
            mpiprint(0,'\tDifference before/after QR/SVD = {}'.format(diff))
        else:
            diff = init_cont.backend.sum(init_cont.backend.abs(init_cont.ten.make_sparse()-fin_cont.ten.make_sparse()))
            mpiprint(0,'\tDifference before/after QR/SVD = {}'.format(diff))

    # Return results
    if return_wgt and return_ent:
        return mps,out[2],out[3],out[4]
    elif return_wgt:
        return mps,out[2]
    elif return_ent:
        return mps,out[2],out[3]
    else:
        return mps
예제 #7
0
def move_gauge_right(mps,site,truncate_mbd=1e100,return_ent=True,return_wgt=True,split_s=False):
    """
    Move the gauge via svd from ten1 to ten2

    Args:
        ten1 : np or ctf array
            The site currently holding the gauge
        ten2 : np or ctf array
            The neighboring right site

    Kwargs:
        return_ent : bool
            Whether or not to return the entanglement 
            entropy and entanglement spectrum
            Default: True
        return_wgt : bool
            Whether or not to return the sum of the 
            discarded weights.
            Default: True
        truncate_mbd : int
            The Maximum retained Bond Dimension

    Returns:
        ten1 : np or ctf array
            The now isometrized tensor
        ten2 : np or ctf array
            The tensor now holding the gauge
        EE : float
            The von neumann entanglement entropy
            Only returned if return_ent == True
        EEs : 1D Array of floats
            The von neumann entanglement spectrum
            Only returned if return_ent == True
        wgt : float
            The sum of the discarded weigths
            Only returned if return_wgt == True
    """
    # Retrieve the relevant tensors
    ten1 = mps[site]
    ten2 = mps[site+1]

    # Check to make sure everything is done correctly
    if True:
        init_cont = einsum('abc,cde->abde',mps[site],mps[site+1])
        old_stuff = [mps[site].copy(),mps[site+1].copy()]

    # Move the gauge
    out = move_gauge_right_tens(ten1,ten2,
                               truncate_mbd=truncate_mbd,
                               return_ent=return_ent,
                               return_wgt=return_wgt,
                               split_s=split_s)
    ten1,ten2 = out[0],out[1]

    # Put back into the mps
    mps[site] = ten1
    mps[site+1] = ten2

    if DEBUG:
        fin_cont = einsum('abc,cde->abde',mps[site],mps[site+1])
        if fin_cont.sym is None:
            diff = init_cont.backend.sum(init_cont.backend.abs(init_cont.ten-fin_cont.ten))
            mpiprint(0,'\tDifference before/after QR/SVD = {}'.format(diff))
        else:
            diff = init_cont.backend.sum(init_cont.backend.abs(init_cont.ten.make_sparse()-fin_cont.ten.make_sparse()))
            mpiprint(0,'\tDifference before/after QR/SVD = {}'.format(diff))
        #if diff > 1e-5:
        #    print('\n\nOh No!'+'!'*100)
        #    print('Ten1:')
        #    print('\tshape: {}'.format(old_stuff[0].ten.array.shape))
        #    print('\tlegs: {}'.format(old_stuff[0].legs))
        #    print('\tsymmetry: {}'.format(old_stuff[0].sym))
        #    print('Ten2:')
        #    print('\tshape: {}'.format(old_stuff[1].ten.array.shape))
        #    print('\tlegs: {}'.format(old_stuff[1].legs))
        #    print('\tsymmetry: {}'.format(old_stuff[1].sym))
        #    import sys
        #    sys.exit()

    # Return results
    if return_wgt and return_ent:
        return mps,out[2],out[3],out[4]
    elif return_wgt:
        return mps,out[2]
    elif return_ent:
        return mps,out[2],out[3]
    else:
        return mps
예제 #8
0
def move_gauge_left_svd(ten1,ten2,truncate_mbd=1e100,split_s=False,return_ent=True,return_wgt=True):
    """
    Move the gauge via svd from ten2 to ten1

    Args:
        ten1 : np or ctf array
            The neighboring left site
        ten2 : np or ctf array
            The site currently holding the gauge

    Kwargs:
        truncate_mbd : int
            The Maximum retained Bond Dimension

    Returns:
        ten1 : np or ctf array
            The tensor now holding the gauge
        ten2 : np or ctf array
            The now isometrized tensor
        EE : float
            The von neumann entanglement entropy
            Only returned if return_ent == True
        EEs : 1D Array of floats
            The von neumann entanglement spectrum
            Only returned if return_ent == True
        wgt : float
            The sum of the discarded weigths
            Only returned if return_wgt == True
    """
    if DEBUG:
        res0 = einsum('abc,cde->abde',ten1,ten2).make_sparse()

    # Perform the svd on the tensor
    #tmpprint('\t\t\t\t\tDoing SVD')
    out = ten2.svd(1,
                   truncate_mbd=truncate_mbd,
                   return_ent=return_ent,
                   return_wgt=return_wgt)
    U,S,V = out[0],out[1],out[2]

    # Multiply remainder into neighboring site
    if split_s:
        #tmpprint('\t\t\t\t\tCombine S & V')
        ten2 = einsum('ca,apb->cpb',S.sqrt(),V)
        #tmpprint('\t\t\t\t\tCombine U & S')
        gauge = einsum('ab,bc->ac',U,S.sqrt())
        #tmpprint('\t\t\t\t\tDetermine ten1')
        ten1 = einsum('apb,bc->apc',ten1,gauge)
    else:
        ten2 = V
        #tmpprint('\t\t\t\t\tCombine U & S')
        gauge = einsum('ab,bc->ac',U,S)
        #tmpprint('\t\t\t\t\tDetermine ten1')
        ten1 = einsum('apb,bc->apc',ten1,gauge)

    if DEBUG:
        res1 = einsum('abc,cde->abde',ten1,ten2).make_sparse()
        mpiprint(0,'SVD (left) Difference = {}'.format((res0-res1).abs().sum()))

    # Return Results
    if return_wgt and return_ent:
        return ten1,ten2,out[3],out[4],out[5]
    elif return_wgt:
        return ten1,ten2,out[3]
    elif return_ent:
        return ten1,ten2,out[3],out[4]
    else:
        return ten1,ten2
예제 #9
0
 def test_energy_contraction_ones_z2(self):
     mpiprint(
         0, '\n' + '=' * 50 +
         '\nPeps Energy (Ham=Identity, peps=ones, Z2 symmetry) calculation\n'
         + '-' * 50)
     # Create a PEPS
     from cyclopeps.tools.peps_tools import PEPS
     Nx = 2
     Ny = 2
     d = 2
     D = 3
     Zn = 2
     backend = 'numpy'
     peps = PEPS(Nx=Nx,
                 Ny=Ny,
                 d=d,
                 D=D,
                 chi=1000,
                 Zn=2,
                 backend=backend,
                 normalize=False)
     # Set all tensor values to 1
     for xind in range(Nx):
         for yind in range(Ny):
             peps[xind][yind].fill_all(1.)
     # Get the Hamiltonian
     from cyclopeps.ops.identity import return_op
     ham = return_op(Nx, Ny, sym='Z2', backend=backend)
     # Calculate initial norm
     norm0 = peps.calc_norm() * 4.
     mpiprint(0, 'Norm (routine) = {}'.format(norm0))
     # Perform the Exact energy calculation:
     bra = einsum('LDWCM,lMXcu->LDluWXCc', peps[0][0],
                  peps[0][1]).remove_empty_ind(0).remove_empty_ind(
                      0).remove_empty_ind(0).remove_empty_ind(0)
     bra = einsum('WXCc,CdYRm->dRWXYcm', bra,
                  peps[1][0]).remove_empty_ind(0).remove_empty_ind(0)
     bra = einsum('WXYcm,cmZru->ruWXYZ', bra,
                  peps[1][1]).remove_empty_ind(0).remove_empty_ind(0)
     norm1 = einsum('WXYZ,WXYZ->', bra, bra.conj())
     norm1 = norm1 * 4.
     mpiprint(0, 'Norm (explicit) = {}'.format(norm1))
     tmp = einsum('WXYZ,wxYZ->WXwx', bra, bra.conj())
     E1 = einsum('WXwx,WXwx->', tmp, ham[0][0][0])
     tmp = einsum('WXYZ,wXyZ->WYwy', bra, bra.conj())
     E1 += einsum('WYwy,WYwy->', tmp, ham[1][0][0])
     tmp = einsum('WXYZ,WXyz->YZyz', bra, bra.conj())
     E1 += einsum('YZyz,YZyz->', tmp, ham[0][1][0])
     tmp = einsum('WXYZ,WxYz->XZxz', bra, bra.conj())
     E1 += einsum('XZxz,XZxz->', tmp, ham[1][1][0])
     mpiprint(0,
              'Explicitly computed energy (not normalized) = {}'.format(E1))
     # Contract Energy again
     E2 = peps.calc_op(ham, normalize=False)
     mpiprint(0, 'Energy via peps Method (not normalized) = {}'.format(E2))
     self.assertTrue(abs((norm0 - norm1) / norm0) < 1e-10)
     print('Check here {}, {}, {}, {}'.format(norm0, norm1, E1, E2))
     self.assertTrue(abs((norm0 - E1) / norm0) < 1e-10)
     self.assertTrue(abs((norm0 - E2) / norm0) < 1e-10)
     mpiprint(0, 'Passed\n' + '=' * 50)
예제 #10
0
 def test_energy_itf_contraction(self):
     mpiprint(
         0, '\n' + '=' * 50 +
         '\nPeps Energy (Ham=ITF, peps=random, no symmetry) calculation\n' +
         '-' * 50)
     # Create a PEPS
     from cyclopeps.tools.peps_tools import PEPS
     Nx = 2
     Ny = 2
     d = 2
     D = 3
     peps = PEPS(Nx=Nx, Ny=Ny, d=d, D=D, chi=1000)
     peps2 = PEPS(Nx=Nx, Ny=Ny, d=d, D=D, chi=1000)
     # Get the Hamiltonian
     from cyclopeps.ops.itf import return_op
     ham = return_op(Nx, Ny, (1., 2.))
     # Perform the Exact energy calculation:
     bra = einsum('LDWCM,lMXcu->WXCc', peps[0][0], peps[0][1])
     bra = einsum('WXCc,CdYRm->WXYcm', bra, peps[1][0])
     bra = einsum('WXYcm,cmZru->WXYZ', bra, peps[1][1])
     ket = einsum('LDWCM,lMXcu->WXCc', peps2[0][0], peps2[0][1])
     ket = einsum('WXCc,CdYRm->WXYcm', ket, peps2[1][0])
     ket = einsum('WXYcm,cmZru->WXYZ', ket, peps2[1][1])
     norm1 = einsum('WXYZ,WXYZ->', bra, ket)
     tmp = einsum('WXYZ,wxYZ->WXwx', bra, ket)
     E1 = einsum('WXwx,WXwx->', tmp, ham[0][0][0])
     tmp = einsum('WXYZ,wXyZ->WYwy', bra, ket)
     E2 = einsum('WYwy,WYwy->', tmp, ham[1][0][0])
     tmp = einsum('WXYZ,WXyz->YZyz', bra, ket)
     E3 = einsum('YZyz,YZyz->', tmp, ham[0][1][0])
     tmp = einsum('WXYZ,WxYz->XZxz', bra, ket)
     E4 = einsum('XZxz,XZxz->', tmp, ham[1][1][0])
     E1 = E1 + E2 + E3 + E4
     # Contract Energy again
     E2 = peps.calc_op(ham, normalize=False, chi=1e100, ket=peps2)
     mpiprint(0, 'Energy (exact)   = {}'.format(E1))
     mpiprint(0, 'Energy (routine) = {}'.format(E2))
     self.assertTrue(abs((E2 - E1) / E1) < 1e-10)
     mpiprint(0, 'Passed\n' + '=' * 50)
예제 #11
0
 def test_energy_contraction(self):
     mpiprint(
         0, '\n' + '=' * 50 +
         '\nPeps Energy (Ham=Identity, peps=random, no symmetry) calculation\n'
         + '-' * 50)
     # Create a PEPS
     from cyclopeps.tools.peps_tools import PEPS
     Nx = 2
     Ny = 2
     d = 2
     D = 3
     peps = PEPS(Nx=Nx, Ny=Ny, d=d, D=D, chi=1000)
     # Get the Hamiltonian
     from cyclopeps.ops.identity import return_op
     ham = return_op(Nx, Ny)
     # Calculate initial norm
     norm0 = peps.calc_norm() * 4.
     # Perform the Exact energy calculation:
     bra = einsum('LDWCM,lMXcu->WXCc', peps[0][0], peps[0][1])
     bra = einsum('WXCc,CdYRm->WXYcm', bra, peps[1][0])
     bra = einsum('WXYcm,cmZru->WXYZ', bra, peps[1][1])
     norm1 = einsum('WXYZ,WXYZ->', bra, bra.conj()) * 4.
     tmp = einsum('WXYZ,wxYZ->WXwx', bra, bra.conj())
     E1 = einsum('WXwx,WXwx->', tmp, ham[0][0][0])
     tmp = einsum('WXYZ,wXyZ->WYwy', bra, bra.conj())
     E1 += einsum('WYwy,WYwy->', tmp, ham[1][0][0])
     tmp = einsum('WXYZ,WXyz->YZyz', bra, bra.conj())
     E1 += einsum('YZyz,YZyz->', tmp, ham[0][1][0])
     tmp = einsum('WXYZ,WxYz->XZxz', bra, bra.conj())
     E1 += einsum('XZxz,XZxz->', tmp, ham[1][1][0])
     # Contract Energy again
     E2 = peps.calc_op(ham, normalize=False)
     self.assertTrue(abs((norm0 - norm1) / norm0) < 1e-10)
     mpiprint(0, 'Passed Norm1')
     self.assertTrue(abs((norm0 - E1) / norm0) < 1e-10)
     mpiprint(0, 'Passed E1')
     mpiprint(0, 'Norm from calc_norm = {}'.format(norm0))
     mpiprint(0, 'Norm from exact contraction {}'.format(norm1))
     mpiprint(0, 'Norm from Energy calc op = {}'.format(E2))
     mpiprint(0, 'Norm from Energy exact contraction {}'.format(E1))
     #mpiprint(0,norm1,E1,norm0,E2,abs((norm0-E2)/norm0))
     self.assertTrue(abs((norm0 - E2) / norm0) < 1e-10)
     mpiprint(0, 'Passed\n' + '=' * 50)
예제 #12
0
 def test_energy_itf_contraction_ones(self):
     mpiprint(
         0, '\n' + '=' * 50 +
         '\nPeps Energy (Ham=ITF, peps=ones, no symmetry) calculation\n' +
         '-' * 50)
     # Create a PEPS
     from cyclopeps.tools.peps_tools import PEPS
     Nx = 2
     Ny = 2
     d = 2
     D = 3
     peps = PEPS(Nx=Nx, Ny=Ny, d=d, D=D, chi=1000, normalize=False)
     # Set all tensor values to 1
     for xind in range(Nx):
         for yind in range(Ny):
             peps[xind][yind].fill_all(1.)
     # Get the Hamiltonian
     from cyclopeps.ops.itf import return_op
     ham = return_op(Nx, Ny, (1., 2.))
     # Calculate initial norm
     norm0 = peps.calc_norm()
     mpiprint(0, 'Norm = {}'.format(norm0))
     # Perform the Exact energy calculation:
     bra = einsum('LDWCM,lMXcu->WXCc', peps[0][0], peps[0][1])
     bra = einsum('WXCc,CdYRm->WXYcm', bra, peps[1][0])
     bra = einsum('WXYcm,cmZru->WXYZ', bra, peps[1][1])
     norm1 = einsum('WXYZ,WXYZ->', bra, bra.conj())
     mpiprint(0, 'Explicitly computed norm = {}'.format(norm1))
     tmp = einsum('WXYZ,wxYZ->WXwx', bra, bra.conj())
     E1 = einsum('WXwx,WXwx->', tmp, ham[0][0][0])
     tmp = einsum('WXYZ,wXyZ->WYwy', bra, bra.conj())
     E1 += einsum('WYwy,WYwy->', tmp, ham[1][0][0])
     tmp = einsum('WXYZ,WXyz->YZyz', bra, bra.conj())
     E1 += einsum('YZyz,YZyz->', tmp, ham[0][1][0])
     tmp = einsum('WXYZ,WxYz->XZxz', bra, bra.conj())
     E1 += einsum('XZxz,XZxz->', tmp, ham[1][1][0])
     mpiprint(0,
              'Explicitly computed energy (not normalized) = {}'.format(E1))
     # Contract Energy again
     E2 = peps.calc_op(ham, normalize=False)
     mpiprint(0, 'Energy (routine) = {}'.format(E2))
     self.assertTrue(abs((E2 - E1) / E1) < 1e-10)
     mpiprint(0, 'Passed\n' + '=' * 50)
예제 #13
0
 def test_energy_contraction_heis_z2(self):
     mpiprint(
         0, '\n' + '=' * 50 +
         '\nPeps Energy (Ham=Heisenberg, peps=random, Z2 symmetry) calculation\n'
         + '-' * 50)
     # Create a PEPS
     from cyclopeps.tools.peps_tools import PEPS
     Nx = 2
     Ny = 2
     d = 2
     D = 3
     Zn = 2
     backend = 'ctf'
     peps = PEPS(Nx=Nx,
                 Ny=Ny,
                 d=d,
                 D=D,
                 chi=1000,
                 Zn=2,
                 backend=backend,
                 normalize=False)
     # Get the Hamiltonian
     from cyclopeps.ops.heis import return_op
     ham = return_op(Nx, Ny, sym='Z2', backend=backend)
     # Calculate initial norm
     norm0 = peps.calc_norm()
     mpiprint(0, 'Norm (routine) = {}'.format(norm0))
     # Perform the Exact energy calculation:
     bra = einsum('LDWCM,lMXcu->LDluWXCc', peps[0][0],
                  peps[0][1]).remove_empty_ind(0).remove_empty_ind(
                      0).remove_empty_ind(0).remove_empty_ind(0)
     bra = einsum('WXCc,CdYRm->dRWXYcm', bra,
                  peps[1][0]).remove_empty_ind(0).remove_empty_ind(0)
     bra = einsum('WXYcm,cmZru->ruWXYZ', bra,
                  peps[1][1]).remove_empty_ind(0).remove_empty_ind(0)
     norm1 = einsum('WXYZ,WXYZ->', bra, bra.conj())
     norm1 = norm1
     mpiprint(0, 'Norm (explicit) = {}'.format(norm1))
     #print(ham[0][0][0])
     tmp = einsum('WXYZ,wxYZ->WXwx', bra, bra.conj())
     E1 = einsum('WXwx,WXwx->', tmp, ham[0][0][0])
     tmp = einsum('WXYZ,wXyZ->WYwy', bra, bra.conj())
     E1 += einsum('WYwy,WYwy->', tmp, ham[1][0][0])
     tmp = einsum('WXYZ,WXyz->YZyz', bra, bra.conj())
     E1 += einsum('YZyz,YZyz->', tmp, ham[0][1][0])
     tmp = einsum('WXYZ,WxYz->XZxz', bra, bra.conj())
     E1 += einsum('XZxz,XZxz->', tmp, ham[1][1][0])
     E1 = E1
     mpiprint(0,
              'Explicitly computed energy (not normalized) = {}'.format(E1))
     # Contract Energy again
     E2 = peps.calc_op(ham, normalize=False)
     mpiprint(0, 'Energy via peps Method (not normalized) = {}'.format(E2))
     self.assertTrue(abs((norm0 - norm1) / norm0) < 1e-10)
     self.assertTrue(abs((E2 - E1) / E1) < 1e-10)
     mpiprint(0, 'Passed\n' + '=' * 50)