Example #1
0
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
Example #2
0
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)))
Example #3
0
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)))
Example #4
0
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
Example #5
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
Example #6
0
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
Example #7
0
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)))
Example #8
0
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
Example #9
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
Example #10
0
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
Example #11
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
Example #12
0
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
Example #13
0
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())
Example #14
0
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