#![forbid(unsafe_code)]
#![deny(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
#[cfg(doc)]
mod readme_doctests {
fn solve_5x5_example() {}
}
#[cfg(doc)]
pub mod guide {
pub mod ldlt {
}
pub mod compile_time {
}
pub mod intervals {
}
pub mod norms {
}
pub mod certified {
}
pub mod adaptive {
}
#[cfg(feature = "exact")]
pub mod exact {
}
}
mod error;
#[cfg(feature = "exact")]
mod exact;
mod gram;
mod interval;
mod ldlt;
mod lu;
mod matrix;
mod norm;
#[cfg(feature = "exact")]
mod rational;
mod rounding;
mod scaled_product;
mod tolerance;
mod vector;
#[cfg(feature = "exact")]
#[cfg_attr(docsrs, doc(cfg(feature = "exact")))]
pub use exact::{DeterminantSign, ExactF64Conversion};
#[cfg(feature = "exact")]
#[cfg_attr(docsrs, doc(cfg(feature = "exact")))]
pub use num_bigint::BigInt;
#[cfg(feature = "exact")]
#[cfg_attr(docsrs, doc(cfg(feature = "exact")))]
pub use num_rational::BigRational;
#[cfg(feature = "exact")]
#[cfg_attr(docsrs, doc(cfg(feature = "exact")))]
pub use num_traits::{FromPrimitive, Signed, ToPrimitive};
#[cfg(feature = "exact")]
#[cfg_attr(docsrs, doc(cfg(feature = "exact")))]
pub use rational::{RationalMatrix, RationalVector};
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;
#[cfg(feature = "exact")]
#[cfg_attr(docsrs, doc(cfg(feature = "exact")))]
pub const MAX_RATIONAL_MATRIX_DISPATCH_DIM: usize = 8;
pub use error::{
ArithmeticOperation, FactorizationKind, IntervalBound, IntervalOperand, InvalidToleranceReason,
LaError, NonFiniteLocation, NonFiniteOrigin, PositiveSemidefiniteViolation, SingularityReason,
UnrepresentableReason,
};
pub use gram::gram_matrix;
pub use interval::{Interval, IntervalDeterminantSign, IntervalMatrix, MAX_INTERVAL_MATRIX_DIM};
pub use ldlt::Ldlt;
pub use lu::Lu;
pub use matrix::{DeterminantWithErrorBound, Matrix};
pub use tolerance::{DEFAULT_SINGULAR_TOL, Tolerance};
pub use vector::{ScalarWithErrorBound, 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 mut __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 mut __la_stack_body = |mut $matrix: $crate::Matrix<$d>| -> $ret { $body };
__la_stack_body($crate::Matrix::<$d>::zero())
}};
}
#[macro_export]
macro_rules! try_with_interval_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_interval_matrix!(@arm 0, $matrix, $ret, $body),
1 => $crate::try_with_interval_matrix!(@arm 1, $matrix, $ret, $body),
2 => $crate::try_with_interval_matrix!(@arm 2, $matrix, $ret, $body),
3 => $crate::try_with_interval_matrix!(@arm 3, $matrix, $ret, $body),
4 => $crate::try_with_interval_matrix!(@arm 4, $matrix, $ret, $body),
5 => $crate::try_with_interval_matrix!(@arm 5, $matrix, $ret, $body),
6 => $crate::try_with_interval_matrix!(@arm 6, $matrix, $ret, $body),
7 => $crate::try_with_interval_matrix!(@arm 7, $matrix, $ret, $body),
requested => Err(::core::convert::From::from(
$crate::LaError::unsupported_dimension(
requested,
$crate::MAX_INTERVAL_MATRIX_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_interval_matrix!(@arm_mut 0, $matrix, $ret, $body),
1 => $crate::try_with_interval_matrix!(@arm_mut 1, $matrix, $ret, $body),
2 => $crate::try_with_interval_matrix!(@arm_mut 2, $matrix, $ret, $body),
3 => $crate::try_with_interval_matrix!(@arm_mut 3, $matrix, $ret, $body),
4 => $crate::try_with_interval_matrix!(@arm_mut 4, $matrix, $ret, $body),
5 => $crate::try_with_interval_matrix!(@arm_mut 5, $matrix, $ret, $body),
6 => $crate::try_with_interval_matrix!(@arm_mut 6, $matrix, $ret, $body),
7 => $crate::try_with_interval_matrix!(@arm_mut 7, $matrix, $ret, $body),
requested => Err(::core::convert::From::from(
$crate::LaError::unsupported_dimension(
requested,
$crate::MAX_INTERVAL_MATRIX_DIM,
),
)),
}
}};
(@arm $d:literal, $matrix:ident, $ret:ty, $body:block) => {{
let mut __la_stack_body = |$matrix: $crate::IntervalMatrix<$d>| -> $ret { $body };
__la_stack_body($crate::IntervalMatrix::<$d>::zero())
}};
(@arm_mut $d:literal, $matrix:ident, $ret:ty, $body:block) => {{
let mut __la_stack_body = |mut $matrix: $crate::IntervalMatrix<$d>| -> $ret { $body };
__la_stack_body($crate::IntervalMatrix::<$d>::zero())
}};
}
#[cfg(feature = "exact")]
#[cfg_attr(docsrs, doc(cfg(feature = "exact")))]
#[macro_export]
macro_rules! try_with_rational_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_rational_matrix!(@arm 0, $matrix, $ret, $body),
1 => $crate::try_with_rational_matrix!(@arm 1, $matrix, $ret, $body),
2 => $crate::try_with_rational_matrix!(@arm 2, $matrix, $ret, $body),
3 => $crate::try_with_rational_matrix!(@arm 3, $matrix, $ret, $body),
4 => $crate::try_with_rational_matrix!(@arm 4, $matrix, $ret, $body),
5 => $crate::try_with_rational_matrix!(@arm 5, $matrix, $ret, $body),
6 => $crate::try_with_rational_matrix!(@arm 6, $matrix, $ret, $body),
7 => $crate::try_with_rational_matrix!(@arm 7, $matrix, $ret, $body),
8 => $crate::try_with_rational_matrix!(@arm 8, $matrix, $ret, $body),
requested => Err(::core::convert::From::from(
$crate::LaError::unsupported_dimension(
requested,
$crate::MAX_RATIONAL_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_rational_matrix!(@arm_mut 0, $matrix, $ret, $body),
1 => $crate::try_with_rational_matrix!(@arm_mut 1, $matrix, $ret, $body),
2 => $crate::try_with_rational_matrix!(@arm_mut 2, $matrix, $ret, $body),
3 => $crate::try_with_rational_matrix!(@arm_mut 3, $matrix, $ret, $body),
4 => $crate::try_with_rational_matrix!(@arm_mut 4, $matrix, $ret, $body),
5 => $crate::try_with_rational_matrix!(@arm_mut 5, $matrix, $ret, $body),
6 => $crate::try_with_rational_matrix!(@arm_mut 6, $matrix, $ret, $body),
7 => $crate::try_with_rational_matrix!(@arm_mut 7, $matrix, $ret, $body),
8 => $crate::try_with_rational_matrix!(@arm_mut 8, $matrix, $ret, $body),
requested => Err(::core::convert::From::from(
$crate::LaError::unsupported_dimension(
requested,
$crate::MAX_RATIONAL_MATRIX_DISPATCH_DIM,
),
)),
}
}};
(@arm $d:literal, $matrix:ident, $ret:ty, $body:block) => {{
let mut __la_stack_body = |$matrix: $crate::RationalMatrix<$d>| -> $ret { $body };
__la_stack_body($crate::RationalMatrix::<$d>::zero())
}};
(@arm_mut $d:literal, $matrix:ident, $ret:ty, $body:block) => {{
let mut __la_stack_body = |mut $matrix: $crate::RationalMatrix<$d>| -> $ret { $body };
__la_stack_body($crate::RationalMatrix::<$d>::zero())
}};
}
#[cfg_attr(feature = "exact", doc = "")]
#[cfg_attr(
feature = "exact",
doc = "When the `exact` feature is enabled, [`RationalMatrix`], [`RationalVector`],"
)]
#[cfg_attr(
feature = "exact",
doc = "[`DeterminantSign`], [`ExactF64Conversion`], [`BigInt`], and [`BigRational`]"
)]
#[cfg_attr(
feature = "exact",
doc = "are also re-exported, together with [`MAX_RATIONAL_MATRIX_DISPATCH_DIM`] and"
)]
#[cfg_attr(
feature = "exact",
doc = "[`try_with_rational_matrix!`] for runtime-to-const exact-matrix dispatch."
)]
#[cfg_attr(
feature = "exact",
doc = "[`ExactF64Conversion`] converts an already-computed exact determinant or solution"
)]
#[cfg_attr(
feature = "exact",
doc = "under either the strict or explicitly rounded binary64 contract, without repeating"
)]
#[cfg_attr(
feature = "exact",
doc = "exact elimination. The number types let callers construct expected exact values"
)]
#[cfg_attr(
feature = "exact",
doc = "without adding `num-bigint` / `num-rational` to their own dependencies. The most"
)]
#[cfg_attr(
feature = "exact",
doc = "commonly needed `num-traits` items are re-exported alongside them: [`FromPrimitive`]"
)]
#[cfg_attr(
feature = "exact",
doc = "for `BigRational::from_f64` / `from_i64`, [`ToPrimitive`] for"
)]
#[cfg_attr(
feature = "exact",
doc = "`BigRational::to_f64` / `to_i64`, and [`Signed`] for `is_positive` / `is_negative` /"
)]
#[cfg_attr(feature = "exact", doc = "`abs`.")]
pub mod prelude {
pub use crate::{
ArithmeticOperation, DEFAULT_SINGULAR_TOL, DeterminantWithErrorBound, FactorizationKind,
Interval, IntervalBound, IntervalDeterminantSign, IntervalMatrix, IntervalOperand,
InvalidToleranceReason, LaError, Ldlt, Lu, MAX_INTERVAL_MATRIX_DIM,
MAX_STACK_MATRIX_DISPATCH_DIM, Matrix, NonFiniteLocation, NonFiniteOrigin,
PositiveSemidefiniteViolation, ScalarWithErrorBound, SingularityReason, Tolerance,
UnrepresentableReason, Vector, gram_matrix, try_with_interval_matrix,
try_with_stack_matrix,
};
#[cfg(feature = "exact")]
#[cfg_attr(docsrs, doc(cfg(feature = "exact")))]
pub use crate::{
BigInt, BigRational, DeterminantSign, ExactF64Conversion, FromPrimitive,
MAX_RATIONAL_MATRIX_DISPATCH_DIM, RationalMatrix, RationalVector, Signed, ToPrimitive,
try_with_rational_matrix,
};
}
#[cfg(test)]
mod tests {
use approx::assert_abs_diff_eq;
use pastey::paste;
use super::*;
macro_rules! gen_stack_matrix_dispatch_tests {
($d:literal) => {
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($d - 1, $d - 1, f64::from($d))?;
assert_abs_diff_eq!(
m.try_get($d - 1, $d - 1)?,
f64::from($d),
epsilon = 0.0
);
}
Ok($d)
});
assert_eq!(got, Ok($d));
}
}
};
}
gen_stack_matrix_dispatch_tests!(1);
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);
macro_rules! gen_interval_matrix_dispatch_tests {
($d:literal) => {
paste! {
#[test]
fn [<try_with_interval_matrix_dispatches_ $d d>]() {
let requested = $d;
let got = try_with_interval_matrix!(
requested,
|mut matrix| -> Result<IntervalDeterminantSign, LaError> {
let mut index = 0;
while index < $d {
matrix.set(index, index, Interval::ONE)?;
index += 1;
}
matrix.det_sign()
},
);
assert_eq!(got, Ok(IntervalDeterminantSign::Positive));
}
}
};
}
gen_interval_matrix_dispatch_tests!(1);
gen_interval_matrix_dispatch_tests!(2);
gen_interval_matrix_dispatch_tests!(3);
gen_interval_matrix_dispatch_tests!(4);
gen_interval_matrix_dispatch_tests!(5);
gen_interval_matrix_dispatch_tests!(6);
gen_interval_matrix_dispatch_tests!(7);
#[cfg(feature = "exact")]
macro_rules! gen_rational_matrix_dispatch_tests {
($d:literal) => {
paste! {
#[test]
fn [<try_with_rational_matrix_dispatches_ $d d>]() {
let requested = $d;
let got = try_with_rational_matrix!(
requested,
|mut matrix| -> Result<DeterminantSign, LaError> {
let mut index = 0;
while index < $d {
matrix.set(
index,
index,
BigRational::from_integer(BigInt::from(1)),
)?;
index += 1;
}
Ok(matrix.det_sign())
},
);
assert_eq!(got, Ok(DeterminantSign::Positive));
}
}
};
}
#[cfg(feature = "exact")]
gen_rational_matrix_dispatch_tests!(1);
#[cfg(feature = "exact")]
gen_rational_matrix_dispatch_tests!(2);
#[cfg(feature = "exact")]
gen_rational_matrix_dispatch_tests!(3);
#[cfg(feature = "exact")]
gen_rational_matrix_dispatch_tests!(4);
#[cfg(feature = "exact")]
gen_rational_matrix_dispatch_tests!(5);
#[cfg(feature = "exact")]
gen_rational_matrix_dispatch_tests!(6);
#[cfg(feature = "exact")]
gen_rational_matrix_dispatch_tests!(7);
#[cfg(feature = "exact")]
gen_rational_matrix_dispatch_tests!(8);
#[cfg(feature = "exact")]
#[test]
fn try_with_rational_matrix_dispatches_zero_dimension() {
let got = try_with_rational_matrix!(0usize, |matrix| -> Result<DeterminantSign, LaError> {
Ok(matrix.det_sign())
});
assert_eq!(got, Ok(DeterminantSign::Positive));
}
#[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_interval_matrix_supports_zero_dimension() {
let got = try_with_interval_matrix!(0usize, |matrix| -> Result<
IntervalDeterminantSign,
LaError,
> { matrix.det_sign() },);
assert_eq!(got, Ok(IntervalDeterminantSign::Positive));
}
#[test]
fn try_with_stack_matrix_evaluates_dimension_once() {
let mut evaluations = 0;
let got = try_with_stack_matrix!(
{
evaluations += 1;
2usize
},
|matrix| -> Result<f64, LaError> { matrix.try_get(1, 1) },
);
assert_eq!(evaluations, 1);
assert_eq!(got, Ok(0.0));
}
#[test]
fn try_with_interval_matrix_evaluates_dimension_once() {
let mut evaluations = 0;
let got = try_with_interval_matrix!(
{
evaluations += 1;
2usize
},
|matrix| -> Result<Interval, LaError> { matrix.try_get(1, 1) },
);
assert_eq!(evaluations, 1);
assert_eq!(got, Ok(Interval::ZERO));
}
#[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,
})
);
}
#[test]
fn try_with_interval_matrix_reports_unsupported_dimension() {
let got = try_with_interval_matrix!(8usize, |matrix| -> Result<
IntervalDeterminantSign,
LaError,
> { matrix.det_sign() },);
assert_eq!(
got,
Err(LaError::UnsupportedDimension {
requested: 8,
max: MAX_INTERVAL_MATRIX_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.norm_inf()?, 0.0, epsilon = 0.0);
Ok(0)
});
assert_eq!(
got,
Err(DownstreamError(LaError::UnsupportedDimension {
requested: 9,
max: MAX_STACK_MATRIX_DISPATCH_DIM,
}))
);
}
#[test]
fn try_with_interval_matrix_converts_unsupported_dimension_error() {
let got = try_with_interval_matrix!(8usize, |matrix| -> Result<
IntervalDeterminantSign,
DownstreamError,
> { Ok(matrix.det_sign()?) },);
assert_eq!(
got,
Err(DownstreamError(LaError::UnsupportedDimension {
requested: 8,
max: MAX_INTERVAL_MATRIX_DIM,
}))
);
}
#[cfg(feature = "exact")]
#[test]
fn try_with_rational_matrix_reports_unsupported_dimension() {
let got = try_with_rational_matrix!(9usize, |matrix| -> Result<BigRational, LaError> {
Ok(matrix.det())
});
assert_eq!(
got,
Err(LaError::UnsupportedDimension {
requested: 9,
max: MAX_RATIONAL_MATRIX_DISPATCH_DIM,
})
);
}
#[cfg(feature = "exact")]
#[test]
fn try_with_rational_matrix_converts_unsupported_dimension_error() {
let got =
try_with_rational_matrix!(9usize, |matrix| -> Result<BigRational, DownstreamError> {
Ok(matrix.det())
});
assert_eq!(
got,
Err(DownstreamError(LaError::UnsupportedDimension {
requested: 9,
max: MAX_RATIONAL_MATRIX_DISPATCH_DIM,
}))
);
}
}