Skip to main content

Crate basin

Crate basin 

Source
Expand description

Basin: a numerical optimization library.

The framework lives in core: problem traits the user implements (CostFunction, Gradient, BoxConstraints, LinearInequalityConstraints, LinearEqualityConstraints, LinearConstraints, NonlinearInequalityConstraints, NonlinearConstraints), state shapes solvers iterate over (State, GradientState, SimplexState), the Solver trait, solver-owned convergence, execution controls (RunControl), and a read-only observer layer (Observe). Concrete optimization solvers are in solver; line searches in line_search; and direct scalar root finders in root.

Start at Executor for optimization runs, root for scalar equations, or core for the trait taxonomy and the iteration-loop contract.

See CONTRIBUTING.md at the repo root for the design tenets that shape these APIs (notably tenet 3 on solver-owned convergence, tenet 4 on first-class constraints, and tenet 5 on backend tiering).

§Example

Implement CostFunction (and Gradient when the solver needs derivatives), then hand the problem, a solver, and an initial state to the Executor:

use basin::{
    BasicState, CostFunction, Executor, Gradient, GradientDescent,
};

struct Sphere;
impl CostFunction for Sphere {
    type Param = Vec<f64>;
    type Output = f64;
    type Error = std::convert::Infallible;
    fn cost(&self, x: &Vec<f64>) -> Result<f64, std::convert::Infallible> {
        Ok(x.iter().map(|xi| xi * xi).sum())
    }
}
impl Gradient for Sphere {
    type Gradient = Vec<f64>;
    fn gradient(
        &self,
        x: &Vec<f64>,
    ) -> Result<Vec<f64>, std::convert::Infallible> {
        Ok(x.iter().map(|xi| 2.0 * xi).collect())
    }
}

let result = Executor::new(
    Sphere,
    (GradientDescent::new(0.1)).with_absolute_gradient_tolerance(1e-8),
    BasicState::new(vec![1.0, 1.0]),
)
.max_iter(1_000)
.run()
.unwrap();
assert!(result.cost() < 1e-12);

§Seeding the initial state

Executor::new takes a fully-built State so you control the initial iterate (a custom simplex, a warm-started inverse Hessian, an anisotropic CMA-ES covariance). For the common case (start at a point, use the solver’s natural defaults) Executor::from_start takes the bare starting vector instead and builds the state for you via InitialState::seed, so you never name the concrete state type:

use basin::{Executor, NelderMead};
let result = Executor::from_start(Sphere, NelderMead::new(), vec![1.0, 1.0])
    .max_iter(500)
    .run()
    .unwrap();

Most solvers support from_start; the few whose natural initialization needs more than a point do not implement InitialState, so calling from_start with one is a compile error (use Executor::new with an explicit state). The map:

SolverStatefrom_start
GradientDescent, SgdBasicState
NonlinearCgFirstOrderState
ProjectedGradientDescentBasicState✓ (f64 only)
BfgsQuasiNewtonState✓ (Vec/nalgebra/ndarray/faer)
Lbfgs, LbfgsbLbfgsState
TrustRegionBasicState
GaussNewton, LevenbergMarquardt, LevenbergMarquardtQr, Trf, TrustRegionReflectiveNllsState
NelderMeadBasicSimplexState
GbnmGbnmState
Newuoa, Bobyqa, Lincoa, CobylaNewuoaState/…
MadsMadsState/ConstrainedMadsState
SolisWetsSolisWetsState
SimulatedAnnealingSimulatedAnnealingState
BarrierMethod, AugmentedLagrangianMethodBasicState
CmaEs, BoundedCmaEs, CmaInject, BoundedCmaInject, MaLsChCma, MaLsChSwCmaEsState/MaLsChState/…✗ (needs a step-size σ or samples the box)
GlobalBestPsoGlobalBestPsoState✗ (samples a swarm from the box)
RandomSearch, Ssga, De, DeInjectBasicPopulationState✗ (sample the box, ignore a point)
DirectPointState✗ (starts at the box midpoint)
Brent, BrentDerivative, GoldenSectionScalarState✗ (bracket, not a point)

§Error model

