def de_pc_correlator(L, N, i, j):
    """Build a many-body correlation operator b^dagger_i*b_j, i != j.

    Args:
        L (int): system's length.
        N (int): particle number.
        i (int): position of the creation operator.
        j (int): position of the annihilation operator.

    Returns:
        C (2darray of floats): many-body corelation operator.

    """
    if i == j:
        raise ValueError('i and j must be different.')

    states = generate_states(L, N)
    num_states = states.size

    C = np.zeros((num_states, num_states), np.float64)

    for ix_s, s in enumerate(states):
        if (not (s>>i)&1) and ((s>>j)&1):
            t = s + (1<<i) - (1<<j)
            ix_t = binsearch(states, t)
            C[ix_t, ix_s] += 1

    return C
Beispiel #2
0
def fcompute_2Pij(basis, state, i, j):
    """Compute P_ij."""
    if i == j:
        return fcompute_1Di(basis, state, i)

    out = 0.
    for ix_s, s in enumerate(basis):
        if not (s >> i) & 1 and (s >> j) & 1:
            t = s + (1 << i) - (1 << j)
            ix_t = binsearch(states, t)
            out += state[ix_s] * np.conj(state[ix_t])
    return out
Beispiel #3
0
def fcompute_3Pijk(basis, state, i, j, k):
    """Compute P^i_jk."""
    if i == j or j == k:
        return 0.
    elif j == k:
        return fcompute_2Dij(basis, state, i, j)

    out = 0.
    for ix_s, s in enumerate(basis):
        if (s >> i) & 1 and not (s >> j) & 1 and (s >> k) & 1:
            t = s + (1 << j) - (1 << k)
            ix_t = binsearch(states, t)
            out += state[ix_s] * np.conj(state[ix_t])
    return out
Beispiel #4
0
def fcompute_4Rijkl(basis, state, i, j, k, l):
    """Compute P^ij_kl."""
    if i == j:
        return fcompute_3Pijk(basis, state, i, k, l)
    elif i == k or i == l or j == k or j == l:
        return 0.
    elif k == l:
        return fcompute_2Dijk(basis, state, i, j, k)

    out = 0.
    for ix_s, s in enumerate(basis):
        if (s >> i) & 1 and (s >> j) & 1 and not (s >> k) & 1 and (s >> l) & 1:
            t = s + (1 << k) - (1 << l)
            ix_t = binsearch(states, t)
            out += state[ix_s] * np.conj(state[ix_t])
    return out
Beispiel #5
0
def fer_de_pc_op(L, N, J, D):
    """Build a many-body fermionic operator with 2-particle terms.

    Note: numba parallel does not support the enumerate function, which
    would make the code more readable. Instead we have to make the
    outermost loops with range().

    Args:
        L (int): system's length.
        N (int): particle number.
        J (2darray of floats): hopping matrix.
        D (2darray of floats): interaction matrix.

    Returns:
        H (2darray of floats): many-body operator.

    """
    states = generate_states(L, N)
    num_states = states.size

    H = np.zeros((num_states, num_states), np.float64)

    # Notation:
    #     s: initial state.
    #     t: final state.
    #     ix_#: index of #.
    for ix_s in prange(num_states):
        s = states[ix_s]
        for i in range(L):
            # On-site terms: n_i.
            if (s >> i) & 1:
                H[ix_s, ix_s] += J[i, i]

            for j in range(L):
                if i != j:
                    # Hopping terms: b^dagger_i b_j.
                    if not (s >> i) & 1 and (s >> j) & 1:
                        t = s + (1 << i) - (1 << j)
                        par = get_parity(t, i, j)
                        ix_t = binsearch(states, t)
                        H[ix_t, ix_s] += par * J[i, j]

                    # Interaction terms: n_i n_j.
                    if (s >> i) & 1 and (s >> j) & 1:
                        H[ix_s, ix_s] += D[i, j]

    return H
