def ising_impo(J, g): site = SpinHalfSite(None) Id, Sp, Sm, Sz = site.Id, site.Sp, site.Sm, 2*site.Sz Sx = Sp + Sm W = [[Id,Sx,g*Sz], [None,None,-J*Sx], [None,None,Id]] H = MPO.from_grids([site], [W], bc='infinite', IdL=0, IdR=-1) return H
def __init__(self, model_param): # model parameters L = get_parameter(model_param, 'L', 2, self.__class__) xi = get_parameter(model_param, 'xi', 0.5, self.__class__) Jxx = get_parameter(model_param, 'Jxx', 1., self.__class__) Jz = get_parameter(model_param, 'Jz', 1.5, self.__class__) hz = get_parameter(model_param, 'hz', 0., self.__class__) conserve = get_parameter(model_param, 'conserve', 'Sz', self.__class__) if xi == 0.: g = 0. elif xi == np.inf: g = 1. else: g = np.exp(-1/(xi)) unused_parameters(model_param, self.__class__) # Define the sites and the lattice, which in this case is a simple uniform chain # of spin 1/2 sites site = SpinHalfSite(conserve=conserve) lat = Chain(L, site, bc_MPS='infinite') # The operators that appear in the Hamiltonian. Standard spin operators are # already defined for the spin 1/2 site, but it is also possible to add new # operators using the add_op method Sz, Sp, Sm, Id = site.Sz, site.Sp, site.Sm, site.Id # yapf:disable # The grid (list of lists) that defines the MPO. It is possible to define the # operators in the grid in the following ways: # 1) NPC arrays, defined above: grid = [[Id, Sp, Sm, Sz, -hz*Sz ], [None, g*Id, None, None, 0.5*Jxx*Sm], [None, None, g*Id, None, 0.5*Jxx*Sp], [None, None, None, g*Id, Jz*Sz ], [None, None, None, None, Id ]] # 2) In the form [("OpName", strength)], where "OpName" is the name of the # operator (e.g. "Sm" for Sm) and "strength" is a number that multiplies it. grid = [[[("Id", 1)], [("Sp",1)], [("Sm",1)], [("Sz",1)], [("Sz", -hz)] ], [None , [("Id",g)], None , None , [("Sm", 0.5*Jxx)]], [None , None , [("Id",g)], None , [("Sp", 0.5*Jxx)]], [None , None , None , [("Id",g)], [("Sz",Jz)] ], [None , None , None , None , [("Id",1)] ]] # 3) It is also possible to write a single "OpName", equivalent to # [("OpName", 1)]. grid = [["Id" , "Sp" , "Sm" , "Sz" , [("Sz", -hz)] ], [None , [("Id",g)], None , None , [("Sm", 0.5*Jxx)]], [None , None , [("Id",g)], None , [("Sp", 0.5*Jxx)]], [None , None , None , [("Id",g)], [("Sz",Jz)] ], [None , None , None , None , "Id" ]] # yapf:enable grids = [grid]*L # Generate the MPO from the grid. Note that it is not necessary to specify # the physical legs and their charges, since the from_grids method can extract # this information from the position of the operators inside the grid. H = MPO.from_grids(lat.mps_sites(), grids, bc='infinite', IdL=0, IdR=-1) MPOModel.__init__(self, lat, H)
def ising_mpo(J, g, N, bc='finite'): site = SpinHalfSite(None) Id, Sp, Sm, Sz = site.Id, site.Sp, site.Sm, 2*site.Sz Sx= Sp + Sm # confirmed from TeNPy docs, lines below directly copied from docs W_bulk = [[Id,Sx,g*Sz],[None,None,-J*Sx],[None,None,Id]] W_first = [W_bulk[0]] # first row W_last = [[row[-1]] for row in W_bulk] # last column Ws = [W_first] + [W_bulk]*(N-2) + [W_last] H = MPO.from_grids([site]*N, Ws, bc, IdL=0, IdR=-1) # (probably leave the IdL,IdR) return H
def xxz_mpo(J=1.0, Delta=1.0, hz=0.2, N=4, bc='finite'): site = SpinHalfSite(None) Id, Sp, Sm, Sz = site.Id, site.Sp, site.Sm, 2*site.Sz W_bulk = [[Id, Sp, Sm, Sz, -hz * Sz], [None, None, None, None, 0.5 * J * Sm], [None, None, None, None, 0.5 * J * Sp], [None, None, None, None, J * Delta * Sz], [None, None, None, None, Id]] W_first = [W_bulk[0]] # first row W_last = [[row[-1]] for row in W_bulk] # last column Ws = [W_first] + [W_bulk]*(N - 2) + [W_last] H = MPO.from_grids([site]*N, Ws, bc, IdL=0, IdR=-1) # (probably leave the IdL,IdR) return H
Sz, Sp, Sm, Id = site.Sz, site.Sp, site.Sm, site.Id mpo_leg = npc.LegCharge.from_qflat(chinfo, [[0], [2], [-2], [0], [0]]) W_grid = [[Id, Sp, Sm, Sz, None ], [None, None, None, None, 0.5 * Jxx * Sm], [None, None, None, None, 0.5 * Jxx * Sp], [None, None, None, None, Jz * Sz ], [None, None, None, None, Id ]] # yapf:disable W = npc.grid_outer(W_grid, [mpo_leg, mpo_leg.conj()]) W.iset_leg_labels(['wL', 'wR', 'p', 'p*']) # wL/wR = virtual left/right of the MPO Ws = [W] * L Ws[0] = W[:1, :] Ws[-1] = W[:, -1:] H = MPO(psi.sites, Ws, psi.bc, IdL=0, IdR=-1) print("3) define 'environments' left and right") # this is automatically done during initialization of MPOEnvironment env = MPOEnvironment(psi, H, psi) envL = env.get_LP(0) envR = env.get_RP(L - 1) print("4) contract MPS and MPO to calculate the energy <psi|H|psi>") E = env.full_contraction(L - 1) print("E =", E) print("5) calculate two-site hamiltonian ``H2`` from the MPO") # label left, right physical legs with p, q
# [ 0. -0.5]] N = 6 # number of sites sites = [spin] * N # repeat entry of list N times pstate = ["up", "down"] * (N // 2) # Neel state psi = MPS.from_product_state(sites, pstate, bc="finite") print("<Sz> =", psi.expectation_value("Sz")) # <Sz> = [ 0.5 -0.5 0.5 -0.5] print("<Sp_i Sm_j> =", psi.correlation_function("Sp", "Sm"), sep="\n") # <Sp_i Sm_j> = # [[1. 0. 0. 0. 0. 0.] # [0. 0. 0. 0. 0. 0.] # [0. 0. 1. 0. 0. 0.] # [0. 0. 0. 0. 0. 0.] # [0. 0. 0. 0. 1. 0.] # [0. 0. 0. 0. 0. 0.]] # define an MPO Id, Sp, Sm, Sz = spin.Id, spin.Sp, spin.Sm, spin.Sz J, Delta, hz = 1., 1., 0.2 W_bulk = [[Id, Sp, Sm, Sz, -hz * Sz], [None, None, None, None, 0.5 * J * Sm], [None, None, None, None, 0.5 * J * Sp], [None, None, None, None, J * Delta * Sz], [None, None, None, None, Id]] W_first = [W_bulk[0]] # first row W_last = [[row[-1]] for row in W_bulk] # last column Ws = [W_first] + [W_bulk] * (N - 2) + [W_last] H = MPO.from_grids([spin] * N, Ws, bc='finite', IdL=0, IdR=-1) print("<psi|H|psi> =", H.expectation_value(psi)) # <psi|H|psi> = -1.25
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