Repository: netket/netket

Issue:
Get rid of netket::Ising and other hard-coded hamiltonians in c++
In the spirit of #199 , we can safely remove the Ising and Heisenberg hamiltonians from the C++ classes, since those are nothing but specific cases of more general hamiltonians (basically they are just LocalOperators or even GraphOperator). 
Convenient constructors can be defined in few lines of python, and moved to the python part of the library. 

The BoseHubbard hamiltonian is a bit trickier, we can keep it as it is for the moment. 


Discussion:

Resolving patch:
diff --git a/netket/operator.py b/netket/operator.py
--- a/netket/operator.py
+++ b/netket/operator.py
@@ -1 +1,52 @@
 from ._C_netket.operator import *
+import numpy as _np
+
+
+def Ising(hilbert, h, J=1.0):
+    """
+    Constructs a new ``Ising`` given a hilbert space, a transverse field,
+    and (if specified) a coupling constant.
+
+    Args:
+        hilbert: Hilbert space the operator acts on.
+        h: The strength of the transverse field.
+        J: The strength of the coupling. Default is 1.0.
+
+    Examples:
+        Constructs an ``Ising`` operator for a 1D system.
+
+        ```python
+        >>> import netket as nk
+        >>> g = nk.graph.Hypercube(length=20, n_dim=1, pbc=True)
+        >>> hi = nk.hilbert.Spin(s=0.5, graph=g)
+        >>> op = nk.operator.Ising(h=1.321, hilbert=hi, J=0.5)
+        >>> print(op.hilbert.size)
+        20
+    """
+    sigma_x = _np.array([[0, 1], [1, 0]])
+    sz_sz = _np.array([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]])
+    return GraphOperator(hilbert, siteops=[-h * sigma_x], bondops=[J * sz_sz])
+
+
+def Heisenberg(hilbert):
+    """
+    Constructs a new ``Heisenberg`` given a hilbert space.
+    Args:
+        hilbert: Hilbert space the operator acts on.
+    Examples:
+     Constructs a ``Heisenberg`` operator for a 1D system.
+        ```python
+        >>> import netket as nk
+        >>> g = nk.graph.Hypercube(length=20, n_dim=1, pbc=True)
+        >>> hi = nk.hilbert.Spin(s=0.5, total_sz=0, graph=g)
+        >>> op = nk.operator.Heisenberg(hilbert=hi)
+        >>> print(op.hilbert.size)
+        20
+    """
+    sz_sz = _np.array([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]])
+    exchange = _np.array([[0, 0, 0, 0], [0, 0, 2, 0], [0, 2, 0, 0], [0, 0, 0, 0]])
+    if hilbert.graph.is_bipartite:
+        heis_term = sz_sz - exchange
+    else:
+        heis_term = sz_sz + exchange
+    return GraphOperator(hilbert, bondops=[heis_term])


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
@@ -52,14 +52,12 @@
 )
 
 # Heisenberg 1d
-g = nk.graph.Hypercube(length=20, n_dim=1, pbc=True)
-hi = nk.hilbert.Spin(s=0.5, total_sz=0.0, graph=g)
-hilberts["Heisenberg 1d"] = hi
+g1 = nk.graph.Hypercube(length=20, n_dim=1, pbc=True)
+hilberts["Heisenberg 1d"] = nk.hilbert.Spin(s=0.5, total_sz=0.0, graph=g1)
 
 # Bose Hubbard
-g = nk.graph.Hypercube(length=20, n_dim=1, pbc=True)
-hi = nk.hilbert.Boson(n_max=4, n_bosons=20, graph=g)
-hilberts["Bose Hubbard"] = hi
+g3 = nk.graph.Hypercube(length=20, n_dim=1, pbc=True)
+hilberts["Bose Hubbard"] = nk.hilbert.Boson(n_max=4, n_bosons=20, graph=g3)
 
 #
 # Small hilbert space tests
@@ -90,6 +88,7 @@
     local_states=[-1232, 132, 0], graph=nk.graph.Hypercube(length=5, n_dim=1)
 )
 
+
 #
 # Tests
 #
@@ -127,7 +126,6 @@ def test_random_states():
                     assert state in local_states
 
 
-# TODO (jamesETsmith)
 def test_hilbert_index():
     """"""
 
@@ -147,14 +145,19 @@ def test_hilbert_index():
             with pytest.raises(RuntimeError):
                 hi.n_states
 
-            op = nk.operator.Heisenberg(hi)
-
-            with pytest.raises(RuntimeError):
-                m1 = op.to_dense()
-            with pytest.raises(RuntimeError):
-                m2 = op.to_sparse()
-            with pytest.raises(RuntimeError):
-                dw = nk.operator.DirectMatrixWrapper(op)
+        # Check that a large hilbert space raises error when constructing matrices
+        op = nk.operator.Heisenberg(
+            hilbert=nk.hilbert.Spin(
+                s=0.5, graph=nk.graph.Hypercube(length=100, n_dim=1)
+            )
+        )
+
+        with pytest.raises(RuntimeError):
+            m1 = op.to_dense()
+        with pytest.raises(RuntimeError):
+            m2 = op.to_sparse()
+        with pytest.raises(RuntimeError):
+            dw = nk.operator.DirectMatrixWrapper(op)
 
 
 def test_state_iteration():