Announcing Numra 0.1.0

Numra 0.1.0 is on crates.io: 21 native-Rust crates of numerical methods that compose under one API — ODE, SDE, DDE, FDE, IDE, PDE, SPDE, optimization, autodiff, FFT, statistics, and more.

  • release
  • numerical-methods
  • rust

Numra 0.1.0 is on crates.io. One cargo add numra gives you ordinary and stochastic differential equations, delay equations, fractional and integral equations, PDEs and SPDEs, optimization, automatic differentiation, FFT, statistics, signal processing, and quadrature — implemented in Rust, sharing one set of abstractions, designed to compose without glue.

cargo add numra

This post is the why. The changelog is the what.

The thesis: one workspace, native Rust, methods that compose

Most numerical computing in Rust today is a thin shell over decades-old C or Fortran libraries. That works, and it gets you battle-tested code, but it has costs you only notice once you try to compose methods:

  • An ODE solver returning a Vec<f64> that an optimizer can’t ingest without conversion.
  • An autodiff library whose gradients an FFI solver can’t accept because the type doesn’t cross the boundary.
  • A PDE discretization that gives you a sparse matrix in one crate’s format and a linear solver in another’s.

Numra is the bet that if you implement the numerics natively in Rust, share a small core of abstractions (Scalar, Vector, Signal, Uncertain) across every subsystem, and design every public type to be a first-class citizen everywhere else in the workspace, the resulting library is qualitatively different to use.

You stop writing adapters. You write the math.

What composability looks like in practice

Here’s a complete, self-contained example: solve a stiff ODE, build a cubic spline through the trajectory, then integrate the spline back to verify conservation — three subsystems, no glue.

use numra::integrate::{quad, QuadOptions};
use numra::interp::{CubicSpline, Interpolant};
use numra::ode::{DoPri5, OdeProblem, Solver, SolverOptions};

// y' = -y, y(0) = 1 on [0, 3]
let problem = OdeProblem::new(
    |_t, y: &[f64], dydt: &mut [f64]| { dydt[0] = -y[0]; },
    0.0, 3.0, vec![1.0],
);
let opts = SolverOptions::default().rtol(1e-8).dense();
let result = DoPri5::solve(&problem, 0.0, 3.0, &[1.0], &opts).unwrap();

// ODE -> Interpolation: just hand the trajectory to a spline.
let y_series = result.component(0).unwrap();
let spline = CubicSpline::natural(&result.t, &y_series).unwrap();

// Interpolation -> Quadrature: the spline IS a callable f64 -> f64.
let q = quad(|t| spline.interpolate(t), 0.0, 3.0, &QuadOptions::default()).unwrap();

// integral_0^3 e^(-t) dt = 1 - e^(-3) ≈ 0.9502
assert!((q.value - (1.0 - (-3.0_f64).exp())).abs() < 1e-3);

No type conversions. No Into impls in your code. No “wrap this with a closure that does the dance.” This is the workflow that’s enforced by an integration test so it stays that way.

Five other workflows are checked the same way: ODE → FFT → signal processing → peak detection, parameter estimation → sensitivity → uncertainty, autodiff → optimization → curve fitting, PDE → statistics, and stats sampling → Monte Carlo ODE.

What’s in 0.1.0

SubsystemWhat it gives you
numra::odeDoPri5, Tsit5, Verner 6/7/8/9, Radau5, ESDIRK, BDF, Auto (stiffness-aware), forward sensitivity, event detection, DAE
numra::sdeEuler-Maruyama, Milstein, stochastic Runge-Kutta, multiple noise correlations
numra::ddeMethod-of-steps with continuous extension; handles state-dependent delays
numra::fdeCaputo and Riemann-Liouville fractional derivatives, predictor-corrector schemes
numra::ideVolterra and Fredholm integro-differential equations
numra::pdeMethod-of-lines on structured 2D/3D grids, sparse operator assembly
numra::spdeStochastic PDEs combining pde discretization with sde time integration
numra::linalgDense linear algebra via faer; decompositions, eigensolvers
numra::nonlinearNewton, Broyden, trust-region methods
numra::optimFirst- and second-order unconstrained, constrained, and stochastic optimizers
numra::ocpParameter estimation and optimal-control problems against ODE/DAE dynamics
numra::integrateAdaptive quadrature (Gauss-Kronrod), tanh-sinh, Romberg
numra::interpLinear, cubic, Akima, B-spline, RBF
numra::specialGamma, beta, error, Bessel, Mittag-Leffler families
numra::fftRadix-2/4 and mixed-radix FFTs, real and complex
numra::statsDistributions, descriptive statistics, hypothesis tests
numra::fitLinear and nonlinear least-squares curve fitting with analytical or AD Jacobians
numra::autodiffForward-mode dual numbers, gradients and Jacobians as closures
numra::dspIIR/FIR filter design, peak detection, windowing
numra::coreThe shared abstractions everything else builds on

Twenty member crates plus the numra facade. The full inventory and per-method coverage matrix is in the public API inventory.

The design constraints I refused to compromise

A few decisions defined what 0.1.0 had to be before I’d ship it:

Native Rust everywhere. No FFI to LAPACK, SUNDIALS, FFTW, or similar. Every algorithm in this release is implemented in Rust source you can read in this repo. Dense linear algebra builds on faer, itself a native-Rust project.

One shared Scalar trait. The same generic numeric trait is used by every solver, every quadrature rule, every optimizer. That’s what lets you flip between f64, f32, and (eventually) extended-precision or AD types without rewriting your problem.

Solvers as first-class values. DoPri5, Tsit5, Radau5, and the rest are types you can name, store, and choose between at runtime via the Auto selector or your own logic. No string-keyed dispatch, no factory functions, no enum-of-everything.

Composability tested, not promised. Six end-to-end workflows are checked by CI as integration tests. If a future change breaks composability between subsystems, the tests fail.

Installation and citation

cargo add numra

Documentation: the user book for narrative, docs.rs for the API reference, the examples gallery for end-to-end programs.

If Numra contributes to published work, please cite it. The repository has a CITATION.cff, and Zenodo has minted two DOIs:

BibTeX, RIS, and a longer note on why citation matters live on the Cite page.

Licensing

Numra ships under the Numra Academic & Research License (Non-Commercial): free for academic and research use, source-available, and a separate commercial license is required for production or for-profit use. The full text is in LICENSE, and the Commercial licensing page explains the why.

What’s next

The 0.1.0 release establishes the surface area. The next few releases will deepen it:

  • Sparse linear algebra (matrix-free and assembled), unlocking larger PDE problems
  • More PDE discretizations beyond method-of-lines (finite volume, spectral)
  • Adjoint sensitivity to complement the forward sensitivity already in numra::ode
  • A constraint solver and DAE index-2/3 reduction in numra::ode
  • GPU acceleration as an opt-in feature, once the CPU story is fully stable

The roadmap page tracks the current direction, and the issue tracker is the right place to push back on it.

If you build something with Numra, tell me. 0.1.0 is the start of the conversation about what composable numerical methods in Rust should look like.