Adaptive: parallel active learning of mathematical functions in Python
:chart_with_upwards_trend: Adaptive: parallel active learning of mathematical functions
At a glance
- What is it?
- Adaptive is a Python library that picks the next evaluation point for a function instead of sampling a dense grid. It suits expensive functions and parallel workers, but the per-evaluation overhead makes it a poor fit for cheap calls.
- Who is it for?
- Adopt Adaptive when a single evaluation of your function costs around 50ms or more and you have parallel workers to keep busy, and start by checking the learner list against your function's shape: Learner1D, Learner2D, LearnerND, AverageLearner, IntegratorLearner or BalancingLearner. Skip it when evaluations are microseconds, when the function is not a mathematical map over a bounded parameter space, or when you need a sampling guarantee rather than an adaptive one.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 9 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Adaptive replaces: dense grids and wasted evaluations
The usual way to study a function is to evaluate it on a grid. That works when the function is cheap, because you can afford to be wrong about where the interesting region is. It stops working when each call takes real time, for example a simulation that runs for a minute or a fit that solves a differential equation. A grid then spends most of its budget on regions that are flat and boring.
Adaptive inverts the decision. The README states the library "intelligently selects the 'best' points in the parameter space based on your provided function and bounds" rather than calculating all points on a dense grid. The intended user is a scientist or engineer with an expensive function, a bounded domain, and access to more than one core. The README also gives the threshold plainly: Adaptive is most efficient when each function evaluation takes at least about 50ms, because selecting the next point carries its own overhead. Below that, the selection cost dominates and a plain grid or a vectorized NumPy call is the better tool.
The learner loop and how parallel sampling works
The central object is the learner. A learner samples a function at what the documentation calls the most interesting locations in its parameter space, and as more points arrive it updates its estimate of where to look next. What counts as interesting is defined by a loss function, and the README notes that while the library ships sensible defaults, the sampling process can be fully customized.
The learners are split by the shape of the problem. Learner1D handles f: R to R^N, Learner2D handles f: R^2 to R^N, and LearnerND handles f: R^N to R^M. AverageLearner averages results over repeated evaluations of a random variable, and AverageLearner1D estimates the mean value at each point of a stochastic 1D function. IntegratorLearner is for integrating a 1D function, and BalancingLearner runs several learners at once and picks the most optimal one as points accumulate. DataSaver exists for functions that do not return a scalar or a vector.
Parallelism is a separate layer. The README says Adaptive offers primitives for parallel sampling across multiple cores or machines, with built-in support for concurrent.futures and mpi4py. The core dependencies in pyproject.toml include loky and cloudpickle, which is how function objects and closures get shipped to worker processes. That combination is the reason the library can accept a locally defined function rather than requiring an importable module-level one.
Installing Adaptive and running a first 1D learner
The README points at conda-forge and PyPI. The conda badge links to the conda-forge package named adaptive, and the PyPI badge links to the adaptive package on pypi.org. There is no installer script and no service to run; it is a library you import.
conda install -c conda-forge adaptiveor, from PyPI:
pip install adaptiveNote the interpreter requirement before you start: pyproject.toml sets requires-python = ">=3.11", and the classifiers list 3.11 through 3.14. On an older interpreter the install will fail rather than degrade.
The README gives a complete first example for a Jupyter notebook. It defines a function with a sharp peak near zero, wraps it in a Learner1D over the bounds -1 to 1, and attaches a Runner with a loss goal.
from adaptive import notebook_extension, Runner, Learner1D
notebook_extension()
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()The notebook_extension() call is what wires up the live widgets; without it the live_info() and live_plot() calls have nothing to render into. What you should see is the learner concentrating points around the narrow peak while leaving the flat regions sparse, with the plot updating as evaluations return. The loss_goal=0.01 is the stopping target, and the README does not document what the Runner does if that target is never met, so treat it as a target rather than a hard timeout.
Export is a method call on the learner. The README shows learner.to_numpy() for a NumPy array, and learner.to_dataframe() if Pandas is installed. Pandas is listed in the notebook extra, not in the core dependencies, so to_dataframe() will not work on a bare install.
Where Adaptive is the wrong tool
The 50ms figure is the first limit and the easiest to ignore. If your function returns in a few microseconds, the bookkeeping that decides the next point costs more than the evaluation it is scheduling. Vectorized NumPy over a fixed grid will finish sooner and be simpler to reason about.
The second limit is shape. The learners are built around a function that maps a bounded parameter space to a scalar or a vector. If your work is a discrete search, a combinatorial problem, or a pipeline of steps where each result changes the next query, there is no learner in the list that matches, and DataSaver does not change that; it only relaxes the return type.
The third limit is reproducibility. Adaptive chooses points based on results seen so far, so the sequence of evaluations depends on the order in which results arrive. The test extra in pyproject.toml includes pytest-randomly, which suggests the project takes ordering effects seriously in its own suite. For a workflow that must be auditable point by point, a fixed grid is easier to defend than an adaptive schedule.
Finally, the classifiers in pyproject.toml mark the project as "Development Status :: 4 - Beta". That is the project's own label, and it is worth weighing against the fact that the last push to the repository was on 2026-09-07.
Adaptive compared with a Bayesian optimization library
The closest alternative in spirit is a Bayesian optimization package such as scikit-optimize or Ax. Both spend a budget on a small number of evaluations of an expensive function, so the overlap is real, but the machinery differs.
Bayesian optimization fits a surrogate model, usually a Gaussian process, and uses an acquisition function to pick the next point. That gives it a probabilistic story about uncertainty and makes it strong when the evaluation budget is tiny, in the tens of calls. The cost is that the surrogate itself becomes the bottleneck as dimensions grow, and the choice of kernel is a modelling decision the user has to make.
Adaptive does not fit a global surrogate. It keeps a triangulation of the sampled points and refines where the local loss is largest, which the README describes as sampling the most interesting locations and improving its understanding of those locations as points accumulate. That is cheaper per iteration and scales to higher dimensions through LearnerND, but it does not give you a posterior over the function. If you need calibrated uncertainty, a Gaussian process is the better fit; if you need to keep thousands of workers busy on a function with a clear error target, the triangulation approach is the one that was designed for it.
Adaptive also differs from plain Dask or mpi4py usage. Those give you the parallel execution and nothing else; you still decide which points to send. Adaptive supplies the decision.
Licence, releases and the cost of staying current
The repository is licensed BSD-3-Clause, and pyproject.toml declares license = { text = "BSD" } with the classifier "License :: OSI Approved :: BSD License". For most users that means the usual permissive terms: keep the copyright notice and the disclaimer, and you can use it in closed-source work. This is a description of what the files say, not legal advice; read LICENSE in the repository if the distinction matters to you.
Release cadence is visible in the tags. v1.5.0, v1.5.1 and v1.5.2 all landed on 2026-06-10, three releases in one day, which usually means a fix rolled forward quickly. The repository has a CHANGELOG.md and a RELEASE.md at the top level, so upgrade notes are kept in-tree rather than only in release pages.
The dependency list is short and mostly stable: scipy, sortedcollections, sortedcontainers, cloudpickle, loky and versioningit. The one to watch is the optional rust extra, adaptive-triangulation>=0.3.1, described in pyproject.toml as a Rust-accelerated triangulation backend. It is optional, so a plain install stays pure Python, but the triangulation is the hot path in Learner2D and LearnerND. If you adopt those learners at scale, the upgrade question is less about the Python package than about whether the compiled backend keeps publishing wheels for your platform.
Editorial conclusion
Adopt Adaptive when a single evaluation of your function costs around 50ms or more and you have parallel workers to keep busy, and start by checking the learner list against your function's shape: Learner1D, Learner2D, LearnerND, AverageLearner, IntegratorLearner or BalancingLearner. Skip it when evaluations are microseconds, when the function is not a mathematical map over a bounded parameter space, or when you need a sampling guarantee rather than an adaptive one. Before committing, verify two things in your own environment: that Python is 3.11 or later, since pyproject.toml sets requires-python = ">=3.11", and that the loss_goal you pick actually stops the Runner, because the README does not state what happens when a goal is never reached.
Frequently asked questions
How do I install Adaptive?
The README points to conda-forge and PyPI, so conda install -c conda-forge adaptive or pip install adaptive both work. Note that pyproject.toml sets requires-python to ">=3.11", so the install will fail on an older interpreter.
What is Adaptive used for?
It is a Python library for parallel active learning of mathematical functions. Instead of evaluating a function on a dense grid, it selects the most interesting points in the parameter space based on your function and bounds, and it is most efficient when each evaluation takes at least about 50ms.
Which learners does Adaptive provide?
The README lists Learner1D, Learner2D, LearnerND, AverageLearner, AverageLearner1D, IntegratorLearner and BalancingLearner, plus DataSaver for functions that do not return a scalar or a vector. BalancingLearner is also described as a meta-learner for running several learners at once.
How do I export the data Adaptive has learned?
The README shows learner.to_numpy() for a NumPy array and learner.to_dataframe() for a Pandas DataFrame. Pandas is in the notebook extra rather than the core dependencies, so the DataFrame method needs it installed separately.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/python-adaptive-adaptive)