Documentation
Basin is a numerical optimization library for Rust. It follows the overall
shape of argmin: a generic executor loop drives a solver over a state, calling into the problem traits you implement
(CostFunction, Gradient, and friends).
You minimize a function by implementing one trait for your objective, picking a
solver, and handing both to the Executor. Basin covers first-order and
quasi-Newton methods (gradient descent, BFGS, L-BFGS, L-BFGS-B), derivative-free
search (Nelder–Mead, Powell’s NEWUOA/BOBYQA family, MADS), nonlinear least
squares (Gauss–Newton, Levenberg–Marquardt, trust-region-reflective), and
global or stochastic optimizers (CMA-ES, a steady-state GA, memetic hybrids),
with box, linear, and nonlinear constraints handled problem-side. Every solver
is generic over the linear-algebra backend, from plain Vec<f64> to nalgebra,
ndarray, or faer.
A Solver Run at a Glance
The smallest complete program: minimize the sphere function with the
derivative-free Nelder–Mead simplex. Implement CostFunction, then let Executor::from_start build the state from your starting point.
use basin::{CostFunction, Executor, MaxIter, NelderMead};
use std::convert::Infallible;
struct Sphere;
impl CostFunction for Sphere {
type Param = Vec<f64>;
type Output = f64;
type Error = Infallible;
fn cost(&self, x: &Vec<f64>) -> Result<f64, Self::Error> {
Ok(x.iter().map(|xi| xi * xi).sum())
}
}
fn main() {
let result = Executor::from_start(Sphere, NelderMead::new(), vec![1.0, 1.0])
.terminate_on(MaxIter(500))
.run()
.unwrap();
println!("x = {:?}", result.param()); // ~[0.0, 0.0]
println!("f = {}", result.cost()); // ~0.0
} Nelder–Mead needs no derivatives, so CostFunction is the only trait to
implement. If you want to reach for a gradient-based solver and you add a Gradient impl; the Getting Started guide walks through
that full path on the Rosenbrock function.
How the Pieces Fit
- Problem: what you implement. A
CostFunction(and aGradient, if your solver needs one) describing the objective. Constraints such as box bounds live here too, not on the solver. - Solver: the algorithm. Gradient descent, Nelder–Mead, L-BFGS, Levenberg–Marquardt, CMA-ES, and more.
- State: the iterate(s) a solver carries: a single point (
BasicState), a simplex (BasicSimplexState), a population, and so on. - Executor: the driver that runs a solver to termination and hands back an
OptimizationResult. - Termination: pluggable stopping criteria (gradient, parameter, and cost tolerances, iteration and time budgets) configured uniformly across solvers.
- Observers: read-only hooks fired around the loop
(
observe_init/observe_iter/observe_final) for logging, progress reporting, or recording a trajectory. Sibling to termination: observers watch, criteria decide.
Choosing a Solver
A rough map from the shape of your problem to where to start. The Solvers catalog has the full list and the backend each one needs.
- Smooth objective, gradient available:
GradientDescentto start, thenBfgsorLbfgsfor faster convergence (Lbfgswhen the dimension is large). - No derivatives, low dimension:
NelderMead, or Powell’s model-basedNewuoa/Bobyqafor smoother functions. - Sum-of-squares residuals: the nonlinear-least-squares family,
GaussNewton,LevenbergMarquardt, orTrf(trust-region-reflective, for bound constraints). - Rugged or multimodal landscape: the global and stochastic optimizers,
CmaEs, the steady-state GA, or a memetic hybrid. - Constraints: box bounds, linear (in)equalities, and nonlinear constraints
describe the problem, so they live problem-side. Bounded solvers
(
Lbfgsb, boundedNelderMead,Trf) consume them directly; adapters (projection, log-barrier, augmented Lagrangian, orCobyla) wrap unconstrained solvers.
Where to Go Next
- Getting Started: install Basin and run your first solve.
- Solvers: the catalog of solvers and which backends they need.
You can also watch solvers converge interactively in the visualizer, or browse the full API on docs.rs.