argmin_math/nalgebra_m/
inv.rs

1// Copyright 2018-2024 argmin developers
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use crate::{Allocator, ArgminInv, Error};
9use nalgebra::{
10    base::{dimension::Dim, storage::Storage},
11    ComplexField, DefaultAllocator, OMatrix, SquareMatrix,
12};
13use std::fmt;
14
15#[derive(Debug, thiserror::Error, PartialEq)]
16struct InverseError;
17
18impl fmt::Display for InverseError {
19    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
20        write!(f, "Non-invertible matrix")
21    }
22}
23
24impl<N, D, S> ArgminInv<OMatrix<N, D, D>> for SquareMatrix<N, D, S>
25where
26    N: ComplexField,
27    D: Dim,
28    S: Storage<N, D, D>,
29    DefaultAllocator: Allocator<N, D, D>,
30{
31    #[inline]
32    fn inv(&self) -> Result<OMatrix<N, D, D>, Error> {
33        match self.clone_owned().try_inverse() {
34            Some(m) => Ok(m),
35            None => Err(InverseError {}.into()),
36        }
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43    use approx::assert_relative_eq;
44    use nalgebra::Matrix2;
45    use paste::item;
46
47    macro_rules! make_test {
48        ($t:ty) => {
49            item! {
50                #[test]
51                fn [<test_inv_ $t>]() {
52                    let a = Matrix2::new(
53                        2 as $t, 5 as $t,
54                        1 as $t, 3 as $t,
55                    );
56                    let target = Matrix2::new(
57                        3 as $t, -5 as $t,
58                        -1 as $t, 2 as $t,
59                    );
60                    let res = <Matrix2<$t> as ArgminInv<Matrix2<$t>>>::inv(&a).unwrap();
61                    for i in 0..2 {
62                        for j in 0..2 {
63                            assert_relative_eq!(res[(i, j)], target[(i, j)], epsilon = $t::EPSILON);
64                        }
65                    }
66                }
67            }
68
69            item! {
70                #[test]
71                fn [<test_inv_error $t>]() {
72                    let a = Matrix2::new(
73                        2 as $t, 5 as $t,
74                        4 as $t, 10 as $t,
75                    );
76                    let err = <Matrix2<$t> as ArgminInv<Matrix2<$t>>>::inv(&a).unwrap_err().downcast::<InverseError>().unwrap();
77                    assert_eq!(err, InverseError {});
78                    assert_eq!(format!("{}", err), "Non-invertible matrix");
79                    assert_eq!(format!("{:?}", err), "InverseError");
80                }
81            }
82        };
83    }
84
85    make_test!(f32);
86    make_test!(f64);
87}