Beispiel #1
0
def proj(hilbert: AbstractHilbert,
         site: int,
         n: int,
         dtype: DType = float) -> _LocalOperator:
    """
    Builds the projector operator :math:`|n\\rangle\\langle n |` acting on the `site`-th of the
    Hilbert space `hilbert` and collapsing on the state with `n` bosons.

    If `hilbert` is a non-Bosonic space of local dimension M, it is considered
    as a bosonic space of local dimension M.

    Args:
        hilbert: The hilbert space
        site: the site on which this operator acts
        n: the state on which to project

    Returns:
        the resulting operator
    """
    import numpy as np

    N = hilbert.size_at_index(site)

    if n >= N:
        raise ValueError("Cannot project on a state above the cutoff.")

    D = np.array([0 for m in np.arange(0, N)])
    D[n] = 1
    mat = np.diag(D, 0)
    return _LocalOperator(hilbert, mat, [site], dtype=dtype)
Beispiel #2
0
def sigmay(hilbert: _AbstractHilbert,
           site: int,
           dtype: _DType = complex) -> _LocalOperator:
    """
    Builds the :math:`\\sigma^y` operator acting on the `site`-th of the Hilbert
    space `hilbert`.

    If `hilbert` is a non-Spin space of local dimension M, it is considered
    as a (M-1)/2 - spin space.

    :param hilbert: The hilbert space
    :param site: the site on which this operator acts
    :return: a nk.operator.LocalOperator
    """
    import numpy as np
    import netket.jax as nkjax

    if not nkjax.is_complex_dtype(dtype):
        import jax.numpy as jnp
        import warnings

        old_dtype = dtype
        dtype = jnp.promote_types(complex, old_dtype)
        warnings.warn(
            np.ComplexWarning(
                f"A complex dtype is required (dtype={old_dtype} specified). "
                f"Promoting to dtype={dtype}."))

    N = hilbert.size_at_index(site)
    S = (N - 1) / 2

    D = np.array(
        [1j * np.sqrt((S + 1) * 2 * a - a * (a + 1)) for a in np.arange(1, N)])
    mat = np.diag(D, -1) + np.diag(-D, 1)
    return _LocalOperator(hilbert, mat, [site], dtype=dtype)
Beispiel #3
0
    def __init__(
        self,
        hilbert: AbstractHilbert,
        operators: Union[List[Array], Array] = [],
        acting_on: Union[List[int], List[List[int]]] = [],
        constant: numbers.Number = 0,
        dtype: Optional[DType] = None,
    ):
        r"""
        Constructs a new ``LocalOperator`` given a hilbert space and (if
        specified) a constant level shift.

        Args:
           hilbert (netket.AbstractHilbert): Hilbert space the operator acts on.
           operators (list(numpy.array) or numpy.array): A list of operators, in matrix form.
           acting_on (list(numpy.array) or numpy.array): A list of sites, which the corresponding operators act on.
           constant (float): Level shift for operator. Default is 0.0.

        Examples:
           Constructs a ``LocalOperator`` without any operators.

           >>> from netket.hilbert import CustomHilbert
           >>> from netket.operator import LocalOperator
           >>> hi = CustomHilbert(local_states=[-1, 1])**20
           >>> empty_hat = LocalOperator(hi)
           >>> print(len(empty_hat.acting_on))
           0
        """
        super().__init__(hilbert)
        self.mel_cutoff = 1.0e-6
        self._initialized = None

        if not all([
                _is_sorted(hilbert.states_at_index(i))
                for i in range(hilbert.size)
        ]):
            raise ValueError(
                dedent(
                    """LocalOperator needs an hilbert space with sorted state values at
                every site.
                """))

        # Canonicalize input. From now on input is guaranteed to be in canonical order
        operators, acting_on, dtype = canonicalize_input(self.hilbert,
                                                         operators,
                                                         acting_on,
                                                         constant,
                                                         dtype=dtype)
        self._dtype = dtype
        self._constant = np.array(constant, dtype=dtype)

        self._operators_dict = {}
        for (op, aon) in zip(operators, acting_on):
            self._add_operator(aon, op)
