def get_KW_unitary(pars): """ The unitary that moves the Kramers-Wannier duality defect of the classical 2D square lattice Ising model. """ CZ = Csigma_np("z") # fmt: off U = ncon((CZ, R(np.pi / 4, 'z'), R(np.pi / 4, 'x'), R(np.pi / 4, 'y')), ([-1, -2, 5, 6], [-3, 5], [3, 6], [-4, 3])) # fmt: on u = symmetry_bases["ising"] u_dg = u.T.conjugate() U = ncon( (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 get_initial_tensor_CQL_3d(pars): delta = np.array([[[1, 0], [0, 0]], [[0, 0], [0, 1]]]) A = np.einsum( ("aeu,fiv,gjq,hbr,mcw,nxk,ols,ptd " "-> abcdefghijklmnopqrstuvwx"), *((delta,) * 8) ) return Tensor.from_ndarray(A.reshape((16, 16, 16, 16, 16, 16)))
def get_initial_tensor_CDL_3d(pars): delta = np.eye(2, dtype=pars["dtype"]) A = np.einsum( ("ae,fi,jm,nb,cq,rk,lu,vd,gs,to,pw,xh -> abcdefghijklmnopqrstuvwx"), *((delta,) * 12) ) return Tensor.from_ndarray(A.reshape((16, 16, 16, 16, 16, 16)))
def get_initial_sixvertex_tensor(pars): try: a = pars["sixvertex_a"] b = pars["sixvertex_b"] c = pars["sixvertex_c"] except KeyError: u = pars["sixvertex_u"] lmbd = pars["sixvertex_lambda"] rho = pars["sixvertex_rho"] a = rho * np.sin(lmbd - u) b = rho * np.sin(u) c = rho * np.sin(lmbd) A_0 = np.zeros((2, 2, 2, 2), dtype=pars["dtype"]) A_0[1, 0, 0, 1] = a A_0[0, 1, 1, 0] = a A_0[0, 0, 1, 1] = b A_0[1, 1, 0, 0] = b A_0[0, 1, 0, 1] = c A_0[1, 0, 1, 0] = c if pars["symmetry_tensors"]: dim = [1, 1] qim = [-1, 1] A_0 = TensorU1.from_ndarray( A_0, shape=[dim] * 4, qhape=[qim] * 4, dirs=[1, 1, 1, 1] ) A_0 = A_0.flip_dir(2) A_0 = A_0.flip_dir(3) else: A_0 = Tensor.from_ndarray(A_0) return A_0
def convertAbe(A): """ Convert A to class abeliantensors if it is not """ if type(A).__module__.split(".")[0] != 'abeliantensors': A = Tensor.from_ndarray(A) return A
def get_initial_impurity(pars, legs=(3,), factor=3, **kwargs): if kwargs: pars = pars.copy() pars.update(kwargs) A_pure = get_initial_tensor(pars) model = pars["model"] impurity = pars["impurity"] try: impurity_matrix = impurity_dict[model][impurity](pars) except KeyError: msg = "Unknown (model, impurity) combination: ({}, {})".format( model, impurity ) raise ValueError(msg) # TODO The expectation that everything is in the symmetry basis # clashes with how 2D ising and potts initial tensors are generated. if not pars["symmetry_tensors"]: impurity_matrix = Tensor.from_ndarray(impurity_matrix.to_ndarray()) # TODO This was commented in before 2019-09-06. Why? It's clearly wrong # for 3D Ising U-impurity and id-impurity. # impurity_matrix *= -1 A_impure = 0 if 0 in legs: A_impure += ncon( (A_pure, impurity_matrix), ([1, -2, -3, -4, -5, -6], [1, -1]) ) if 1 in legs: A_impure += ncon( (A_pure, impurity_matrix), ([-1, 2, -3, -4, -5, -6], [2, -2]) ) if 2 in legs: A_impure += ncon( (A_pure, impurity_matrix.transpose()), ([-1, -2, 3, -4, -5, -6], [3, -3]), ) if 3 in legs: A_impure += ncon( (A_pure, impurity_matrix.transpose()), ([-1, -2, -3, 4, -5, -6], [4, -4]), ) if 4 in legs: A_impure += ncon( (A_pure, impurity_matrix), ([-1, -2, -3, -4, 5, -6], [5, -5]) ) if 5 in legs: A_impure += ncon( (A_pure, impurity_matrix.transpose()), ([-1, -2, -3, -4, -5, 6], [6, -6]), ) # TODO This was commented in before 2019-09-06. Why? It's clearly wrong # for 3D Ising U-impurity and id-impurity. # A_impure *= factor return A_impure
def get_initial_tensor_CDL_3d_v2(pars): delta = np.eye(2, dtype=pars["dtype"]) A = ncon( (delta,) * 12, # fmt: off ( [-11, -21], [-12, -41], [-13, -51], [-14, -61], [-31, -22], [-32, -42], [-33, -52], [-34, -62], [-23, -63], [-64, -43], [-44, -53], [-54, -24], ), # fmt: on ) return Tensor.from_ndarray(A.reshape((16, 16, 16, 16, 16, 16)))
def get_initial_tensor_ising_3d(pars): beta = pars["beta"] ham = ising3d_ham(beta) A_0 = np.einsum( "ai,aj,ak,al,am,an -> ijklmn", ham, ham, ham, ham, ham, ham ) if pars["symmetry_tensors"]: cls, dim, qim = TensorZ2, [1, 1], [0, 1] A_0 = cls.from_ndarray( A_0, shape=[dim] * 6, qhape=[qim] * 6, dirs=[1, 1, -1, -1, 1, -1] ) else: A_0 = Tensor.from_ndarray(A_0) return A_0
def get_initial_dilute_sixvertex_tensor(pars): # Copied and adapted from Roman # 1 # | # 4 -- A -- 2 # | # 3 # "dilute six-vertex model", # states : 0:empty, 1:top arrow, 1:bot arrow. Time flows toward N-E w = np.exp(1j * np.pi / 8.0) z = 0.57 # Close to critical # z = 1.0 # XXZ universality class A = np.zeros((3, 3, 3, 3), dtype=np.complex) A[1, 1, 1, 1] = 1.0 A[2, 1, 1, 2] = z * w A[1, 2, 2, 1] = z / w A[0, 1, 1, 0] = z / w A[1, 0, 0, 1] = z * w A[2, 0, 1, 1] = z / w A[0, 2, 1, 1] = z * w A[1, 1, 2, 0] = z * w A[1, 1, 0, 2] = z / w A[2, 2, 2, 2] = z ** 2 A[0, 0, 0, 0] = z ** 2 A[2, 0, 2, 0] = z ** 2 A[0, 2, 0, 2] = z ** 2 A[2, 0, 0, 2] = (z ** 2) * (w ** 2 + 1.0 / w ** 2) A[0, 2, 2, 0] = (z ** 2) * (w ** 2 + 1.0 / w ** 2) # U(1) charge : Q=(-1,0,+1) Q1+Q2=Q3+Q4 if pars["symmetry_tensors"]: dim = [1, 1, 1] qim = [-1, 0, 1] A = TensorU1.from_ndarray( A, shape=[dim] * 4, qhape=[qim] * 4, dirs=[1, 1, -1, -1] ) else: A = Tensor.from_ndarray(A) # To translate between Roman's convention and mine. A = A.transpose((3, 0, 1, 2)) return A
def get_initial_tensor(pars, **kwargs): if kwargs: pars = pars.copy() pars.update(kwargs) model_name = pars["model"].strip().lower() if model_name == "dilute_sixvertex": return get_initial_dilute_sixvertex_tensor(pars) elif model_name == "sixvertex": return get_initial_sixvertex_tensor(pars) elif model_name == "ising3d": return get_initial_tensor_ising_3d(pars) elif model_name == "potts33d": return get_initial_tensor_potts33d(pars) elif model_name == "complexion_qising": ham = get_ham(pars, model="qising") complexion = build_complexion(ham, pars) return complexion elif model_name == "complexion_qising_tricrit": ham = get_ham(pars, model="qising_tricrit") complexion = build_complexion(ham, pars) return complexion elif model_name == "complexion_sq_qising": ham = get_ham(pars, model="qising") complexion = build_complexion(ham, pars, square_hamiltonian=True) return complexion else: ham = hamiltonians[model_name](pars) boltz = np.exp(-pars["beta"] * ham) A_0 = np.einsum("ab,bc,cd,da->abcd", boltz, boltz, boltz, boltz) u = symmetry_bases[model_name] u_dg = u.T.conjugate() A_0 = ncon( (A_0, u, u, u_dg, u_dg), ([1, 2, 3, 4], [-1, 1], [-2, 2], [3, -3], [4, -4]), ) if pars["symmetry_tensors"]: cls, dim, qim = symmetry_classes_dims_qims[model_name] A_0 = cls.from_ndarray( A_0, shape=[dim] * 4, qhape=[qim] * 4, dirs=[1, 1, -1, -1] ) else: A_0 = Tensor.from_ndarray(A_0) return A_0
def get_initial_tensor_potts33d(pars): beta = pars["beta"] Q = potts_Q(beta, 3) A = np.einsum( "ai,aj,ak,al,am,an -> ijklmn", Q, Q, Q.conjugate(), Q.conjugate(), Q, Q.conjugate(), ) if np.linalg.norm(np.imag(A)) < 1e-12: A = np.real(A) if pars["symmetry_tensors"]: cls, dim, qim = symmetry_classes_dims_qims["potts3"] A = cls.from_ndarray( A, shape=[dim] * 6, qhape=[qim] * 6, dirs=[1, 1, -1, -1, 1, -1] ) else: A = Tensor.from_ndarray(A) return A
def get_KW_tensor(pars): """ The Kramers-Wannier duality defect of the classical 2D square lattice Ising model. """ eye = np.eye(2, dtype=np.complex_) ham = hamiltonians["ising"](pars) B = np.exp(-pars["beta"] * ham) H = np.array([[1, 1], [1, -1]], dtype=np.complex_) / np.sqrt(2) y_trigged = np.ndarray((2, 2, 2), dtype=np.complex_) y_trigged[:, :, 0] = eye y_trigged[:, :, 1] = sigma("y") D_sigma = np.sqrt(2) * np.einsum( "ab,abi,ic,ad,adk,kc->abcd", B, y_trigged, H, B, y_trigged.conjugate(), H, ) u = symmetry_bases["ising"] u_dg = u.T.conjugate() D_sigma = ncon( (D_sigma, u, u, u_dg, u_dg), ([1, 2, 3, 4], [-1, 1], [-2, 2], [3, -3], [4, -4]), ) if pars["symmetry_tensors"]: D_sigma = TensorZ2.from_ndarray( D_sigma, shape=[[1, 1]] * 4, qhape=[[0, 1]] * 4, dirs=[1, 1, -1, -1], ) else: D_sigma = Tensor.from_ndarray(D_sigma, dirs=[1, 1, -1, -1]) return D_sigma
def test_product_invariant(n_iters, tensorclass, n_qnums, rshape, rqhape, rdirs, rcharge, rtensor): """Generate two invariant tensors, contract them over a random set of legs, and compare with NumPy. """ for iter_num in range(n_iters): shp1 = rshape(nlow=1) # Shape of the first tensor # Choose how many indices to contract order, and which indices of # tensor #1 those should be. n = np.random.randint(low=1, high=len(shp1) + 1) if n: i_list = list(np.random.choice(len(shp1), size=n, replace=False)) else: i_list = [] # Generate the shape of the second tensor, and which indices it should # be contracted over. shp2 = rshape(nlow=n) if n: j_list = list(np.random.choice(len(shp2), size=n, replace=False)) else: j_list = [] # Make sure contracted indices have a dimension of at least 1. for k in range(n): dim1 = shp1[i_list[k]] if np.sum(dim1) < 1: dim1 = rshape(n=1, chilow=1)[0] shp1[i_list[k]] = dim1 shp2[j_list[k]] = dim1 # Generate tensor #1. qhp1 = rqhape(shp1) qhp2 = rqhape(shp2) if qhp1 is not None: for k in range(n): qhp2[j_list[k]] = qhp1[i_list[k]] T1 = rtensor(shape=shp1, qhape=qhp1) T1_orig = T1.copy() # Generate tensor #2. if T1.dirs is not None: dirs2 = rdirs(shape=shp2) for i, j in zip(i_list, j_list): dirs2[j] = -T1.dirs[i] else: dirs2 = None T2 = rtensor(shape=shp2, qhape=qhp2, dirs=dirs2) T2_orig = T2.copy() # Do the product. T1_np = T1.to_ndarray() T2_np = T2.to_ndarray() T = T1.dot(T2, (i_list, j_list)) assert (T1 == T1_orig).all() assert (T2 == T2_orig).all() check_internal_consistency(T) # Assert that the result has the right shape. i_list_compl = sorted(set(range(len(shp1))) - set(i_list)) j_list_compl = sorted(set(range(len(shp2))) - set(j_list)) product_shp = [shp1[i] for i in i_list_compl ] + [shp2[j] for j in j_list_compl] if type(T) == Tensor: product_shp = Tensor.flatten_shape(product_shp) assert T.shape == product_shp # Do the product using NumPy and compare. T_np = np.tensordot(T1_np, T2_np, (i_list, j_list)) assert np.allclose(T_np, T.to_ndarray())
def truncate_func( s, u=None, v=None, chis=None, eps=0, trunc_err_func=None, norm_sq=None, return_error=False, ): chis = s.matrix_decomp_format_chis(chis, eps) if hasattr(s, "sects"): if trunc_err_func is None: trunc_err_func = fct.partial(type(s).default_trunc_err_func, norm_sq=norm_sq) # First, find what chi will be. s_flat = s.to_ndarray() s_flat = -np.sort(-np.abs(s_flat)) chi, err = Tensor.find_trunc_dim(s_flat, chis=chis, eps=eps, trunc_err_func=trunc_err_func) # Find out which values to keep, i.e. how to distribute chi in # the different blocks. dim_sum = 0 minusabs_next_els = [] dims = {} for k, val in s.sects.items(): heapq.heappush(minusabs_next_els, (-np.abs(val[0]), k)) dims[k] = 0 while dim_sum < chi: try: minusabs_el_to_add, key = heapq.heappop(minusabs_next_els) except IndexError: # All the dimensions are fully included. break dims[key] += 1 this_key_els = s[key] if dims[key] < len(this_key_els): next_el = this_key_els[dims[key]] heapq.heappush(minusabs_next_els, (-np.abs(next_el), key)) dim_sum += 1 # Truncate each block and create the dim for the new index. new_dim = [] todelete = [] for k in s.sects.keys(): d = dims[k] new_dim.append(d) if d > 0: s[k] = s[k][:d] else: # Avoiding changing the dictionary during the loop. todelete.append(k) for k in todelete: del s[k] if u is not None: todelete = [] for k in u.sects.keys(): klast = k[-1] d = dims[(klast, )] if d > 0: u[k] = u[k][..., :d] else: todelete.append(k) for k in todelete: del u[k] if v is not None: todelete = [] for k in v.sects.keys(): k0 = k[0] d = dims[(k0, )] if d > 0: v[k] = v[k][:d, ...] else: todelete.append(k) for k in todelete: del v[k] # Remove zero dimension sectors from qim. new_qim = s.qhape[0] new_dim = [d for d in new_dim if d > 0] for k, d in dims.items(): if d == 0: new_qim = [q for q in new_qim if q != k[0]] s.shape = [new_dim] s.qhape = [new_qim] if u is not None: u.shape = u.shape[0:-1] + [new_dim] u.qhape = u.qhape[0:-1] + [new_qim] if v is not None: v.shape = [new_dim] + v.shape[1:] v.qhape = [new_qim] + v.qhape[1:] else: chi, err = type(s).find_trunc_dim(s, chis=chis, eps=eps, trunc_err_func=trunc_err_func) s = s[:chi] if u is not None: u = u[..., :chi] if v is not None: v = v[:chi, ...] retval = (s, ) if u is not None: retval += (u, ) if v is not None: retval += (v, ) if return_error: retval += (err, ) if len(retval) == 1: retval = retval[0] return retval