Basin distinguishes three outcomes a run can produce. The split is a stable part of the public contract; downstream code can rely on it:

  • Soft reject: return Ok(f64::INFINITY) from CostFunction::cost to reject a single point without stopping the solve. Line searches treat +∞ as worse and retreat; population solvers treat it as worst fitness. This is the channel for “this x is outside my domain, but the solve should continue.”
  • Clean stop: the run ends normally with a TerminationReason, either because convergence or an execution limit was reached, an attached CancellationToken was cancelled, or the Solver reported a mid-iteration stop. Executor::run returns Ok(OptimizationResult) carrying that reason. This is not an error.
  • Hard abort: return Err(_) from a problem-trait method to terminate the entire solve. The error is your own type and bubbles out of Executor::run untouched, typed as Result<_, P::Error>. Use it when the failure is not about a particular x: a downstream service vanished, an expensive evaluation detected a fine-grained cancellation request, or an early-stop condition in your own problem state fired.

§One error type, threaded through

The hard-abort error is chosen once, on the problem (CostFunction::Error, or Residual::Error for nonlinear least squares). Every downstream trait mirrors it: Solver::Error and LineSearch::Error are set to P::Error, so a custom problem error flows through the solver and line search out to the caller with no conversion glue. Problems that cannot fail pick std::convert::Infallible; its niche optimization keeps Result<f64, Infallible> the same layout as a bare f64, so the happy path stays zero-cost.

The direct scalar root APIs follow the same typed-error principle without using optimization state: callback failures are wrapped in BrentRootError::Evaluation or RootError::Evaluation, structural bracket failures have distinct variants, and an iteration-limit exit is a clean RootResult.

The problem module docs carry the per-trait detail.

§Backends

Parameters and linear algebra are generic over the backend. Vec<f64> needs no features. Each external backend has exact version features and a moving alias:

BackendExact featuresMoving alias
nalgebranalgebra_v0_32 through nalgebra_v0_35nalgebra_latest
ndarrayndarray_v0_15 through ndarray_v0_17ndarray_latest
faerfaer_v0_22 through faer_v0_24faer_latest

The original features remain frozen for Basin 1.x compatibility: nalgebra selects 0.34, ndarray selects 0.17, and faer selects 0.24. If dependency feature unification enables several releases of one backend, Basin implements the newest enabled release.

Each nalgebra release includes its matching nalgebra-sparse release: 0.32/0.9, 0.33/0.10, 0.34/0.11, and 0.35/0.12. Versioned acceleration uses the nalgebra_v0_XX-lapack and ndarray_v0_XX-blas features. The moving aliases are nalgebra_latest-lapack and ndarray_latest-blas; the original nalgebra-lapack and ndarray-blas features remain frozen at nalgebra 0.34 and ndarray 0.17.

BLAS/LAPACK acceleration is off by default, is not wasm-compatible, and expects the application to supply BLAS/LAPACK symbols at link time. The default build is wasm-friendly and single-threaded; parallelism is behind the opt-in parallel feature.

Basin’s package MSRV is Rust 1.87. nalgebra_v0_35 and nalgebra_latest require Rust 1.89 because nalgebra 0.35 does.

§Citation

If you use Basin in your research, please cite the paper:

Larsson, J. (2026). Basin: Efficient and Extensible Numerical Optimization in Rust (arXiv:2608.11279). arXiv. https://doi.org/10.48550/arXiv.2608.11279

@misc{larsson2026basin,
  title         = {Basin: Efficient and Extensible Numerical Optimization in {{Rust}}},
  shorttitle    = {Basin},
  author        = {Larsson, Johan},
  year          = {2026},
  month         = aug,
  number        = {arXiv:2608.11279},
  eprint        = {2608.11279},
  primaryclass  = {cs.LG},
  publisher     = {arXiv},
  doi           = {10.48550/arXiv.2608.11279},
  archiveprefix = {arXiv}
}

CITATION.cff at the repo root carries the same reference in machine-readable form.

Re-exports§

