# construct random J matrix Jij = tfim.Jij_instance(N, J, "bimodal", Jij_seed) # In[6]: # List out all the spin_states, corresponding indices and energies Energies = -tfim.JZZ_SK_ME(basis, Jij) for index in range(2**N): print(index, basis.state(index), Energies[index]) # In[7]: # Build 2nd order approximated matrix GS_energy, GS_indices = tfim_perturbation.GS(Energies) H_app_0 = tfim_perturbation.H_app_0(GS_energy, GS_indices) H_app_1 = tfim_perturbation.H_app_1(basis, GS_indices, N) H_app_2 = tfim_perturbation.H_app_2(basis, Jij, GS_indices, N, GS_energy) print(H_app_2) # In[8]: # Build exact matrix V_exc = tfim_perturbation.V_exact(basis, lattice) H_0_exc = tfim_perturbation.H_0_exact(Energies)
def tfim_analysis(L, Jij_seed, perturbation_order, h_x_range=np.arange(0, 0.005, 0.0001), PBC=True, J=1): #Initialize the output dictionary containing all the information that we want to know about a specific instance # - isEmpty # - isWorking # both of which contains logical True or False values #Initial set up info = {} # Configure the number of spins to the correct format for analysis L = [L] # Build lattice and basis ################################### lattice = tfim.Lattice(L, PBC) N = lattice.N basis = tfim.IsingBasis(lattice) ################################### # construct random J matrix Jij = tfim.Jij_instance(N, J, "bimodal", Jij_seed) # List out all the spin_states, corresponding indices and energies Energies = -tfim.JZZ_SK_ME(basis, Jij) # for index in range(2 ** N): # print(index, basis.state(index), Energies[index]) GS_energy, GS_indices = tfim_perturbation.GS(Energies) # Specify perturbation order if perturbation_order == 3: analysis_func = tfim_perturbation.app_3_eigensystem_general_matrices H_app_3 = tfim_perturbation.H_app_3(basis, Jij, GS_indices, N, GS_energy) isEmpty = np.allclose(H_app_3, np.zeros((len(GS_indices), len(GS_indices)))) elif perturbation_order == 4: analysis_func = tfim_perturbation.app_4_eigensystem_general_matrices isEmpty = False # Check to see if the max order perturbative term is empty and store this information in "info" info['isEmpty'] = isEmpty # Calculate approximated eigenvalues and eigenstates for range(h_x) app_eigenvalues, app_eigenstates = analysis_func(GS_indices, GS_energy, h_x_range, J, N, basis, Jij) # Calculate exact eigenvalues and eigenstates for range(h_x) exc_eigenvalues, exc_eigenstates = tfim_perturbation.exc_eigensystem( basis, h_x_range, lattice, Energies) # Extract exact ground states exc_GS_eigenstates = np.zeros( (len(h_x_range), len(GS_indices), len(GS_indices))) for i in range(len(h_x_range)): for m, j in enumerate(GS_indices): for n, k in enumerate(GS_indices): exc_GS_eigenstates[i, m, n] = exc_eigenstates[i, j, n] # Extract exact ground energy reordered_app_eigenstates = np.zeros( [len(h_x_range), len(GS_indices), len(GS_indices)]) epsilon = 1 * 10**(-6) for h_x_index in range(len(h_x_range)): if h_x_index < 2: reordered_app_eigenstates[h_x_index] = app_eigenstates[h_x_index] else: for k in range(len(GS_indices) // 2): fidelity_array = [] for v1 in [ reordered_app_eigenstates[h_x_index - 1, :, 2 * k], reordered_app_eigenstates[h_x_index - 1, :, 2 * k + 1] ]: for v2 in [ app_eigenstates[h_x_index, :, 2 * k], app_eigenstates[h_x_index, :, 2 * k + 1] ]: fidelity_array = np.append( fidelity_array, tfim_perturbation.fidelity(v1, v2)) if abs(fidelity_array[0] - max(fidelity_array)) < epsilon: reordered_app_eigenstates[ h_x_index, :, 2 * k] = app_eigenstates[h_x_index, :, 2 * k] reordered_app_eigenstates[h_x_index, :, 2 * k + 1] = app_eigenstates[ h_x_index, :, 2 * k + 1] else: reordered_app_eigenstates[ h_x_index, :, 2 * k] = app_eigenstates[h_x_index, :, 2 * k + 1] reordered_app_eigenstates[h_x_index, :, 2 * k + 1] = app_eigenstates[ h_x_index, :, 2 * k] reordered_exc_GS_eigenstates = np.zeros( [len(h_x_range), len(GS_indices), len(GS_indices)]) epsilon = 1 * 10**(-12) for h_x_index in range(len(h_x_range)): if h_x_index < 2: reordered_exc_GS_eigenstates[h_x_index] = exc_GS_eigenstates[ h_x_index] else: for k in range(len(GS_indices) // 2): fidelity_array = [] for v1 in [ reordered_exc_GS_eigenstates[h_x_index - 1, :, 2 * k], reordered_exc_GS_eigenstates[h_x_index - 1, :, 2 * k + 1] ]: for v2 in [ exc_GS_eigenstates[h_x_index, :, 2 * k], exc_GS_eigenstates[h_x_index, :, 2 * k + 1] ]: fidelity_array = np.append( fidelity_array, tfim_perturbation.fidelity(v1, v2)) if abs(fidelity_array[0] - max(fidelity_array)) < epsilon: reordered_exc_GS_eigenstates[ h_x_index, :, 2 * k] = exc_GS_eigenstates[h_x_index, :, 2 * k] reordered_exc_GS_eigenstates[ h_x_index, :, 2 * k + 1] = exc_GS_eigenstates[h_x_index, :, 2 * k + 1] else: reordered_exc_GS_eigenstates[ h_x_index, :, 2 * k] = exc_GS_eigenstates[h_x_index, :, 2 * k + 1] reordered_exc_GS_eigenstates[ h_x_index, :, 2 * k + 1] = exc_GS_eigenstates[h_x_index, :, 2 * k] # Calculate and plot energy errors corrected_exc_eigenvalues = np.zeros((len(GS_indices), len(h_x_range))) for i in range(len(GS_indices)): for j in range(len(h_x_range)): corrected_exc_eigenvalues[i, j] = exc_eigenvalues[i, j] error_array = np.absolute(corrected_exc_eigenvalues - app_eigenvalues) # Curve fit coeff_matrix = np.zeros((len(GS_indices), 2)) for i in range(len(GS_indices)): pars, cov = curve_fit(f=power_law, xdata=h_x_range, ydata=error_array[i]) coeff_matrix[i] = pars # Check to see if perturbation is working and store it in the info dictionary if info['isEmpty'] == False: judgment, error_classical_GS_index = isWorking(coeff_matrix, perturbation_order) info['isWorking'] = bool(judgment) info['error state index'] = error_classical_GS_index info['error order'] = coeff_matrix[error_classical_GS_index, 1] else: info['isWorking'] = None info['error order'] = None info['error state index'] = None # return info dictionary return info, coeff_matrix[:, 1]
def lanczos(L, seed, h_x_range, PBC, h_z, maxiter): # In[3]: start_time = time.time() def Jij_2D_NN(seed, N, PBC, xwidth, yheight, lattice, p): def bond_list_unequal(seed, N, PBC, xwidth, yheight, p): # p is the probability distribution of ferromagnetic bonds np.random.seed(seed) if PBC == True: num_of_bonds = 2 * N else: num_of_bonds = (xwidth - 1) * (yheight) + (xwidth) * (yheight - 1) i = [np.random.random() for _ in range(num_of_bonds)] # print(i) a = np.zeros(len(i)) for index, prob_seed in enumerate(i): if prob_seed <= p: a[index] += 1 else: a[index] -= 1 return a def make_Jij(N, b_list, lattice): #Goes through the list of bonds to make the jij matrix that tells you how all of the spins are bonded to each other bond_index = 0 Jij = np.zeros((N, N)) for i in range(0, N): NNs = lattice.NN(i) for j in NNs: if Jij[i][j] == 0: Jij[i][j] = b_list[bond_index] Jij[j][i] = b_list[bond_index] bond_index += 1 return Jij b_list = bond_list_unequal(seed, N, PBC, xwidth, yheight, p) return make_Jij(N, b_list, lattice) # In[4]: # Build lattice and basis lattice = tfim.Lattice(L, PBC) N = lattice.N basis = tfim.IsingBasis(lattice) # In[5]: #construct random J matrix Jij = Jij_2D_NN(seed, N, PBC, L[0], L[1], lattice, p=0.5) # In[6]: # List out all the spin_states, corresponding indices and energies Ising_energy_arr = np.zeros(2**N) for index in range(2**N): state = basis.state(index) # modify state from 0 and 1 base to -1, 1 base for i in range(N): if state[i] == 0: state[i] -= 1 Ising_energy = 0 for i in range(N): for j in range(i + 1, N, 1): bond_energy = Jij[i, j] * state[i] * state[j] Ising_energy += bond_energy Ising_energy_arr[index] = Ising_energy print(index, basis.state(index), Ising_energy) print("----%s seconds ----" % (time.time() - start_time)) # In[7]: GS_energy, GS_indices = tfim_perturbation.GS(Ising_energy_arr) # initialize Lanczos vector v0 = np.zeros(2**N) for i in GS_indices: v0[i] = 1 # In[8]: # modified exact Hamiltonians using compressed sparse row matrices def V_exact_csr(basis, lattice): row = [] col = [] for ket in range(basis.M): state = basis.state(ket) for i in range(lattice.N): basis.flip(state, i) bra = basis.index(state) row.append(bra) col.append(ket) basis.flip(state, i) data = np.ones(len(col)) V_exact = sparse.csr_matrix((data, (np.array(row), np.array(col))), shape=(2**N, 2**N)) return V_exact def H_0_exact_csr(Energies): return sparse.diags(Energies) # In[9]: # modified function to eigendecompose the exact Hamiltonian using Lanczos method def exc_eigensystem(basis, h_x_range, lattice, Energies): # Calculate exact eigenvalues and eigenstates for range(h_x) exc_eigenvalues = np.zeros(len(h_x_range)) first_excited_exc_energies = np.zeros(len(h_x_range)) exc_eigenstates = np.zeros((len(h_x_range), basis.M)) V_exc_csr = V_exact_csr(basis, lattice) H_0_exc_csr = H_0_exact_csr(Energies) for j, h_x in enumerate(h_x_range): H = H_0_exc_csr - V_exc_csr.multiply(h_x) exc_eigenvalue, exc_eigenstate = spla.eigsh( H, k=2, which='SA', v0=v0, maxiter=maxiter, tol=1e-5, return_eigenvectors=True) print("----%s seconds for h_x = %s----" % (time.time() - start_time, h_x)) exc_eigenvalues[j] = exc_eigenvalue[0] first_excited_exc_energies[j] = exc_eigenvalue[1] for k in range(basis.M): exc_eigenstates[j][k] = exc_eigenstate[k, 0] return V_exc_csr, H_0_exc_csr, exc_eigenvalues, first_excited_exc_energies, exc_eigenstates # In[10]: # Calculate exact eigenvalues and eigenstates for range(h_x) V_exc, H_0_exc, exc_eigenvalues, first_excited__exc_energies, exc_eigenstates = exc_eigensystem( basis, h_x_range, lattice, Ising_energy_arr) print("----%s seconds ----" % (time.time() - start_time)) # In[13]: final = h_x_range[-1] init = h_x_range[1] num_steps = len(h_x_range) # first and second derivative of ground state energy per site first_derivative_exc_eigenvalues = np.gradient( exc_eigenvalues, (final - init) / float(num_steps)) second_derivative_exc_eigenvalues = np.gradient( first_derivative_exc_eigenvalues, (final - init) / float(num_steps)) # compute susciptibility # In[16]: chi_aa_matrix = np.zeros((len(h_x_range), lattice.N)) for i, h_x in enumerate(h_x_range): for a in range(lattice.N): sigma_z = np.zeros(basis.M) for ket in range(basis.M): state = basis.state(ket) if state[a] == 1: sigma_z[ket] += 1 else: sigma_z[ket] -= 1 longitudinal_energy = spla.eigsh(H_0_exc - V_exc.multiply(h_x) - h_z * sparse.diags(sigma_z), k=1, which='SA', v0=v0, tol=1e-5, maxiter=maxiter, return_eigenvectors=False)[0] print("----%s seconds for h_x = %s----" % (time.time() - start_time, h_x)) chi_aa = 2. * abs( abs(exc_eigenvalues[i]) - abs(longitudinal_energy)) / (h_z**2) chi_aa_matrix[i, a] += chi_aa # In[18]: chi_ab_matrix = np.zeros((len(h_x_range), basis.N, basis.N)) for i, h_x in enumerate(h_x_range): for a in range(lattice.N): sigma_z_a = np.zeros(basis.M) for ket in range(basis.M): state = basis.state(ket) if state[a] == 1: sigma_z_a[ket] += 1 else: sigma_z_a[ket] -= 1 for b in range(a, lattice.N, 1): sigma_z_b = np.zeros(basis.M) for ket in range(basis.M): state = basis.state(ket) if state[b] == 1: sigma_z_b[ket] += 1 else: sigma_z_b[ket] -= 1 H = H_0_exc - V_exc.multiply(h_x) - ( sparse.diags(sigma_z_a) + sparse.diags(sigma_z_b)).multiply(h_z) longitudinal_energy = spla.eigsh(H, k=1, which='SA', v0=v0, tol=1e-5, maxiter=maxiter, return_eigenvectors=False)[0] print("----%s seconds for h_x = %s----" % (time.time() - start_time, h_x)) chi_ab = abs( abs(exc_eigenvalues[i]) - abs(longitudinal_energy)) / ( h_z** 2.) - 0.5 * (chi_aa_matrix[i, a] + chi_aa_matrix[i, b]) chi_ab_matrix[i, a, b] += chi_ab chi_ab_matrix[i, b, a] += chi_ab # adding the diagonal elements for c in range(N): chi_ab_matrix[i, c, c] = chi_aa_matrix[i, c] chi_arr = np.zeros(len(h_x_range)) for i, h_x in enumerate(h_x_range): chi_arr[i] += np.sum(chi_ab_matrix[i]) print("----%s seconds ----" % (time.time() - start_time)) # compute structure factor S_SG_arr = np.zeros(np.shape(h_x_range)) for i, h_x in enumerate(h_x_range): psi0 = exc_eigenstates[i] for a in range(N): for b in range(N): sigma_z_a = np.zeros(basis.M) sigma_z_b = np.zeros(basis.M) for ket in range(basis.M): state = basis.state(ket) if state[a] == 1: sigma_z_a[ket] += 1 else: sigma_z_a[ket] -= 1 for ket in range(basis.M): state = basis.state(ket) if state[b] == 1: sigma_z_b[ket] += 1 else: sigma_z_b[ket] -= 1 S_ab = psi0 @ sparse.diags(sigma_z_a) @ sparse.diags( sigma_z_b) @ psi0.T S_SG_arr[i] += S_ab**2. print("----%s seconds ----" % (time.time() - start_time)) return N, h_x_range, exc_eigenvalues, first_excited__exc_energies, second_derivative_exc_eigenvalues, chi_arr, S_SG_arr
def main(): # Parse command line arguments ################################### parser = argparse.ArgumentParser(description=( "Build approximate matrices using first and second order perturbation theory and return its eigenvalues and eigenstates" )) parser.add_argument('lattice_specifier', help=("Linear dimensions of the system")) parser.add_argument('-PBC', type=bool, default=True, help="Specifying PBC") parser.add_argument('-J', type=float, default=1.0, help='Nearest neighbor Ising coupling') parser.add_argument( '-seed', type=int, default=5, help="Specifying the seed for generating random Jij matrices") parser.add_argument('--h_min', type=float, default=0.0, help='Minimum value of the transverse field') parser.add_argument('--h_max', type=float, default=0.1, help='Maximum value of the transverse field') parser.add_argument('--dh', type=float, default=0.01, help='Tranverse fied step size') parser.add_argument('-o', default='output', help='output filename base') parser.add_argument('-d', default='Output_file', help='output directory base') args = parser.parse_args() ################################### # Parameter specification out_filename_base = args.o # Transverse field h_x_range = np.arange(args.h_min, args.h_max + args.dh / 2, args.dh) L = [int(args.lattice_specifier)] PBC = args.PBC J = args.J seed = args.seed # Build lattice and basis lattice = tfim.Lattice(L, PBC) N = lattice.N basis = tfim.IsingBasis(lattice) # Construct random J matrix Jij = tfim.Jij_instance(N, J, "bimodal", seed) ################################### Energies = -tfim.JZZ_SK_ME(basis, Jij) GS_energy, GS_indices = tfim_perturbation.GS(Energies) ################################### H_0 = tfim_perturbation.H_0(GS_energy, GS_indices) H_app_1 = tfim_perturbation.H_app_1(basis, GS_indices, N) H_app_2 = tfim_perturbation.H_app_2(basis, Jij, GS_indices, N, GS_energy) ################################### # Diagonalization loop over h_x_range on H_app app_eigenvalues = np.zeros((len(GS_indices), len(h_x_range))) app_eigenstates = np.zeros( (len(GS_indices), len(GS_indices), len(h_x_range))) for j, h_x in enumerate(h_x_range): H_app = tfim_perturbation.H_app(h_x, H_0, H_app_1, H_app_2, J) app_eigenvalue, app_eigenstate = np.linalg.eigh(H_app) for i in range(len(GS_indices)): app_eigenvalues[i][j] = app_eigenvalue[i] for k in range(len(GS_indices)): app_eigenstates[i][k][j] = app_eigenstate[i][k] ################################### # Make output directory Output = args.d os.mkdir(Output) os.chdir(Output) ################################### # Output Eigenvalue file out_filename_E = out_filename_base + '.dat' # Quantities to write Eigenvalue ouput file phys_keys_E = [] for i in range(len(app_eigenvalues)): eigenvalue_num = 'Eigenvalue ' + str(i + 1) phys_keys_E.append(eigenvalue_num) phys_keys_E.insert(0, 'h_x') phys_E = {} # Dictionary for values # Setup output Eigenvalue data files parameter_string = ("L = {}, PBC = {}, J = {}".format(L, PBC, J)) width = 25 precision = 16 header_list = phys_keys_E header = ''.join( ['{:>{width}}'.format(head, width=width) for head in header_list]) out_eigenvalue_file = open(out_filename_E, 'w') print("\tData will write to {}".format(out_filename_E)) out_eigenvalue_file.write('#\ttfim_diag parameters:\t' + parameter_string + '\n' + '#' + header[1:] + '\n') # Put eigenvalues in phys_E dictionary for i, h_x in enumerate(h_x_range): phys_E['h_x'] = h_x for j, key in enumerate(phys_keys_E[1:]): phys_E[key] = app_eigenvalues[j, i] # Write eigenvalues to output files data_list = [phys_E[key] for key in phys_keys_E] data_line = ''.join([ '{:{width}.{prec}e}'.format(data, width=width, prec=precision) for data in data_list ]) out_eigenvalue_file.write(data_line + '\n') # Close files out_eigenvalue_file.close() ################################### # Output Eigenstate files for file_num in range(len(app_eigenstates)): out_filename_V = out_filename_base + '_' + str(file_num) + '.dat' # Quantities to write Eigenstate output file phys_keys_V = [] for i in range(len(app_eigenstates)): basis = 'Basis ' + str(i + 1) phys_keys_V.append(basis) phys_keys_V.insert(0, 'h_x') phys_V = {} # Dictionary for values # Setup output Eigenstate data files parameter_string = ("L = {}, PBC = {}, J = {}".format(L, PBC, J)) width = 25 precision = 16 header_list = phys_keys_V header = ''.join( ['{:>{width}}'.format(head, width=width) for head in header_list]) out_eigenstate_file = open(out_filename_V, 'w') print("\tData will write to {}".format(out_filename_V)) out_eigenstate_file.write('#\ttfim_diag parameters:\t' + parameter_string + '\n' + '#' + header[1:] + '\n') # Put eigenstates in phys_V dictionary for i, h_x in enumerate(h_x_range): phys_V['h_x'] = h_x for j, key in enumerate(phys_keys_V[1:]): phys_V[key] = app_eigenstates[file_num][j, i] # Write eigenvalues to output files data_list = [phys_V[key] for key in phys_keys_V] data_line = ''.join([ '{:{width}.{prec}e}'.format(data, width=width, prec=precision) for data in data_list ]) out_eigenstate_file.write(data_line + '\n') # Close files out_eigenstate_file.close() ####################################################### # Exit "Output" directory os.chdir("../")