def get_T(pars): T = tensordispenser.get_normalized_tensor(pars) parts = () if pars["do_coarse_momenta"]: # TODO Should this bond dimension be an independent parameter? chis = [chi for chi in pars["chis_trg"]] T_orig = T if pars["print_errors"]: print("Building the coarse-grained transfer matrix times " "translation.") T_dg = T.conjugate().transpose((0, 3, 2, 1)) y_env = scon( (T, T, T_dg, T_dg), ([1, -1, 5, 2], [5, -2, 3, 4], [1, 2, 6, -3], [6, 4, 3, -4])) U = y_env.eig((0, 1), (2, 3), hermitian=True, chis=chis)[1] y = U.conjugate().transpose((2, 0, 1)) y_dg = y.conjugate().transpose((1, 2, 0)) SW, NE = T.split((0, 3), (1, 2), chis=chis) SW = SW.transpose((0, 2, 1)) NE = NE.transpose((1, 2, 0)) T = scon( (NE, y, T, SW, y_dg), ([1, 3, -1], [-2, 1, 4], [3, 4, 6, 5], [6, -3, 2], [5, 2, -4])) parts = (y, y_dg, NE, SW, T_orig) # TODO quantify the error in this coarse-graining and print it. return T, parts
def optimize_isometries(u, v, w, A_list, A_part_list, pars, gauges, chi=None): # Compute environments M_v and M_w if chi is None: chi_v = type(u).flatten_dim(v.shape[1]) chi_w = type(u).flatten_dim(w.shape[0]) else: chi_v = chi chi_w = chi if pars["print_errors"] > 2: print('Optimizing isometries.') for i in range(pars["opt_iters_tens"]): upb = upper_block(u, v, w, A_list, A_part_list, pars) if pars["use_parts"]: # TODO Could figure out a way to do this in O(chi^6). I # wasn't able to come up with one and probably this won't be # used much, so I left it be. warnings.warn("In TNR, use_parts is True but is not utilized in " "optimize_disentanglers. Reverting back to the " "O(chi^7) algorithm.") # This is O(chi^7). env_top = scon( (v, u, A_list[0], A_list[1]), ([1, -3, 3], [-2, 1, 4, 2], [-1, 4, 5, -4], [5, 2, 3, -5])) env_top_dg = env_top.conjugate().transpose((0, 3, 4, 2, 1)) M_w = scon( (env_top, upb_dg(upb), upb, env_top_dg), ([-1, -2, 7, 3, 4], [3, 4, 2, 1], [1, 2, 5, 6], [-3, 5, 6, 7, -4])) # Get the optimal isometry by SVDing the environment. S, U = M_w.eig((0, 1), (2, 3), chis=chi_w, hermitian=True, print_errors=pars["print_errors"] - 2) w = U.conjugate().transpose((2, 1, 0)) if pars["horz_refl"]: v = w_hat(w, gauges) else: if pars["use_parts"]: # TODO see above. warnings.warn("In TNR, use_parts is True but is not utilized " "in optimize_disentanglers. Reverting back to " "the O(chi^7) algorithm.") # This is O(chi^7). env_top = scon( (w, u, A_list[0], A_list[1]), ([-1, 1, 2], [1, -2, 3, 4], [2, 3, 5, -4], [5, 4, -3, -5])) env_top_dg = env_top.conjugate().transpose((3, 4, 2, 1, 0)) M_v = scon((env_top, upb_dg(upb), upb, env_top_dg), ([5, -1, -2, 3, 4 ], [3, 4, 2, 1], [1, 2, 6, 7], [6, 7, -4, -3, 5])) S, U = M_v.eig((0, 1), (2, 3), chis=chi_v, hermitian=True, print_errors=pars["print_errors"] - 2) v = U.conjugate().transpose((0, 2, 1)) v = v.flip_dir(1) return v, w
def fix_A_new_gauge_optimize_X(A_new, A, X, Y): X_dg = matrix_dagger(X) Y_dg = matrix_dagger(Y) env = scon((A.conjugate(), Y, A_new, Y_dg, X_dg), ([6, -2, 4, 5], [6, 2], [2, -1, 1, 3], [1, 4], [3, 5])) env_U, env_S, env_V = env.svd((0, ), (1, )) env_U_dg = matrix_dagger(env_U) env_V_dg = matrix_dagger(env_V) X = scon((env_V_dg, env_U_dg), ([-1, 1], [1, -2])) return X
def A4_frob_norm_sq(A_list, A_part_list, pars): # This is O(chi^6). Thus no need to utilize A_part_list even if # pars["use_parts"] is True. NW_corner = scon((A_list[0], A_list[0].conjugate()), ([1, 2, -1, -2], [1, 2, -3, -4])) NE_corner = scon((A_list[1], A_list[1].conjugate()), ([-1, 1, 2, -2], [-3, 1, 2, -4])) N_row = scon((NW_corner, NE_corner), ([1, -1, 2, -3], [1, -2, 2, -4])) norm_sq = N_row.norm_sq() return norm_sq
def get_T_first(T, pars, alpha=0): if pars["KW"]: T_first = initialtensors.get_KW_tensor(pars) elif pars["do_momenta"]: defect_horz = get_defect(alpha, T, 0) defect_vert = get_defect(alpha, T, 1).conjugate().transpose() T_first = scon((T, defect_horz, defect_vert), ([1, -2, -3, 4], [-1, 1], [4, -4])) else: defect_horz = get_defect(alpha, T, 0) T_first = scon((T, defect_horz), ([1, -2, -3, -4], [-1, 1])) return T_first
def fix_A_new_gauge_update_to_gauge(A_new, BUS, BSV, z, X, Y, G_hh=None): X_dg = matrix_dagger(X) Y_dg = matrix_dagger(Y) A_new = scon((A_new, Y, X, Y_dg, X_dg), ([1, 2, 3, 4], [-1, 1], [-2, 2], [3, -3], [4, -4])) BUS = scon((BUS, Y_dg), ([-1, 2, -3], [2, -2])) BSV = scon((Y, BSV), ([-1, 1], [1, -2, -3])) z = scon((X, z), ([-1, 1], [1, -2, -3])) return_value = (A_new, BUS, BSV, z) if G_hh is not None: G_hh = scon((Y, G_hh, Y_dg), ([-1, 1], [1, 2], [2, -2])) return_value += (G_hh, ) return return_value
def transfer_op(v, charge=0): v = np.reshape(v, (flatdim1, flatdim2) + (flatdimT,)*(block_width-2)) v = caster(v, charge=charge) if pars["do_momenta"] and not pars["do_coarse_momenta"]: v = np.transpose(v, translation) v = scon((U, v), ([-1,-2,-3,1,2,3], [1,2,3] + [-i for i in range(4, block_width+1)])) print(".", end='', flush=True) scon_list = [v] + scon_list_end Av = scon(scon_list, index_list) Av = Av.to_ndarray() Av = np.reshape(Av, (matrix_flatdim,)) return Av
def upper_block(u, v, w, A_list, A_part_list, pars): """ upper_block is the upper half of B. Often also called upb in the code. """ if pars["use_parts"]: # This is O(chi^8), O(chi^6) if A can be split with just chi. upb = scon((u, v, w, A_part_list[0][0], A_part_list[0][1], A_part_list[1][0], A_part_list[1][1]), ([3, 5, 4, 6], [5, -2, 2], [-1, 3, 1], [1, 4, 7], [7, 9, -3], [6, 2, 8], [9, 8, -4])) else: # This is O(chi^7). upb = scon((w, v, u, A_list[0], A_list[1]), ([-1, 6, 2], [4, -2, 1], [6, 4, 5, 3], [2, 5, 7, -3 ], [7, 3, 1, -4])) return upb
def build_A_new(v, w, z, BUS, BSV): # This is O(chi^6). A_new = scon( (z, v_dg(v), w_dg(w), BSV, BUS, v_prime(v), w_prime(w), z_dg(z)), ([-2, 3, 4], [3, 1, 7], [1, 4, 10], [-1, 7, 9], [10, -3, 8], [9, 2, 5], [2, 8, 6], [5, 6, -4])) return A_new
def split_B(B, pars): if pars["print_errors"] > 0: print('-Splitting B.') if pars["horz_refl"]: S, U = B.eig((0, 3), (1, 2), chis=pars["chis_trg"], hermitian=True, eps=pars["opt_eps_chi"], print_errors=pars["print_errors"]) # G_hh is a diagonal matrix populated with signs of the # eigenvalues of B. G_hh = S.sign() G_hh = G_hh.diag() S_sqrt = S.abs().sqrt() U = U.transpose((0, 2, 1)) V = scon((U.conjugate(), G_hh), ([-2, 1, -3], [-1, 1])) else: U, S, V = B.svd((0, 3), (1, 2), chis=pars["chis_trg"], eps=pars["opt_eps_chi"], print_errors=pars["print_errors"]) S_sqrt = S.sqrt() U = U.transpose((0, 2, 1)) G_hh = None US = U.multiply_diag(S_sqrt, 1, direction="r") SV = V.multiply_diag(S_sqrt, 0, direction="l") return_value = (US, SV) if pars["return_gauges"]: return_value += (G_hh, ) if pars["print_errors"] > 0: print('Truncated bond dimension in split_B: %i' % len(S)) return return_value
def get_T(pars): # We always get the invariant tensor here, and cast it to the # non-invariant if needed. This gets around the silly fact that the # basis of the original tensor depends on symmetry_tensors, # something that should be fixed. T = tensordispenser.get_tensor(pars, iter_count=0, symmetry_tensors=True)[0] if not pars["symmetry_tensors"]: T = Tensor.from_ndarray(T.to_ndarray()) log_fact = 0 Fs = [] Ns = [] cum = T for i in range(1, pars["n_normalization"]): cum = toolbox.contract2x2(cum) log_fact *= 4 m = cum.abs().max() if m != 0: cum /= m log_fact += np.log(m) N = 4**i F = np.log(scon(cum, [1, 2, 1, 2]).value()) + log_fact Fs.append(F) Ns.append(N) A, B = np.polyfit(Ns[pars["n_discard"]:], Fs[pars["n_discard"]:], 1) T /= np.exp(A) return T
def compute_TNR_error(u, v, w, A_list, A_part_list, pars, upb=None, B_norm=None, orig_norm=None): if orig_norm is None: orig_norm_sq = A4_frob_norm_sq(A_list, A_part_list, pars) orig_norm = np.sqrt(orig_norm_sq) else: orig_norm_sq = orig_norm**2 if B_norm is None: if upb is None: upb = upper_block(u, v, w, A_list, A_part_list, pars) B = scon((upb, upb_dg(upb)), ([-1, -2, 1, 2], [1, 2, -3, -4])) B_norm_sq = B.norm_sq() else: B_norm_sq = B_norm**2 # The norm |TNR block - original block| can be reduced to this form diff_norm = np.sqrt(orig_norm_sq - B_norm_sq) err = diff_norm / orig_norm return err
def generate_normalized_tensors(pars): # Number of tensors to use to fix the normalization n = max(8, pars["iter_count"] + 4) # Number of tensors from the beginning to discard n_discard = max(min(pars["iter_count"]-3, 3), 0) tensors_and_log_facts = [] for i in reversed(list(range(n+1))): tensors_and_log_facts.append(get_tensors(pars=pars, iter_count=i)) tensors_and_log_facts = tuple(reversed(tensors_and_log_facts)) A_lists = tuple(map(lambda t: t[0], tensors_and_log_facts)) log_fact_lists = np.array(tuple(map(lambda t: t[1], tensors_and_log_facts))) Us = np.array(tuple(map(lambda t: t[2], tensors_and_log_facts))) Zs = np.array(tuple(scon(A_list, ([1,2,3,2], [3,4,1,4])).norm() for A_list in A_lists)) log_Zs = np.log(Zs) log_Zs = np.array(tuple(log_Z + log_fact_list[0] + log_fact_list[1] for log_Z, log_fact_list in zip(log_Zs, log_fact_lists))) Ns = np.array([4*4**i-2**i for i in range(n+1)]) A, B = np.polyfit(Ns[pars["n_discard"]:], log_Zs[pars["n_discard"]:], 1) if pars["print_errors"]: print("Fit when normalizing Ts: %.3e * N + %.3e"%(A,B)) A_lists = [[A_list[0]/np.exp(N*A/2 - log_fact_list[0]), A_list[1]/np.exp(N*A/2 - log_fact_list[1])] for A_list, N, log_fact_list in zip(A_lists, Ns, log_fact_lists)] id_pars = get_tensor_id_pars(pars) for i, (A_list, U) in enumerate(zip(A_lists, Us)): write_tensor_file(data=(A_list, U), prefix="KW_tensors_normalized", pars=id_pars, iter_count=i, filename=filename) A_list = A_lists[pars["iter_count"]] U = Us[pars["iter_count"]] return A_list, U
def ascend_U(U, u, u1, z, z1, z2, pars): # This is O(chi^10). if pars["print_errors"]: print("Ascending U.") U1 = U.flip_dir(1) U2 = U.flip_dir(2) U2 = U2.flip_dir(3) U2 = U2.flip_dir(5) u_dg = u.conjugate().transpose((2,3,0,1)) u1_dg = u1.conjugate().transpose((2,3,0,1)) z_dg = z.conjugate().transpose((1,2,0)) z1_dg = z1.conjugate().transpose((1,2,0)) z2_dg = z2.conjugate().transpose((1,2,0)) asc_U = scon((z1, z2, z, u1, u, U1, U2, u_dg, u1_dg, z_dg, z1_dg, z2_dg), ([-1,15,16], [-2,5,6], [-3,19,18], [16,5,1,2], [6,19,7,14], [1,2,7,11,12,13], [12,13,14,9,3,4], [11,9,17,10], [3,4,8,20], [15,17,-4], [10,8,-5], [20,18,-6])) return asc_U
def get_T_last(T, pars, alpha=0, parts=None): if pars["do_coarse_momenta"]: if pars["print_errors"] > 1: print("Optimizing y_last, alpha =", alpha) y, y_dg, NE, SW, T_orig = parts defect_horz = get_defect(alpha, T_orig, 0) defect_vert = get_defect(alpha, T_orig, 1).conjugate().transpose() cost = np.inf cost_change = np.inf counter = 0 y_last = y y_last_dg = y_dg # TODO Should this bond dimension be an independent parameter? chis = [chi for chi in pars["chis_trg"]] while cost_change > 1e-11 and counter < 10000: # The optimization step # This is O(chi^6). Could use a pre-environment too. env_part1 = scon( (NE, defect_horz, T_orig, SW, defect_vert, y_last_dg), ([-2, 1, -1], [1, 6], [6, -3, 5, 2], [5, -4, 3 ], [2, 4], [4, 3, -5])) env = scon((env_part1, env_part1.conjugate()), ([1, -1, -2, 2, 3], [1, -3, -4, 2, 3])) U = env.eig((0, 1), (2, 3), hermitian=True, chis=chis)[1] y_last_dg = U y_last = y_last_dg.conjugate().transpose((2, 0, 1)) old_cost = cost cost = scon((env, y_last, y_last_dg), ([1, 2, 3, 4], [5, 1, 2], [3, 4, 5])).value() if np.imag(cost) > 1e-13: warnings.warn("optimize y_last cost is complex: " + str(cost)) else: cost = np.real(cost) cost_change = np.abs((old_cost - cost) / cost) counter += 1 T_last = scon( (y_last, NE, defect_horz, T_orig, SW, defect_vert, y_last_dg), ([-2, 7, 8], [7, 1, -1], [1, 6], [6, 8, 5, 2], [5, -3, 3], [2, 4], [4, 3, -4])) if pars["print_errors"] > 1: orig_T_last = scon( (NE, defect_horz, T_orig, SW, defect_vert), ([-2, 1, -1], [1, 6], [6, -3, 5, 2], [5, -4, -6], [2, -5])) coarsed_T_last = scon((y_last_dg, T_last, y_last), ([-2, -3, 1], [-1, 1, -4, 2], [2, -5, -6])) err = (orig_T_last - coarsed_T_last).norm() / orig_T_last.norm() print("After %i iterations, error in optimize y_last is %.3e." % (counter, err)) elif pars["do_momenta"]: defect_horz = get_defect(alpha, T, 0) defect_vert = get_defect(alpha, T, 1).conjugate().transpose() T_last = scon((T, defect_horz, defect_vert), ([1, -2, -3, 4], [-1, 1], [4, -4])) else: defect_horz = get_defect(alpha, T, 0) T_last = scon((T, defect_horz), ([1, -2, -3, -4], [-1, 1])) return T_last
def fix_A_new_gauge_optimize_Y(A_new, A, X, Y, return_cost=False): X_dg = matrix_dagger(X) Y_dg = matrix_dagger(Y) env = scon((A.conjugate(), X, A_new, Y_dg, X_dg), ([-2, 6, 4, 5], [6, 2], [-1, 2, 1, 3], [1, 4], [3, 5])) env_U, env_S, env_V = env.svd((0, ), (1, )) env_U_dg = matrix_dagger(env_U) env_V_dg = matrix_dagger(env_V) Y = scon((env_V_dg, env_U_dg), ([-1, 1], [1, -2])) if return_cost: cost = scon((env, Y), ([1, 2], [2, 1])).value() if np.imag(cost) > 1e-13: warnings.warn("fix_A_new_gauge cost is complex: " + str(cost)) else: cost = np.real(cost) return Y, cost else: return Y
def transfer_op(v, charge=0): v = np.reshape(v, (flatdim1, flatdim2) + (flatdimT, ) * (block_width - 2)) v = caster(v, charge=charge) print(".", end='', flush=True) scon_list = [v] + scon_list_end Av = scon(scon_list, index_list) Av = Av.to_ndarray() Av = np.reshape(Av, (matrix_flatdim, )) return Av
def get_KW_unitary(pars): eye = np.eye(2, dtype=np.complex_) CZ = Csigma_np("z") U = scon( (CZ, R_np(np.pi / 4, 'z'), R_np(np.pi / 4, 'x'), R_np(np.pi / 4, 'y')), ([-1, -2, 5, 6], [-3, 5], [3, 6], [-4, 3])) u = symmetry_bases["ising"] u_dg = u.T.conjugate() U = scon((U, u, u_dg, u_dg, u), ([1, 2, 3, 4], [-1, 1], [-2, 2], [3, -3], [4, -4])) U *= -1j if pars["symmetry_tensors"]: U = TensorZ2.from_ndarray(U, shape=[[1, 1]] * 4, qhape=[[0, 1]] * 4, dirs=[1, 1, -1, -1]) else: U = Tensor.from_ndarray(U, dirs=[1, 1, 1, -1, -1, -1]) return U
def generate_initial_tensors(pars): T = initialtensors.get_initial_tensor(pars) D_sigma = initialtensors.get_KW_tensor(pars) U = initialtensors.get_KW_unitary(pars) eye = np.eye(2, dtype=np.complex_) eye = type(U).from_ndarray(eye, shape=[[1,1]]*2, qhape=[[0,1]]*2, dirs=[1,-1]) U = scon((U, eye), ([-1,-2,-4,-5], [-3,-6])) A_list = [D_sigma, T] log_fact_list = [0,0] return A_list, log_fact_list, U
def optimize_G_hv(A_new, G_hh, pars): if pars["print_errors"] > 1: print("Optimizing G_hv.") Ah = A_new.conjugate().transpose((2, 1, 0, 3)) dim = A_new.shape[1] try: qim = A_new.qhape[1] except TypeError: qim = None G_hv = type(A_new).eye(dim, qim=qim, dtype=A_new.dtype) G_hv = G_hv.flip_dir(0) cost = np.inf cost_change = np.inf counter = 0 pre_env_A_new = scon((A_new, G_hh, G_hh), ([1, -2, 3, -4], [-1, 1], [3, -3])) while cost_change > 1e-11 and counter < 10000: # The optimization step G_hv_dg = matrix_dagger(G_hv) env = scon((pre_env_A_new, G_hv_dg, Ah.conjugate()), ([3, -1, 4, 1], [1, 2], [3, -2, 4, 2])) env_U, env_S, env_V = env.svd((0, ), (1, )) env_U_dg = matrix_dagger(env_U) env_V_dg = matrix_dagger(env_V) G_hv = scon((env_V_dg, env_U_dg), ([-1, 1], [1, -2])) old_cost = cost cost = scon((env, G_hv), ([1, 2], [2, 1])).value() if np.imag(cost) > 1e-13: warnings.warn("optimize_G_hv cost is complex: " + str(cost)) else: cost = np.real(cost) cost_change = np.abs((old_cost - cost) / cost) counter += 1 if pars["print_errors"] > 1: GAG_dg = scon((G_hv, pre_env_A_new, G_hv.conjugate().transpose()), ([-2, 2], [-1, 2, -3, 4], [4, -4])) err = (GAG_dg - Ah).norm() print("After %i iterations, error in optimize_G_hv is %.3e." % (counter, err)) return G_hv
def contract2x2_Tensor(T_list, vert_flip=False): if vert_flip: def flip(T): T.transpose((0, 3, 2, 1)) flip(T_list[2]) flip(T_list[3]) T4 = scon((T_list[0], T_list[1], T_list[2], T_list[3]), ([-2, -3, 1, 3], [1, -4, -6, 4], [-1, 3, 2, -7], [2, 4, -5, -8])) T4 = T4.join_indices((0, 1), (2, 3), (4, 5), (6, 7), dirs=[1, 1, -1, -1]) return T4
def print_Z_error(A_list, log_fact, A_new, new_log_fact): """ Error in the partition function that is the trace of a block of 4 A tensors. """ A4_Z = A4_trace(A_list) A_new_Z_tensor = scon(A_new, [1, 2, 1, 2]) * np.exp(new_log_fact - 4 * log_fact) A_new_Z = A_new_Z_tensor.norm() err = (A4_Z - A_new_Z) / A_new_Z print( 'Relative difference in Z from exact A4 and from A_new: %.3e + %gj' % (np.real(err), np.imag(err))) return err
def symmetrize_A_list(A_list, pars, gauges): """ Symmetrizes A_list according to the value of horz_refl. """ new_list = A_list.copy() if pars["horz_refl"]: new_list[1] = new_list[0].conjugate().transpose((2, 1, 0, 3)) if gauges["G_hh"] is not None: new_list[1] = scon((new_list[1], gauges["G_hh"], gauges["G_hh"]), ([1, -2, 3, -4], [-1, 1], [3, -3])) else: if A_list[1].dirs == A_list[0].dirs: new_list[1] = A_list[1].flip_dir(1) new_list[1] = new_list[1].flip_dir(3) return new_list
def optimize_disentangler(u, v, w, A_list, A_part_list, pars, return_B_norm=False): # Compute environment M for i in range(pars["opt_iters_tens"]): upb = upper_block(u, v, w, A_list, A_part_list, pars) # Note that by construction, B = B^dg. B = build_B(u, v, w, A_list, A_part_list, pars, upb=upb) if pars["use_parts"]: # This is O(chi^8), O(chi^6) if A can be split with just chi. M = scon((w, v, A_part_list[0][0], A_part_list[0][1], A_part_list[1][0], A_part_list[1][1], upb_dg(upb), B), ([10, -3, 3], [-4, 8, 4], [3, -1, 9], [9, 5, 6], [-2, 4, 11], [5, 11, 7], [6, 7, 2, 1], [1, 2, 8, 10])) else: # This is O(chi^7). M = scon((w, v, A_list[0], A_list[1], upb_dg(upb), B), ([3, -3, 4], [-4, 9, 6], [4, -1, 7, 5], [7, -2, 6, 8], [5, 8, 2, 1], [1, 2, 9, 3])) U, S, V = M.svd((0, 1), (2, 3)) u = scon((U.conjugate(), V.conjugate()), ([-3, -4, 1], [1, -1, -2])) if pars["horz_refl"]: # u should be symmetric under a horizontal reflection # already, but we symmetrize to kill numerical errors. u = (u + u.conjugate().transpose((1, 0, 3, 2))) / 2 if return_B_norm: B_norm_sq = scon((M, u), ([1, 2, 3, 4], [3, 4, 1, 2])).value() if np.imag(B_norm_sq) / np.abs(B_norm_sq) > 1e-13: warnings.warn("B_norm_sq is complex: " + str(B_norm_sq)) else: B_norm_sq = np.real(B_norm_sq) B_norm = np.sqrt(B_norm_sq) return u, B_norm else: return u
def contract2x2_ndarray(T_list, vert_flip=False): if vert_flip: def flip(T): return np.transpose(T.conjugate(), (0, 3, 2, 1)) T_list[2] = flip(T_list[2]) T_list[3] = flip(T_list[3]) T4 = scon((T_list[0], T_list[1], T_list[2], T_list[3]), ([-2, -3, 1, 3], [1, -4, -6, 4], [-1, 3, 2, -7], [2, 4, -5, -8])) sh = T4.shape S = np.reshape( T4, (sh[0] * sh[1], sh[2] * sh[3], sh[4] * sh[5], sh[6] * sh[7])) return S
def initial_uvw(A_list, chi, pars, gauges, pieces): """ Returns the initial disentangler and isometries that are the starting point of the optimization. The initial u is the identity, the initial isometries are SVDed from A just like in TRG, truncated to dimension chi. """ do_reuse = False if (pars["reuse_initial"] and pieces["w"] is not None and pieces["u"] is not None and pieces["v"] is not None): w_shp = pieces["w"].shape w_chi = type(A_list[0]).flatten_dim(w_shp[0]) if (w_chi == chi and A_list[0].compatible_indices(pieces["u"], 1, 2) and A_list[0].compatible_indices(pieces["w"], 0, 2)): do_reuse = True if do_reuse: u = pieces["u"].copy() v = pieces["v"].copy() w = pieces["w"].copy() else: # Some performance could be saved here if the As would not be SVDed # again. This would be simple if A_part_list elements didn't have # the S_sqrt multiplied into them. The performance of this part # should be very much subleading though. # u: dim1 = A_list[0].shape[1] try: qim1 = A_list[0].qhape[1] except TypeError: qim1 = None dim2 = A_list[1].shape[1] try: qim2 = A_list[1].qhape[1] except TypeError: qim2 = None eye1 = type(A_list[0]).eye(dim1, qim=qim1) eye2 = type(A_list[0]).eye(dim2, qim=qim2) u = scon((eye1, eye2), ([-1, -3], [-4, -2])) # w: w_dg = A_list[0].svd((0, 1), (2, 3), chis=chi)[0] w = w_dg.transpose((2, 1, 0)).conjugate() # v: if pars["horz_refl"]: v = w_hat(w, gauges) else: v_dg = A_list[1].svd((0, 3), (1, 2), chis=chi)[2] v = v_dg.transpose((1, 0, 2)).conjugate() return u, v, w
def scon_op(v, charge=0): v = np.reshape(v, in_flatdims) v = type_.from_ndarray(v, shape=in_dims, qhape=in_qims, charge=charge, dirs=in_dirs) if scon_func is None: scon_list = tensor_list + [v] Av = scon(scon_list, index_list) else: Av = scon_func(v) Av = Av.to_ndarray() Av = np.transpose(Av, perm) Av = np.reshape(Av, (matrix_flatdim, )) if print_progress: print(".", end='', flush=True) return Av
def get_initial_tensor(pars, **kwargs): if kwargs: pars = pars.copy() pars.update(kwargs) model_name = pars["model"].strip().lower() ham = hamiltonians[model_name](pars) boltz = np.exp(-pars["beta"] * ham) T_0 = np.einsum('ab,bc,cd,da->abcd', boltz, boltz, boltz, boltz) if pars["symmetry_tensors"]: u = symmetry_bases[model_name] u_dg = u.T.conjugate() T_0 = scon((T_0, u, u, u_dg, u_dg), ([1, 2, 3, 4], [-1, 1], [-2, 2], [3, -3], [4, -4])) cls, dim, qim = symmetry_classes_dims_qims[model_name] T_0 = cls.from_ndarray(T_0, shape=[dim] * 4, qhape=[qim] * 4, dirs=[1, 1, -1, -1]) else: T_0 = Tensor.from_ndarray(T_0) return T_0
def qnums_from_eigenvectors(evects, pars): sites = len(evects.shape) - 1 if pars["model"].strip().lower() == "ising": symop = np.array([[1, 0], [0, -1]], dtype=np.float_) symop = type(evects).from_ndarray(symop) else: # TODO generalize symop to models other than Ising. NotImplementedError("Symmetry operators for models other than " "Ising have not been implemented.") ncon_list = (evects, evects.conjugate()) + (symop, ) * sites index_list = ((list(range(1, sites + 1)) + [-1], list(range(sites + 1, 2 * sites + 1)) + [-2]) + tuple([i + sites, i] for i in range(1, sites + 1))) qnums = scon(ncon_list, index_list) qnums = qnums.diag() # Round to the closest possible qnum for the given model. pos_qnums = initialtensors.symmetry_classes_dims_qims[pars["model"]][2] max_qnum = max(pos_qnums) qnums = qnums.astype(np.complex_).log() * (max_qnum + 1) / (2j * np.pi) qnums = [min(pos_qnums, key=lambda x: abs(x - q)) for q in qnums] return qnums
def get_D(t, pars): ham = (-pars["J"] * np.array([[1, -1], [-1, 1]], dtype=pars["dtype"]) + pars["H"] * np.array([[-1, 0], [0, 1]], dtype=pars["dtype"])) boltz = np.exp(-pars["beta"] * ham) ham_g = -pars["g"] * pars["J"] * np.array([[1, -1], [-1, 1]], dtype=pars["dtype"]) boltz_g = np.exp(-pars["beta"] * ham_g) ones = np.ones((2, 2), dtype=pars["dtype"]) D = np.einsum('ab,bc,cd,da->abcd', boltz, boltz_g, boltz_g, boltz) u = np.array([[1, 1], [1, -1]]) / np.sqrt(2) u_dg = u.T.conjugate() D = scon((D, u, u, u_dg, u_dg), ([1, 2, 3, 4], [-1, 1], [-2, 2], [3, -3], [4, -4])) if pars["symmetry_tensors"]: D = TensorZ2.from_ndarray(D, shape=[[1, 1]] * 4, qhape=[[0, 1]] * 4, dirs=[1, 1, -1, -1]) else: D = Tensor.from_ndarray(D) return D