From cd2750c164a5a9af33c74b9898cfbbf32724ce95 Mon Sep 17 00:00:00 2001
From: Joseph Weston <joseph@weston.cloud>
Date: Tue, 10 Sep 2019 13:31:18 +0200
Subject: [PATCH] add a deprecation warning to "hamiltonian" for vectorized
 systems

Update internal uses of 'hamiltonian' to suppress this warning
and add a TODO to update to the new API.
---
 kwant/builder.py            |  7 +++++
 kwant/operator.pyx          | 11 ++++++--
 kwant/tests/test_builder.py | 53 ++++++++++++++++++++++++++-----------
 3 files changed, 54 insertions(+), 17 deletions(-)

diff --git a/kwant/builder.py b/kwant/builder.py
index 9a0bfcff..31019c34 100644
--- a/kwant/builder.py
+++ b/kwant/builder.py
@@ -2048,6 +2048,13 @@ class _VectorizedFinalizedBuilderMixin(_FinalizedBuilderMixin):
         't' (< 0) then the associated hopping is stored in term '-t - 1'.
     """
     def hamiltonian(self, i, j, *args, params=None):
+        warnings.warn(
+            "Querying individual matrix elements with 'hamiltonian' is "
+            "deprecated, and now takes O(log N) time where N is the number "
+            "of matrix elements per hamiltonian term. Query many matrix "
+            "elements at once using 'hamiltonian_term'.",
+            KwantDeprecationWarning
+        )
         site_offsets = np.cumsum([0] + [len(s) for s in self.site_arrays])
         if i == j:
             which_term = self._onsite_term_by_site_id[i]
diff --git a/kwant/operator.pyx b/kwant/operator.pyx
index 8de1cad8..2a3e71ed 100644
--- a/kwant/operator.pyx
+++ b/kwant/operator.pyx
@@ -13,6 +13,7 @@ import cython
 from operator import itemgetter
 import functools as ft
 import collections
+import warnings
 
 import numpy as np
 import tinyarray as ta
@@ -25,7 +26,9 @@ from .graph.core import DisabledFeatureError, NodeDoesNotExistError
 from .graph.defs cimport gint
 from .graph.defs import gint_dtype
 from .system import is_infinite, Site
-from ._common import UserCodeError, get_parameters, deprecate_args
+from ._common import (
+    UserCodeError, KwantDeprecationWarning, get_parameters, deprecate_args
+)
 
 
 ################ Generic Utility functions
@@ -699,7 +702,11 @@ cdef class _LocalOperator:
             return mat
 
         offsets, norbs = _get_all_orbs(self.where, self._site_ranges)
-        return  BlockSparseMatrix(self.where, offsets, norbs, get_ham)
+        # TODO: update operators to use 'hamiltonian_term' rather than
+        #       'hamiltonian'.
+        with warnings.catch_warnings():
+            warnings.simplefilter("ignore", category=KwantDeprecationWarning)
+            return  BlockSparseMatrix(self.where, offsets, norbs, get_ham)
 
     def __getstate__(self):
         return (
diff --git a/kwant/tests/test_builder.py b/kwant/tests/test_builder.py
index 575d0caf..253c9d7c 100644
--- a/kwant/tests/test_builder.py
+++ b/kwant/tests/test_builder.py
@@ -20,7 +20,7 @@ from numpy.testing import assert_almost_equal
 
 import kwant
 from kwant import builder, system
-from kwant._common import ensure_rng
+from kwant._common import ensure_rng, KwantDeprecationWarning
 
 
 def test_bad_keys():
@@ -590,12 +590,16 @@ def test_hamiltonian_evaluation(vectorize):
     for i in range(len(tags)):
         site = fsyst.sites[i]
         assert site in sites
-        assert fsyst.hamiltonian(i, i) == f_onsite(site)
+        with warnings.catch_warnings():
+            warnings.filterwarnings("ignore", category=KwantDeprecationWarning)
+            assert fsyst.hamiltonian(i, i) == f_onsite(site)
 
     for t, h in fsyst.graph:
         tsite = fsyst.sites[t]
         hsite = fsyst.sites[h]
-        assert fsyst.hamiltonian(t, h) == f_hopping(tsite, hsite)
+        with warnings.catch_warnings():
+            warnings.filterwarnings("ignore", category=KwantDeprecationWarning)
+            assert fsyst.hamiltonian(t, h) == f_hopping(tsite, hsite)
 
     # test when user-function raises errors
     def onsite_raises(site):
@@ -608,13 +612,18 @@ def test_hamiltonian_evaluation(vectorize):
         a, b = hop
         # exceptions are converted to kwant.UserCodeError and we add our message
         with raises(kwant.UserCodeError) as exc_info:
-            fsyst.hamiltonian(a, a)
+            with warnings.catch_warnings():
+                warnings.filterwarnings("ignore", category=KwantDeprecationWarning)
+                fsyst.hamiltonian(a, a)
         msg = 'Error occurred in user-supplied value function "onsite_raises"'
         assert msg in str(exc_info.value)
 
         for hop in [(a, b), (b, a)]:
             with raises(kwant.UserCodeError) as exc_info:
-                fsyst.hamiltonian(*hop)
+                with warnings.catch_warnings():
+                    # Ignore deprecation warnings for 'hamiltonian'
+                    warnings.filterwarnings("ignore", category=KwantDeprecationWarning)
+                    fsyst.hamiltonian(*hop)
             msg = ('Error occurred in user-supplied '
                    'value function "hopping_raises"')
             assert msg in str(exc_info.value)
@@ -682,14 +691,18 @@ def test_vectorized_hamiltonian_evaluation():
     for i in range(len(tags)):
         site = fsyst_vectorized.sites[i]
         assert site in sites
-        assert (
-            fsyst_vectorized.hamiltonian(i, i)
-            == fsyst_simple.hamiltonian(i, i))
+        with warnings.catch_warnings():
+            warnings.filterwarnings("ignore", category=KwantDeprecationWarning)
+            assert (
+                fsyst_vectorized.hamiltonian(i, i)
+                == fsyst_simple.hamiltonian(i, i))
 
     for t, h in fsyst_vectorized.graph:
-        assert (
-            fsyst_vectorized.hamiltonian(t, h)
-            == fsyst_simple.hamiltonian(t, h))
+        with warnings.catch_warnings():
+            warnings.filterwarnings("ignore", category=KwantDeprecationWarning)
+            assert (
+                fsyst_vectorized.hamiltonian(t, h)
+                == fsyst_simple.hamiltonian(t, h))
 
     # Test infinite system, including hoppings that go both ways into
     # the next cell
@@ -1419,15 +1432,25 @@ def test_argument_passing(vectorize):
 
     # test that mixing 'args' and 'params' raises TypeError
     with raises(TypeError):
-        syst.hamiltonian(0, 0, *(2, 1), params=dict(p1=2, p2=1))
+        with warnings.catch_warnings():
+            warnings.filterwarnings("ignore", category=KwantDeprecationWarning)
+            syst.hamiltonian(0, 0, *(2, 1), params=dict(p1=2, p2=1))
+
     with raises(TypeError):
-        inf_syst.hamiltonian(0, 0, *(2, 1), params=dict(p1=2, p2=1))
+        with warnings.catch_warnings():
+            warnings.filterwarnings("ignore", category=KwantDeprecationWarning)
+            inf_syst.hamiltonian(0, 0, *(2, 1), params=dict(p1=2, p2=1))
 
     # Missing parameters raises TypeError
     with raises(TypeError):
-        syst.hamiltonian(0, 0, params=dict(p1=2))
+        with warnings.catch_warnings():
+            warnings.filterwarnings("ignore", category=KwantDeprecationWarning)
+            syst.hamiltonian(0, 0, params=dict(p1=2))
+
     with raises(TypeError):
-        syst.hamiltonian_submatrix(params=dict(p1=2))
+        with warnings.catch_warnings():
+            warnings.filterwarnings("ignore", category=KwantDeprecationWarning)
+            syst.hamiltonian_submatrix(params=dict(p1=2))
 
     # test that passing parameters without default values works, and that
     # passing parameters with default values fails
-- 
GitLab