Beispiel #6
0
def fcompute_4Pijkl(basis, state, i, j, k, l):
    """Compute P_ijkl."""
    if i == j or k == l:
        return 0.
    elif i == k:
        return fcompute_3Pijk(basis, state, i, j, l)
    elif i == l:
        return fcompute_3Pijk(basis, state, i, j, k)
    elif j == k:
        return fcompute_3Pijk(basis, state, j, i, l)
    elif j == l:
        return fcompute_3Pijk(basis, state, j, i, k)

    out = 0.
    for ix_s, s in enumerate(basis):
        if not (s >> i) & 1 and not (s >> j) & 1 and (s >> k) & 1 and (
                s >> l) & 1:
            t = s + (1 << i) + (1 << j) - (1 << k) - (1 << l)
            ix_t = binsearch(states, t)
            out += state[ix_s] * np.conj(state[ix_t])
    return out
Beispiel #7
0
def fer_sp_pc_correlator(L, N, i, j):
    """Build a many-body fermionic correlation c^dagger_i*c_j, i != j.

    Args:
        L (int): system's length.
        N (int): particle number.
        i (int): position of the creation operator.
        j (int): position of the annihilation operator.

    Returns:
        vals, rows, cols (1darray of floats): sparse
            representation of the mb corelation operator.
        num_states (int): number of states.

    """
    if i == j:
        raise ValueError('i and j must be different.')

    states = generate_states(L, N)
    num_states = states.size

    number_nnz_vals = binom(L - 2, N - 1)
    vals = np.zeros(number_nnz_vals, dtype=np.float64)
    rows = np.zeros(number_nnz_vals, dtype=np.int32)
    cols = np.zeros(number_nnz_vals, dtype=np.int32)

    c = 0
    for ix_s, s in enumerate(states):
        if (not (s >> i) & np.uint16(1)) and ((s >> j) & np.uint16(1)):
            t = s + (1 << i) - (1 << j)
            ix_t = binsearch(states, t)
            par = get_parity(s, i, j)
            vals[c] += par
            rows[c] = ix_t
            cols[c] = ix_s
            c += 1

    return vals, rows, cols, num_states
def de_sym_pc_op(L, N, J, D):
    """Build a many-body symmetric operator with 2-particle terms.

    Note: numba parallel does not support the enumerate function, which
    would make the code more readable. Instead we have to make the
    outermost loops with range().

    Args:
        L (int): system's length.
        N (int): particle number.
        J (2darray of floats): symmetric hopping matrix.
        D (2darray of floats): interaction matrix.

    Returns:
        H (2darray of floats): many-body operator.

    """
    if np.sum(np.abs(J - J.T)**2) > 1e-7:
        raise ValueError('J is not symmetric.')

    # Put all elts of D in the lower triangle. Making a copy of D
    # instead of working with views prevents the function to make
    # changes in D outside of it.
    D = np.copy(D)
    for i in range(L):
        for j in range(i):  # j < i.
            D[i, j] += D[j, i]
            D[j, i] = 0

    states = generate_states(L, N)
    num_states = states.size

    H = np.zeros((num_states, num_states), J.dtype)

    # Notation:
    #     s: initial state.
    #     t: final state.
    #     ix_#: index of #.
    for ix_s in prange(num_states):
        s = states[ix_s]
        for i in range(L):
            # On-site terms: n_i.
            if np.abs(J[i, i]) > 1e-7:
                if (s>>i)&1:
                    H[ix_s, ix_s] += J[i, i]

            for j in range(i):
                # Hopping terms: b^dagger_i b_j.
                if np.abs(J[i, j]) > 1e-7:
                    if not (s>>i)&1 and (s>>j)&1:
                        t = s + (1<<i) - (1<<j)
                        ix_t = binsearch(states, t)
                        H[ix_t, ix_s] += J[i, j]
                        H[ix_s, ix_t] += J[i, j]

                # Interaction terms: n_i n_j.
                if np.abs(D[i, j]) > 1e-7:
                    if (s>>i)&1 and (s>>j)&1:
                        H[ix_s, ix_s] += D[i, j]

    return H
