generic_matrix_impl/
lib.rs

1#![deny(
2    missing_docs,
3    missing_debug_implementations,
4    missing_copy_implementations,
5    trivial_casts,
6    trivial_numeric_casts,
7    unsafe_code,
8    unstable_features,
9    unused_import_braces,
10    unused_qualifications
11)]
12
13//! Contains matrix definitions and the generic implementation
14pub mod matrix_operations {
15    //! Generic implementation for Mul, Add, and Sub from core::ops
16    use core::ops::Add;
17    use core::ops::Mul;
18    use core::ops::Sub;
19
20    /// Generic struct definition
21    #[derive(Debug, Clone, PartialEq)]
22    pub struct Matrix<T> {
23        rows: u32,
24        columns: u32,
25        dimension: (u32, u32),
26        data: Vec<Vec<T>>,
27    }
28
29    impl<T> Matrix<T>
30    where
31        T: Mul<Output = T> + Add<Output = T> + Sub<Output = T> + Default + Clone,
32    {
33        /// Create an NxM matrix given a 2d vector with elements of type T
34        pub fn from_data(data: Vec<Vec<T>>) -> Self {
35            let rows: u32 = data.len() as u32;
36            let columns: u32 = data.get(0).unwrap().len() as u32;
37
38            Matrix {
39                rows: rows,
40                columns: columns,
41                dimension: (rows.clone(), columns.clone()),
42                data: data,
43            }
44        }
45
46        /// Create an NxM matrix where every entry is populated with the value passed in the constant param
47        pub fn from_constant(dimension: (u32, u32), constant: T) -> Self {
48            Matrix {
49                rows: dimension.0,
50                columns: dimension.1,
51                dimension: dimension,
52                data: Matrix::data_from_constant(dimension.0, dimension.1, constant),
53            }
54        }
55
56        /// Create an NxM diagonal matrix with diagonal taking values from the supplied constant
57        pub fn diagonal_from_constant(dimension: (u32, u32), constant: T) -> Self {
58            let mut m: Matrix<T> = Matrix::default_from_dimension(dimension);
59            let min_dim: u32 = std::cmp::min(dimension.0, dimension.1);
60
61            for i in 0..min_dim {
62                m.data[i as usize][i as usize] = constant.clone();
63            }
64
65            return m;
66        }
67
68        /// Initialize a new NxM unit matrix, assuming T::default() is the unit for T.
69        pub fn default_diagonal(dimension: (u32, u32)) -> Self {
70            Self::diagonal_from_constant(dimension, T::default())
71        }
72
73        /// Create an NxM matrix by dimension where every entry is populated with default values for T
74        pub fn default_from_dimension(dimension: (u32, u32)) -> Self {
75            Matrix {
76                rows: dimension.0,
77                columns: dimension.1,
78                dimension: dimension,
79                data: Matrix::data_from_zeroes(dimension.0, dimension.1),
80            }
81        }
82        /// Create an NxM matrix by row and column numbers where every entry is populated with default values for T
83        pub fn default_from_rows_and_columns(rows: u32, columns: u32) -> Self {
84            Matrix {
85                rows: rows,
86                columns: columns,
87                dimension: (rows, columns),
88                data: Matrix::data_from_zeroes(rows, columns),
89            }
90        }
91
92        fn data_from_zeroes(r: u32, c: u32) -> Vec<Vec<T>> {
93            return vec![vec![T::default(); c as usize]; r as usize];
94        }
95
96        fn data_from_constant(r: u32, c: u32, v: T) -> Vec<Vec<T>> {
97            return vec![vec![v; c as usize]; r as usize];
98        }
99
100        /// Transpose matrix (i.e retrieve M^T for a matrix M)
101        pub fn transpose(self) -> Matrix<T> {
102            let mut transposed_matrix: Matrix<T> =
103                Matrix::default_from_dimension((self.columns, self.rows));
104
105            for i in 0..self.rows {
106                let iu: usize = i as usize;
107                for j in 0..self.columns {
108                    let ju: usize = j as usize;
109                    transposed_matrix.data[ju][iu] = self.data[iu][ju].clone();
110                }
111            }
112
113            return transposed_matrix;
114        }
115    }
116
117    impl<T> Add for Matrix<T>
118    where
119        T: Mul<Output = T> + Add<Output = T> + Sub<Output = T> + Default + Clone,
120    {
121        type Output = Matrix<T>;
122
123        fn add(self, rhs: Self) -> Self::Output {
124            if self.dimension != rhs.dimension {
125                panic!("Matrices must have the same dimension to perform addition");
126            }
127
128            let mut resultant: Matrix<T> = Matrix::default_from_dimension(self.dimension);
129            for i in 0..self.rows {
130                let iu: usize = i as usize;
131                for j in 0..self.columns {
132                    let ju: usize = j as usize;
133                    resultant.data[iu][ju] = self.data[iu][ju].clone() + rhs.data[iu][ju].clone();
134                }
135            }
136
137            return resultant;
138        }
139    }
140
141    impl<T> Mul for Matrix<T>
142    where
143        T: Mul<Output = T> + Add<Output = T> + Sub<Output = T> + Default + Clone,
144    {
145        type Output = Matrix<T>;
146
147        fn mul(self, rhs: Self) -> Self::Output {
148            if self.columns != rhs.rows {
149                panic!("Incompatible dimensions for matrix multiplication.")
150            }
151
152            let mut resultant: Matrix<T> =
153                Matrix::<T>::default_from_rows_and_columns(self.rows, rhs.columns);
154
155            for i in 0..self.rows {
156                let iu: usize = i as usize;
157                for j in 0..rhs.columns {
158                    let ju: usize = j as usize;
159                    for k in 0..self.columns {
160                        let ku: usize = k as usize;
161                        resultant.data[iu][ju] = resultant.data[iu][ju].clone()
162                            + self.data[iu][ku].clone() * rhs.data[ku][ju].clone();
163                    }
164                }
165            }
166
167            return resultant;
168        }
169    }
170
171    impl<T> Sub for Matrix<T>
172    where
173        T: Mul<Output = T> + Add<Output = T> + Sub<Output = T> + Default + Clone,
174    {
175        type Output = Matrix<T>;
176
177        fn sub(self, rhs: Self) -> Self::Output {
178            if self.dimension != rhs.dimension {
179                panic!("Matrices must have the same dimension to perform subtraction");
180            }
181
182            let mut resultant: Matrix<T> = Matrix::default_from_dimension(self.dimension);
183            for i in 0..self.rows {
184                let iu: usize = i as usize;
185                for j in 0..self.columns {
186                    let ju: usize = j as usize;
187                    resultant.data[iu][ju] = self.data[iu][ju].clone() - rhs.data[iu][ju].clone();
188                }
189            }
190
191            return resultant;
192        }
193    }
194
195    impl<T> Default for Matrix<T>
196    where
197        T: Mul<Output = T> + Add<Output = T> + Sub<Output = T> + Default + Clone,
198    {
199        fn default() -> Self {
200            Self {
201                rows: 3,
202                columns: 3,
203                dimension: (3, 3),
204                data: Matrix::data_from_zeroes(3, 3),
205            }
206        }
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use super::matrix_operations::Matrix;
213
214    #[test]
215    fn test_addition() {
216        let mat_ones: Matrix<i32> = Matrix::from_constant((3, 3), 1);
217        assert_eq!(
218            mat_ones.clone() + mat_ones,
219            Matrix::from_constant((3, 3), 2)
220        );
221    }
222
223    #[test]
224    fn test_multiplication() {
225        let mat_tens: Matrix<i32> = Matrix::from_constant((5, 5), 10);
226        let mat_ones: Matrix<i32> = Matrix::from_constant((5, 5), 1);
227
228        let product: Matrix<i32> = mat_tens * mat_ones;
229
230        assert_eq!(product, Matrix::from_constant((5, 5), 50));
231    }
232
233    #[test]
234    fn test_subtraction() {
235        let mat_ones: Matrix<i32> = Matrix::from_constant((3, 3), 1);
236        assert_eq!(
237            mat_ones.clone() - mat_ones,
238            Matrix::default_from_dimension((3, 3))
239        );
240    }
241
242    #[test]
243    fn test_diagonal_matrix_initialization() {
244        let unit_matrix: Matrix<f32> = Matrix::diagonal_from_constant((5, 5), 1.0);
245        let mat_tens: Matrix<f32> = Matrix::from_constant((5, 5), 10.0);
246
247        assert_eq!(mat_tens.clone() * unit_matrix, mat_tens);
248    }
249}