Example #1
0
    def psi_AKLT(self):
        """Initialize the chi=2 MPS which is exact ground state of the AKLT model."""
        # build each B tensor of the MPS as contraction of
        #    --sR  sL--
        #       |  |
        #       proj
        #         |
        #
        # where  SL--SR forms a singlet between neighboring spin-1/2,
        # and projS1 is the projector from two spin-1/2 to spin-1.
        # indexing of basis states:
        # spin-1/2: 0=|m=-1/2>, 1=|m=+1/2>;  spin-1: 0=|m=-1>, 1=|m=0> 2=|m=+1>
        sL = np.sqrt(0.5) * np.array([[1., 0.], [0., -1.]]) # p2 vR
        sR = np.array([[0., 1.], [1., 0.]]) # vL p1
        proj = np.zeros([3, 2, 2]) # p p1 p2
        proj[0, 0, 0] = 1.  # |m=-1> = |down down>
        proj[1, 0, 1] = proj[1, 1, 0] = np.sqrt(0.5)  # |m=0> = (|up down> + |down, up>)/sqrt(2)
        proj[2, 1, 1] = 1.  # |m=+1> = |up up>
        B = np.tensordot(sR, proj, axes=[1, 1]) # vL [p1], p [p1] p2
        B = np.tensordot(B, sL, axes=[2, 0]) # vL p [p2], [p2] vR
        B = B * np.sqrt(4./3.)  # normalize after projection
        B = B.transpose([1, 0, 2]) # p vL vR
        S = np.sqrt(0.5) * np.ones([2])
        # it's easy to check that B is right-canonical:
        BB = np.tensordot(B, B.conj(), axes=[[0, 2], [0, 2]])
        assert np.linalg.norm(np.eye(2) - BB) < 1.e-14

        L = self.lat.N_sites
        Bs = [B] * L
        Ss = [S] * (L + 1)

        spin1 = self.lat.unit_cell[0]
        legL = None  # default
        if self.lat.bc_MPS == 'finite':
            # project onto one of the two virtual states on the left/right most state.
            # It's a ground state whatever you choose here,
            # but we project to different indices to allow Sz convservation
            # and fix overall Sz=0 sector
            Bs[0] = Bs[0][:, :1, :]
            Bs[-1] = Bs[-1][:, -1:, :]
            Ss[0] = Ss[-1] = np.ones([1.])
        elif spin1.conserve in ['Sz', 'parity']:
            chinfo = spin1.leg.chinfo
            legL = npc.LegCharge.from_qflat(chinfo, [[0], [2]])
        return MPS.from_Bflat(self.lat.mps_sites(),
                              Bs,
                              bc=self.lat.bc_MPS,
                              permute=True,
                              form='B',
                              legL=legL)
