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)
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
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
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
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