Skip to main content

use_linear/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4//! Linear-algebra utilities for `RustUse`.
5
6use core::fmt;
7
8use use_matrix::Matrix2;
9use use_vector::Vector2;
10
11/// Errors returned by linear helpers when a system cannot be solved.
12#[derive(Clone, Copy, Debug, PartialEq)]
13pub enum LinearError {
14    /// The matrix is singular or non-finite and does not have a usable inverse.
15    SingularMatrix { determinant: f64 },
16}
17
18impl fmt::Display for LinearError {
19    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
20        match self {
21            Self::SingularMatrix { determinant } => {
22                write!(
23                    formatter,
24                    "matrix is singular with determinant {determinant}"
25                )
26            },
27        }
28    }
29}
30
31impl std::error::Error for LinearError {}
32
33/// Solves `matrix * x = rhs` for `x`.
34///
35/// # Errors
36///
37/// Returns [`LinearError::SingularMatrix`] when the determinant is zero or
38/// non-finite.
39///
40/// # Examples
41///
42/// ```
43/// use use_linear::solve_2x2;
44/// use use_matrix::Matrix2;
45/// use use_vector::Vector2;
46///
47/// let matrix = Matrix2::new(2.0, 1.0, 5.0, 3.0);
48/// let rhs = Vector2::new(1.0, 2.0);
49///
50/// assert_eq!(solve_2x2(matrix, rhs)?, Vector2::new(1.0, -1.0));
51/// # Ok::<(), use_linear::LinearError>(())
52/// ```
53pub fn solve_2x2(matrix: Matrix2, rhs: Vector2) -> Result<Vector2, LinearError> {
54    let determinant = matrix.determinant();
55
56    matrix
57        .inverse()
58        .map(|inverse| inverse * rhs)
59        .ok_or(LinearError::SingularMatrix { determinant })
60}
61
62pub mod prelude;
63
64#[cfg(test)]
65mod tests {
66    use super::{LinearError, solve_2x2};
67    use use_matrix::Matrix2;
68    use use_vector::Vector2;
69
70    #[test]
71    fn solves_nonsingular_systems_and_rejects_unusable_ones() {
72        let matrix = Matrix2::new(2.0, 1.0, 5.0, 3.0);
73        let rhs = Vector2::new(1.0, 2.0);
74        let solution = solve_2x2(matrix, rhs).expect("system should solve");
75
76        assert_eq!(solution, Vector2::new(1.0, -1.0));
77        assert_eq!(matrix * solution, rhs);
78        assert_eq!(
79            solve_2x2(Matrix2::new(1.0, 2.0, 2.0, 4.0), Vector2::new(1.0, 2.0)),
80            Err(LinearError::SingularMatrix { determinant: 0.0 })
81        );
82        assert!(matches!(
83            solve_2x2(
84                Matrix2::new(f64::INFINITY, 0.0, 0.0, 1.0),
85                Vector2::new(1.0, 2.0)
86            ),
87            Err(LinearError::SingularMatrix { determinant }) if !determinant.is_finite()
88        ));
89    }
90}