Example #2
0
def random_MPS(L, d, chimax, func=randmat.standard_normal_complex, bc='finite', form='B'):
    site = Site(charges.LegCharge.from_trivial(d))
    chi = [chimax] * (L + 1)
    if bc == 'finite':
        for i in range(L // 2 + 1):
            chi[i] = chi[L - i] = min(chi[i], d**i)
    Bs = []
    for i in range(L):
        Bs.append(func((d, chi[i], chi[i + 1])))
    dtype = np.common_type(*Bs)
    psi = MPS.from_Bflat([site] * L, Bs, bc=bc, dtype=dtype, form=None)
    if form is not None:
        psi.canonical_form()
        psi.convert_form(form)
    return psi
Example #3
0
def circuit_mps(params, circuit, N, bc='finite'):
    site = SpinHalfSite(None)    
    tensors = [circuit.get_tensor(params)]*N    
    B_arrs = [np.swapaxes(tensor[:,:,0,:],1,2) for tensor in tensors]
    B_arrs[0] = B_arrs[0][:,0:1,:]
    B_arrs[-1] = B_arrs[-1][:,:,0:1]
    
    psi = MPS.from_Bflat([site]*N, B_arrs, bc, dtype=complex, form=None)    
    if psi.form is not None:
        try:
            psi.canonical_form()
            psi.convert_form(psi.form)
        except:
            print("psi form thing didn't work")    
    return psi
Example #4
0
def circuit_imps(params, circuit):
    site = SpinHalfSite(None)
    # evaluate circuit, get rank-4 (p_out, b_out, p_in, b_in) unitary
    unitary = circuit.get_tensor(params)
    # change the order of indices to [p, vL, vR] = [p_out, b_in, b_out] 
    # (with p_in = 0 to go from unitary to isometry)
    B = [np.swapaxes(unitary[:,:,0,:],1,2)]
    psi = MPS.from_Bflat([site], B, bc='infinite', dtype=complex, form=None)    
    if psi.form is not None:
        try:
            psi.canonical_form()
            psi.convert_form(psi.form)
        except:
            print("psi form thing didn't work")
    return psi
Example #5
0
    def network_from_cells(self, network_type, 
                           L, chi_MPO=None, 
                           params=None, bdry_vecs=None, 
                           method=None, T=None):      
        """
        Returns network of finite thermal-holographic Matrix Product State (random-holoMPS), finite 
        holo-MPS, finite holographic Matrix Product Operator (holoMPO), or MPO of a given model.
        --------------
        Inputs:
              --the input assumes the list of unitary tensors at each unit-cell or rank-4 numpy.ndarray--       
          network_type: str
             One of "random_state", "circuit_MPS", "circuit_MPO", or "MPO" options.
          L: int
             Length (number) of repetitions of unit cell in the main network chain.
          chi_MPO: int
             Bond leg dimension for MPO-based structures. 
          params: numpy.ndarray
             Optimized parameters for unitary structure and probability weights.
          bdry_vecs: list
            List of left (first element) and right (second element) boundary vectors.
            (must be set to [None,None] by default, which gives left and right boundary vectors = |0> 
            for MPO-based structures. For holoMPS-based structures, the default [None,None]
            would give left boundary = |0> while the right boundary is traced over). 
          method: str 
            One of "thermal_state_class" or "tenpy" options. (if set to "tenpy", the returned structure
            would be one of physics-TenPy networks). This option is currently only available for 
            "random_state", "circuit_MPS", and "MPO" options. 
           T: float
            Temperature (for thermal-holoMPS option).
        Note:
          -For random_state, circuit_MPS and circuit_MPO options, the original circuit with 
           parameters must be inserted as args. In this case, the returned list of bulk tensors
           includes rank-3 numpy.ndarray for random_state/circuit_MPS and rank-4 numpy.ndarray for
           circuit_MPO.
          -For holoMPS-based structures, the index ordering is: site, physical_out, bond-in, bond-out
           while for holoMPO-based structures, the index ordering is: physical-out, bond-out,
           physical-in, bond-in (with "in/out" referring to right canonical form ordering).
          -For MPO structures constructed by "thermal_state_class method", the unit cell tensor of MPO 
           network must be inserted as arg (e.g. Hamiltonian unit cell). In this case, the bulk tensors 
           would be rank-4 numpy.ndarray (consistent with final structure of MPO). For "tenpy"-method-based
           structures, the list of bulk tensors must be inserted (see TeNPy docs for more detail).     
          -Tracing over right boundary for holoMPS-based structures is appropriate for 
           holographic simulations. 
          -Set bdry_vecs to None by default for "tenpy" method. Set method to None for holoMPO-based 
           structures.
        """
        
        # for circuit-based structures:
        # both circuit and params must be included
        if network_type == 'random_state' or network_type == 'circuit_MPS' or network_type == 'circuit_MPO':
            
            l_uc = len(self) # length of unit-cell
            N = l_uc * L # number of sites
            unitary_list = L * self # list of unitaries
            
            # if network_type is set to random-holoMPS:
            if network_type == 'random_state':
                
                # defining tensor dimensions
                tensor = np.swapaxes(unitary_list[0][:,:,0,:],1,2) # change to MPS-based structure
                d = tensor[:,0,0].size # physical leg dimension (for random state)
                chi = tensor[0,:,0].size # bond leg dimension (for random state)
                
                if T == 0:
                    tensor_list1 = [np.swapaxes(unitary[:,:,0,:],1,2) for unitary in unitary_list]
                else:
                    # list of variational probability weights and random selections at each site
                    probs_list = L * thermal_state.prob_list(self,params,T)
                    random_list = [random.choice(p) for p in probs_list]
                    index_list = [probs_list[j].index(random_list[j]) for j in range(N)]
                    tensor_list1 = [np.swapaxes(unitary[:,:,j,:],1,2) for unitary,j in zip(unitary_list,
                                                                                           index_list)]      

            # if network_type is set to holoMPS:
            elif network_type == 'circuit_MPS':
                
                # defining tensor dimensions
                tensor = np.swapaxes(unitary_list[0][:,:,0,:],1,2) # change to MPS-based structure
                d = tensor[:,0,0].size # physical leg dimension (for random state)
                chi = tensor[0,:,0].size # bond leg dimension (for random state)
                # bulk tensors of holoMPS structure
                tensor_list1 = [np.swapaxes(unitary[:,:,0,:],1,2) for unitary in unitary_list]  

            # if network_type is set to circuit_MPO 
            # this option assumes original, circuit-based MPO structures (e.g. holoMPO)
            elif network_type == 'circuit_MPO':
                
                # defining tensor dimensions (consistent with rank-4 structures)
                # index ordering consistent with holographic-based MPO structures
                d = unitary_list[0][:,0,0,0].size # physical leg dimension (for MPO)
                chi = unitary_list[0][0,:,0,0].size # bond leg dimension (for MPO)
                tensor_list1 = unitary_list
            
            # testing boundary conditions 
            
            if network_type == 'random_state' or network_type == 'circuit_MPS':
                # specific to holoMPS-based structures
                
                if method == 'tenpy':
                    # based on previous circuit file
                    tensor_list1[0] = tensor_list1[0][:,0:1,:]
                    tensor_list1[-1] = tensor_list1[-1][:,:,0:1]
                    site = SpinHalfSite(None) 
                    M = MPS.from_Bflat([site]*N, tensor_list1, bc='finite', dtype=complex, form=None)
                    MPS.canonical_form_finite(M,renormalize=True,cutoff=0.0)
                
                elif method == 'thermal_state_class':
                    bdry = []
                    # if boundary vectors are not specified for holoMPS-based structures:     
                    # checking left boundary vector
                    # if left boundary vector not specified, set to (1,0,0,0...)
                    if np.array(bdry_vecs[0] == None).all():
                        bdry += [np.zeros(chi)]
                        bdry[0][0] = 1
                    else:
                        if bdry_vecs[0].size != chi:
                            raise ValueError('left boundary vector different size than bulk tensors')
                        bdry += [bdry_vecs[0]]
                
                    # checking right boundary vector (special to holoMPS-based structures)
                    if np.array(bdry_vecs[1] == None).all():
                        bdry += [None]
                    else:
                        if bdry_vecs[1].size != chi:
                            raise ValueError('right boundary vector different size than bulk tensors')
                        bdry += [bdry_vecs[1]]
                    
                    # if both boundary vectors are specified
                    for j in range(2):
                        if np.array(bdry_vecs[j] != None).all() and bdry_vecs[j].size == chi:
                            bdry.append(bdry_vecs[j]) 
                    
                    M = [[bdry[0]],tensor_list1,[bdry[1]]] # final state structure
                    
                else: 
                    raise ValueError('only one of "thermal_state_class" or "tenpy" options')
                        
            elif network_type == 'circuit_MPO': # specific to holoMPO-based structures
                bdry = []
                for j in range(2):
                    # if both boundary vectors are specified 
                    if np.array(bdry_vecs[j] != None).all() and bdry_vecs[j].size == chi:
                        bdry.append(bdry_vecs[j])
                    
                    # if boundary vectors not specified, set to (1,0,0,0...)
                    elif np.array(bdry_vecs[j] == None).all():
                        bdry += [np.zeros(chi)]
                        bdry[j][0] = 1
                    else:
                        if bdry_vecs[j].size != chi:
                            raise ValueError('boundary vectors different size than bulk tensors')
                        bdry += [bdry_vecs[j]]         
            
                M = [[bdry[0]],tensor_list1,[bdry[1]]] # final state structure
                      
        # if network_type is set to MPO: 
        # this option assumes genuine MPO_based structures (e.g. Hamiltonian MPO)  
        elif network_type == 'MPO':  
            
            if method == 'tenpy': # tenpy-based MPO
                site = SpinHalfSite(None)
                M = MPO.from_grids([site]*L, self, bc = 'finite', IdL=0, IdR=-1)  
                
            elif method == 'thermal_state_class':               
                # only bulk tensors of the main chain must be included (w/out params)
                tensor_list1 = [self]*L
                # testing boundary conditions
                bdry = []
                for j in range(2):
                    # if both boundary vectors are specified 
                    if np.array(bdry_vecs[j] != None).all() and bdry_vecs[j].size == chi_MPO:
                        bdry.append(bdry_vecs[j])
                
                    # if boundary vectors not specified, set to (1,0,0,0...)
                    elif np.array(bdry_vecs[j] == None).all():
                        bdry += [np.zeros(chi_MPO)]
                        bdry[j][0] = 1
                    else:
                        if bdry_vecs[j].size != chi_MPO:
                            raise ValueError('boundary vectors different size than bulk tensors')
                        bdry += [bdry_vecs[j]]
                        
                M = [[bdry[0]],tensor_list1,[bdry[1]]] # final state structure
            else: 
                raise ValueError('only one of "thermal_state_class" or "tenpy" options')              
        else:
            raise ValueError('only one of "random_state", "circuit_MPS", "circuit_MPO", "MPO" options')
            
        return M