russell
russell copied to clipboard
Rust Scientific Libary. ODE and DAE (Runge-Kutta) solvers. Special functions (Bessel, Elliptic, Beta, Gamma, Erf). Linear algebra. Sparse solvers (MUMPS, UMFPACK). Probability distributions. Tensor ca...
Russell - Rust Scientific Library
(CC0. Photo: Bertrand Russell)
Russell assists in the development of scientific computations using the Rust language. We focus on numerical methods and solvers for differential equations; however, anything is possible ๐.
An essential goal of this library is to bring the best (fastest) solutions while maintaining a very clean (and idiomatic) code, thoroughly tested (min coverage of 95%), and yet simple to use. The best solutions are brought by wrapping powerful libraries such as OpenBLAS, MUMPS, and SuiteSparse (UMFPACK).
Available crates:
-
chk Functions to check vectors and other data in tests
-
lab Matrix-vector laboratory including linear algebra tools
-
openblas Thin wrapper to some OpenBLAS routines
-
sparse Sparse matrix tools and solvers
-
stat Statistics calculations, probability distributions, and pseudo random numbers
-
tensor Tensor analysis structures and functions for continuum mechanics
External recommended crate:
- plotpy Plotting tools using Python3/Matplotlib as an engine
Installation
Install the following Debian packages:
sudo apt-get install \
liblapacke-dev \
libmumps-seq-dev \
libopenblas-dev \
libsuitesparse-dev
Add this to your Cargo.toml (select only the crates you want and replace the right version):
[dependencies]
russell_chk = "*"
russell_lab = "*"
russell_openblas = "*"
russell_sparse = "*"
russell_stat = "*"
russell_tensor = "*"
Number of threads
By default OpenBLAS will use all available threads, including Hyper-Threads that make the performance worse. Thus, it is best to set the following environment variable:
export OPENBLAS_NUM_THREADS=<real-core-count>
Furthermore, if working on a multi-threaded application, it is recommended to set:
export OPENBLAS_NUM_THREADS=1
Examples
Compute a singular value decomposition
use russell_lab::{sv_decomp, Matrix, Vector, StrError};
fn main() -> Result<(), StrError> {
// set matrix
let mut a = Matrix::from(&[
[2.0, 4.0],
[1.0, 3.0],
[0.0, 0.0],
[0.0, 0.0],
]);
// allocate output structures
let (m, n) = a.dims();
let min_mn = if m < n { m } else { n };
let mut s = Vector::new(min_mn);
let mut u = Matrix::new(m, m);
let mut vt = Matrix::new(n, n);
// perform SVD
sv_decomp(&mut s, &mut u, &mut vt, &mut a)?;
// define correct data
let s_correct = "โ โ\n\
โ 5.46 โ\n\
โ 0.37 โ\n\
โ โ";
let u_correct = "โ โ\n\
โ -0.82 -0.58 0.00 0.00 โ\n\
โ -0.58 0.82 0.00 0.00 โ\n\
โ 0.00 0.00 1.00 0.00 โ\n\
โ 0.00 0.00 0.00 1.00 โ\n\
โ โ";
let vt_correct = "โ โ\n\
โ -0.40 -0.91 โ\n\
โ -0.91 0.40 โ\n\
โ โ";
// check solution
assert_eq!(format!("{:.2}", s), s_correct);
assert_eq!(format!("{:.2}", u), u_correct);
assert_eq!(format!("{:.2}", vt), vt_correct);
// check SVD: a == u * s * vt
let mut usv = Matrix::new(m, n);
for i in 0..m {
for j in 0..n {
for k in 0..min_mn {
usv[i][j] += u[i][k] * s[k] * vt[k][j];
}
}
}
let usv_correct = "โ โ\n\
โ 2 4 โ\n\
โ 1 3 โ\n\
โ 0 0 โ\n\
โ 0 0 โ\n\
โ โ";
assert_eq!(format!("{}", usv), usv_correct);
Ok(())
}
Solve a linear system
use russell_lab::{solve_lin_sys, Matrix, Vector, StrError};
fn main() -> Result<(), StrError> {
// set matrix and right-hand side
let mut a = Matrix::from(&[
[1.0, 3.0, -2.0],
[3.0, 5.0, 6.0],
[2.0, 4.0, 3.0],
]);
let mut b = Vector::from(&[5.0, 7.0, 8.0]);
// solve linear system b := aโปยนโ
b
solve_lin_sys(&mut b, &mut a)?;
// check
let x_correct = "โ โ\n\
โ -15.000 โ\n\
โ 8.000 โ\n\
โ 2.000 โ\n\
โ โ";
assert_eq!(format!("{:.3}", b), x_correct);
Ok(())
}
Solve a sparse linear system
use russell_lab::{Matrix, Vector, StrError};
use russell_sparse::{ConfigSolver, Solver, SparseTriplet, Symmetry};
fn main() -> Result<(), StrError> {
// allocate a square matrix
let mut trip = SparseTriplet::new(5, 5, 13, Symmetry::No)?;
trip.put(0, 0, 1.0)?; // << (0, 0, a00/2)
trip.put(0, 0, 1.0)?; // << (0, 0, a00/2)
trip.put(1, 0, 3.0)?;
trip.put(0, 1, 3.0)?;
trip.put(2, 1, -1.0)?;
trip.put(4, 1, 4.0)?;
trip.put(1, 2, 4.0)?;
trip.put(2, 2, -3.0)?;
trip.put(3, 2, 1.0)?;
trip.put(4, 2, 2.0)?;
trip.put(2, 3, 2.0)?;
trip.put(1, 4, 6.0)?;
trip.put(4, 4, 1.0)?;
// print matrix
let (m, n) = trip.dims();
let mut a = Matrix::new(m, n);
trip.to_matrix(&mut a)?;
let correct = "โ โ\n\
โ 2 3 0 0 0 โ\n\
โ 3 0 4 0 6 โ\n\
โ 0 -1 -3 2 0 โ\n\
โ 0 0 1 0 0 โ\n\
โ 0 4 2 0 1 โ\n\
โ โ";
assert_eq!(format!("{}", a), correct);
// allocate x and rhs
let mut x = Vector::new(5);
let rhs = Vector::from(&[8.0, 45.0, -3.0, 3.0, 19.0]);
// initialize, factorize, and solve
let config = ConfigSolver::new();
let mut solver = Solver::new(config)?;
solver.initialize(&trip)?;
solver.factorize()?;
solver.solve(&mut x, &rhs)?;
let correct = "โ โ\n\
โ 1.000000 โ\n\
โ 2.000000 โ\n\
โ 3.000000 โ\n\
โ 4.000000 โ\n\
โ 5.000000 โ\n\
โ โ";
assert_eq!(format!("{:.6}", x), correct);
Ok(())
}
Todo list
- [x] Add complex numbers functions to
russell_openblas
- [ ] Add more complex numbers functions to
russell_lab
- [ ] Add fundamental functions to
russell_lab
- [ ] Implement the modified Bessel functions
- [ ] Implement some numerical methods in
russell_lab
- [ ] Implement Brent's solver
- [ ] Implement solver for the cubic equation
- [ ] Implement numerical derivation
- [ ] Implement numerical Jacobian function
- [ ] Implement Newton's method for nonlinear systems
- [ ] Implement numerical quadrature
- [ ] Add interpolation and polynomials to
russell_lab
- [ ] Implement Chebyshev interpolation and polynomials
- [ ] Implement Orthogonal polynomials
- [ ] Implement Lagrange interpolation
- [x] Add probability distribution functions to
russell_stat
- [x] Finalize drawing of ASCII histogram in
russell_stat
- [ ] Implement standard continuum mechanics tensors in
russell_tensor
- [ ] Implement more integration tests for linear algebra
- [ ] Implement more examples