From 77e302ac8ee4d3fa627502f44428db9af19680d6 Mon Sep 17 00:00:00 2001 From: Bas Nijholt Date: Thu, 15 Oct 2026 11:42:53 -0700 Subject: [PATCH 1/3] Rewrite the README, fix the docs badge and the dark-mode live_info table - Restructure the README: centered logo and title, the animations right after a plain summary, a short feature list, a quick start with a script and a Jupyter example, a learner table, executors, documentation links, and a table of the optional extras. - Use the shields.io Read the Docs badge; the readthedocs.org badge redirects and does not load as an image. Add a tests badge and drop the GitHub stars badge. - Drop the doctoc table of contents (GitHub shows an outline) and the TOC generator workflow that maintained it. - In dark mode, give the white widget card the light theme colors, so the rows of the live_info table are no longer dark. --- .github/workflows/toc.yaml | 10 -- README.md | 242 ++++++++++++++++--------------------- docs/_static/extra.css | 6 + 3 files changed, 110 insertions(+), 148 deletions(-) delete mode 100644 .github/workflows/toc.yaml diff --git a/.github/workflows/toc.yaml b/.github/workflows/toc.yaml deleted file mode 100644 index 28dac9125..000000000 --- a/.github/workflows/toc.yaml +++ /dev/null @@ -1,10 +0,0 @@ -on: push -name: TOC Generator -jobs: - generateTOC: - name: TOC Generator - runs-on: ubuntu-latest - steps: - - uses: technote-space/toc-generator@v4 - with: - TOC_TITLE: "" diff --git a/README.md b/README.md index 906b33ea9..1d403a7db 100644 --- a/README.md +++ b/README.md @@ -1,204 +1,173 @@ +

+ Adaptive logo +

+ +

Adaptive

+ +

Parallel active learning of mathematical functions

