Repository: netket/netket

Issue:
Bug: incompatible matrix is accepted as input when creating local operator
I caught the following bug:

```python
>>> import netket as nk
/Users/vladimirvargas/.local/share/virtualenvs/physics-jfSzH1Jb/lib/python3.9/site-packages/jax/_src/lib/__init__.py:33: UserWarning: JAX on Mac ARM machines is experimental and minimally tested. Please see https://github.com/google/jax/issues/5501 in the event of problems.
  warnings.warn("JAX on Mac ARM machines is experimental and minimally tested. "
>>> hi = nk.hilbert.Fock(5, N=3)
>>> ham = nk.operator.boson.number(hi, 0) * nk.operator.boson.number(hi, 1)
>>> sparse_ham = ham.to_sparse()
>>> ham2 = nk.operator.LocalOperator(hi, sparse_ham, [0, 1])
>>> sparse_ham.shape
(216, 216)
>>> 6**3
216
```

You see, the Hilbert space has three parts. First I create `ham` acting on parts 0 and 1 only. However, when I invoke the sparse form of this Hamiltonian `ham`, I get a matrix for the whole Hilbert space, i.e. this is an operator that acts on the three parts. When I create another operator whose matrix is `sparse_ham` acting on the parts 0 and 1 of the Hilbert space, an error should be raised.

I guess this can be solved by checking that the product of the elements of the input matrix shape must equal the product of the local sizes onto which the matrix acts on in the Hilbert space.
Bug: incompatible matrix is accepted as input when creating local operator
I caught the following bug:

```python
>>> import netket as nk
/Users/vladimirvargas/.local/share/virtualenvs/physics-jfSzH1Jb/lib/python3.9/site-packages/jax/_src/lib/__init__.py:33: UserWarning: JAX on Mac ARM machines is experimental and minimally tested. Please see https://github.com/google/jax/issues/5501 in the event of problems.
  warnings.warn("JAX on Mac ARM machines is experimental and minimally tested. "
>>> hi = nk.hilbert.Fock(5, N=3)
>>> ham = nk.operator.boson.number(hi, 0) * nk.operator.boson.number(hi, 1)
>>> sparse_ham = ham.to_sparse()
>>> ham2 = nk.operator.LocalOperator(hi, sparse_ham, [0, 1])
>>> sparse_ham.shape
(216, 216)
>>> 6**3
216
```

You see, the Hilbert space has three parts. First I create `ham` acting on parts 0 and 1 only. However, when I invoke the sparse form of this Hamiltonian `ham`, I get a matrix for the whole Hilbert space, i.e. this is an operator that acts on the three parts. When I create another operator whose matrix is `sparse_ham` acting on the parts 0 and 1 of the Hilbert space, an error should be raised.

I guess this can be solved by checking that the product of the elements of the input matrix shape must equal the product of the local sizes onto which the matrix acts on in the Hilbert space.


Discussion:

Resolving patch:
diff --git a/netket/operator/_local_operator_helpers.py b/netket/operator/_local_operator_helpers.py
--- a/netket/operator/_local_operator_helpers.py
+++ b/netket/operator/_local_operator_helpers.py
@@ -116,6 +116,8 @@ def canonicalize_input(
     canonicalized_operators = []
     canonicalized_acting_on = []
     for (operator, acting_on) in zip(operators, acting_on):
+        check_valid_opmatrix(hilbert, operator, acting_on)
+
         if operator.dtype is not dtype:
             operator = cast_operator_matrix_dtype(operator, dtype=dtype)
 
@@ -127,6 +129,18 @@ def canonicalize_input(
     return canonicalized_operators, canonicalized_acting_on, dtype
 
 
+def check_valid_opmatrix(hi, mat, acting_on):
+    """ """
+    expected_size = np.prod([hi.shape[aon] for aon in acting_on])
+
+    if mat.shape != (expected_size, expected_size):
+        raise ValueError(
+            f"The matrix of the sub-operator acting on sites {acting_on} "
+            f"must have shape {expected_size, expected_size}, "
+            f"but it has shape {mat.shape}."
+        )
+
+
 # TODO: support sparse arrays without returning dense arrays
 def _reorder_kronecker_product(hi, mat, acting_on) -> Tuple[Array, Tuple]:
     """


Test patch:
diff --git a/test/operator/test_local_operator.py b/test/operator/test_local_operator.py
--- a/test/operator/test_local_operator.py
+++ b/test/operator/test_local_operator.py
@@ -469,3 +469,12 @@ def test_operator():
     op2 = sp * a
 
     assert_same_matrices(op1, op2)
+
+
+def test_error_if_wrong_shape():
+    # Issue #1157
+    # https://github.com/netket/netket/issues/1157
+    hi = nk.hilbert.Fock(5, N=3)
+    mat = np.random.rand(3, 3)
+    with pytest.raises(ValueError):
+        nk.operator.LocalOperator(hi, mat, [0, 1])