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)
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)
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)
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)
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)
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)
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, }