Beispiel #4
0
def sigmaz(hilbert: _AbstractHilbert,
           site: int,
           dtype: _DType = float) -> _LocalOperator:
    """
    Builds the :math:`\\sigma^z` operator acting on the `site`-th of the Hilbert
    space `hilbert`.

    If `hilbert` is a non-Spin space of local dimension M, it is considered
    as a (M-1)/2 - spin space.

    :param hilbert: The hilbert space
    :param site: the site on which this operator acts
    :return: a nk.operator.LocalOperator
    """
    import numpy as np

    N = hilbert.size_at_index(site)
    S = (N - 1) / 2

    D = np.array([2 * m for m in np.arange(S, -(S + 1), -1)])
    mat = np.diag(D, 0)
    return _LocalOperator(hilbert, mat, [site], dtype=dtype)
Beispiel #5
0
def sigmax(hilbert: AbstractHilbert,
           site: int,
           dtype: DType = float) -> _LocalOperator:
    """
    Builds the :math:`\\sigma^x` operator acting on the `site`-th of the Hilbert
    space `hilbert`.

    If `hilbert` is a non-Spin space of local dimension M, it is considered
    as a (M-1)/2 - spin space.

    :param hilbert: The hilbert space
    :param site: the site on which this operator acts
    :return: a nk.operator.LocalOperator
    """
    import numpy as np

    N = hilbert.size_at_index(site)
    S = (N - 1) / 2

    D = [np.sqrt((S + 1) * 2 * a - a * (a + 1)) for a in np.arange(1, N)]
    mat = np.diag(D, 1) + np.diag(D, -1)
    return _LocalOperator(hilbert, mat, [site], dtype=dtype)
Beispiel #6
0
def number(hilbert: AbstractHilbert, site: int, dtype: DType = float) -> _LocalOperator:
    """
    Builds the number operator :math:`\\hat{a}^\\dagger\\hat{a}`  acting on the
    `site`-th of the Hilbert space `hilbert`.

    If `hilbert` is a non-Bosonic space of local dimension M, it is considered
    as a bosonic space of local dimension M.

    Args:
        hilbert: The hilbert space
        site: the site on which this operator acts

    Returns:
        The resulting Local Operator
    """
    import numpy as np

    N = hilbert.size_at_index(site)

    D = np.array([m for m in np.arange(0, N)])
    mat = np.diag(D, 0)
    return _LocalOperator(hilbert, mat, [site], dtype=dtype)
Beispiel #7
0
def sigmam(hilbert: AbstractHilbert,
           site: int,
           dtype: DType = float) -> _LocalOperator:
    """
    Builds the :math:`\\sigma^{-} = \\frac{1}{2}(\\sigma^x - i \\sigma^y)` operator acting on the
    `site`-th of the Hilbert space `hilbert`.

    If `hilbert` is a non-Spin space of local dimension M, it is considered
    as a (M-1)/2 - spin space.

    :param hilbert: The hilbert space
    :param site: the site on which this operator acts
    :return: a nk.operator.LocalOperator
    """
    import numpy as np

    N = hilbert.size_at_index(site)
    S = (N - 1) / 2

    S2 = (S + 1) * S
    D = np.array([np.sqrt(S2 - m * (m - 1)) for m in np.arange(S, -S, -1)])
    mat = np.diag(D, -1)
    return _LocalOperator(hilbert, mat, [site], dtype=dtype)
Beispiel #8
0
    def __init__(
        self,
        hilbert: AbstractHilbert,
        operators: Union[List[Array], Array] = [],
        acting_on: Union[List[int], List[List[int]]] = [],
        constant: numbers.Number = 0,
        dtype: Optional[DType] = None,
    ):
        r"""
        Constructs a new ``LocalOperator`` given a hilbert space and (if
        specified) a constant level shift.

        Args:
           hilbert (netket.AbstractHilbert): Hilbert space the operator acts on.
           operators (list(numpy.array) or numpy.array): A list of operators, in matrix form.
           acting_on (list(numpy.array) or numpy.array): A list of sites, which the corresponding operators act on.
           constant (float): Level shift for operator. Default is 0.0.

        Examples:
           Constructs a ``LocalOperator`` without any operators.

           >>> from netket.hilbert import CustomHilbert
           >>> from netket.operator import LocalOperator
           >>> hi = CustomHilbert(local_states=[-1, 1])**20
           >>> empty_hat = LocalOperator(hi)
           >>> print(len(empty_hat.acting_on))
           0
        """
        super().__init__(hilbert)
        self._constant = constant

        if not all(
            [_is_sorted(hilbert.states_at_index(i)) for i in range(hilbert.size)]
        ):
            raise ValueError(
                dedent(
                    """LocalOperator needs an hilbert space with sorted state values at
                every site.
                """
                )
            )

        # check if passing a single operator or a list of operators
        if isinstance(acting_on, numbers.Number):
            acting_on = [acting_on]

        is_nested = any(hasattr(i, "__len__") for i in acting_on)

        if not is_nested:
            operators = [operators]
            acting_on = [acting_on]

        operators = [np.asarray(operator) for operator in operators]

        # If we asked for a specific dtype, enforce it.
        if dtype is None:
            dtype = functools.reduce(
                lambda dt, op: np.promote_types(dt, op.dtype), operators, np.float32
            )

        self._dtype = dtype
        self._init_zero()

        self.mel_cutoff = 1.0e-6

        self._nonzero_diagonal = np.abs(self._constant) >= self.mel_cutoff
        """True if at least one element in the diagonal of the operator is
        nonzero"""

        for op, act in zip(operators, acting_on):
            if len(act) > 0:
                self._add_operator(op, act)