pub use crate::bracket::BracketError;
pub use crate::bracket::BracketTerminationReason;
pub use crate::bracket::MinimumBracketResult;
pub use crate::bracket::MinimumBracketer;
pub use crate::bracket::RootBracketResult;
pub use crate::bracket::RootBracketer;
pub use crate::core::augmented_lagrangian::AugmentedLagrangian;
pub use crate::core::barrier::LogBarrier;
pub use crate::core::checkpoint::CheckpointSink;
pub use crate::core::checkpoint::ExactCheckpoint;
pub use crate::core::checkpoint::CheckpointStatus;serde and non-WebAssembly
pub use crate::core::checkpoint::CheckpointWriteError;serde and non-WebAssembly
pub use crate::core::checkpoint::ExactCheckpointWriter;serde and non-WebAssembly
pub use crate::core::checkpoint::read_exact_checkpoint;serde and non-WebAssembly
pub use crate::core::constraint::BoxConstraints;
pub use crate::core::constraint::ConstraintJacobian;
pub use crate::core::constraint::FoldedConstraints;
pub use crate::core::constraint::LinearConstraints;
pub use crate::core::constraint::LinearEqualityConstraints;
pub use crate::core::constraint::LinearInequalityConstraints;
pub use crate::core::constraint::NonlinearConstraints;
pub use crate::core::constraint::NonlinearInequalityConstraints;
pub use crate::core::convergence::ConfiguredSolver;
pub use crate::core::executor::CancellationToken;
pub use crate::core::executor::Executor;
pub use crate::core::executor::OptimizationResult;
pub use crate::core::executor::OptimizationResultWithSolver;
pub use crate::core::executor::StepOutcome;
pub use crate::core::executor::Stepper;
pub use crate::core::executor::run_loop;Deprecated
pub use crate::core::executor::run_loop_with_control;
pub use crate::core::inner::InitialState;
pub use crate::core::inner::InnerExecutor;
pub use crate::core::inner::ResumableInner;
pub use crate::core::inner::WarmStart;
pub use crate::core::least_squares::ArctanLoss;
pub use crate::core::least_squares::CauchyLoss;
pub use crate::core::least_squares::HuberLoss;
pub use crate::core::least_squares::LossEvaluation;
pub use crate::core::least_squares::LossFunction;
pub use crate::core::least_squares::RobustLeastSquares;
pub use crate::core::least_squares::SoftL1Loss;
pub use crate::core::least_squares::SquaredLoss;
pub use crate::core::math::AddDiagonalVectorInPlace;
pub use crate::core::math::ClampInPlace;
pub use crate::core::math::ComponentMulAssign;
pub use crate::core::math::DenseMatrix;
pub use crate::core::math::DenseMatrixFromFn;
pub use crate::core::math::Dot;
pub use crate::core::math::FactorizePivotedQr;
pub use crate::core::math::GramMatrix;
pub use crate::core::math::LinearSolveError;
pub use crate::core::math::LinearSolveLstsq;
pub use crate::core::math::LinearSolveSpd;
pub use crate::core::math::MatTransposeVec;
pub use crate::core::math::MatVec;
pub use crate::core::math::MatrixFromDiagonal;
pub use crate::core::math::MatrixIdentity;
pub use crate::core::math::MatrixIndex;
pub use crate::core::math::MaxDiagonal;
pub use crate::core::math::NegInPlace;
pub use crate::core::math::NormInfinity;
pub use crate::core::math::NormSquared;
pub use crate::core::math::QrFactorization;
pub use crate::core::math::QrSolveError;
pub use crate::core::math::RegularizedQrSolve;
pub use crate::core::math::SampleStandardNormal;
pub use crate::core::math::SampleUniformBox;
pub use crate::core::math::Scalar;
pub use crate::core::math::ScaleInPlace;
pub use crate::core::math::ScaleRowsInPlace;
pub use crate::core::math::ScaledAdd;
pub use crate::core::math::SymmetricEigen;
pub use crate::core::math::SymmetricEigenError;
pub use crate::core::math::VectorIndex;
pub use crate::core::math::VectorLen;
pub use crate::core::numdiff::BoundedFiniteDiff;
pub use crate::core::numdiff::DerivativeCheckError;
pub use crate::core::numdiff::DerivativeCheckReport;
pub use crate::core::numdiff::DerivativeChecker;
pub use crate::core::numdiff::DerivativeComparison;
pub use crate::core::numdiff::DerivativeSource;
pub use crate::core::numdiff::FiniteDiff;
pub use crate::core::numdiff::Method;
pub use crate::core::numdiff::central_difference_gradient;
pub use crate::core::numdiff::central_difference_hessian;
pub use crate::core::numdiff::central_difference_hessian_product;
pub use crate::core::numdiff::central_difference_jacobian;
pub use crate::core::numdiff::forward_difference_gradient;
pub use crate::core::numdiff::forward_difference_hessian;
pub use crate::core::numdiff::forward_difference_hessian_product;
pub use crate::core::numdiff::forward_difference_jacobian;
pub use crate::core::observer::CheckpointWriter;serde and non-WebAssembly
pub use crate::core::observer::read_checkpoint;serde and non-WebAssembly
pub use crate::core::observer::History;
pub use crate::core::observer::Observe;
pub use crate::core::observer::ObserverMode;
pub use crate::core::observer::Report;
pub use crate::core::problem::CostFunction;
pub use crate::core::problem::EvalCounts;
pub use crate::core::problem::EvaluationKind;
pub use crate::core::problem::Gradient;
pub use crate::core::problem::Hessian;
pub use crate::core::problem::HessianProduct;
pub use crate::core::problem::Jacobian;
pub use crate::core::problem::MiniBatchGradient;
pub use crate::core::problem::Problem;
pub use crate::core::problem::Residual;
pub use crate::core::run_control::RunControl;
pub use crate::core::solver::Solver;
pub use crate::core::state::FaerQuasiNewtonState;faer_all
pub use crate::core::state::NalgebraQuasiNewtonState;nalgebra_all
pub use crate::core::state::NdarrayQuasiNewtonState;ndarray_all
pub use crate::core::state::AcceptanceState;
pub use crate::core::state::BasicPopulationState;
pub use crate::core::state::BasicSimplexState;
pub use crate::core::state::BasicState;
pub use crate::core::state::BobyqaState;
pub use crate::core::state::CmaEsState;
pub use crate::core::state::CobylaState;
pub use crate::core::state::ConstrainedMadsState;
pub use crate::core::state::CountsMirror;
pub use crate::core::state::EvaluatedGradientState;
pub use crate::core::state::EvaluatedState;
pub use crate::core::state::ExactResumeState;
pub use crate::core::state::FirstOrderState;
pub use crate::core::state::GbnmState;
pub use crate::core::state::GlobalBestPsoState;
pub use crate::core::state::GradientDimensionMismatch;
pub use crate::core::state::GradientState;
pub use crate::core::state::IncumbentRef;
pub use crate::core::state::IncumbentState;
pub use crate::core::state::IntoInitialSimplex;
pub use crate::core::state::LbfgsState;
pub use crate::core::state::LincoaState;
pub use crate::core::state::MadsState;
pub use crate::core::state::MeshState;
pub use crate::core::state::NewuoaState;
pub use crate::core::state::NllsState;
pub use crate::core::state::ObjectiveIncumbentState;
pub use crate::core::state::PointState;
pub use crate::core::state::PopulationState;
pub use crate::core::state::RawEvaluationState;
pub use crate::core::state::RhoState;
pub use crate::core::state::ScalarGradientState;
pub use crate::core::state::ScalarState;
pub use crate::core::state::SimplexState;
pub use crate::core::state::SimulatedAnnealingState;
pub use crate::core::state::SlsqpState;
pub use crate::core::state::SolisWetsState;
pub use crate::core::state::State;
pub use crate::core::state::DenseQuasiNewtonState;
pub use crate::core::state::QuasiNewtonState;
pub use crate::core::termination::CmaEsTolerance;Deprecated
pub use crate::core::termination::CostTolerance;Deprecated
pub use crate::core::termination::GradientTolerance;Deprecated
pub use crate::core::termination::MaxCostEvals;Deprecated
pub use crate::core::termination::MaxGradientEvals;Deprecated
pub use crate::core::termination::MaxIter;Deprecated
pub use crate::core::termination::MaxTime;Deprecated
pub use crate::core::termination::MeshTolerance;Deprecated
pub use crate::core::termination::NoAcceptance;Deprecated
pub use crate::core::termination::NoImprovement;Deprecated
pub use crate::core::termination::ParamTolerance;Deprecated
pub use crate::core::termination::ProjectedGradientTolerance;Deprecated
pub use crate::core::termination::RelativeCostTolerance;Deprecated
pub use crate::core::termination::RelativeGradientTolerance;Deprecated
pub use crate::core::termination::RelativeParamTolerance;Deprecated
pub use crate::core::termination::RhoTolerance;Deprecated
pub use crate::core::termination::SimplexTolerance;Deprecated
pub use crate::core::termination::TargetCost;Deprecated
pub use crate::core::termination::TerminationCriterion;Deprecated
pub use crate::core::termination::TerminationReason;
pub use crate::line_search::Backtracking;
pub use crate::line_search::Constant;
pub use crate::line_search::HagerZhang;
pub use crate::line_search::LineSearch;
pub use crate::line_search::LineSearchBounds;
pub use crate::line_search::LineSearchEvaluation;
pub use crate::line_search::LineSearchOutcome;
pub use crate::line_search::LineSearchResult;
pub use crate::line_search::MoreThuente;
pub use crate::line_search::Wolfe;
pub use crate::root::BrentRoot;
pub use crate::root::BrentRootError;
pub use crate::root::HalleyRoot;
pub use crate::root::NewtonRoot;
pub use crate::root::RootError;
pub use crate::root::RootResult;
pub use crate::root::RootTerminationReason;
pub use crate::root::SecantRoot;
pub use crate::root::Toms748Root;
pub use crate::solver::Bfgs;
pub use crate::solver::lbfgs::Lbfgs;
pub use crate::solver::lbfgs::Lbfgsb;
pub use crate::solver::trust_region::CauchyPoint;
pub use crate::solver::trust_region::Dogleg;
pub use crate::solver::trust_region::ExactHessian;
pub use crate::solver::trust_region::MatrixFree;
pub use crate::solver::trust_region::MoreSorensen;
pub use crate::solver::trust_region::Steihaug;
pub use crate::solver::trust_region::TrustRegion;
pub use crate::solver::AcceptanceTest;
pub use crate::solver::AugmentedLagrangianMethod;
pub use crate::solver::BarrierMethod;
pub use crate::solver::BasinHopping;
pub use crate::solver::Bobyqa;
pub use crate::solver::BoundedCmaEs;
pub use crate::solver::BoundedCmaInject;
pub use crate::solver::Brent;
pub use crate::solver::BrentDerivative;
pub use crate::solver::CgUpdate;
pub use crate::solver::ClosureInner;
pub use crate::solver::CmaEs;
pub use crate::solver::CmaInject;
pub use crate::solver::Cobyla;
pub use crate::solver::De;
pub use crate::solver::DeInject;
pub use crate::solver::Direct;
pub use crate::solver::GaussNewton;
pub use crate::solver::Gbnm;
pub use crate::solver::GlobalBestPso;
pub use crate::solver::GoldenSection;
pub use crate::solver::GradientDescent;
pub use crate::solver::LevenbergMarquardt;
pub use crate::solver::LevenbergMarquardtQr;
pub use crate::solver::Lincoa;
pub use crate::solver::LmDamping;
pub use crate::solver::MaLsCh;
pub use crate::solver::MaLsChCma;
pub use crate::solver::MaLsChGenericState;
pub use crate::solver::MaLsChState;
pub use crate::solver::MaLsChSw;
pub use crate::solver::MaLsChSwState;
pub use crate::solver::Mads;
pub use crate::solver::MemeticInner;
pub use crate::solver::Metropolis;
pub use crate::solver::Neighbor;
pub use crate::solver::NelderMead;
pub use crate::solver::Newuoa;
pub use crate::solver::NonlinearCg;
pub use crate::solver::ProjectedGradientDescent;
pub use crate::solver::PsoBoundaryHandling;
pub use crate::solver::PsoVelocityLimit;
pub use crate::solver::RandomDisplacement;
pub use crate::solver::RandomSearch;
pub use crate::solver::Reannealing;
pub use crate::solver::Sgd;
pub use crate::solver::SimulatedAnnealing;
pub use crate::solver::Slsqp;
pub use crate::solver::SlsqpFailure;
pub use crate::solver::SolisWets;
pub use crate::solver::Ssga;
pub use crate::solver::StepTaker;
pub use crate::solver::TemperatureSchedule;
pub use crate::solver::Trf;
pub use crate::solver::TrustRegionReflective;

Modules§

bracket
Automatic scalar root and minimum bracketing. Automatic scalar root and minimum bracketing.
core
Framework: traits, state shapes, the iteration driver, and the convergence and execution controls. The slot taxonomy is:
line_search
Line searches: produce a step size α along a caller-supplied descent direction. Used by first-order solvers (gradient descent, BFGS).
problemsproblems
Catalog of test problems used by the example tests and benchmarks. Standard optimization test problems.
root
Scalar root-finding algorithms with direct solve APIs. Scalar root-finding algorithms.
solver
Concrete solver implementations.