-# ![logo](https://adaptive.readthedocs.io/en/latest/_static/logo.png) *Adaptive*: Parallel Active Learning of Mathematical Functions -[![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/python-adaptive/adaptive/main?filepath=example-notebook.ipynb) -[![Conda](https://img.shields.io/badge/install%20with-conda-green.svg)](https://anaconda.org/conda-forge/adaptive) +[![Tests](https://img.shields.io/github/actions/workflow/status/python-adaptive/adaptive/nox.yml?branch=main&label=tests)](https://github.com/python-adaptive/adaptive/actions/workflows/nox.yml) +[![PyPI](https://img.shields.io/pypi/v/adaptive.svg)](https://pypi.org/project/adaptive/) +[![Conda](https://img.shields.io/conda/vn/conda-forge/adaptive.svg)](https://anaconda.org/conda-forge/adaptive) +[![Python](https://img.shields.io/pypi/pyversions/adaptive.svg)](https://pypi.org/project/adaptive/) +[![Documentation](https://img.shields.io/readthedocs/adaptive)](https://adaptive.readthedocs.io/en/latest/) [![Coverage](https://img.shields.io/codecov/c/github/python-adaptive/adaptive)](https://codecov.io/gh/python-adaptive/adaptive) -[![DOI](https://img.shields.io/badge/doi-10.5281%2Fzenodo.1182437-blue.svg)](https://doi.org/10.5281/zenodo.1182437) -[![Documentation](https://readthedocs.org/projects/adaptive/badge/?version=latest)](https://adaptive.readthedocs.io/en/latest/?badge=latest) [![Downloads](https://img.shields.io/conda/dn/conda-forge/adaptive.svg)](https://anaconda.org/conda-forge/adaptive) -[![GitHub](https://img.shields.io/github/stars/python-adaptive/adaptive.svg?style=social)](https://github.com/python-adaptive/adaptive/stargazers) -[![PyPI](https://img.shields.io/pypi/v/adaptive.svg)](https://pypi.python.org/pypi/adaptive) -[![Python](https://img.shields.io/pypi/pyversions/adaptive.svg)](https://pypi.python.org/pypi/adaptive) +[![DOI](https://img.shields.io/badge/doi-10.5281%2Fzenodo.1182437-blue.svg)](https://doi.org/10.5281/zenodo.1182437) +[![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/python-adaptive/adaptive/main?filepath=example-notebook.ipynb) -Adaptive is an open-source Python library that streamlines adaptive parallel function evaluations. -Rather than calculating all points on a dense grid, it intelligently selects the "best" points in the parameter space based on your provided function and bounds. -With minimal code, you can perform evaluations on a computing cluster, display live plots, and optimize the adaptive sampling algorithm. +Adaptive samples a function where it matters. +Instead of evaluating it on a dense grid, Adaptive places each new point where its current approximation of the function is worst, so sharp features are resolved with far fewer evaluations. +The points are evaluated in parallel, on your own cores or on a cluster, and in Jupyter you can watch the result take shape while it runs. -Adaptive is most efficient for computations where each function evaluation takes at least ≈50ms due to the overhead of selecting potentially interesting points. +Adaptive pays off when a single evaluation takes at least about 50 ms, as with simulations, experiments, or expensive numerics. +For cheaper functions, choosing the points costs more than evaluating them. -To see Adaptive in action, try the [example notebook on Binder](https://mybinder.org/v2/gh/python-adaptive/adaptive/main?filepath=example-notebook.ipynb) or explore the [tutorial on Read the Docs](https://adaptive.readthedocs.io/en/latest/tutorial/tutorial/). +Try it in your browser [on Binder](https://mybinder.org/v2/gh/python-adaptive/adaptive/main?filepath=example-notebook.ipynb), or start with the [tutorial](https://adaptive.readthedocs.io/en/latest/tutorial/tutorial/). -
Table of contents - - - - -- [Key features](#key-features) -- [Example usage](#example-usage) - - [Exporting Data](#exporting-data) -- [Implemented Algorithms](#implemented-algorithms) -- [Installation](#installation) - - [Faster triangulation (optional)](#faster-triangulation-optional) -- [Development](#development) - - [Documentation](#documentation) -- [Citing](#citing) -- [Draft Paper](#draft-paper) -- [Credits](#credits) - - - -
+

+ Learner1D: homogeneous grid versus adaptive sampling of a sharp peak + Learner2D: homogeneous grid versus adaptive triangulation of a ring + LearnerND: sampled points concentrate on a spherical shell +

+

The same number of points on a homogeneous grid and placed by Adaptive.

-## Key features +## Features -- **Intelligent Adaptive Sampling**: Adaptive focuses on areas of interest within a function, ensuring better results with fewer evaluations, saving time, and computational resources. -- **Parallel Execution**: The library leverages parallel processing for faster function evaluations, making optimal use of available computational resources. -- **Live Plotting and Info Widgets**: When working in Jupyter notebooks, Adaptive offers real-time visualization of the learning process, making it easier to monitor progress and identify areas of improvement. -- **Customizable Loss Functions**: Adaptive supports various loss functions and allows customization, enabling users to tailor the learning process according to their specific needs. -- **Support for Multidimensional Functions**: The library can handle functions with scalar or vector outputs in one or multiple dimensions, providing flexibility for a wide range of problems. -- **Seamless Integration**: Adaptive offers a simple and intuitive interface, making it easy to integrate with existing Python projects and workflows. -- **Flexible Data Export**: The library provides options to export learned data as NumPy arrays or Pandas DataFrames, ensuring compatibility with various data processing tools. -- **Open-Source and Community-Driven**: Adaptive is an open-source project, encouraging contributions from the community to continuously improve and expand the library's features and capabilities. +- **Fewer evaluations**: points go where the function changes, not where a grid puts them. +- **Parallel**: runners use all local cores by default, and scale to a cluster with any `concurrent.futures`-style executor, such as ipyparallel, Dask's `distributed`, or mpi4py. +- **Live in Jupyter**: the runner works in the background while `live_plot()` and `live_info()` show its progress, so the notebook stays usable. +- **Customizable**: stop on a loss, a number of points, or a time limit, and replace the default loss to define what "interesting" means for your problem. +- **Your data, your way**: save and resume runs with `learner.save()` and `learner.load()`, and export to NumPy or pandas with `to_numpy()` and `to_dataframe()`. -## Example usage +## Quick start -Adaptively learning a 1D function and live-plotting the process in a Jupyter notebook: +```bash +pip install adaptive +``` -```python -from adaptive import notebook_extension, Runner, Learner1D +Learn a function with a sharp peak, evaluating it in parallel until the loss is below 0.01: -notebook_extension() +```python +import adaptive def peak(x, a=0.01): return x + a**2 / (a**2 + x**2) -learner = Learner1D(peak, bounds=(-1, 1)) -runner = Runner(learner, loss_goal=0.01) -runner.live_info() -runner.live_plot() -``` - -With the same number of points, adaptive sampling resolves features that a homogeneous grid misses: - -

- Learner1D: homogeneous grid versus adaptive sampling of a sharp peak - Learner2D: homogeneous grid versus adaptive triangulation of a ring - LearnerND: sampled points concentrate on a spherical shell -

+learner = adaptive.Learner1D(peak, bounds=(-1, 1)) +adaptive.BlockingRunner(learner, loss_goal=0.01) -### Exporting Data - -You can export the learned data as a NumPy array: - -```python -data = learner.to_numpy() +data = learner.to_numpy() # (x, y) rows; or learner.to_dataframe() with pandas ``` -If you have Pandas installed, you can also export the data as a DataFrame: +In Jupyter (`pip install "adaptive[notebook]"`), use the non-blocking `Runner` and watch it work: ```python -df = learner.to_dataframe() +adaptive.notebook_extension() + +learner = adaptive.Learner1D(peak, bounds=(-1, 1)) +runner = adaptive.Runner(learner, loss_goal=0.01) +runner.live_info() # progress, and a button to stop the runner +runner.live_plot() # updates as new points come in ``` -## Implemented Algorithms +## Learners -The core concept in `adaptive` is the *learner*. -A *learner* samples a function at the most interesting locations within its parameter space, allowing for optimal sampling of the function. -As the function is evaluated at more points, the learner improves its understanding of the best locations to sample next. +A *learner* decides where to sample next, and a *runner* evaluates those points and feeds the results back until a goal is reached. +Which point is "best" depends on the problem: every learner has a sensible default, and you can replace its loss function with your own. -The definition of the "best locations" depends on your application domain. -While `adaptive` provides sensible default choices, the adaptive sampling process can be fully customized. - -The following learners are implemented: +Adaptive comes with these learners: -- `Learner1D`: for 1D functions `f: ℝ → ℝ^N`, -- `Learner2D`: for 2D functions `f: ℝ^2 → ℝ^N`, -- `LearnerND`: for ND functions `f: ℝ^N → ℝ^M`, -- `AverageLearner`: for random variables, allowing averaging of results over multiple evaluations, -- `AverageLearner1D`: for stochastic 1D functions, estimating the mean value at each point, -- `IntegratorLearner`: for integrating a 1D function `f: ℝ → ℝ`, -- `SequenceLearner`: for evaluating a fixed sequence of points while still using the runners, parallelism, and data export of Adaptive. +| Learner | For | +| ------------------- | -------------------------------------------------------------------------------------- | +| `Learner1D` | 1D functions `f: ℝ → ℝ^N` | +| `Learner2D` | 2D functions `f: ℝ^2 → ℝ^N` | +| `LearnerND` | ND functions `f: ℝ^N → ℝ^M` | +| `AverageLearner` | random variables, averaged over many evaluations | +| `AverageLearner1D` | noisy 1D functions, estimating the mean at each point | +| `IntegratorLearner` | the integral of a 1D function `f: ℝ → ℝ` | +| `SequenceLearner` | a fixed sequence of points, with the runners and data export of Adaptive | +| `BalancingLearner` | several learners at once, each new point going to the learner that needs it most | +| `DataSaver` | wrapping another learner when the function returns more than a scalar or a vector | + +Runners accept any executor, so the same code runs on a laptop or a cluster, with built-in support for +[concurrent.futures](https://docs.python.org/3/library/concurrent.futures.html), +[loky](https://loky.readthedocs.io/en/stable/) (the default), +[ipyparallel](https://ipyparallel.readthedocs.io/en/latest/), +[distributed](https://distributed.readthedocs.io/en/latest/), and +[mpi4py](https://mpi4py.readthedocs.io/en/stable/mpi4py.futures.html): + +```python +from distributed import Client -Meta-learners (to be used with other learners): +runner = adaptive.Runner(learner, executor=Client(), loss_goal=0.01) +``` -- `BalancingLearner`: for running several learners at once, selecting the "most optimal" one each time you get more points, -- `DataSaver`: for when your function doesn't return just a scalar or a vector. +## Documentation -In addition to learners, `adaptive` offers primitives for parallel sampling across multiple cores or machines, with built-in support for: -[concurrent.futures](https://docs.python.org/3/library/concurrent.futures.html), -[mpi4py](https://mpi4py.readthedocs.io/en/stable/mpi4py.futures.html), -[loky](https://loky.readthedocs.io/en/stable/), -[ipyparallel](https://ipyparallel.readthedocs.io/en/latest/), and -[distributed](https://distributed.readthedocs.io/en/latest/). +The [documentation](https://adaptive.readthedocs.io/) has a [tutorial](https://adaptive.readthedocs.io/en/latest/tutorial/tutorial/) for each learner, [interactive examples](https://adaptive.readthedocs.io/en/latest/algorithms_and_examples/) comparing adaptive and homogeneous sampling, [benchmarks](https://adaptive.readthedocs.io/en/latest/benchmarks/), a [gallery](https://adaptive.readthedocs.io/en/latest/gallery/) of projects that use Adaptive, and the [API reference](https://adaptive.readthedocs.io/en/latest/reference/adaptive/). ## Installation -`adaptive` works with Python 3.11 and higher on Linux, Windows, or Mac, and provides optional extensions for working with Jupyter. - -The recommended way to install adaptive is using `conda`: +Adaptive needs Python 3.11 or newer and runs on Linux, macOS, and Windows. +Install it from PyPI or conda-forge: ```bash +pip install adaptive conda install -c conda-forge adaptive ``` -`adaptive` is also available on PyPI: - -```bash -pip install "adaptive[notebook]" -``` - -The `[notebook]` above also installs the optional dependencies for live plotting and widgets in JupyterLab and Jupyter Notebook; no extra Jupyter extensions need to be installed. - -### Faster triangulation (optional) +Optional extras: -Installing the optional [adaptive-triangulation](https://github.com/python-adaptive/adaptive-triangulation) package makes `adaptive.LearnerND` significantly faster by replacing the pure-Python triangulation with a Rust implementation: +| Extra | Adds | +| -------------------- | --------------------------------------------------------------------------------------- | +| `adaptive[notebook]` | live plots and widgets in JupyterLab and Jupyter Notebook; no Jupyter extensions needed | +| `adaptive[rust]` | [adaptive-triangulation](https://github.com/python-adaptive/adaptive-triangulation), a faster Rust triangulation for `LearnerND` | +| `adaptive[other]` | ipyparallel and distributed executors, and dill for serialization | -```bash -pip install "adaptive[rust]" -``` - -No code changes are needed — the Rust backend is detected and used automatically. -To control the selection, pass `LearnerND(..., triangulation_backend="python" | "rust" | "auto")` per learner, or set the environment variable `ADAPTIVE_TRIANGULATION_BACKEND=python` to force the pure-Python implementation globally (or to `rust` to make a missing Rust backend an error instead of a silent fallback). +The Rust triangulation is used automatically when it is installed. +To choose explicitly, pass `LearnerND(..., triangulation_backend="python" | "rust" | "auto")`, or set the environment variable `ADAPTIVE_TRIANGULATION_BACKEND`; with `rust`, a missing backend is an error instead of a silent fallback. ## Development -Clone the repository and install it in editable mode with the optional dependencies: +Clone the repository and install it in editable mode, for example with [uv](https://docs.astral.sh/uv/): ```bash -git clone git@github.com:python-adaptive/adaptive.git +git clone https://github.com/python-adaptive/adaptive.git cd adaptive -pip install -e ".[notebook,test,other]" +uv sync --extra notebook --extra test --extra other ``` -We recommend a virtual environment, for example with [uv](https://docs.astral.sh/uv/) (`uv sync --extra notebook --extra test --extra other`) or Conda. +or with pip in a virtual environment: `pip install -e ".[notebook,test,other]"`. -To avoid polluting the history with notebook output, set up the git filter by running: +Then set up the git filter that strips notebook outputs, and [pre-commit](https://pre-commit.com) for the code style: ```bash python ipynb_filter.py -``` - -in the repository. - -To maintain consistent code style, we use [pre-commit](https://pre-commit.com). Install it by running: - -```bash pre-commit install ``` -in the repository. - -### Documentation +### Building the documentation The documentation is built with [Zensical](https://zensical.org). The executable pages in `docs/notebooks/` are Jupyter notebooks in [jupytext's MyST Markdown format](https://jupytext.readthedocs.io/en/latest/formats-markdown.html). @@ -209,11 +178,11 @@ uv run --group docs python docs/build_notebooks.py uv run --group docs zensical serve # preview on http://localhost:8000 ``` -The animations in this README are rendered from real learner runs with `uv run --group docs python docs/make_animations.py` (requires `ffmpeg` and Chrome or Chromium). +The README animations are rendered from real learner runs with `uv run --group docs python docs/make_animations.py`, which needs `ffmpeg` and Chrome or Chromium. ## Citing -If you used Adaptive in a scientific work, please cite it as follows. +If you use Adaptive in scientific work, please cite it: ```bib @misc{Nijholt2019, @@ -225,22 +194,19 @@ If you used Adaptive in a scientific work, please cite it as follows. } ``` -## Draft Paper - -If you're interested in the scientific background and principles behind Adaptive, we recommend taking a look at the [draft paper](https://github.com/python-adaptive/paper) that is currently being written. -This paper provides a comprehensive overview of the concepts, algorithms, and applications of the Adaptive library. +The algorithms are described in more detail in a [draft paper](https://github.com/python-adaptive/paper). ## Credits -We would like to give credits to the following people: - -- Pedro Gonnet for his implementation of [CQUAD](https://www.gnu.org/software/gsl/manual/html_node/CQUAD-doubly_002dadaptive-integration.html), “Algorithm 4” as described in “Increasing the Reliability of Adaptive Quadrature Using Explicit Interpolants”, P. Gonnet, ACM Transactions on Mathematical Software, 37 (3), art. no. 26, 2010. -- Pauli Virtanen for his `AdaptiveTriSampling` script (no longer available online since SciPy Central went down) which served as inspiration for the `adaptive.Learner2D`. +- Pedro Gonnet for his implementation of [CQUAD](https://www.gnu.org/software/gsl/manual/html_node/CQUAD-doubly_002dadaptive-integration.html), "Algorithm 4" as described in "Increasing the Reliability of Adaptive Quadrature Using Explicit Interpolants", P. Gonnet, ACM Transactions on Mathematical Software, 37 (3), art. no. 26, 2010. +- Pauli Virtanen for his `AdaptiveTriSampling` script (no longer available online since SciPy Central went down), which inspired `adaptive.Learner2D`. -For general discussion, we have a [Gitter chat channel](https://gitter.im/python-adaptive/adaptive). -If you find any bugs or have any feature suggestions please file a GitHub [issue](https://github.com/python-adaptive/adaptive/issues/new) or submit a [pull request](https://github.com/python-adaptive/adaptive/pulls). +## Community + +For general discussion, there is a [Gitter chat channel](https://gitter.im/python-adaptive/adaptive). +Found a bug or have an idea? Open an [issue](https://github.com/python-adaptive/adaptive/issues/new) or a [pull request](https://github.com/python-adaptive/adaptive/pulls). diff --git a/docs/_static/extra.css b/docs/_static/extra.css index cc89dcd41..d1473e0a4 100644 --- a/docs/_static/extra.css +++ b/docs/_static/extra.css @@ -44,6 +44,12 @@ [data-md-color-scheme="slate"] .md-typeset .nb-output:has([data-root-id]:not(:empty)), [data-md-color-scheme="slate"] .md-typeset .nb-output:has(.plotly-graph-div), [data-md-color-scheme="slate"] .md-typeset .nb-output:has(.jupyter-widgets) { + /* light theme colors, so e.g. the `live_info` table is not dark inside */ + --md-default-bg-color: #ffffff; + --md-default-fg-color: #000000de; + --md-typeset-color: #000000de; + --md-typeset-table-color: #0000001f; + --md-typeset-table-color--light: #00000009; background: #ffffff; color: #000000; border-radius: 0.4rem; From 6d04d72fda6a4713955144d214da267a0b78c45c Mon Sep 17 00:00:00 2001 From: Bas Nijholt Date: Thu, 15 Oct 2026 11:52:11 -0700 Subject: [PATCH 2/3] Rewrite the README in plain prose, drop the Zensical footer credit Go back to the voice of the original README: plain sentences instead of a bold-labelled feature list and tagline, the learner list instead of a table, and the installation and development steps as before. The docs home page no longer includes the removed feature list. Set extra.generator = false so the footer no longer says "Made with Zensical". --- README.md | 180 ++++++++++++++++++++++++-------------------------- docs/index.md | 2 - zensical.toml | 3 + 3 files changed, 88 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index 1d403a7db..432fd0089 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,4 @@ -

- Adaptive logo -

- -

Adaptive

- -

Parallel active learning of mathematical functions

- +# ![logo](https://adaptive.readthedocs.io/en/latest/_static/logo.png) *Adaptive*: Parallel Active Learning of Mathematical Functions [![Tests](https://img.shields.io/github/actions/workflow/status/python-adaptive/adaptive/nox.yml?branch=main&label=tests)](https://github.com/python-adaptive/adaptive/actions/workflows/nox.yml) @@ -22,148 +15,145 @@ -Adaptive samples a function where it matters. -Instead of evaluating it on a dense grid, Adaptive places each new point where its current approximation of the function is worst, so sharp features are resolved with far fewer evaluations. -The points are evaluated in parallel, on your own cores or on a cluster, and in Jupyter you can watch the result take shape while it runs. +`adaptive` is an open-source Python library for adaptive parallel function evaluation. +You supply a function and its bounds, and `adaptive` evaluates it at the "best" points in parameter space, instead of at *all* points of a dense grid. +With a few lines of code you can run the evaluations on a computing cluster, live-plot the data as it comes in, and fine-tune the sampling algorithm. -Adaptive pays off when a single evaluation takes at least about 50 ms, as with simulations, experiments, or expensive numerics. -For cheaper functions, choosing the points costs more than evaluating them. +`adaptive` works best when each function evaluation takes *at least* ≈50 ms, because picking the next points has some overhead. -Try it in your browser [on Binder](https://mybinder.org/v2/gh/python-adaptive/adaptive/main?filepath=example-notebook.ipynb), or start with the [tutorial](https://adaptive.readthedocs.io/en/latest/tutorial/tutorial/). +Try the example notebook [on Binder](https://mybinder.org/v2/gh/python-adaptive/adaptive/main?filepath=example-notebook.ipynb) or read the [tutorial](https://adaptive.readthedocs.io/en/latest/tutorial/tutorial/). +With the same number of points, adaptive sampling resolves features that a homogeneous grid misses: +

Learner1D: homogeneous grid versus adaptive sampling of a sharp peak Learner2D: homogeneous grid versus adaptive triangulation of a ring LearnerND: sampled points concentrate on a spherical shell

-

The same number of points on a homogeneous grid and placed by Adaptive.

- - - -## Features -- **Fewer evaluations**: points go where the function changes, not where a grid puts them. -- **Parallel**: runners use all local cores by default, and scale to a cluster with any `concurrent.futures`-style executor, such as ipyparallel, Dask's `distributed`, or mpi4py. -- **Live in Jupyter**: the runner works in the background while `live_plot()` and `live_info()` show its progress, so the notebook stays usable. -- **Customizable**: stop on a loss, a number of points, or a time limit, and replace the default loss to define what "interesting" means for your problem. -- **Your data, your way**: save and resume runs with `learner.save()` and `learner.load()`, and export to NumPy or pandas with `to_numpy()` and `to_dataframe()`. +## Example - - -## Quick start - -```bash -pip install adaptive -``` - -Learn a function with a sharp peak, evaluating it in parallel until the loss is below 0.01: +Learning a 1D function and live-plotting the process in a Jupyter notebook: ```python -import adaptive +from adaptive import notebook_extension, Runner, Learner1D + +notebook_extension() def peak(x, a=0.01): return x + a**2 / (a**2 + x**2) -learner = adaptive.Learner1D(peak, bounds=(-1, 1)) -adaptive.BlockingRunner(learner, loss_goal=0.01) - -data = learner.to_numpy() # (x, y) rows; or learner.to_dataframe() with pandas +learner = Learner1D(peak, bounds=(-1, 1)) +runner = Runner(learner, loss_goal=0.01) +runner.live_info() +runner.live_plot() ``` -In Jupyter (`pip install "adaptive[notebook]"`), use the non-blocking `Runner` and watch it work: +The `Runner` works in the background, so the notebook stays usable while it runs. +In a script, use `BlockingRunner`, which returns once the goal is reached: ```python -adaptive.notebook_extension() +from adaptive import BlockingRunner, Learner1D -learner = adaptive.Learner1D(peak, bounds=(-1, 1)) -runner = adaptive.Runner(learner, loss_goal=0.01) -runner.live_info() # progress, and a button to stop the runner -runner.live_plot() # updates as new points come in +learner = Learner1D(peak, bounds=(-1, 1)) +BlockingRunner(learner, loss_goal=0.01) ``` +Afterwards, `learner.to_numpy()` returns the data as a NumPy array, and `learner.to_dataframe()` as a pandas DataFrame. + -## Learners +## Implemented algorithms -A *learner* decides where to sample next, and a *runner* evaluates those points and feeds the results back until a goal is reached. -Which point is "best" depends on the problem: every learner has a sensible default, and you can replace its loss function with your own. +The core concept in `adaptive` is the *learner*. +A learner samples a function at the points in its parameter space that tell it the most about the function. +As it evaluates more points, it gets a better idea of where to sample next. -Adaptive comes with these learners: +What the "best" points are depends on your application. +`adaptive` makes reasonable default choices, but the details of the sampling are completely customizable. - +The following learners are implemented: -| Learner | For | -| ------------------- | -------------------------------------------------------------------------------------- | -| `Learner1D` | 1D functions `f: ℝ → ℝ^N` | -| `Learner2D` | 2D functions `f: ℝ^2 → ℝ^N` | -| `LearnerND` | ND functions `f: ℝ^N → ℝ^M` | -| `AverageLearner` | random variables, averaged over many evaluations | -| `AverageLearner1D` | noisy 1D functions, estimating the mean at each point | -| `IntegratorLearner` | the integral of a 1D function `f: ℝ → ℝ` | -| `SequenceLearner` | a fixed sequence of points, with the runners and data export of Adaptive | -| `BalancingLearner` | several learners at once, each new point going to the learner that needs it most | -| `DataSaver` | wrapping another learner when the function returns more than a scalar or a vector | - -Runners accept any executor, so the same code runs on a laptop or a cluster, with built-in support for -[concurrent.futures](https://docs.python.org/3/library/concurrent.futures.html), -[loky](https://loky.readthedocs.io/en/stable/) (the default), -[ipyparallel](https://ipyparallel.readthedocs.io/en/latest/), -[distributed](https://distributed.readthedocs.io/en/latest/), and -[mpi4py](https://mpi4py.readthedocs.io/en/stable/mpi4py.futures.html): + -```python -from distributed import Client +- `Learner1D`, for 1D functions `f: ℝ → ℝ^N`, +- `Learner2D`, for 2D functions `f: ℝ^2 → ℝ^N`, +- `LearnerND`, for ND functions `f: ℝ^N → ℝ^M`, +- `AverageLearner`, for random variables where you want to average the result over many evaluations, +- `AverageLearner1D`, for stochastic 1D functions where you want to estimate the mean value of the function at each point, +- `IntegratorLearner`, for when you want to integrate a 1D function `f: ℝ → ℝ`, +- `SequenceLearner`, for when you want to evaluate a fixed sequence of points while still using the runners and data export of `adaptive`. -runner = adaptive.Runner(learner, executor=Client(), loss_goal=0.01) -``` +Meta-learners (to be used with other learners): -## Documentation +- `BalancingLearner`, for when you want to run several learners at once, selecting the "best" one each time you get more points, +- `DataSaver`, for when your function doesn't just return a scalar or a vector. -The [documentation](https://adaptive.readthedocs.io/) has a [tutorial](https://adaptive.readthedocs.io/en/latest/tutorial/tutorial/) for each learner, [interactive examples](https://adaptive.readthedocs.io/en/latest/algorithms_and_examples/) comparing adaptive and homogeneous sampling, [benchmarks](https://adaptive.readthedocs.io/en/latest/benchmarks/), a [gallery](https://adaptive.readthedocs.io/en/latest/gallery/) of projects that use Adaptive, and the [API reference](https://adaptive.readthedocs.io/en/latest/reference/adaptive/). +In addition to the learners, `adaptive` provides primitives for running the sampling across several cores and even several machines, with built-in support for +[concurrent.futures](https://docs.python.org/3/library/concurrent.futures.html), +[mpi4py](https://mpi4py.readthedocs.io/en/stable/mpi4py.futures.html), +[loky](https://loky.readthedocs.io/en/stable/), +[ipyparallel](https://ipyparallel.readthedocs.io/en/latest/), and +[distributed](https://distributed.readthedocs.io/en/latest/). ## Installation -Adaptive needs Python 3.11 or newer and runs on Linux, macOS, and Windows. -Install it from PyPI or conda-forge: +`adaptive` works with Python 3.11 and higher on Linux, Windows, and macOS. + +Install it with `conda`: ```bash -pip install adaptive conda install -c conda-forge adaptive ``` -Optional extras: +or from PyPI: + +```bash +pip install "adaptive[notebook]" +``` + +The `[notebook]` extra installs the dependencies for live plotting and widgets in JupyterLab and Jupyter Notebook; no Jupyter extensions are needed. -| Extra | Adds | -| -------------------- | --------------------------------------------------------------------------------------- | -| `adaptive[notebook]` | live plots and widgets in JupyterLab and Jupyter Notebook; no Jupyter extensions needed | -| `adaptive[rust]` | [adaptive-triangulation](https://github.com/python-adaptive/adaptive-triangulation), a faster Rust triangulation for `LearnerND` | -| `adaptive[other]` | ipyparallel and distributed executors, and dill for serialization | +### Faster triangulation (optional) -The Rust triangulation is used automatically when it is installed. -To choose explicitly, pass `LearnerND(..., triangulation_backend="python" | "rust" | "auto")`, or set the environment variable `ADAPTIVE_TRIANGULATION_BACKEND`; with `rust`, a missing backend is an error instead of a silent fallback. +Installing the optional [adaptive-triangulation](https://github.com/python-adaptive/adaptive-triangulation) package makes `adaptive.LearnerND` significantly faster by replacing the pure-Python triangulation with a Rust implementation: + +```bash +pip install "adaptive[rust]" +``` + +No code changes are needed. The Rust backend is detected and used automatically. +To control the selection, pass `LearnerND(..., triangulation_backend="python" | "rust" | "auto")` per learner, or set the environment variable `ADAPTIVE_TRIANGULATION_BACKEND=python` to force the pure-Python implementation globally (or to `rust` to make a missing Rust backend an error instead of a silent fallback). ## Development -Clone the repository and install it in editable mode, for example with [uv](https://docs.astral.sh/uv/): +Clone the repository and install it in editable mode with the optional dependencies: ```bash git clone https://github.com/python-adaptive/adaptive.git cd adaptive -uv sync --extra notebook --extra test --extra other +pip install -e ".[notebook,test,other]" ``` -or with pip in a virtual environment: `pip install -e ".[notebook,test,other]"`. +We recommend a virtual environment, for example with [uv](https://docs.astral.sh/uv/) (`uv sync --extra notebook --extra test --extra other`) or Conda. -Then set up the git filter that strips notebook outputs, and [pre-commit](https://pre-commit.com) for the code style: +To avoid polluting the history with notebook output, set up the git filter by running ```bash python ipynb_filter.py +``` + +in the repository. + +We use [pre-commit](https://pre-commit.com) for a consistent code style; install the hooks with + +```bash pre-commit install ``` @@ -178,11 +168,11 @@ uv run --group docs python docs/build_notebooks.py uv run --group docs zensical serve # preview on http://localhost:8000 ``` -The README animations are rendered from real learner runs with `uv run --group docs python docs/make_animations.py`, which needs `ffmpeg` and Chrome or Chromium. +The README animations are made from real learner runs with `uv run --group docs python docs/make_animations.py`, which needs `ffmpeg` and Chrome or Chromium. ## Citing -If you use Adaptive in scientific work, please cite it: +If you used `adaptive` in a scientific work, please cite it as follows. ```bib @misc{Nijholt2019, @@ -198,15 +188,15 @@ The algorithms are described in more detail in a [draft paper](https://github.co ## Credits +We would like to give credits to the following people: + - Pedro Gonnet for his implementation of [CQUAD](https://www.gnu.org/software/gsl/manual/html_node/CQUAD-doubly_002dadaptive-integration.html), "Algorithm 4" as described in "Increasing the Reliability of Adaptive Quadrature Using Explicit Interpolants", P. Gonnet, ACM Transactions on Mathematical Software, 37 (3), art. no. 26, 2010. -- Pauli Virtanen for his `AdaptiveTriSampling` script (no longer available online since SciPy Central went down), which inspired `adaptive.Learner2D`. +- Pauli Virtanen for his `AdaptiveTriSampling` script (no longer available online since SciPy Central went down), which served as inspiration for `adaptive.Learner2D`. -## Community - -For general discussion, there is a [Gitter chat channel](https://gitter.im/python-adaptive/adaptive). -Found a bug or have an idea? Open an [issue](https://github.com/python-adaptive/adaptive/issues/new) or a [pull request](https://github.com/python-adaptive/adaptive/pulls). +For general discussion, we have a [Gitter chat channel](https://gitter.im/python-adaptive/adaptive). +If you find any bugs or have any feature suggestions, please file a GitHub [issue](https://github.com/python-adaptive/adaptive/issues/new) or submit a [pull request](https://github.com/python-adaptive/adaptive/pulls). diff --git a/docs/index.md b/docs/index.md index d8d64d9d1..fb89608ca 100644 --- a/docs/index.md +++ b/docs/index.md @@ -22,5 +22,3 @@ hide: [Start the tutorial](tutorial/tutorial.Learner1D.md){ .md-button .md-button--primary } [See the examples](algorithms_and_examples.md){ .md-button } [API reference](reference/adaptive.md){ .md-button } - ---8<-- "README.md:key-features" diff --git a/zensical.toml b/zensical.toml index e51cdc87f..b88f88319 100644 --- a/zensical.toml +++ b/zensical.toml @@ -134,6 +134,9 @@ code = "JetBrains Mono" [project.theme.icon] repo = "fontawesome/brands/github" +[project.extra] +generator = false # no "Made with Zensical" in the footer + [[project.extra.social]] icon = "fontawesome/brands/github" link = "https://github.com/python-adaptive/adaptive" From 738e35263ad936dccd049eb349aea4f8e376dee4 Mon Sep 17 00:00:00 2001 From: Bas Nijholt Date: Thu, 15 Oct 2026 17:28:31 -0700 Subject: [PATCH 3/3] Address the README review: runnable script example, scope, executors - Make the script example self-contained (it used peak from the notebook example) and show to_numpy() in it. - Say that adaptive is meant for functions of a few parameters. - Explain what the loss function is and point to the built-in alternatives and the custom loss tutorial. - Say that the runners use all local cores by default, how to pass another executor, and link the parallelism tutorial. - Install with plain pip install adaptive; the [notebook] extra adds live plotting and pandas for to_dataframe(). - Development: add the test command; the notebook git filter needs nbconvert and only matters for example-notebook.ipynb. - Move the animation caption into the summary snippet, so the docs home page shows it too; label the conda downloads badge; fix the CQUAD link. - Parallelism tutorial: drop the stale claim that Windows defaults to distributed.Client; loky is the default everywhere. --- README.md | 39 +++++++++++-------- .../tutorial/tutorial.parallelism.md | 2 - 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 432fd0089..961d5ca30 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Python](https://img.shields.io/pypi/pyversions/adaptive.svg)](https://pypi.org/project/adaptive/) [![Documentation](https://img.shields.io/readthedocs/adaptive)](https://adaptive.readthedocs.io/en/latest/) [![Coverage](https://img.shields.io/codecov/c/github/python-adaptive/adaptive)](https://codecov.io/gh/python-adaptive/adaptive) -[![Downloads](https://img.shields.io/conda/dn/conda-forge/adaptive.svg)](https://anaconda.org/conda-forge/adaptive) +[![Downloads](https://img.shields.io/conda/dn/conda-forge/adaptive.svg?label=conda%20downloads)](https://anaconda.org/conda-forge/adaptive) [![DOI](https://img.shields.io/badge/doi-10.5281%2Fzenodo.1182437-blue.svg)](https://doi.org/10.5281/zenodo.1182437) [![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/python-adaptive/adaptive/main?filepath=example-notebook.ipynb) @@ -20,13 +20,14 @@ You supply a function and its bounds, and `adaptive` evaluates it at the "best" With a few lines of code you can run the evaluations on a computing cluster, live-plot the data as it comes in, and fine-tune the sampling algorithm. `adaptive` works best when each function evaluation takes *at least* ≈50 ms, because picking the next points has some overhead. +It is meant for functions of a few parameters: with more dimensions, `LearnerND` quickly becomes slower and less effective. Try the example notebook [on Binder](https://mybinder.org/v2/gh/python-adaptive/adaptive/main?filepath=example-notebook.ipynb) or read the [tutorial](https://adaptive.readthedocs.io/en/latest/tutorial/tutorial/). - - With the same number of points, adaptive sampling resolves features that a homogeneous grid misses: + +

Learner1D: homogeneous grid versus adaptive sampling of a sharp peak Learner2D: homogeneous grid versus adaptive triangulation of a ring @@ -59,11 +60,17 @@ In a script, use `BlockingRunner`, which returns once the goal is reached: ```python from adaptive import BlockingRunner, Learner1D + +def peak(x, a=0.01): + return x + a**2 / (a**2 + x**2) + + learner = Learner1D(peak, bounds=(-1, 1)) BlockingRunner(learner, loss_goal=0.01) +data = learner.to_numpy() # array with columns x, y ``` -Afterwards, `learner.to_numpy()` returns the data as a NumPy array, and `learner.to_dataframe()` as a pandas DataFrame. +`learner.to_dataframe()` returns the same data as a pandas DataFrame (pandas comes with the `[notebook]` extra). @@ -74,7 +81,8 @@ A learner samples a function at the points in its parameter space that tell it t As it evaluates more points, it gets a better idea of where to sample next. What the "best" points are depends on your application. -`adaptive` makes reasonable default choices, but the details of the sampling are completely customizable. +Each learner ranks candidate points with a *loss function*. +The default losses put more points where the function changes quickly; you can pass a built-in alternative, such as `curvature_loss_function()` or `resolution_loss_function()`, or write your own (see the [custom loss tutorial](https://adaptive.readthedocs.io/en/latest/tutorial/tutorial.custom_loss/)). The following learners are implemented: @@ -100,6 +108,9 @@ In addition to the learners, `adaptive` provides primitives for running the samp [ipyparallel](https://ipyparallel.readthedocs.io/en/latest/), and [distributed](https://distributed.readthedocs.io/en/latest/). +By default, both runners evaluate the function in parallel on all cores of the local machine, using loky. +To use one of the others, pass it as `executor`, for example `Runner(learner, executor=distributed.Client())`; the [parallelism tutorial](https://adaptive.readthedocs.io/en/latest/tutorial/tutorial.parallelism/) has an example for each. + ## Installation @@ -115,10 +126,10 @@ conda install -c conda-forge adaptive or from PyPI: ```bash -pip install "adaptive[notebook]" +pip install adaptive ``` -The `[notebook]` extra installs the dependencies for live plotting and widgets in JupyterLab and Jupyter Notebook; no Jupyter extensions are needed. +Add the `[notebook]` extra (`pip install "adaptive[notebook]"`) for live plotting and widgets in JupyterLab and Jupyter Notebook, and for `to_dataframe()`; no Jupyter extensions are needed. ### Faster triangulation (optional) @@ -143,20 +154,16 @@ pip install -e ".[notebook,test,other]" We recommend a virtual environment, for example with [uv](https://docs.astral.sh/uv/) (`uv sync --extra notebook --extra test --extra other`) or Conda. -To avoid polluting the history with notebook output, set up the git filter by running - -```bash -python ipynb_filter.py -``` - -in the repository. - We use [pre-commit](https://pre-commit.com) for a consistent code style; install the hooks with ```bash pre-commit install ``` +Run the tests with `pytest`. + +If you edit `example-notebook.ipynb`, install `nbconvert` and run `python ipynb_filter.py` once, so git strips the notebook output on commit. + ### Building the documentation The documentation is built with [Zensical](https://zensical.org). @@ -190,7 +197,7 @@ The algorithms are described in more detail in a [draft paper](https://github.co We would like to give credits to the following people: -- Pedro Gonnet for his implementation of [CQUAD](https://www.gnu.org/software/gsl/manual/html_node/CQUAD-doubly_002dadaptive-integration.html), "Algorithm 4" as described in "Increasing the Reliability of Adaptive Quadrature Using Explicit Interpolants", P. Gonnet, ACM Transactions on Mathematical Software, 37 (3), art. no. 26, 2010. +- Pedro Gonnet for his implementation of [CQUAD](https://www.gnu.org/software/gsl/doc/html/integration.html#cquad-doubly-adaptive-integration), "Algorithm 4" as described in "Increasing the Reliability of Adaptive Quadrature Using Explicit Interpolants", P. Gonnet, ACM Transactions on Mathematical Software, 37 (3), art. no. 26, 2010. - Pauli Virtanen for his `AdaptiveTriSampling` script (no longer available online since SciPy Central went down), which served as inspiration for `adaptive.Learner2D`. diff --git a/docs/notebooks/tutorial/tutorial.parallelism.md b/docs/notebooks/tutorial/tutorial.parallelism.md index f6daeefd2..782b8b3cf 100644 --- a/docs/notebooks/tutorial/tutorial.parallelism.md +++ b/docs/notebooks/tutorial/tutorial.parallelism.md @@ -45,8 +45,6 @@ runner.live_plot() ## `distributed.Client` -On Windows by default [`adaptive.Runner`][adaptive.Runner] uses a `distributed.Client`. - ```python import distributed