def pack_internals(
    hilbert: AbstractHilbert,
    operators_dict: dict,
    constant,
    dtype: DType,
    mel_cutoff: float,
):
    """
    Take the internal lazy representation of a local operator and returns the arrays
    needed for the numba implementation.

    This takes as input a dictionary with Tuples as keys, the `acting_on` and matrices as values.
    The keys represent the sites upon which the matrix acts.
    It is assumed that the integer in the tuples are sorted.

    Returns a dictionary with all the data fields
    """
    op_acting_on = list(operators_dict.keys())
    operators = list(operators_dict.values())
    n_operators = len(operators_dict)
    """Analyze the operator strings and precompute arrays for get_conn inference"""
    acting_size = np.array([len(aon) for aon in op_acting_on], dtype=np.intp)
    max_acting_on_sz = np.max(acting_size)
    max_local_hilbert_size = max(
        [max(map(hilbert.size_at_index, aon)) for aon in op_acting_on])
    max_op_size = max(map(lambda x: x.shape[0], operators))

    acting_on = np.full((n_operators, max_acting_on_sz), -1, dtype=np.intp)
    for (i, aon) in enumerate(op_acting_on):
        acting_on[i][:len(aon)] = aon

    local_states = np.full(
        (n_operators, max_acting_on_sz, max_local_hilbert_size), np.nan)
    basis = np.full((n_operators, max_acting_on_sz), 1e10, dtype=np.int64)

    diag_mels = np.full((n_operators, max_op_size), np.nan, dtype=dtype)
    mels = np.full(
        (n_operators, max_op_size, max_op_size - 1),
        np.nan,
        dtype=dtype,
    )
    x_prime = np.full(
        (n_operators, max_op_size, max_op_size - 1, max_acting_on_sz),
        -1,
        dtype=np.float64,
    )
    n_conns = np.full((n_operators, max_op_size), -1, dtype=np.intp)

    for (i, (aon, op)) in enumerate(operators_dict.items()):
        aon_size = len(aon)
        n_local_states_per_site = np.asarray(
            [hilbert.size_at_index(i) for i in aon])

        ## add an operator to local_states
        for (j, site) in enumerate(aon):
            local_states[i, j, :hilbert.shape[site]] = np.asarray(
                hilbert.states_at_index(site))

        ba = 1
        for s in range(aon_size):
            basis[i, s] = ba
            ba *= hilbert.shape[aon_size - s - 1]

        # eventually could support sparse matrices
        # if isinstance(op, sparse.spmatrix):
        #    op = op.todense()

        _append_matrix(
            op,
            diag_mels[i],
            mels[i],
            x_prime[i],
            n_conns[i],
            aon_size,
            local_states[i],
            mel_cutoff,
            n_local_states_per_site,
        )

    nonzero_diagonal = (np.any(np.abs(diag_mels) >= mel_cutoff)
                        or np.abs(constant) >= mel_cutoff)

    max_conn_size = 1 if nonzero_diagonal else 0
    for op in operators:
        nnz_mat = np.abs(op) > mel_cutoff
        nnz_mat[np.diag_indices(nnz_mat.shape[0])] = False
        nnz_rows = np.sum(nnz_mat, axis=1)
        max_conn_size += np.max(nnz_rows)

    return {
        "acting_on": acting_on,
        "acting_size": acting_size,
        "diag_mels": diag_mels,
        "mels": mels,
        "x_prime": x_prime,
        "n_conns": n_conns,
        "local_states": local_states,
        "basis": basis,
        "nonzero_diagonal": nonzero_diagonal,
        "max_conn_size": max_conn_size,
    }