Repository: python-adaptive/adaptive

Issue:
Returning np.nan breaks the 1D learner
From commit: 4381a9e52faef7332b2ad0d3354ebb44616df7d9 onward,
returning (a np.array of) np.nan cancels the runner.
Example code, to compare the working commit, f38d20fe2899582c46fcc23f0612242cce6904bd,
with the not working one, 4381a9e52faef7332b2ad0d3354ebb44616df7d9.
```python
import adaptive
adaptive.notebook_extension()
import numpy as np
def f(x):
    if 0 < x < 1:
        return x
    else:
        return np.nan

learner = adaptive.Learner1D(f, 0.1*np.array([-1, 1]))
runner = adaptive.Runner(learner)
runner.live_info()
```

Discussion:
jbweston: LGTM

Patch:
diff --git a/adaptive/learner/learner1D.py b/adaptive/learner/learner1D.py
index 9aaae66..012f930 100644
--- a/adaptive/learner/learner1D.py
+++ b/adaptive/learner/learner1D.py
@@ -684,7 +684,7 @@ def finite_loss(ival, loss, x_scale):
     sort intervals that have infinite loss."""
     # If the loss is infinite we return the
     # distance between the two points.
-    if math.isinf(loss):
+    if math.isinf(loss) or math.isnan(loss):
         loss = (ival[1] - ival[0]) / x_scale
         if len(ival) == 3:
             # Used when constructing quals. Last item is


Test patch:
diff --git a/adaptive/tests/test_learner1d.py b/adaptive/tests/test_learner1d.py
index 937b9dc..c100c82 100644
--- a/adaptive/tests/test_learner1d.py
+++ b/adaptive/tests/test_learner1d.py
@@ -363,3 +363,15 @@ def test_curvature_loss_vectors():
     learner = Learner1D(f, (-1, 1), loss_per_interval=loss)
     simple(learner, goal=lambda l: l.npoints > 100)
     assert learner.npoints > 100
+
+
+def test_NaN_loss():
+    # see https://github.com/python-adaptive/adaptive/issues/145
+    def f(x):
+        a = 0.01
+        if random.random() < 0.2:
+            return np.NaN
+        return x + a**2 / (a**2 + x**2)
+
+    learner = Learner1D(f, bounds=(-1, 1))
+    simple(learner, lambda l: l.npoints > 100)