#![forbid(unsafe_code)]
#![deny(missing_docs)]
#![doc = include_str!("../README.md")]
#[cfg(doc)]
mod readme_doctests {
fn solve_5x5_example() {}
fn det_5x5_ldlt_example() {}
fn det_direct_4x4_const_example() {}
#[cfg(feature = "exact")]
fn exact_arithmetic_example() {}
}
mod error;
#[cfg(feature = "exact")]
mod exact;
mod ldlt;
mod lu;
mod matrix;
mod tolerance;
mod vector;
#[cfg(feature = "exact")]
pub use num_bigint::BigInt;
#[cfg(feature = "exact")]
pub use num_rational::BigRational;
#[cfg(feature = "exact")]
pub use num_traits::{FromPrimitive, Signed, ToPrimitive};
const EPS: f64 = f64::EPSILON;
pub const ERR_COEFF_2: f64 = 3.0 * EPS + 16.0 * EPS * EPS;
pub const ERR_COEFF_3: f64 = 8.0 * EPS + 64.0 * EPS * EPS;
pub const ERR_COEFF_4: f64 = 12.0 * EPS + 128.0 * EPS * EPS;
pub const MAX_STACK_MATRIX_DISPATCH_DIM: usize = 7;
pub use error::{LaError, UnrepresentableReason};
pub use ldlt::Ldlt;
pub use lu::Lu;
pub use matrix::Matrix;
pub(crate) use tolerance::LDLT_SYMMETRY_REL_TOL;
pub use tolerance::{DEFAULT_SINGULAR_TOL, Tolerance};
pub use vector::Vector;
#[macro_export]
macro_rules! try_with_stack_matrix {
($dim:expr, |$matrix:ident| -> $ret:ty $body:block $(,)?) => {{
let __la_stack_requested_dim: usize = $dim;
match __la_stack_requested_dim {
0 => $crate::try_with_stack_matrix!(@arm 0, $matrix, $ret, $body),
1 => $crate::try_with_stack_matrix!(@arm 1, $matrix, $ret, $body),
2 => $crate::try_with_stack_matrix!(@arm 2, $matrix, $ret, $body),
3 => $crate::try_with_stack_matrix!(@arm 3, $matrix, $ret, $body),
4 => $crate::try_with_stack_matrix!(@arm 4, $matrix, $ret, $body),
5 => $crate::try_with_stack_matrix!(@arm 5, $matrix, $ret, $body),
6 => $crate::try_with_stack_matrix!(@arm 6, $matrix, $ret, $body),
7 => $crate::try_with_stack_matrix!(@arm 7, $matrix, $ret, $body),
requested => Err(::core::convert::From::from(
$crate::LaError::unsupported_dimension(
requested,
$crate::MAX_STACK_MATRIX_DISPATCH_DIM,
),
)),
}
}};
($dim:expr, |mut $matrix:ident| -> $ret:ty $body:block $(,)?) => {{
let __la_stack_requested_dim: usize = $dim;
match __la_stack_requested_dim {
0 => $crate::try_with_stack_matrix!(@arm_mut 0, $matrix, $ret, $body),
1 => $crate::try_with_stack_matrix!(@arm_mut 1, $matrix, $ret, $body),
2 => $crate::try_with_stack_matrix!(@arm_mut 2, $matrix, $ret, $body),
3 => $crate::try_with_stack_matrix!(@arm_mut 3, $matrix, $ret, $body),
4 => $crate::try_with_stack_matrix!(@arm_mut 4, $matrix, $ret, $body),
5 => $crate::try_with_stack_matrix!(@arm_mut 5, $matrix, $ret, $body),
6 => $crate::try_with_stack_matrix!(@arm_mut 6, $matrix, $ret, $body),
7 => $crate::try_with_stack_matrix!(@arm_mut 7, $matrix, $ret, $body),
requested => Err(::core::convert::From::from(
$crate::LaError::unsupported_dimension(
requested,
$crate::MAX_STACK_MATRIX_DISPATCH_DIM,
),
)),
}
}};
(@arm $d:literal, $matrix:ident, $ret:ty, $body:block) => {{
let __la_stack_body = |$matrix: $crate::Matrix<$d>| -> $ret { $body };
__la_stack_body($crate::Matrix::<$d>::zero())
}};
(@arm_mut $d:literal, $matrix:ident, $ret:ty, $body:block) => {{
let __la_stack_body = |mut $matrix: $crate::Matrix<$d>| -> $ret { $body };
__la_stack_body($crate::Matrix::<$d>::zero())
}};
}
pub mod prelude {
pub use crate::{
DEFAULT_SINGULAR_TOL, ERR_COEFF_2, ERR_COEFF_3, ERR_COEFF_4, LaError, Ldlt, Lu,
MAX_STACK_MATRIX_DISPATCH_DIM, Matrix, Tolerance, UnrepresentableReason, Vector,
try_with_stack_matrix,
};
#[cfg(feature = "exact")]
pub use crate::{BigInt, BigRational, FromPrimitive, Signed, ToPrimitive};
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_abs_diff_eq;
mod prelude_tests {
use approx::assert_abs_diff_eq;
use crate::prelude::*;
#[test]
fn prelude_reexports_compile_and_work() -> Result<(), LaError> {
let m = Matrix::<2>::identity();
let v = Vector::<2>::try_new([1.0, 2.0])?;
let tol = Tolerance::new(0.0)?;
assert_abs_diff_eq!(tol.get(), 0.0, epsilon = 0.0);
assert_abs_diff_eq!(m.inf_norm()?, 1.0, epsilon = 0.0);
assert_abs_diff_eq!(v.norm2_sq()?, 5.0, epsilon = 0.0);
let _ = m.lu(DEFAULT_SINGULAR_TOL)?.solve(v)?;
let _ = m.ldlt(DEFAULT_SINGULAR_TOL)?.solve(v)?;
assert_eq!(
LaError::unrepresentable(None, UnrepresentableReason::RequiresRounding),
LaError::Unrepresentable {
index: None,
reason: UnrepresentableReason::RequiresRounding,
}
);
assert_eq!(MAX_STACK_MATRIX_DISPATCH_DIM, 7);
Ok(())
}
}
macro_rules! gen_stack_matrix_dispatch_tests {
($d:literal) => {
pastey::paste! {
#[test]
fn [<try_with_stack_matrix_dispatches_ $d d>]() {
let requested = $d;
let got = try_with_stack_matrix!(requested, |mut m| -> Result<usize, LaError> {
if $d > 0 {
m.set_checked($d - 1, $d - 1, f64::from($d))?;
assert_abs_diff_eq!(
m.get_checked($d - 1, $d - 1)?,
f64::from($d),
epsilon = 0.0
);
}
Ok($d)
});
assert_eq!(got, Ok($d));
}
}
};
}
gen_stack_matrix_dispatch_tests!(2);
gen_stack_matrix_dispatch_tests!(3);
gen_stack_matrix_dispatch_tests!(4);
gen_stack_matrix_dispatch_tests!(5);
gen_stack_matrix_dispatch_tests!(6);
gen_stack_matrix_dispatch_tests!(7);
#[test]
fn try_with_stack_matrix_supports_zero_dimension() {
let got = try_with_stack_matrix!(0usize, |m| -> Result<Option<f64>, LaError> {
m.det_direct()
});
assert_eq!(got, Ok(Some(1.0)));
}
#[test]
fn try_with_stack_matrix_reports_unsupported_dimension() {
let got = try_with_stack_matrix!(8usize, |m| -> Result<f64, LaError> { m.det() });
assert_eq!(
got,
Err(LaError::UnsupportedDimension {
requested: 8,
max: MAX_STACK_MATRIX_DISPATCH_DIM,
})
);
}
#[derive(Debug, PartialEq)]
struct DownstreamError(LaError);
impl From<LaError> for DownstreamError {
fn from(err: LaError) -> Self {
Self(err)
}
}
#[test]
fn try_with_stack_matrix_converts_unsupported_dimension_error() {
let got = try_with_stack_matrix!(9usize, |m| -> Result<usize, DownstreamError> {
assert_abs_diff_eq!(m.inf_norm()?, 0.0, epsilon = 0.0);
Ok(0)
});
assert_eq!(
got,
Err(DownstreamError(LaError::UnsupportedDimension {
requested: 9,
max: MAX_STACK_MATRIX_DISPATCH_DIM,
}))
);
}
#[cfg(feature = "exact")]
#[test]
fn prelude_exact_reexports_compile_and_work() {
use crate::prelude::*;
let n = BigInt::from(7);
let r = BigRational::from_integer(n.clone());
assert_eq!(*r.numer(), n);
let half = BigRational::new(BigInt::from(1), BigInt::from(2));
let two = BigRational::from_integer(BigInt::from(2));
assert_eq!(BigRational::from_f64(0.5), Some(half.clone()));
assert_eq!(BigRational::from_i64(2), Some(two.clone()));
assert_eq!(
half.clone() + half.clone(),
BigRational::from_integer(BigInt::from(1))
);
assert!(half.is_positive());
assert!(!half.is_negative());
let neg = -half.clone();
assert!(neg.is_negative());
assert_eq!(neg.abs(), half);
assert_eq!(half.to_f64(), Some(0.5));
assert_eq!(two.to_i64(), Some(2));
}
}