🛠️ project Announcing Basin: A Numerical Optimization Library for Rust
Hi!
I've been working on Basin, a numerical optimization library for Rust. It's heavily inspired by argmin; same overall shape (Executor driver loop, Solver/Problem trait split, per-solver State, pluggable termination). Basin exists because I wanted to push on a few specific design directions that were awkward to retrofit.
What's Different
- Framework-level termination, bound to state shape.
max_iter, the*_tolerancefamily,max_time, eval budgets all live on theExecutorand compose across solvers. Each criterion binds on the minimum state it needs, so asking for aGradientToleranceon a derivative-free solver is a compile error, not a runtime surprise. - First-class constraints. Constraints describe the problem, so they live problem-side (not on the executor, never on state). A constrained problem handed to an unconstrained solver is a compile error; there are opt-in adapters (projection/log-barrier/augmented Lagrangian) to wrap unconstrained solvers. Box bounds and linear (in)equalities today; nonlinear is on the roadmap.
- Backend-generic linear algebra. Solvers are generic over
Vec<f64>(no features), nalgebra, ndarray, and faer. A small universal vector tier keeps first-order/derivative-free solvers backend-generic; a richerlinalgtier holds matrix ops that LA-heavy solvers bound on by the minimum subset they need. - WASM in the default build. No BLAS/LAPACK, no threads, no
std::time::Instantin default paths. CI enforceswasm32-unknown-unknown. Parallelism and BLAS-backed paths are opt-in features.
Current Solvers Supported
- First-order/quasi-Newton: gradient descent (with momentum + pluggable line searches), BFGS, L-BFGS, L-BFGS-B
- Derivative-free: Nelder--Mead, Brent
- Nonlinear least squares: Gauss--Newton, Levenberg--Marquardt, trust-region-reflective
- Global/stochastic: random search, CMA-ES, steady-state GA, memetic combinations
- Constrained: projected gradient, bounded Nelder--Mead/L-BFGS-B/CMA-ES, log-barrier, augmented Lagrangian
Example
use basin::{BasicState, CostFunction, Executor, Gradient, GradientDescent, GradientTolerance};
struct Rosenbrock;
impl CostFunction for Rosenbrock {
type Param = Vec<f64>;
type Output = f64;
type Error = std::convert::Infallible;
fn cost(&self, x: &Vec<f64>) -> Result<f64, std::convert::Infallible> {
Ok((1.0 - x[0]).powi(2) + 100.0 * (x[1] - x[0].powi(2)).powi(2))
}
}
impl Gradient for Rosenbrock {
type Gradient = Vec<f64>;
fn gradient(&self, x: &Vec<f64>) -> Result<Vec<f64>, std::convert::Infallible> {
Ok(vec![
-2.0 * (1.0 - x[0]) - 400.0 * x[0] * (x[1] - x[0].powi(2)),
200.0 * (x[1] - x[0].powi(2)),
])
}
}
let result = Executor::new(Rosenbrock, GradientDescent::new(1e-3), BasicState::new(vec![-1.2, 1.0]))
.max_iter(50_000)
.terminate_on(GradientTolerance(1e-6))
.run()
.unwrap();
Links
- Crate: https://crates.io/crates/basin (
cargo add basin) - Docs: https://basin.bz/docs/, API: https://docs.rs/basin
- In-browser solver visualizer: https://basin.bz/visualizer/
- Benchmarks vs other crates and across backends: https://basin.bz/benchmarks/
- Source: https://github.com/jolars/basin (MIT)
Feedback is very welcome, especially on the trait surface, naming, and any solver/backend combinations you'd want that aren't there yet.
Duplicates
optimization • u/johlars • 5d ago
Announcing Basin: A Numerical Optimization Library for Rust
ScientificComputing • u/johlars • 5d ago