Repository: netket/netket

Issue:
Core dumped error when multypling hilbert spaces
Came across this while discussing with Jannes.
This is  a numba error because it core-dumps and disabling numba makes it work, but maybe we can find a workaround.

This works
```python
NUMBA_DISABLE_JIT=1 python -c 'import netket as nk; nk.hilbert.Fock(3)*nk.hilbert.Spin(0.5, 2, total_sz=0.0)'
````

This crashes with no info
```python
➜ python -c 'import netket as nk; nk.hilbert.Fock(3)*nk.hilbert.Spin(0.5, 2, total_sz=0.0)'
Fatal Python error: Cannot recover from stack overflow.
Python runtime state: initialized

Current thread 0x00007f2289bee740 (most recent call first):
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/controlflow.py", line 591 in _find_back_edges
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/controlflow.py", line 207 in _back_edges
  File "/usr/lib64/python3.8/functools.py", line 967 in __get__
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/controlflow.py", line 665 in _find_loops
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/controlflow.py", line 219 in _loops
  File "/usr/lib64/python3.8/functools.py", line 967 in __get__
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/controlflow.py", line 561 in _find_post_dominators
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/controlflow.py", line 227 in _post_doms
  File "/usr/lib64/python3.8/functools.py", line 967 in __get__
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/controlflow.py", line 268 in backbone
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/byteflow.py", line 1505 in __init__
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/interpreter.py", line 360 in interpret
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/untyped_passes.py", line 86 in run_pass
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/compiler_machinery.py", line 269 in check
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/compiler_machinery.py", line 296 in _runPass
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/compiler_lock.py", line 35 in _acquire_compile_lock
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/compiler_machinery.py", line 341 in run
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/compiler.py", line 463 in _compile_core
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/compiler.py", line 497 in _compile_bytecode
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/compiler.py", line 429 in compile_extra
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/compiler.py", line 693 in compile_extra
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/dispatcher.py", line 152 in _compile_core
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/dispatcher.py", line 139 in _compile_cached
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/dispatcher.py", line 125 in compile
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/dispatcher.py", line 965 in compile
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/dispatcher.py", line 363 in get_call_template
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/types/functions.py", line 541 in get_call_type
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/experimental/jitclass/base.py", line 417 in generic
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/typing/templates.py", line 351 in apply
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/types/functions.py", line 400 in get_call_type
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/typing/context.py", line 248 in _resolve_user_function_type
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/typing/context.py", line 196 in resolve_function_type
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/typeinfer.py", line 1555 in resolve_call
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/typeinfer.py", line 601 in resolve
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/typeinfer.py", line 578 in __call__
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/typeinfer.py", line 155 in propagate
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/typeinfer.py", line 1078 in propagate
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/typed_passes.py", line 83 in type_inference_stage
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/typed_passes.py", line 105 in run_pass
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/compiler_machinery.py", line 269 in check
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/compiler_machinery.py", line 296 in _runPass
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/compiler_lock.py", line 35 in _acquire_compile_lock
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/compiler_machinery.py", line 341 in run
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/compiler.py", line 463 in _compile_core
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/compiler.py", line 497 in _compile_bytecode
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/compiler.py", line 429 in compile_extra
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/compiler.py", line 693 in compile_extra
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/dispatcher.py", line 152 in _compile_core
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/dispatcher.py", line 139 in _compile_cached
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/dispatcher.py", line 125 in compile
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/dispatcher.py", line 965 in compile
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/core/dispatcher.py", line 420 in _compile_for_args
  File "/home/filippovicentini/Documents/pythonenvs/netket_env/lib64/python3.8/site-packages/numba/experimental/jitclass/boxing.py", line 59 in wrapper
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/homogeneous.py", line 162 in _get_hilbert_index
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/homogeneous.py", line 117 in n_states
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 68 in <listcomp>
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 68 in __init__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 180 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 227 in __mul__
  File "/home/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/tensor_hilbert.py", line 172 in __mul__
  ...