def sp_sym_pc_op(L, N, J, D):
    """Compute sparse matrix of a symmetric operator.

    Note: if jit is not used, the loops must be done with np.arange
        because the 'right shift' function (>>) needs to operate with
        two uint's as types. Example:
        >>> for i in np.arange(L, dtype=np.uint16):

    Args:
        L (int): system's length.
        N (int): particle number.
        J (2darray of floats): hopping matrix. Must be symmetric.
        D (2darray of floats): interaction matrix.

    Returns:
        vals, rows, cols: arrays to build the sparse Hamiltonian in
            CSR format.

    """
    if (np.sum((J - J.T)**2) > 1e-7) or (np.sum((D - D.T)**2) > 1e-7):
        raise ValueError('J and/or D is not symmetric.')

    states = generate_states(L, N)
    num_states = states.size

    number_nnz_vals = binom(
        L, N) + count_nnz_off_diagonal(J) * binom(L - 2, N - 1)
    vals = np.zeros(number_nnz_vals, dtype=np.float64)
    rows = np.zeros(number_nnz_vals, dtype=np.int32)
    cols = np.zeros(number_nnz_vals, dtype=np.int32)

    # Notation:
    #     s: initial state.
    #     t: final state.
    #     ix_#: index of #.
    c = 0
    for ix_s, s in enumerate(states):
        # On-site terms: n_i.
        for i in range(L):
            if ((s >> i) & np.uint16(1)):
                vals[c] += J[i, i]

        # Interaction terms: n_i*n_j.
        for i in range(L):
            for j in range(i):  # j < i.
                if (np.abs(D[i, j]) > 1e-7):
                    if ((s >> i) & np.uint16(1)) and ((s >> j) & np.uint16(1)):
                        vals[c] += 2 * D[i, j]

        cols[c] += ix_s
        rows[c] += ix_s
        c += 1

        # Hopping terms: b^dagger_i*b_j.
        for i in range(L):
            for j in range(i):
                if (np.abs(J[i, j]) > 1e-7):
                    if (not (s >> i) & np.uint16(1)) and ((s >> j)
                                                          & np.uint16(1)):
                        t = s + (1 << i) - (1 << j)
                        ix_t = binsearch(states, t)
                        vals[c] += J[i, j]
                        rows[c] += ix_t
                        cols[c] += ix_s
                        c += 1
                        vals[c] += J[i, j]
                        rows[c] += ix_s
                        cols[c] += ix_t
                        c += 1

    return vals, rows, cols, num_states
 def test_binary_search(self):
     """Test generated states for some combinations."""
     a = np.arange(0, 18, 2)
     for i in range(9):
         ix = binsearch(a, 2*i)
         self.assertEqual(ix, i)
Beispiel #11
0
def fer_sp_pc_op(L, N, J, D):
    """Compute the sparse CSR data of a mb fermionic operator.

    Note: if jit is not used, the loops must be done with np.arange
        because the 'right shift' function (>>) needs to operate with
        two uint's as types. Example:
        >>> for i in np.arange(L, dtype=np.uint16):

    Args:
        L (int): system's length.
        N (int): particle number.
        J (2darray of floats): hopping matrix.
        D (2darray of floats): interaction matrix.

    Returns:
        vals, rows, cols: arrays to build the sparse Hamiltonian in
            CSR format.

    """
    states = generate_states(L, N)
    num_states = states.size

    number_nnz_vals = binom(
        L, N) + count_nnz_off_diagonal(J) * binom(L - 2, N - 1)
    vals = np.zeros(number_nnz_vals, dtype=np.float64)
    rows = np.zeros(number_nnz_vals, dtype=np.int32)
    cols = np.zeros(number_nnz_vals, dtype=np.int32)

    # Notation:
    #     s: initial state.
    #     t: final state.
    #     ix_#: index of #.
    c = 0
    for ix_s, s in enumerate(states):
        # On-site terms: n_i.
        for i in range(L):
            if ((s >> i) & np.uint16(1)):
                vals[c] += J[i, i]

        # Interaction terms: n_i*n_j.
        for i in range(L):
            for j in range(L):
                if (np.abs(D[i, j]) > 1e-6) and (i != j):
                    if ((s >> i) & np.uint16(1)) and ((s >> j) & np.uint16(1)):
                        vals[c] += D[i, j]
        cols[c] += ix_s
        rows[c] += ix_s
        c += 1

        # Hopping terms: b^dagger_i*b_j.
        for i in range(L):
            for j in range(L):
                if (np.abs(J[i, j]) > 1e-6) and (j != i):
                    if (not (s >> i) & np.uint16(1)) and ((s >> j)
                                                          & np.uint16(1)):
                        t = s + (1 << i) - (1 << j)
                        ix_t = binsearch(states, t)
                        par = get_parity(s, i, j)
                        vals[c] += par * J[i, j]
                        rows[c] += ix_t
                        cols[c] += ix_s
                        c += 1

    return vals, rows, cols, num_states