Repository: netket/netket

Issue:
Error when using Pauli string operator 
Hey guys, just seen that you released a beta version of netket3. I skimmed over the documentation and ran a first few test and it looks super cool and runs very fast. Awesome work!

However, when I am trying to use the PauliString operator I get the following error: 
<img width="1728" alt="grafik" src="https://user-images.githubusercontent.com/50867386/112296334-19744c80-8c95-11eb-90b3-1ffcc2f91fb1.png">

My code looks the following: 
```python
import netket as nk
import numpy as np
import json
import time

ha = nk.operator.PauliStrings(operators=['XX','ZZ'], weights=[1,3])
hi = ha.hilbert
ma = nk.models.RBM(alpha=1)
sa = nk.sampler.MetropolisHamiltonian(hilbert=hi, hamiltonian=ha)
op = nk.optimizer.Sgd(learning_rate=0.01)
vs = nk.variational.MCState(sa, ma, n_samples=1000)
sr = nk.optimizer.SR(diag_shift=.1)
gs = nk.VMC(hamiltonian=ha,optimizer=op,sr=sr,variational_state=vs)
gs.run(n_iter=250)
```

Best
Thorben


Discussion:

Resolving patch:
diff --git a/netket/operator/_pauli_strings.py b/netket/operator/_pauli_strings.py
--- a/netket/operator/_pauli_strings.py
+++ b/netket/operator/_pauli_strings.py
@@ -13,6 +13,8 @@
 # limitations under the License.
 
 import re
+from typing import List, Union
+from netket.utils.types import Dtype
 
 import numpy as np
 from numba import jit
@@ -25,7 +27,13 @@
 class PauliStrings(AbstractOperator):
     """A Hamiltonian consisiting of the sum of products of Pauli operators."""
 
-    def __init__(self, operators, weights, cutoff=1.0e-10, dtype=complex):
+    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.
 
@@ -72,7 +80,7 @@ def valid_match(strg, search=re.compile(r"^[XYZI]+$").search):
         n_operators = len(operators)
 
         self._cutoff = cutoff
-        b_weights = np.asarray(weights, dtype=np.complex128)
+        b_weights = np.asarray(weights, dtype=dtype)
 
         b_to_change = [] * n_operators
         b_z_check = [] * n_operators
@@ -124,7 +132,7 @@ def append(key, k):
         _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=np.complex128)
+        _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)
 
@@ -151,11 +159,18 @@ def append(key, k):
         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)
 
     @property
-    def dtype(self):
+    def dtype(self) -> Dtype:
+        """The dtype of the operator's matrix elements ⟨σ|Ô|σ'⟩."""
         return self._dtype
 
+    @property
+    def is_hermitian(self) -> bool:
+        """Returns true if this operator is hermitian."""
+        return self._is_hermitian
+
     @staticmethod
     @jit(nopython=True)
     def _flattened_kernel(
@@ -171,10 +186,11 @@ def _flattened_kernel(
         z_check,
         cutoff,
         max_conn,
+        pad=False,
     ):
 
-        x_prime = np.empty((x.shape[0] * max_conn, x_prime.shape[1]))
-        mels = np.empty((x.shape[0] * max_conn), dtype=mels.dtype)
+        x_prime = np.zeros((x.shape[0] * max_conn, x_prime.shape[1]))
+        mels = np.zeros((x.shape[0] * max_conn), dtype=mels.dtype)
 
         n_c = 0
         for b in range(x.shape[0]):
@@ -196,10 +212,14 @@ def _flattened_kernel(
                         x_prime[n_c, site] = 1 - x_prime[n_c, site]
                     mels[n_c] = mel
                     n_c += 1
+
+            if pad:
+                n_c = (b + 1) * max_conn
+
             sections[b] = n_c
         return x_prime[:n_c], mels[:n_c]
 
-    def get_conn_flattened(self, x, sections):
+    def get_conn_flattened(self, x, sections, pad=False):
         r"""Finds the connected elements of the Operator. Starting
         from a given quantum number x, it finds all other quantum numbers x' such
         that the matrix element :math:`O(x,x')` is different from zero. In general there
@@ -234,6 +254,7 @@ def get_conn_flattened(self, x, sections):
             self._z_check,
             self._cutoff,
             self._n_operators,
+            pad,
         )
 
     def _get_conn_flattened_closure(self):
@@ -247,7 +268,7 @@ def _get_conn_flattened_closure(self):
         _z_check = self._z_check
         _cutoff = self._cutoff
         _n_operators = self._n_operators
-        fun = self._get_conn_flattened_kernel
+        fun = self._flattened_kernel
 
         def gccf_fun(x, sections):
             return fun(
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -30,8 +30,9 @@
     url="http://github.com/netket/netket",
     author_email="netket@netket.org",
     license="Apache 2.0",
-    long_description="""NetKet is an open - source project delivering cutting - edge
-         methods for the study of many - body quantum systems with artificial
+    summmary="Netket : Machine Learning techniques for many-body quantum systems.",
+    long_description="""NetKet is an open-source project delivering cutting-edge
+         methods for the study of many-body quantum systems with artificial
          neural networks and machine learning techniques.""",
     classifiers=[
         "Programming Language :: Python :: 3",


Test patch: