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 (Constraints, CostFunction, Gradient, Residual, and the nonlinear constraint evaluation capabilities), 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::{
    FirstOrderState, 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),
    FirstOrderState::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};
impl basin::CostFunction for Sphere {
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
GradientDescentFirstOrderState✓
SgdPointState✓
NonlinearCgFirstOrderState✓
ProjectedGradientDescentFirstOrderState✓
BfgsFirstOrderState✓ (Vec/nalgebra/ndarray/faer)
Lbfgs, LbfgsbFirstOrderState✓
TrustRegionFirstOrderState✓
GaussNewton, LevenbergMarquardt, LevenbergMarquardtQr, BoundedLevenbergMarquardt, TrustRegionReflectivePointState✓
NelderMeadSimplexProgress✓
GbnmPointState✓
Newuoa, Bobyqa, LincoaPointState✓
CobylaSelectedState✓
MadsPointState or SelectedState✓
BasinHoppingPointState✓
SolisWetsPointState✓
SimulatedAnnealingProposalState✓
BarrierMethodPointState✓
SlsqpSelectedFirstOrderState✓
AugmentedLagrangianMethodSelectedState✓
CmaEs, BoundedCmaEsPopulationProgress✓ (scale is configured on the solver)
CmaInject, BoundedCmaInjectPopulationProgress✗ (explicit mean seed)
MaLsChCma, MaLsChSwPopulationProgress✗ (samples the box)
GlobalBestPsoPopulationProgress✗ (samples a swarm from the box)
RandomSearch, Ssga, De, DeInjectPopulationProgress✗ (samples the box or accepts explicit members)
DirectPointState✗ (starts at the box midpoint)
BrentDerivativeFirstOrderState<F, F>✗ (bracket, not a point)
Brent, GoldenSectionPointState<F, F>✗ (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 TerminationReport, 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. Executor::run preserves the problem error in the solver’s error type, either directly or in a typed wrapper described below. 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). Unconstrained solvers and line searches use P::Error directly. Solvers that validate constraints use ConstraintSolveError<P::Error>: its Evaluation variant preserves the original callback error, while InvalidConstraints reports invalid declarations or malformed callback outputs. Executor::run returns the solver’s Solver::Error, so callers can handle both failures without boxing or stringifying the original error. 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.

§Features

Basin enables no features by default. Enable problems to use the standard test-function corpus through basin::problems, for example with cargo add basin --features problems.

§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
ndarrayndarray_v0_15 through ndarray_v0_17ndarray
faerfaer_v0_22 through faer_v0_24faer

nalgebra selects 0.35, ndarray selects 0.17, and faer selects 0.24. The *_latest features remain available as deprecated synonyms. If dependency feature unification enables several releases of one backend, Basin implements every enabled release independently.

BFGS uses FirstOrderState with a solver-owned inverse Hessian. Its DenseBackend association selects the matrix belonging to the parameter’s backend version, so feature unification cannot change the concrete type.

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-lapack and ndarray-blas; the nalgebra_latest-lapack and ndarray_latest-blas features remain available as deprecated synonyms.

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 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::BoundSide;
pub use crate::core::constraint::BoxBounds;
pub use crate::core::constraint::Constrained;
pub use crate::core::constraint::ConstraintAxis;
pub use crate::core::constraint::ConstraintBlock;
pub use crate::core::constraint::ConstraintDescription;
pub use crate::core::constraint::ConstraintError;
pub use crate::core::constraint::ConstraintJacobian;
pub use crate::core::constraint::ConstraintSet;
pub use crate::core::constraint::ConstraintSolveError;
pub use crate::core::constraint::Constraints;
pub use crate::core::constraint::LinearBlock;
pub use crate::core::constraint::LinearBlocks;
pub use crate::core::constraint::NoBounds;
pub use crate::core::constraint::NoConstraints;
pub use crate::core::constraint::NoLinearBlock;
pub use crate::core::constraint::NoLinearConstraints;
pub use crate::core::constraint::NoNonlinearBlock;
pub use crate::core::constraint::NoNonlinearConstraints;
pub use crate::core::constraint::NonlinearBlock;
pub use crate::core::constraint::NonlinearBlocks;
pub use crate::core::constraint::NonlinearEqualityValues;
pub use crate::core::constraint::NonlinearInequalityValues;
pub use crate::core::constraint::RelaxedInner;
pub use crate::core::constraint::SamplingBounds;
pub use crate::core::constraint::SamplingDomain;
pub use crate::core::convergence::ConfiguredSolver;
pub use crate::core::convergence::NativeConvergenceDiagnostics;
pub use crate::core::convergence::NativeConvergenceTest;
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_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::DenseBackend;
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::GeneralRankOneUpdate;
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::MatTransposeVecInto;
pub use crate::core::math::MatVec;
pub use crate::core::math::MatVecInto;
pub use crate::core::math::MatrixFinite;
pub use crate::core::math::MatrixFromDiagonal;
pub use crate::core::math::MatrixIdentity;
pub use crate::core::math::MatrixIndex;
pub use crate::core::math::MatrixShape;
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::ObservationEvent;
pub use crate::core::observer::Observe;
pub use crate::core::observer::ObserveSolver;
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::solver::SolverStep;
pub use crate::core::state::AcceptanceState;
pub use crate::core::state::ConstraintViolationState;
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::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::MeshState;
pub use crate::core::state::ObjectiveIncumbentState;
pub use crate::core::state::PointState;
pub use crate::core::state::PopulationProgress;
pub use crate::core::state::PopulationShapeError;
pub use crate::core::state::PopulationState;
pub use crate::core::state::ProposalState;
pub use crate::core::state::RawEvaluationState;
pub use crate::core::state::RhoState;
pub use crate::core::state::SelectedFirstOrderState;
pub use crate::core::state::SelectedState;
pub use crate::core::state::SimplexProgress;
pub use crate::core::state::SimplexShapeError;
pub use crate::core::state::SimplexState;
pub use crate::core::state::State;
pub use crate::core::state::ViolationMeasure;
pub use crate::core::state::ViolationReport;
pub use crate::core::state::ViolationScaling;
pub use crate::core::state::ViolationTransformation;
pub use crate::core::termination::ApplicationStop;
pub use crate::core::termination::BinaryValue;
pub use crate::core::termination::Convergence;
pub use crate::core::termination::ConvergenceCriterion;
pub use crate::core::termination::ConvergenceEvidence;
pub use crate::core::termination::ConvergenceTest;
pub use crate::core::termination::ExecutionLimit;
pub use crate::core::termination::Measurement;
pub use crate::core::termination::NumericalFailure;
pub use crate::core::termination::PartialResultPolicy;
pub use crate::core::termination::Stall;
pub use crate::core::termination::Termination;
pub use crate::core::termination::TerminationCode;
pub use crate::core::termination::TerminationReport;
pub use crate::core::termination::TerminationStage;
pub use crate::core::termination::Threshold;
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::BoundedLevenbergMarquardt;
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::DeCrossover;
pub use crate::solver::DeInject;
pub use crate::solver::DeMutation;
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::MaLsChSw;
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::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.
root
Scalar root-finding algorithms with direct solve APIs. Scalar root-finding algorithms.
solver
Concrete solver implementations.