def __init__( self, hilbert: AbstractHilbert, operators: List[str] = None, weights: List[Union[float, complex]] = None, *, cutoff: float = 1.0e-10, dtype: DType = complex, ): """ Constructs a new ``PauliStrings`` operator given a set of Pauli operators. This class has two possible forms for initialization: ``PauliStrings(hilbert, operators, ...)`` or ``PauliStrings(operators, ...)``. When no hilbert argument is passed, the hilbert defaults to Qubit, where the number of qubits is automatically deduced from the operators. Args: hilbert: A hilbert space, optional (is no ``AbstractHilbert`` is passed, default is Qubit) operators (list(string)): A list of Pauli operators in string format, e.g. ['IXX', 'XZI']. weights: A list of amplitudes of the corresponding Pauli operator. cutoff (float): a cutoff to remove small matrix elements Examples: Constructs a new ``PauliStrings`` operator X_0*X_1 + 3.*Z_0*Z_1 with both construction schemes. >>> import netket as nk >>> operators, weights = ['XX','ZZ'], [1,3] >>> op = nk.operator.PauliStrings(operators, weights) >>> op.hilbert Qubit(N=2) >>> op.hilbert.size 2 >>> hilbert = nk.hilbert.Spin(1/2, 2) >>> op = nk.operator.PauliStrings(hilbert, operators, weights) >>> op.hilbert Spin(s=1/2, N=2) """ if hilbert is None: raise ValueError("None-valued hilbert passed.") if not isinstance(hilbert, AbstractHilbert): # if first argument is not Hilbert, then shift all arguments by one hilbert, operators, weights = None, hilbert, operators if operators is None: raise ValueError( "None valued operators passed. (Might arised when passing None valued hilbert explicitly)" ) if len(operators) == 0: raise ValueError("No Pauli operators passed.") if weights is None: # default weight is 1 weights = [True for i in operators] if len(weights) != len(operators): raise ValueError("weights should have the same length as operators.") if not np.isscalar(cutoff) or cutoff < 0: raise ValueError("invalid cutoff in PauliStrings.") _hilb_size = len(operators[0]) consistent = all(len(op) == _hilb_size for op in operators) if not consistent: raise ValueError("Pauli strings have inhomogeneous lengths.") consistent = all(bool(valid_pauli_regex.search(op)) for op in operators) if not consistent: raise ValueError( """Operators in string must be one of the Pauli operators X,Y,Z, or the identity I""" ) if hilbert is None: hilbert = Qubit(_hilb_size) super().__init__(hilbert) if self.hilbert.local_size != 2: raise ValueError( "PauliStrings only work for local hilbert size 2 where PauliMatrices are defined" ) self._cutoff = cutoff b_weights = np.asarray(weights, dtype=dtype) self._is_hermitian = np.allclose(b_weights.imag, 0.0) self._orig_operators = np.array(operators, dtype=str) self._orig_weights = np.array(weights, dtype=dtype) self._dtype = dtype self._initialized = False
# # Small hilbert space tests # # Spin 1/2 hilberts["Spin 1/2 Small"] = Spin(s=0.5, N=10) # Spin 3 hilberts["Spin 1/2 with total Sz Small"] = Spin(s=3, total_sz=1.0, N=4) # Boson hilberts["Fock Small"] = Fock(n_max=3, N=5) # Qubit hilberts["Qubit Small"] = Qubit(N=1) # Custom Hilbert hilberts["Custom Hilbert Small"] = CustomHilbert(local_states=[-1232, 132, 0], N=5) # Custom Hilbert hilberts["DoubledHilbert[Spin]"] = DoubledHilbert(Spin(0.5, N=5)) hilberts["DoubledHilbert[Spin(total_sz=0.5)]"] = DoubledHilbert( Spin(0.5, N=5, total_sz=0.5)) hilberts["DoubledHilbert[Fock]"] = DoubledHilbert(Spin(0.5, N=5)) hilberts["DoubledHilbert[CustomHilbert]"] = DoubledHilbert( CustomHilbert(local_states=[-1232, 132, 0], N=5))
def __init__( self, operators: List[str], weights: List[Union[float, complex]], cutoff: float = 1.0e-10, dtype: DType = complex, ): """ Constructs a new ``PauliStrings`` operator given a set of Pauli operators. Args: operators (list(string)): A list of Pauli operators in string format, e.g. ['IXX', 'XZI']. weights: A list of amplitudes of the corresponding Pauli operator. cutoff (float): a cutoff to remove small matrix elements Examples: Constructs a new ``PauliStrings`` operator X_0*X_1 + 3.*Z_0*Z_1. >>> import netket as nk >>> op = nk.operator.PauliStrings(operators=['XX','ZZ'], weights=[1,3]) >>> op.hilbert.size 2 """ if len(operators) == 0: raise ValueError("No Pauli operators passed.") if len(weights) != len(operators): raise ValueError( "weights should have the same length as operators.") if not np.isscalar(cutoff) or cutoff < 0: raise ValueError("invalid cutoff in PauliStrings.") _n_qubits = len(operators[0]) consistent = all(len(op) == _n_qubits for op in operators) if not consistent: raise ValueError("Pauli strings have inhomogeneous lengths.") def valid_match(strg, search=re.compile(r"^[XYZI]+$").search): return bool(search(strg)) consistent = all(valid_match(op) for op in operators) if not consistent: raise ValueError("""Operators in string must be one of the Pauli operators X,Y,Z, or the identity I""") self._n_qubits = _n_qubits super().__init__(Qubit(_n_qubits)) n_operators = len(operators) self._cutoff = cutoff b_weights = np.asarray(weights, dtype=dtype) b_to_change = [] * n_operators b_z_check = [] * n_operators acting = {} def find_char(s, ch): return [i for i, ltr in enumerate(s) if ltr == ch] def append(key, k): # convert list to tuple key = tuple(key) if key in acting: acting[key].append(k) else: acting[key] = [k] _n_z_check_max = 0 for i, op in enumerate(operators): b_to_change = [] b_z_check = [] b_weights = weights[i] x_ops = find_char(op, "X") if len(x_ops): b_to_change += x_ops y_ops = find_char(op, "Y") if len(y_ops): b_to_change += y_ops b_weights *= (1.0j)**(len(y_ops)) b_z_check += y_ops z_ops = find_char(op, "Z") if len(z_ops): b_z_check += z_ops _n_z_check_max = max(_n_z_check_max, len(b_z_check)) append(b_to_change, (b_weights, b_z_check)) # now group together operators with same final state n_operators = len(acting) _n_op_max = max(list(map(lambda x: len(x), list(acting.values()))), default=n_operators) # unpacking the dictionary into fixed-size arrays _sites = np.empty((n_operators, _n_qubits), dtype=np.intp) _ns = np.empty((n_operators), dtype=np.intp) _n_op = np.empty(n_operators, dtype=np.intp) _weights = np.empty((n_operators, _n_op_max), dtype=dtype) _nz_check = np.empty((n_operators, _n_op_max), dtype=np.intp) _z_check = np.empty((n_operators, _n_op_max, _n_z_check_max), dtype=np.intp) for i, act in enumerate(acting.items()): sites = act[0] nsi = len(sites) _sites[i, :nsi] = sites _ns[i] = nsi values = act[1] _n_op[i] = len(values) for j in range(_n_op[i]): _weights[i, j] = values[j][0] _nz_check[i, j] = len(values[j][1]) _z_check[i, j, :_nz_check[i, j]] = values[j][1] self._sites = _sites self._ns = _ns self._n_op = _n_op self._weights = _weights self._nz_check = _nz_check self._z_check = _z_check self._x_prime_max = np.empty((n_operators, _n_qubits)) self._mels_max = np.empty((n_operators), dtype=dtype) self._n_operators = n_operators self._dtype = dtype self._is_hermitian = np.allclose(self._weights.imag, 0.0)