示例#1
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)
示例#2
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,
    }