Getting started

Install

Add Basin to your project:

cargo add basin

Basin works on plain Vec<f64> out of the box. Linear-algebra backends are opt-in, one feature each — for example, to use the nalgebra backend (required by some solvers, see Solvers):

cargo add basin --features nalgebra

Available backend features: nalgebra, ndarray, faer. The problems feature (a corpus of standard test functions) is on by default; disable it with default-features = false to shrink a wasm build.

Your first solve

Implement CostFunction for your objective. Add a Gradient impl when the solver needs derivatives (gradient descent does; Nelder–Mead does not). Then hand the problem, a solver, and an initial state to the Executor.

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

struct Rosenbrock;

impl CostFunction for Rosenbrock {
    type Param = Vec<f64>;
    type Output = f64;
    type Error = Infallible;

    fn cost(&self, x: &Vec<f64>) -> Result<f64, Self::Error> {
        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>, Self::Error> {
        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)),
        ])
    }
}

fn main() {
    let solver = GradientDescent::new(1e-3);
    let state = BasicState::new(vec![-1.2, 1.0]);

    let result = Executor::new(Rosenbrock, solver, state)
        .max_iter(50_000)
        .run()
        .unwrap();

    println!("x = {:?}", result.param());
    println!("f = {}", result.cost());
    println!("stopped: {:?}", result.reason);
}

Termination criteria

max_iter is one stopping condition; the others are pluggable and composed via terminate_on. They are framework-level, so the same criteria work across solvers — and they are bound to the state a solver actually exposes (asking for a gradient tolerance on a derivative-free solver is a compile error):

use basin::{Executor, GradientDescent, GradientTolerance, BasicState};

let result = Executor::new(problem, GradientDescent::new(1e-3), BasicState::new(x0))
    .max_iter(50_000)
    .terminate_on(GradientTolerance(1e-6))
    .run()
    .unwrap();

The solver stops at whichever criterion fires first, and result.reason reports which one.

Next

  • Solvers — pick the right algorithm and backend.
  • Browse the full API reference on docs.rs.