basin 0.7.0

Numerical optimization in pure Rust, with pluggable linear-algebra backends and WASM support.
Documentation
[package]
name = "basin"
version = "0.7.0"
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
authors.workspace = true
description = "Numerical optimization in pure Rust, with pluggable linear-algebra backends and WASM support."
homepage = "https://basin.bz"
documentation = "https://docs.rs/basin"
readme = "README.md"
keywords = ["optimization", "math", "numerics", "solver", "nonlinear"]
categories = ["mathematics", "science", "algorithms"]

# Build docs.rs with every backend enabled so the feature-gated solver and
# problem impls (nalgebra / ndarray / faer) render — not just the default
# `Vec<f64>` surface. `--cfg docsrs` turns on the `doc(auto_cfg)` attribute
# in `lib.rs` ("Available on crate feature `nalgebra` only").
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[lib]
name = "basin"
path = "src/lib.rs"

[features]
default = ["problems"]
# Pin one version per backend (tenet 2 in AGENTS.md). A backend major bump
# becomes a basin major bump — do not introduce per-version features.
# `nalgebra-sparse` rides along on the same feature: it's a separate crate,
# but logically part of the nalgebra story (sparse Jacobians, sparse
# Cholesky). Folding it in keeps the manifest to one nalgebra feature.
nalgebra = ["dep:nalgebra", "dep:nalgebra-sparse"]
ndarray = ["dep:ndarray"]
# Opt-in BLAS-backed ndarray. Off by default per the WASM hard constraint
# (cblas-sys is not wasm-compatible) and because BLAS wiring is a heavy
# toolchain story most basin users don't need. This feature only flips
# ndarray's `blas` switch; the consumer is still responsible for picking
# a BLAS source crate (`openblas-src`, `netlib-src`, `intel-mkl-src`,
# `accelerate-src`, ...) and adding it as a direct dependency. No analogue
# exists for nalgebra (0.34 has no `blas` feature; its BLAS path lives in
# the separate `nalgebra-lapack` crate) or for faer (pure-Rust by design).
ndarray-blas = ["ndarray", "ndarray/blas"]
# `generativity` is a transitive dep of faer, surfaced here so it only enters
# the graph when `faer` is enabled.
faer = ["dep:faer", "dep:generativity"]
# Opt-in parallel evaluation (currently faer's rayon-backed linalg). Off by
# default because basin is typically embedded inside a caller that already
# manages parallelism (multi-start sweeps, R/Python schedulers, outer rayon
# loops) — a default-on thread pool causes oversubscription and fights the
# caller. WASM-incompatible: enabling this on wasm32-unknown-unknown will
# fail to compile. No-op unless a backend that hooks into it is also enabled.
parallel = ["faer?/rayon"]
# Standard test-problem corpus (Rosenbrock, ...). Pure-Rust, no extra deps;
# default-on so plain `cargo test` keeps working without `--features problems`
# (integration tests can't see `cfg(test)` lib gating). Downstream can drop
# it via `default-features = false` to shrink the WASM bundle.
problems = []

[dependencies]
nalgebra = { version = "0.34", optional = true }
nalgebra-sparse = { version = "0.11", optional = true }
# Pure-Rust (no BLAS feature) so it stays wasm-compatible per the WASM hard
# constraint in AGENTS.md.
ndarray = { version = "0.17", optional = true, default-features = false }
# default-features off because faer's defaults pull in rayon (breaks wasm,
# tenet: no parallelism in default features) and npy (file I/O). We enable
# only `std` + `linalg` so the core dense types work on native and wasm.
# Rayon is reachable via the opt-in `parallel` feature above.
faer = { version = "0.24", optional = true, default-features = false, features = [
  "std",
  "linalg",
] }
# Transitive dep of faer, surfaced as an optional dep so it only enters the
# graph behind the `faer` feature. basin doesn't use this crate directly.
generativity = { version = "1", optional = true }
# Re-exports `std::time::Instant` on native and a wasm-compatible shim on
# `wasm32-unknown-unknown`, so `MaxTime` works on both without feature gating.
# Tiny crate, MSRV 1.60, no transitive deps on native.
web-time = "1"
# Seedable, wasm-safe RNG used by stochastic solvers (`RandomSearch`, CMA-ES,
# ...). `default-features = false` drops `std_rng` / `thread_rng` and the
# implicit `getrandom` JS-feature pull-in, so a
# `ChaCha8Rng::seed_from_u64(seed)` works on `wasm32-unknown-unknown` with
# no JS shim.
rand = { version = "0.10", default-features = false }
rand_chacha = { version = "0.10", default-features = false }
# Standard-normal sampling for CMA-ES. rand_distr 0.6 pairs with rand 0.10.
# default-features off to stay consistent with the wasm-safe rand setup.
rand_distr = { version = "0.6", default-features = false }

[dev-dependencies]
# Statistical bench harness. dev-dependency only — never enters the
# published or WASM dependency graph. `default-features = false` drops
# rayon (tenet: no parallelism by default) and plotters/html_reports
# (heavy tree, no value for CLI microbenchmarks), keeping just the
# `cargo bench` integration shim.
criterion = { version = "0.8", default-features = false, features = [
  "cargo_bench_support",
] }

[[bench]]
name = "rosenbrock"
harness = false

# Backend-vs-backend microbenchmark for the LM hot path (issue #9):
# nalgebra vs faer on the small dense ops LM runs each iteration. Needs
# both backends plus the test-problem corpus.
[[bench]]
name = "lm_backends"
harness = false
required-features = ["nalgebra", "faer", "problems"]

# Backend axis (axis 1): GD / Nelder-Mead / unconstrained L-BFGS on
# Rosenbrock across all four backends (Vec / nalgebra / ndarray / faer),
# isolating the cost of the linear-algebra layer. Needs every backend plus
# the test-problem corpus.
[[bench]]
name = "solver_backends"
harness = false
required-features = ["nalgebra", "ndarray", "faer", "problems"]

# Per-backend integration tests (`tests/rosenbrock_<backend>.rs`) gate
# themselves with `#![cfg(feature = "<backend>")]` rather than declaring
# `required-features` here — keeps the manifest free of one stanza per
# backend.