From 2bdb12b57bc235e41d6d0044106e931f6061cbed Mon Sep 17 00:00:00 2001
From: Bas Nijholt <basnijholt@gmail.com>
Date: Fri, 21 Sep 2018 11:40:15 +0200
Subject: [PATCH] 2D: do not use points outside of the bounds in the point
 chosing algorithms

---
 adaptive/learner/learner2D.py | 56 +++++++++++++++++++++++++----------
 1 file changed, 41 insertions(+), 15 deletions(-)

diff --git a/adaptive/learner/learner2D.py b/adaptive/learner/learner2D.py
index d6176b24..b8dd70c8 100644
--- a/adaptive/learner/learner2D.py
+++ b/adaptive/learner/learner2D.py
@@ -269,48 +269,74 @@ class Learner2D(BaseLearner):
         return not any((p in self.pending_points or p in self._stack)
                        for p in self._bounds_points)
 
-    def data_combined(self):
-        # Interpolate the unfinished points
-        data_combined = copy(self.data)
+    def _data_in_bounds(self):
+        if self.data:
+            points = np.array(list(self.data.keys()))
+            values = np.array(list(self.data.values()), dtype=float)
+            ll, ur = np.reshape(self.bounds, (2, 2)).T
+            inds = np.all(np.logical_and(ll <= points, points <= ur), axis=1)
+            return points[inds], values[inds].reshape(-1, self.vdim)
+        return np.zeros((0, 2)), np.zeros((0, self.vdim), dtype=float)
+
+    def _data_interp(self):
         if self.pending_points:
-            points_interp = list(self.pending_points)
+            points = list(self.pending_points)
             if self.bounds_are_done:
-                values_interp = self.ip()(self._scale(points_interp))
+                values = self.ip()(self._scale(points))
             else:
                 # Without the bounds the interpolation cannot be done properly,
                 # so we just set everything to zero.
-                values_interp = np.zeros((len(points_interp), self.vdim))
-
-            for point, value in zip(points_interp, values_interp):
-                data_combined[point] = value
+                values = np.zeros((len(points), self.vdim))
+            return points, values
+        return np.zeros((0, 2)), np.zeros((0, self.vdim), dtype=float)
+
+    def _data_combined(self):
+        points, values = self._data_in_bounds()
+        if not self.pending_points:
+            return points, values
+        points_interp, values_interp = self._data_interp()
+        points_combined = np.vstack([points, points_interp])
+        values_combined = np.vstack([values, values_interp])
+        return points_combined, values_combined
 
-        return data_combined
+    def data_combined(self):
+        # Interpolate the unfinished points
+        points, values = self._data_combined()
+        return {tuple(k): v for k, v in zip(points, values)}
 
     def ip(self):
         if self._ip is None:
-            points = self._scale(list(self.data.keys()))
-            values = np.array(list(self.data.values()), dtype=float)
+            points, values = self._data_in_bounds()
+            points = self._scale(points)
             self._ip = interpolate.LinearNDInterpolator(points, values)
         return self._ip
 
     def ip_combined(self):
         if self._ip_combined is None:
-            data_combined = self.data_combined()
-            points = self._scale(list(data_combined.keys()))
-            values = np.array(list(data_combined.values()), dtype=float)
+            points, values = self._data_combined()
+            points = self._scale(points)
             self._ip_combined = interpolate.LinearNDInterpolator(points,
                                                                  values)
         return self._ip_combined
 
+    def inside_bounds(self, xy):
+        x, y = xy
+        (xmin, xmax), (ymin, ymax) = self.bounds
+        return xmin <= x <= xmax and ymin <= y <= ymax
+
     def tell(self, point, value):
         point = tuple(point)
         self.data[point] = value
+        if not self.inside_bounds(point):
+            return
         self.pending_points.discard(point)
         self._ip = None
         self._stack.pop(point, None)
 
     def tell_pending(self, point):
         point = tuple(point)
+        if not self.inside_bounds(point):
+            return
         self.pending_points.add(point)
         self._ip_combined = None
         self._stack.pop(point, None)
-- 
GitLab