jbweston: LGTM
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

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()
```
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)

environment.yml
pip install -e .[notebook]
parse_log_pytest
environment.yml
pytest pytest-randomly pytest-cov
apt-get update apt-get install -y git
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
-e git+https://github.com/python-adaptive/adaptive.git@f1ea46320a64aa3996879de0c0219cd2397885fa#egg=adaptive
argon2-cffi @ file:///home/conda/feedstock_root/build_artifacts/argon2-cffi_1633990451307/work
async_generator @ file:///home/conda/feedstock_root/build_artifacts/async_generator_1722652753231/work
attrs @ file:///home/conda/feedstock_root/build_artifacts/attrs_1671632566681/work
backcall @ file:///home/conda/feedstock_root/build_artifacts/backcall_1592338393461/work
backports.functools-lru-cache @ file:///home/conda/feedstock_root/build_artifacts/backports.functools_lru_cache_1702571698061/work
bleach @ file:///home/conda/feedstock_root/build_artifacts/bleach_1696630167146/work
bokeh @ file:///home/conda/feedstock_root/build_artifacts/bokeh_1625756939897/work
brotlipy==0.7.0
certifi==2021.5.30
cffi @ file:///home/conda/feedstock_root/build_artifacts/cffi_1631636256886/work
charset-normalizer @ file:///home/conda/feedstock_root/build_artifacts/charset-normalizer_1661170624537/work
click==7.1.2
cloudpickle @ file:///home/conda/feedstock_root/build_artifacts/cloudpickle_1674202310934/work
colorama @ file:///home/conda/feedstock_root/build_artifacts/colorama_1655412516417/work
colorcet @ file:///home/conda/feedstock_root/build_artifacts/colorcet_1664843244467/work
contextvars==2.4
coverage==6.2
cryptography @ file:///home/conda/feedstock_root/build_artifacts/cryptography_1634230300355/work
cycler @ file:///home/conda/feedstock_root/build_artifacts/cycler_1635519461629/work
cytoolz==0.11.0
dask @ file:///home/conda/feedstock_root/build_artifacts/dask-core_1614995065708/work
decorator @ file:///home/conda/feedstock_root/build_artifacts/decorator_1641555617451/work
defusedxml @ file:///home/conda/feedstock_root/build_artifacts/defusedxml_1615232257335/work
distributed @ file:///home/conda/feedstock_root/build_artifacts/distributed_1615002625500/work
entrypoints @ file:///home/conda/feedstock_root/build_artifacts/entrypoints_1643888246732/work
HeapDict==1.0.1
holoviews @ file:///home/conda/feedstock_root/build_artifacts/holoviews_1652084638358/work
idna @ file:///home/conda/feedstock_root/build_artifacts/idna_1726459485162/work
immutables @ file:///home/conda/feedstock_root/build_artifacts/immutables_1628601257972/work
importlib-metadata @ file:///home/conda/feedstock_root/build_artifacts/importlib-metadata_1630267465156/work
iniconfig==1.1.1
ipykernel @ file:///home/conda/feedstock_root/build_artifacts/ipykernel_1620912934572/work/dist/ipykernel-5.5.5-py3-none-any.whl
ipyparallel @ file:///home/conda/feedstock_root/build_artifacts/ipyparallel_1681479856252/work
ipython @ file:///home/conda/feedstock_root/build_artifacts/ipython_1609697613279/work
ipython_genutils @ file:///home/conda/feedstock_root/build_artifacts/ipython_genutils_1716278396992/work
ipywidgets @ file:///home/conda/feedstock_root/build_artifacts/ipywidgets_1679421482533/work
jedi @ file:///home/conda/feedstock_root/build_artifacts/jedi_1605054537831/work
Jinja2 @ file:///home/conda/feedstock_root/build_artifacts/jinja2_1636510082894/work
joblib @ file:///home/conda/feedstock_root/build_artifacts/joblib_1663332044897/work
jsonschema @ file:///home/conda/feedstock_root/build_artifacts/jsonschema_1634752161479/work
jupyter-client @ file:///home/conda/feedstock_root/build_artifacts/jupyter_client_1642858610849/work
jupyter-core @ file:///home/conda/feedstock_root/build_artifacts/jupyter_core_1631852698933/work
jupyterlab-pygments @ file:///home/conda/feedstock_root/build_artifacts/jupyterlab_pygments_1601375948261/work
jupyterlab-widgets @ file:///home/conda/feedstock_root/build_artifacts/jupyterlab_widgets_1655961217661/work
kiwisolver @ file:///home/conda/feedstock_root/build_artifacts/kiwisolver_1610099771815/work
Markdown @ file:///home/conda/feedstock_root/build_artifacts/markdown_1710435156458/work
MarkupSafe @ file:///home/conda/feedstock_root/build_artifacts/markupsafe_1621455668064/work
matplotlib @ file:///home/conda/feedstock_root/build_artifacts/matplotlib-suite_1611858699142/work
mistune @ file:///home/conda/feedstock_root/build_artifacts/mistune_1673904152039/work
msgpack @ file:///home/conda/feedstock_root/build_artifacts/msgpack-python_1610121702224/work
nbclient @ file:///home/conda/feedstock_root/build_artifacts/nbclient_1637327213451/work
nbconvert @ file:///home/conda/feedstock_root/build_artifacts/nbconvert_1605401832871/work
nbformat @ file:///home/conda/feedstock_root/build_artifacts/nbformat_1617383142101/work
nest_asyncio @ file:///home/conda/feedstock_root/build_artifacts/nest-asyncio_1705850609492/work
notebook @ file:///home/conda/feedstock_root/build_artifacts/notebook_1616419146127/work
numpy @ file:///home/conda/feedstock_root/build_artifacts/numpy_1626681920064/work
olefile @ file:///home/conda/feedstock_root/build_artifacts/olefile_1602866521163/work
packaging @ file:///home/conda/feedstock_root/build_artifacts/packaging_1637239678211/work
pandas==1.1.5
pandocfilters @ file:///home/conda/feedstock_root/build_artifacts/pandocfilters_1631603243851/work
panel @ file:///home/conda/feedstock_root/build_artifacts/panel_1628860141514/work
param @ file:///home/conda/feedstock_root/build_artifacts/param_1678790747312/work
parso @ file:///home/conda/feedstock_root/build_artifacts/parso_1595548966091/work
pexpect @ file:///home/conda/feedstock_root/build_artifacts/pexpect_1667297516076/work
pickleshare @ file:///home/conda/feedstock_root/build_artifacts/pickleshare_1602536217715/work
Pillow @ file:///home/conda/feedstock_root/build_artifacts/pillow_1630696616009/work
plotly @ file:///home/conda/feedstock_root/build_artifacts/plotly_1726179518483/work
pluggy==1.0.0
prometheus-client @ file:///home/conda/feedstock_root/build_artifacts/prometheus_client_1689032443210/work
prompt-toolkit @ file:///home/conda/feedstock_root/build_artifacts/prompt-toolkit_1670414775770/work
psutil @ file:///home/conda/feedstock_root/build_artifacts/psutil_1610127101219/work
ptyprocess @ file:///home/conda/feedstock_root/build_artifacts/ptyprocess_1609419310487/work/dist/ptyprocess-0.7.0-py2.py3-none-any.whl
py==1.11.0
pyaml @ file:///home/conda/feedstock_root/build_artifacts/pyaml_1684289801692/work
pycparser @ file:///home/conda/feedstock_root/build_artifacts/pycparser_1636257122734/work
pyct==0.4.6
Pygments @ file:///home/conda/feedstock_root/build_artifacts/pygments_1672682006896/work
pyOpenSSL @ file:///home/conda/feedstock_root/build_artifacts/pyopenssl_1663846997386/work
pyparsing @ file:///home/conda/feedstock_root/build_artifacts/pyparsing_1724616129934/work
pyrsistent @ file:///home/conda/feedstock_root/build_artifacts/pyrsistent_1610146795286/work
PySocks @ file:///home/conda/feedstock_root/build_artifacts/pysocks_1610291458349/work
pytest==7.0.1
pytest-cov==4.0.0
pytest-randomly==3.10.3
python-dateutil @ file:///home/conda/feedstock_root/build_artifacts/python-dateutil_1626286286081/work
pytz @ file:///home/conda/feedstock_root/build_artifacts/pytz_1693930252784/work
pyviz_comms @ file:///home/conda/feedstock_root/build_artifacts/pyviz_comms_1722535616369/work
PyYAML==5.4.1
pyzmq @ file:///home/conda/feedstock_root/build_artifacts/pyzmq_1631793305981/work
requests @ file:///home/conda/feedstock_root/build_artifacts/requests_1656534056640/work
scikit-learn @ file:///home/conda/feedstock_root/build_artifacts/scikit-learn_1630910533947/work
scikit-optimize @ file:///home/conda/feedstock_root/build_artifacts/scikit-optimize_1639544373814/work
scipy @ file:///home/conda/feedstock_root/build_artifacts/scipy_1629411471490/work
Send2Trash @ file:///home/conda/feedstock_root/build_artifacts/send2trash_1682601222253/work
six @ file:///home/conda/feedstock_root/build_artifacts/six_1620240208055/work
sortedcollections @ file:///home/conda/feedstock_root/build_artifacts/sortedcollections_1611011426343/work
sortedcontainers @ file:///home/conda/feedstock_root/build_artifacts/sortedcontainers_1621217038088/work
tblib @ file:///home/conda/feedstock_root/build_artifacts/tblib_1616261298899/work
tenacity @ file:///home/conda/feedstock_root/build_artifacts/tenacity_1677600641219/work
terminado @ file:///home/conda/feedstock_root/build_artifacts/terminado_1631128154882/work
testpath @ file:///home/conda/feedstock_root/build_artifacts/testpath_1645693042223/work
threadpoolctl @ file:///home/conda/feedstock_root/build_artifacts/threadpoolctl_1643647933166/work
tomli==1.2.3
toolz @ file:///home/conda/feedstock_root/build_artifacts/toolz_1657485559105/work
tornado @ file:///home/conda/feedstock_root/build_artifacts/tornado_1610094701020/work
tqdm @ file:///home/conda/feedstock_root/build_artifacts/tqdm_1677887202771/work
traitlets @ file:///home/conda/feedstock_root/build_artifacts/traitlets_1631041982274/work
typing_extensions @ file:///home/conda/feedstock_root/build_artifacts/typing_extensions_1644850595256/work
urllib3 @ file:///home/conda/feedstock_root/build_artifacts/urllib3_1678635778344/work
wcwidth @ file:///home/conda/feedstock_root/build_artifacts/wcwidth_1699959196938/work
webencodings @ file:///home/conda/feedstock_root/build_artifacts/webencodings_1694681268211/work
widgetsnbextension @ file:///home/conda/feedstock_root/build_artifacts/widgetsnbextension_1655939017940/work
zict==2.0.0
zipp @ file:///home/conda/feedstock_root/build_artifacts/zipp_1633302054558/work

name: adaptive
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - argon2-cffi=21.1.0=py36h8f6f2f9_0
  - async_generator=1.10=pyhd8ed1ab_1
  - attrs=22.2.0=pyh71513ae_0
  - backcall=0.2.0=pyh9f0ad1d_0
  - backports=1.0=pyhd8ed1ab_4
  - backports.functools_lru_cache=2.0.0=pyhd8ed1ab_0
  - bleach=6.1.0=pyhd8ed1ab_0
  - bokeh=2.3.3=py36h5fab9bb_0
  - brotlipy=0.7.0=py36h8f6f2f9_1001
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h5fab9bb_0
  - cffi=1.14.6=py36hd8eec40_1
  - charset-normalizer=2.1.1=pyhd8ed1ab_0
  - click=7.1.2=pyh9f0ad1d_0
  - cloudpickle=2.2.1=pyhd8ed1ab_0
  - colorama=0.4.5=pyhd8ed1ab_0
  - colorcet=3.0.1=pyhd8ed1ab_0
  - contextvars=2.4=py_0
  - cryptography=35.0.0=py36hb60f036_0
  - cycler=0.11.0=pyhd8ed1ab_0
  - cytoolz=0.11.0=py36h8f6f2f9_3
  - dask-core=2021.3.0=pyhd8ed1ab_0
  - decorator=5.1.1=pyhd8ed1ab_0
  - defusedxml=0.7.1=pyhd8ed1ab_0
  - distributed=2021.3.0=py36h5fab9bb_0
  - entrypoints=0.4=pyhd8ed1ab_0
  - freetype=2.10.4=h0708190_1
  - heapdict=1.0.1=py_0
  - holoviews=1.14.9=pyhd8ed1ab_0
  - idna=3.10=pyhd8ed1ab_0
  - immutables=0.16=py36h8f6f2f9_0
  - importlib-metadata=4.8.1=py36h5fab9bb_0
  - ipykernel=5.5.5=py36hcb3619a_0
  - ipyparallel=8.6.1=pyhd8ed1ab_0
  - ipython=7.16.1=py36he448a4c_2
  - ipython_genutils=0.2.0=pyhd8ed1ab_1
  - ipywidgets=7.7.4=pyhd8ed1ab_0
  - jbig=2.1=h7f98852_2003
  - jedi=0.17.2=py36h5fab9bb_1
  - jinja2=3.0.3=pyhd8ed1ab_0
  - joblib=1.2.0=pyhd8ed1ab_0
  - jpeg=9e=h166bdaf_1
  - jsonschema=4.1.2=pyhd8ed1ab_0
  - jupyter_client=7.1.2=pyhd8ed1ab_0
  - jupyter_core=4.8.1=py36h5fab9bb_0
  - jupyterlab_pygments=0.1.2=pyh9f0ad1d_0
  - jupyterlab_widgets=1.1.1=pyhd8ed1ab_0
  - kiwisolver=1.3.1=py36h605e78d_1
  - lcms2=2.12=hddcbb42_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - lerc=2.2.1=h9c3ff4c_0
  - libblas=3.9.0=16_linux64_openblas
  - libcblas=3.9.0=16_linux64_openblas
  - libdeflate=1.7=h7f98852_5
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgfortran-ng=13.2.0=h69a702a_0
  - libgfortran5=13.2.0=ha4646dd_0
  - libgomp=11.2.0=h1234567_1
  - liblapack=3.9.0=16_linux64_openblas
  - libopenblas=0.3.21=h043d6bf_0
  - libpng=1.6.37=h21135ba_2
  - libsodium=1.0.18=h36c2ea0_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - libtiff=4.3.0=hf544144_1
  - libwebp-base=1.2.2=h7f98852_1
  - lz4-c=1.9.3=h9c3ff4c_1
  - markdown=3.6=pyhd8ed1ab_0
  - markupsafe=2.0.1=py36h8f6f2f9_0
  - matplotlib-base=3.3.4=py36hd391965_0
  - mistune=0.8.4=pyh1a96a4e_1006
  - msgpack-python=1.0.2=py36h605e78d_1
  - nbclient=0.5.9=pyhd8ed1ab_0
  - nbconvert=6.0.7=py36h5fab9bb_3
  - nbformat=5.1.3=pyhd8ed1ab_0
  - ncurses=6.4=h6a678d5_0
  - nest-asyncio=1.6.0=pyhd8ed1ab_0
  - notebook=6.3.0=py36h5fab9bb_0
  - numpy=1.19.5=py36hfc0c790_2
  - olefile=0.46=pyh9f0ad1d_1
  - openjpeg=2.4.0=hb52868f_1
  - openssl=1.1.1o=h166bdaf_0
  - packaging=21.3=pyhd8ed1ab_0
  - pandas=1.1.5=py36h284efc9_0
  - pandoc=2.19.2=ha770c72_0
  - pandocfilters=1.5.0=pyhd8ed1ab_0
  - panel=0.12.1=pyhd8ed1ab_0
  - param=1.13.0=pyh1a96a4e_0
  - parso=0.7.1=pyh9f0ad1d_0
  - pexpect=4.8.0=pyh1a96a4e_2
  - pickleshare=0.7.5=py_1003
  - pillow=8.3.2=py36h676a545_0
  - pip=21.3.1=pyhd8ed1ab_0
  - plotly=5.24.1=pyhd8ed1ab_0
  - prometheus_client=0.17.1=pyhd8ed1ab_0
  - prompt-toolkit=3.0.36=pyha770c72_0
  - psutil=5.8.0=py36h8f6f2f9_1
  - ptyprocess=0.7.0=pyhd3deb0d_0
  - pyaml=23.5.9=pyhd8ed1ab_0
  - pycparser=2.21=pyhd8ed1ab_0
  - pyct=0.4.6=py_0
  - pyct-core=0.4.6=py_0
  - pygments=2.14.0=pyhd8ed1ab_0
  - pyopenssl=22.0.0=pyhd8ed1ab_1
  - pyparsing=3.1.4=pyhd8ed1ab_0
  - pyrsistent=0.17.3=py36h8f6f2f9_2
  - pysocks=1.7.1=py36h5fab9bb_3
  - python=3.6.13=hb7a2778_2_cpython
  - python-dateutil=2.8.2=pyhd8ed1ab_0
  - python_abi=3.6=2_cp36m
  - pytz=2023.3.post1=pyhd8ed1ab_0
  - pyviz_comms=3.0.3=pyhd8ed1ab_0
  - pyyaml=5.4.1=py36h8f6f2f9_1
  - pyzmq=22.3.0=py36h7068817_0
  - readline=8.2=h5eee18b_0
  - requests=2.28.1=pyhd8ed1ab_0
  - scikit-learn=0.24.2=py36hc89565f_1
  - scikit-optimize=0.9.0=pyhd8ed1ab_1
  - scipy=1.5.3=py36h81d768a_1
  - send2trash=1.8.2=pyh41d4057_0
  - setuptools=58.0.4=py36h5fab9bb_2
  - six=1.16.0=pyh6c4a22f_0
  - sortedcollections=2.1.0=pyhd8ed1ab_0
  - sortedcontainers=2.4.0=pyhd8ed1ab_0
  - sqlite=3.45.3=h5eee18b_0
  - tblib=1.7.0=pyhd8ed1ab_0
  - tenacity=8.2.2=pyhd8ed1ab_0
  - terminado=0.12.1=py36h5fab9bb_0
  - testpath=0.6.0=pyhd8ed1ab_0
  - threadpoolctl=3.1.0=pyh8a188c0_0
  - tk=8.6.14=h39e8969_0
  - toolz=0.12.0=pyhd8ed1ab_0
  - tornado=6.1=py36h8f6f2f9_1
  - tqdm=4.65.0=pyhd8ed1ab_0
  - traitlets=4.3.3=pyhd8ed1ab_2
  - typing-extensions=4.1.1=hd8ed1ab_0
  - typing_extensions=4.1.1=pyha770c72_0
  - tzdata=2025a=h04d1e81_0
  - urllib3=1.26.15=pyhd8ed1ab_0
  - wcwidth=0.2.10=pyhd8ed1ab_0
  - webencodings=0.5.1=pyhd8ed1ab_2
  - wheel=0.37.1=pyhd8ed1ab_0
  - widgetsnbextension=3.6.1=pyha770c72_0
  - xz=5.6.4=h5eee18b_1
  - yaml=0.2.5=h7f98852_2
  - zeromq=4.3.4=h9c3ff4c_1
  - zict=2.0.0=py_0
  - zipp=3.6.0=pyhd8ed1ab_0
  - zlib=1.2.13=h5eee18b_1
  - zstd=1.5.0=ha95c52a_0
  - pip:
      - coverage==6.2
      - iniconfig==1.1.1
      - pluggy==1.0.0
      - py==1.11.0
      - pytest==7.0.1
      - pytest-cov==4.0.0
      - pytest-randomly==3.10.3
      - tomli==1.2.3
prefix: /opt/conda/envs/adaptive

adaptive/tests/test_learner1d.py::test_NaN_loss
adaptive/tests/test_learner1d.py::test_uniform_sampling1D_v2 adaptive/tests/test_learner1d.py::test_tell_many adaptive/tests/test_learner1d.py::test_loss_interpolation adaptive/tests/test_learner1d.py::test_adding_existing_point_passes_silently adaptive/tests/test_learner1d.py::test_add_data_unordered adaptive/tests/test_learner1d.py::test_curvature_loss adaptive/tests/test_learner1d.py::test_curvature_loss_vectors adaptive/tests/test_learner1d.py::test_loss_at_machine_precision_interval_is_zero adaptive/tests/test_learner1d.py::test_ask_does_not_return_known_points_when_returning_bounds adaptive/tests/test_learner1d.py::test_loss_interpolation_for_unasked_point adaptive/tests/test_learner1d.py::test_termination_on_discontinuities adaptive/tests/test_learner1d.py::test_first_iteration adaptive/tests/test_learner1d.py::test_pending_loss_intervals adaptive/tests/test_learner1d.py::test_order_adding_points adaptive/tests/test_learner1d.py::test_small_deviations
adaptive/learner/learner1D.py
adaptive/learner/learner1D.py