zsh: IOT instruction (core dumped)  python -c 
```


Discussion:
Minimised this bug.

Happens because of a stack-overflow or something similar due to a loop where `n_states` calls `get_hilbert_index` which calls `n_states`...

```python
>>> a=nk.hilbert.Spin(0.5, 4, total_sz=0)
>>> a.n_states
doing
b4 :  True True False
getting hilbert index
- first time self.is_indexable = True, Spin(s=1/2, total_sz=0, N=4)
self._has_constraint = True
   building bare self._constraint_fn = <function Spin.__init__.<locals>.constraints at 0x10cbcc160> and self._hilbert_index=<numba.experimental.jitclass.boxing.HilbertIndex object at 0x10c6d85b0>
doing
b4 :  True True False
getting hilbert index
af :  True True False
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/homogeneous.py", line 119, in n_states
    hind = self._get_hilbert_index()
  File "/Users/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/homogeneous.py", line 167, in _get_hilbert_index
    all_states = self.all_states()
  File "/Users/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/discrete_hilbert.py", line 207, in all_states
    numbers = np.arange(0, self.n_states, dtype=np.int64)
  File "/Users/filippovicentini/Dropbox/Ricerca/Codes/Python/netket/netket/hilbert/homogeneous.py", line 124, in n_states
    return self._bare_numbers.shape[0]
AttributeError: 'Spin' object has no attribute '_bare_numbers'. Did you mean: '_to_bare_numbers'?
>>>
```

Resolving patch:
diff --git a/netket/hilbert/discrete_hilbert.py b/netket/hilbert/discrete_hilbert.py
--- a/netket/hilbert/discrete_hilbert.py
+++ b/netket/hilbert/discrete_hilbert.py
@@ -247,4 +247,4 @@ def __mul__(self, other: "DiscreteHilbert"):
                 if res is not NotImplemented:
                     return res
 
-            return TensorHilbert(self) * other
+            return TensorHilbert(self, other)
diff --git a/netket/hilbert/tensor_hilbert.py b/netket/hilbert/tensor_hilbert.py
--- a/netket/hilbert/tensor_hilbert.py
+++ b/netket/hilbert/tensor_hilbert.py
@@ -36,7 +36,7 @@ class TensorHilbert(DiscreteHilbert):
 
         >>> from netket.hilbert import Spin, Fock
         >>> Fock(3)*Spin(0.5, 5)
-        Fock(n_max=3, N=1)*Spin(s=1/2, N=5)
+        Fock(n_max=3, N=1)⊗Spin(s=1/2, N=5)
         >>> type(_)
         <class 'netket.hilbert.tensor_hilbert.TensorHilbert'>
 
@@ -158,9 +158,12 @@ def states_to_local_indices(self, x):
         return out
 
     def __repr__(self):
+        if len(self._hilbert_spaces) == 1:
+            return f"TensorHilbert({self._hilbert_spaces[0]})"
+
         _str = "{}".format(self._hilbert_spaces[0])
         for hi in self._hilbert_spaces[1:]:
-            _str += "*{}".format(hi)
+            _str += "⊗{}".format(hi)
 
         return _str
 
@@ -179,14 +182,12 @@ def __mul__(self, other):
             space_center_r = other
             spaces_r = tuple()
 
-        try:
-            spaces_center = space_center_l * space_center_r
-            if isinstance(spaces_center, TensorHilbert):
-                spaces_center = (space_center_l, space_center_r)
-            else:
-                spaces_center = (spaces_center,)
-        except Exception:
+        # Attempt to 'merge' the two spaces at the interface.
+        spaces_center = space_center_l * space_center_r
+        if isinstance(spaces_center, TensorHilbert):
             spaces_center = (space_center_l, space_center_r)
+        else:
+            spaces_center = (spaces_center,)
 
         return TensorHilbert(*spaces_l, *spaces_center, *spaces_r)
 


Test patch:
diff --git a/test/hilbert/test_hilbert.py b/test/hilbert/test_hilbert.py
--- a/test/hilbert/test_hilbert.py
+++ b/test/hilbert/test_hilbert.py
@@ -502,3 +502,9 @@ def test_no_particles():
     with pytest.raises(ValueError):
         # also test negative particles
         _ = Fock(n_max=3, n_particles=-1, N=4)
+
+
+def test_tensor_no_recursion():
+    # Issue https://github.com/netket/netket/issues/1101
+    hi = nk.hilbert.Fock(3) * nk.hilbert.Spin(0.5, 2, total_sz=0.0)
+    assert isinstance(hi, nk.hilbert.TensorHilbert)