math_rs/matrix/float/
arith.rs

1use std::str::FromStr;
2
3use crate::{
4    matrix::traits::{
5        ArithmeticallyOperable, CheckedAdd, CheckedMul, CheckedSub, Identity, Matrix, Parseable,
6        Zero,
7    },
8    result::{MathError, Result},
9};
10
11use super::f32::MatrixF32;
12
13impl ArithmeticallyOperable<Result<MatrixF32>> for MatrixF32 {}
14
15impl FromStr for MatrixF32 {
16    type Err = MathError;
17
18    /// Performs the conversion from a string to the matrix, with default tolerance 1e-12
19    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
20        Self::parse(s, 1e-12)
21    }
22}
23
24impl PartialEq for MatrixF32 {
25    fn eq(&self, other: &Self) -> bool {
26        for i in 0..self.rows() {
27            for j in 0..self.columns() {
28                if (self.get(i, j).unwrap() - other.get(i, j).unwrap()).abs() > self.tolerance() {
29                    return false;
30                }
31            }
32        }
33        true
34    }
35}
36
37impl Zero for MatrixF32 {
38    fn zero(rows: usize, columns: usize, tolerance: f32) -> Self {
39        MatrixF32::new(vec![vec![f32::zero(0, 0, 0.0); columns]; rows], tolerance).unwrap()
40    }
41
42    fn is_zero(&self) -> bool {
43        self.content
44            .iter()
45            .all(|row| row.iter().all(|x| x.is_zero()))
46    }
47}
48
49impl Identity for MatrixF32 {
50    fn id(dimensions: usize, tolerance: f32) -> Self {
51        let mut result = vec![vec![f32::zero(0, 0, 0.0); dimensions]; dimensions];
52        for i in 0..dimensions {
53            result[i][i] = f32::id(0, 0.0);
54        }
55        MatrixF32::new(result, tolerance).unwrap()
56    }
57}
58
59impl CheckedAdd for MatrixF32 {
60    type Output = Result<MatrixF32>;
61    fn checked_add(&self, rhs: &Self) -> Self::Output {
62        if self.rows() != rhs.rows() || self.columns() != rhs.columns() {
63            return Err(MathError::MatrixError(
64                "Matrices must be of the same size".to_string(),
65            ));
66        } else {
67            let mut result: Vec<Vec<f32>> = vec![];
68            for i in 0..self.rows() {
69                let mut result_row: Vec<f32> = vec![];
70                for j in 0..self.columns() {
71                    let left = self.get(i, j)?.to_owned();
72                    let right = rhs.get(i, j)?.to_owned();
73                    result_row.push(left.checked_add(&right)?)
74                }
75                result.push(result_row)
76            }
77            MatrixF32::new(result, self.tolerance())
78        }
79    }
80}
81
82impl CheckedSub for MatrixF32 {
83    type Output = Result<MatrixF32>;
84
85    fn checked_sub(&self, rhs: &Self) -> Self::Output {
86        if self.rows() != rhs.rows() || self.columns() != rhs.columns() {
87            return Err(MathError::MatrixError(
88                "Matrices must be of the same size".to_string(),
89            ));
90        } else {
91            let mut result: Vec<Vec<f32>> = vec![];
92            for i in 0..self.rows() {
93                let mut result_row: Vec<f32> = vec![];
94                for j in 0..self.columns() {
95                    let left = self.get(i, j)?.to_owned();
96                    let right = rhs.get(i, j)?.to_owned();
97                    result_row.push(left - right)
98                }
99                result.push(result_row)
100            }
101            MatrixF32::new(result, self.tolerance())
102        }
103    }
104}
105
106impl CheckedMul for MatrixF32 {
107    type Output = Result<MatrixF32>;
108
109    fn checked_mul(&self, rhs: &Self) -> Self::Output {
110        if self.columns() != rhs.rows() {
111            return Err(MathError::MatrixError(format!(
112                "Cannot multiply matrices of dimensions {}x{} and {}x{}",
113                self.rows(),
114                self.columns(),
115                rhs.rows(),
116                rhs.columns()
117            )));
118        }
119        let rows = self.rows();
120        let columns = rhs.columns();
121        let mut result = Vec::with_capacity(rows);
122        for i in 0..rows {
123            let mut row = Vec::with_capacity(columns);
124            for j in 0..columns {
125                let mut sum = f32::from_str("0")
126                    .map_err(|_| MathError::MatrixError("Cannot build T from 0".to_string()))?;
127                for k in 0..self.columns() {
128                    sum = sum + self.get(i, k)?.clone() * rhs.get(k, j)?.clone()
129                }
130                row.push(sum);
131            }
132            result.push(row)
133        }
134        MatrixF32::new(result, self.tolerance())
135    }
136}
137
138#[cfg(test)]
139mod test {
140    use crate::matrix::float::f32::MatrixF32;
141    use crate::matrix::traits::{CheckedAdd, CheckedSub, Parseable, CheckedMul};
142    use crate::matrix_f32;
143
144    const TOL: f32 = 1e-12;
145
146    #[test]
147    fn add_2x2_f32() {
148        let mat_a = matrix_f32!("{{1,1},{1,1}}", TOL).unwrap();
149        let mat_b = matrix_f32!("{{2,2},{2,2}}", TOL).unwrap();
150        let computed = mat_a.checked_add(&mat_b).unwrap();
151        let expected = matrix_f32!("{{3,3},{3,3}}", TOL).unwrap();
152
153        pretty_assertions::assert_eq!(computed, expected)
154    }
155
156    #[test]
157    fn add_3x5_f32() {
158        let mat_a = matrix_f32!("{{1,1,1,1,1}, {2,2,2,2,2}, {3,3,3,3,3}}", TOL).unwrap();
159        let mat_b = matrix_f32!("{ {3,3,3,3,3},{2,2,2,2,2},  {1,1,1,1,1}}", TOL).unwrap();
160        let computed = mat_a.checked_add(&mat_b).unwrap();
161        let expected = matrix_f32!("{{4,4,4,4,4},{4,4,4,4,4},{4,4,4,4,4}}", TOL).unwrap();
162
163        pretty_assertions::assert_eq!(computed, expected)
164    }
165
166    #[test]
167    fn add_different_rows_should_fail() {
168        let mat_a = matrix_f32!("{{1,1},{1,1}}", TOL).unwrap();
169        let mat_b = matrix_f32!("{{2,2},{2,2}, {2,2}}", TOL).unwrap();
170        let computed = mat_a.checked_add(&mat_b);
171        assert!(computed.is_err())
172    }
173
174    #[test]
175    fn add_different_cols_should_fail() {
176        let mat_a = matrix_f32!("{{1,1,1},{1,1,1}}", TOL).unwrap();
177        let mat_b = matrix_f32!("{{2,2},{2,2}}", TOL).unwrap();
178        let computed = mat_a.checked_add(&mat_b);
179        assert!(computed.is_err())
180    }
181
182    #[test]
183    fn sub_2x2_f32() {
184        let mat_a = matrix_f32!("{{1,1},{1,1}}", TOL).unwrap();
185        let mat_b = matrix_f32!("{{2,2},{2,2}}", TOL).unwrap();
186        let computed = mat_a.checked_sub(&mat_b).unwrap();
187        let expected = matrix_f32!("{{-1,-1},{-1,-1}}", TOL).unwrap();
188
189        pretty_assertions::assert_eq!(computed, expected)
190    }
191
192    #[test]
193    fn sub_3x5_f32() {
194        let mat_a = matrix_f32!("{{1,1,1,1,1}, {2,2,2,2,2}, {3,3,3,3,3}}", TOL).unwrap();
195        let mat_b = matrix_f32!("{ {3,3,3,3,3},{2,2,2,2,2},  {1,1,1,1,1}}", TOL).unwrap();
196        let computed = mat_a.checked_sub(&mat_b).unwrap();
197        let expected = matrix_f32!("{{-2,-2,-2,-2,-2},{0,0,0,0,0},{2,2,2,2,2}}", TOL).unwrap();
198
199        pretty_assertions::assert_eq!(computed, expected)
200    }
201
202    #[test]
203    fn sub_different_rows_should_fail() {
204        let mat_a = matrix_f32!("{{1,1},{1,1}}", TOL).unwrap();
205        let mat_b = matrix_f32!("{{2,2},{2,2}, {2,2}}", TOL).unwrap();
206        let computed = mat_a.checked_sub(&mat_b);
207        assert!(computed.is_err())
208    }
209
210    #[test]
211    fn sub_different_cols_should_fail() {
212        let mat_a = matrix_f32!("{{1,1,1},{1,1,1}}", TOL).unwrap();
213        let mat_b = matrix_f32!("{{2,2},{2,2}}", TOL).unwrap();
214        let computed = mat_a.checked_sub(&mat_b);
215        assert!(computed.is_err())
216    }
217
218    #[test]
219    fn mul_2x2_f32() {
220        let mat_a = matrix_f32!("{{1,1},{1,1}}", TOL).unwrap();
221        let mat_b = matrix_f32!("{{2,2},{2,2}}", TOL).unwrap();
222        let computed = mat_a.checked_mul(&mat_b).unwrap();
223        let expected = matrix_f32!("{{4,4},{4,4}}", TOL).unwrap();
224
225        pretty_assertions::assert_eq!(computed, expected)
226    }
227
228    #[test]
229    fn mul_3x5x2_f32() {
230        let mat_a = matrix_f32!("{{1,2,1,2,1}, {-1,2,-3,2,1}, {0,1,-3,2,1}}", TOL).unwrap();
231        let mat_b = matrix_f32!("{{1,1}, {2,2}, {-1,-1}, {-2,-2}, {0,1}}", TOL).unwrap();
232        let computed = mat_a.checked_mul(&mat_b).unwrap();
233        let expected = matrix_f32!("{{0,1},{2,3},{1,2}}", TOL).unwrap();
234
235        pretty_assertions::assert_eq!(computed, expected)
236    }
237}