nml_matrix/matrix/
mod.rs

1use std::fmt::Display;
2use std::ops::{Add, AddAssign, Mul, MulAssign, Sub};
3use num_traits::{Num, Signed};
4use rand::distributions::uniform::SampleUniform;
5use crate::util::{ErrorKind, NmlError};
6use rand::Rng;
7use crate::util::ErrorKind::{CreateMatrix, InvalidCols, InvalidRows};
8
9
10/// Nml_Matrix represents a matrix with a given number of rows and columns, the Data is stored in a one dimensional array using row-major-ordering (data[i][j] = data_new[i * m +j], where m is the number of columns)
11/// The Library contains a few methods to create matrices with or without data.
12#[derive(Debug)]
13pub struct NmlMatrix<T> {
14    pub num_rows: u32,
15    pub num_cols: u32,
16    pub data: Box<[T]>,
17    pub is_square: bool,
18}
19
20impl<T> NmlMatrix<T> where T: Num + Copy + Default + Signed + PartialOrd + MulAssign + AddAssign + Display + SampleUniform {
21    ///creates a matrix without data and reserves the capacity for the Data Vector
22    pub fn new(num_rows: u32, num_cols: u32) -> NmlMatrix<T> {
23        let data: Box<[T]> = vec![T::default(); (num_rows * num_cols) as usize].into_boxed_slice();
24        Self {
25            num_rows,
26            num_cols,
27            data,
28            is_square: num_rows == num_cols,
29        }
30    }
31    ///use a 2d Vector to initialize the matrix. Each Vector in the 2d Vector is a row and the length of these vectors are the columns
32    pub fn new_with_2d_vec(num_rows: u32, num_cols: u32, data_2d: &mut Vec<Vec<T>>) -> Result<Self, NmlError> {
33        let rows: u32 = data_2d.len() as u32;
34        let mut cols_match = true;
35        let mut vec_data: Vec<T> = Vec::with_capacity((num_rows*num_cols) as usize);
36        for element in data_2d {
37            if element.len() as u32 != num_cols {
38                cols_match = false;
39                break;
40            }
41            vec_data.append(element);
42        }
43        let data: Box<[T]> = vec_data.into_boxed_slice();
44        match cols_match && rows == num_rows{
45            true => {Ok(Self{
46                num_cols,
47                num_rows,
48                data,
49                is_square: num_rows == num_rows
50            })},
51            false => {Err(NmlError::new(ErrorKind::CreateMatrix))}
52        }
53    }
54
55    ///Constructor that uses a vector to initialize the matrix. checks if the entered rows and columns fit the vector size
56    pub fn new_with_data(num_rows: u32, num_cols: u32, data: Box<[T]>) -> Result<Self, NmlError> {
57        match (num_rows * num_cols) as usize == data.len() {
58            false => Err(NmlError::new(ErrorKind::CreateMatrix)),
59            true => {
60                let is_square = num_rows == num_cols;
61                Ok(NmlMatrix {
62                    num_rows,
63                    num_cols,
64                    data,
65                    is_square,
66                })
67            },
68        }
69    }
70
71    ///Returns a matrix with defined size and random data between minimum and maximum values
72    pub fn nml_mat_rnd(num_rows: u32, num_cols: u32, minimum: T, maximum: T) -> Self {
73        let mut rng = rand::thread_rng();
74        let random_numbers: Vec<T> = (0..100).map(|_| (rng.gen_range(minimum..maximum))).collect();
75        Self {
76            num_rows,
77            num_cols,
78            data: random_numbers.into_boxed_slice(),
79            is_square: num_rows == num_cols,
80        }
81    }
82
83    ///Creates a square matrix of a given size, where all cells are filled with 0.0
84    pub fn nml_mat_sqr(size: u32) -> Self {
85        Self {
86            num_rows: size,
87            num_cols: size,
88            data: vec![T::default(); (size * size) as usize].into_boxed_slice(),
89            is_square: true,
90        }
91    }
92
93    ///creates a identity matrix with the given size
94    pub fn nml_mat_eye(size: u32) -> Self {
95        let mut data: Vec<T> = vec![T::default(); (size * size) as usize];
96        for i in 0..size {
97            data[(i * size + i) as usize] = T::one();
98        }
99        Self {
100            num_rows: size,
101            num_cols: size,
102            data: data.into_boxed_slice(),
103            is_square: true,
104        }
105    }
106    ///Creates a new matrix which is a copy of a given matrix. Uses only the reference to the matrix, so that the original matrix is not moved
107    pub fn nml_mat_cp(matrix: &NmlMatrix<T>) -> Self {
108        Self {
109            num_rows: matrix.num_rows,
110            num_cols: matrix.num_cols,
111            data: matrix.data.clone(),
112            is_square: matrix.is_square,
113        }
114    }
115
116    ///Checks if two matrices are equal by checking their dimensions and after that every cell. Method is also used for implementation of trait PatialEq
117    pub fn equality(self: &Self, matrix: &NmlMatrix<T>) -> bool {
118        if self.num_rows != matrix.num_rows || self.num_cols != matrix.num_cols {
119            return false;
120        }
121        for i in 0..self.num_rows {
122            for j in 0..self.num_cols {
123                if self.data[(i * self.num_cols + j) as usize] != matrix.data[(i * matrix.num_cols + j) as usize] {
124                    return false;
125                }
126            }
127        }
128        true
129    }
130    ///Checks if two matrices are equal with a given tolerance
131    pub fn equality_in_tolerance(self: &Self, matrix: NmlMatrix<T>, tolerance: T) -> bool {
132        if self.num_rows != matrix.num_rows || self.num_cols != matrix.num_cols {
133            return false;
134        }
135        for i in 0..self.num_rows {
136            for j in 0..self.num_cols {
137                if (self.data[(i * self.num_cols + j) as usize] - matrix.data[(i * matrix.num_cols + j) as usize]).abs() > tolerance {
138                    return false;
139                }
140            }
141        }
142        true
143    }
144
145    ///Returns the value a specified at A[i,j]
146    pub fn at(self: &Self, row: u32, col: u32) -> Result<T, NmlError> {
147        match row < self.num_rows as u32 && col < self.num_cols as u32 {
148            false => Err(NmlError::new(InvalidRows)),
149            true => Ok(self.data[(row * self.num_cols + col) as usize]),
150        }
151    }
152
153    ///Returns a result with a specified Column of a matrix, which in itself also is a matrix. If the specified matrix is not in the matrix the result will contain an error
154    pub fn get_column(self: &Self, column: u32) -> Result<Self, NmlError> {
155        match column < self.num_cols {
156            false => Err(NmlError::new(InvalidCols)),
157            true => {
158                let mut data: Vec<T> = Vec::with_capacity(self.num_rows as usize);
159                for i in 0..self.num_rows {
160                    data.push(self.data[(i * self.num_rows + column) as usize]);
161                }
162                Ok(Self {
163                    num_cols: 1,
164                    num_rows: self.num_rows,
165                    data: data.into_boxed_slice(),
166                    is_square: false
167                })
168            },
169        }
170    }
171
172    ///Returns a result which either contains a row of a matrix (which is also a matrix) or a error
173    pub fn get_row(self: &Self, row: u32) -> Result<Self, NmlError> {
174        match row < self.num_rows {
175            true => {
176                let data: Vec<T> = self.data[(row * self.num_cols) as usize..(row * self.num_cols + self.num_cols) as usize].to_vec().clone();
177                Ok(Self {
178                    num_cols: self.num_cols,
179                    num_rows: 1,
180                    data: data.into_boxed_slice(),
181                    is_square: false
182                })
183            },
184            false => Err(NmlError::new(InvalidRows)),
185        }
186    }
187    /// Method sets the value of a given cell in the matrix through a mutable reference
188    pub fn set_value(self: &mut Self, row: u32, col: u32, data: T) -> Result<(), NmlError> {
189        let valid_tuple: (bool, bool) = (row < self.num_rows, col < self.num_cols);
190        match valid_tuple {
191            (false, _) => Err(NmlError::new(InvalidRows)),
192            (_, false) => Err(NmlError::new(InvalidCols)),
193            (true, true) => {
194                self.data[(row * self.num_cols + col) as usize] = data;
195                Ok(())
196            },
197        }
198    }
199    /// Method sets the values of all cells ro a given value
200    pub fn set_all_values(self: &mut Self, value: T) {
201        for i in 0..self.num_rows {
202            for j in 0..self.num_cols {
203                self.data[(i * self.num_cols + j) as usize] = value;
204            }
205        }
206    }
207    ///checks if the matrix is square and sets the diagonal values to a given value
208    pub fn set_dig_values(self: &mut Self, value: T) -> Result<(), NmlError> {
209        if self.is_square == true {
210            for i in 0..self.num_rows {
211                self.data[(i * self.num_cols + i) as usize] = value;
212            }
213        }
214        match self.is_square {
215            true => Ok(()),
216            false => Err(NmlError::new(ErrorKind::MatrixNotSquare)),
217        }
218    }
219    ///multiplies a given row with a given scalar in place. If the row-index is not in the matrix the returned Result will contain an error
220    pub fn multiply_row_scalar(self: &mut Self, row: u32, scalar: T) -> Result<(), NmlError> {
221        match row < self.num_rows {
222            false => Err(NmlError::new(InvalidRows)),
223            true => {
224                for i in 0..self.num_cols {
225                    self.data[(row * self.num_cols + i) as usize] *= scalar;
226                }
227                Ok(())
228            },
229        }
230    }
231    ///multiplies a given column with a given scalar in place. If the row-index is not in the matrix the returned Result will contain an error
232    pub fn multiply_col_scalar(self: &mut Self, col: u32, scalar: T) -> Result<(), NmlError> {
233        match col < self.num_cols {
234            false => Err(NmlError::new(InvalidCols)),
235            true => {
236                for i in 0..self.num_rows {
237                    self.data[(i * self.num_cols + col) as usize] *= scalar;
238                }
239                Ok(())
240            }
241        }
242    }
243    ///multiplies the matrix in place with a given scalar
244    pub fn multiply_matrix_scalar(self: &mut Self, scalar: T) {
245        for i in 0..self.data.len() {
246            self.data[i] *= scalar;
247        }
248    }
249
250    /// row_1 is multiplied with scalar_1, this is analog for 2. row_1 will be modified with the solution (row_1 = row_1 * scalar + row_2 * scalar_2)
251    pub fn add_rows(self: &mut Self, row_1: u32, scalar_1: T, row_2: u32, scalar_2: T) -> Result<(), NmlError> {
252        match row_1 < self.num_rows && row_2 < self.num_rows {
253            false => Err(NmlError::new(InvalidRows)),
254            true => {
255                for i in 0..self.num_cols {
256                    let value = self.data[(row_1 * self.num_cols + i) as usize];
257                    self.data[(row_1 * self.num_cols + i) as usize] = value * scalar_1 + self.data[(row_2 * self.num_cols + i) as usize] * scalar_2;
258                }
259                Ok(())
260            }
261        }
262    }
263    ///Method that swaps two given rows of a matrix object in place. Returns either nothing or an NmlError if the specified rows are not in the matrix
264    pub fn swap_rows(self: &mut Self, row_1: u32, row_2: u32) -> Result<(), NmlError> {
265        match row_1 < self.num_rows && row_2 < self.num_rows {
266            false => Err(NmlError::new(InvalidRows)),
267            true => {
268                for i in 0..self.num_cols {
269                    let temp = self.data[(row_1 * self.num_cols + i) as usize];
270                    self.data[(row_1 * self.num_cols + i) as usize] = self.data[(row_2 * self.num_cols + i) as usize];
271                    self.data[(row_2 * self.num_cols + i) as usize] = temp;
272                }
273                Ok(())
274            }
275        }
276    }
277    ///Method that swaps two given rows of a matrix object in place. Returns either nothing or an NmlError if the specified rows are not in the matrix
278    pub fn swap_columns(self: &mut Self, col_1: u32, col_2: u32) -> Result<(), NmlError> {
279        match col_1 < self.num_cols && col_2 < self.num_cols {
280            false => Err(NmlError::new(InvalidCols)),
281            true => {
282                for i in 0..self.num_rows {
283                    let temp = self.data[(i * self.num_cols + col_1) as usize];
284                    self.data[(i * self.num_cols + col_1) as usize] = self.data[(i * self.num_cols + col_2) as usize];
285                    self.data[(i * self.num_cols + col_2) as usize] = temp;
286                }
287                Ok(())
288            }
289        }
290    }
291    ///Tries to remove a column of a matrix and returns the rest of the matrix as a now on. Does not move the original matrix.
292    ///If the column is not in the original matrix the result will return an error
293    pub fn remove_column(self: &Self, col: u32) -> Result<Self, NmlError> {
294        match col < self.num_cols {
295            false => Err(NmlError::new(InvalidCols)),
296            true => {
297                let mut data: Vec<T> = Vec::with_capacity(self.data.len());
298                let indexes: Vec<usize> = (col as usize..self.data.len()).step_by(self.num_cols as usize).collect();
299                for i in 0..self.data.len() {
300                    if !indexes.contains(&i) {
301                        data.push(self.data[i]);
302                    }
303                }
304                Ok(
305                    Self {
306                        num_cols: 1,
307                        num_rows: self.num_rows,
308                        data: data.into_boxed_slice(),
309                        is_square: false
310                    }
311                )
312            }
313        }
314    }
315    ///Tries to remove a column of a matrix and returns the rest of the matrix as a now on. Does not move the original matrix.
316    ///If the column is not in the original matrix the result will return an error
317    pub fn remove_row(self: &Self, row: u32) -> Result<Self, NmlError> {
318        match row < self.num_rows {
319            false => Err(NmlError::new(InvalidRows)),
320            true => {
321                let data: Vec<T> = self.data[((row + 1) * self.num_cols) as usize..self.data.len()].to_vec();
322                Ok(Self {
323                    num_cols: self.num_cols,
324                    num_rows: 1,
325                    data: data.into_boxed_slice(),
326                    is_square: false,
327                })
328            }
329        }
330    }
331
332    pub fn get_sub_mtr(self: &Self, row_start: u32, row_end: u32, col_start: u32, col_end: u32) -> Result<Self, NmlError> {
333        match row_start < self.num_rows && row_end < self.num_rows && col_start < self.num_cols && col_end < self.num_cols {
334            false => Err(NmlError::new(InvalidRows)),
335            true => {
336                let mut data: Vec<T> = Vec::new();
337                for i in row_start - 1..row_end {
338                    for j in col_start - 1..col_end {
339                        data.push(self.data[(i * self.num_cols + j) as usize]);
340                    }
341                }
342                Ok(Self {
343                    num_rows: row_end - row_start,
344                    num_cols: col_end - col_start,
345                    data: data.into_boxed_slice(),
346                    is_square: false,
347                })
348            }
349        }
350    }
351
352    ///Computes the transpose matrix b' of a matrix b. This is achieved by going from row-major-ordering to a more efficient storage. The input matrix will not be modified or moved.
353    pub fn transpose(self: &Self) -> Self{
354        let mut data: Vec<T> = Vec::with_capacity(self.data.len());
355        for i in 0..self.num_cols {
356            for j in 0..self.num_rows {
357                data.push(self.data[(i + j*self.num_cols) as usize]);
358            }
359        }
360        Self {
361            num_rows: self.num_cols,
362            num_cols: self.num_rows,
363            data: data.into_boxed_slice(),
364            is_square: self.is_square,
365        }
366    }
367    ///The naive matrix multiplication algorihm applied with the transponse of the one matrix
368    pub fn mul_transpose(self: &Self, other: &Self) -> Result<Self, NmlError> {
369        match self.num_cols == other.num_rows {
370            false => Err(NmlError::new(InvalidCols)),
371            true => {
372                let m: u32 = self.num_rows;
373                let n: u32 = self.num_cols;
374                let p: u32 = other.num_cols;
375                let transpose: NmlMatrix<T> = other.transpose();
376                let mut data: Vec<T> = Vec::new();
377                for i in 0..m {
378                    for j in 0..p {
379                        data.insert((i * p + j) as usize, T::default());
380                        for k in 0..n {
381                            data[(i*p+j) as usize] += self.data[(i * n + k) as usize] * transpose.data[(p * k + j) as usize];
382                        }
383                    }
384                }
385                Ok(Self{
386                    num_rows: self.num_rows,
387                    num_cols: other.num_cols,
388                    data: data.into_boxed_slice(),
389                    is_square: self.num_rows == other.num_cols
390                })
391            }
392        }
393    }
394    ///The naive matrix multiplication algorithm. It iterates trough all values of both matrices. These matrices are not moved or modified
395    pub fn mul_naive(self: &Self, other: &Self) -> Result<Self,NmlError> {
396        let m = self.num_rows;
397        let n_1 = self.num_cols;
398        let n_2 = other.num_rows;
399        let p = other.num_cols;
400        match n_1 == n_2 {
401            false => {Err(NmlError::new(CreateMatrix))},
402            true => {
403                let mut data: Vec<T> = Vec::with_capacity((m*p) as usize);
404                for i in 0..m {
405                    for j in 0..p {
406                        data.insert((i * p + j) as usize, T::default());
407                        for k in 0..n_1 {
408                            data[(i*p+j) as usize] += self.data[(i * n_1 + k) as usize] * other.data[(p * k + j) as usize];
409                        }
410                    }
411                }
412                Ok(Self{
413                    num_rows: m,
414                    num_cols: p,
415                    data: data.into_boxed_slice(),
416                    is_square: m == p,
417                })
418            }
419        }
420    }
421
422    pub fn evcxr_display(&self) {
423        let mut html = String::new();
424        html.push_str("<table>");
425        for i in 0..self.num_rows {
426            html.push_str("<tr>");
427            for j in 0..self.num_cols {
428                html.push_str("<td>");
429                html.push_str(&self.data[(i * self.num_cols + j) as usize].to_string());
430                html.push_str("</td>");
431            }
432            html.push_str("</tr>");
433        }
434        html.push_str("</table>");
435        println!("EVCXR_BEGIN_CONTENT text/html\n{}\nEVCXR_END_CONTENT", html);
436    }
437
438}
439impl<T> Sub for NmlMatrix<T> where T: Num + Copy + Default + Signed + PartialOrd + MulAssign + AddAssign {
440    type Output = Result<Self, NmlError>;
441
442    fn sub(self, rhs: Self) -> Self::Output {
443        match self.num_rows == rhs.num_rows && self.num_cols == rhs.num_cols {
444            false => Err(NmlError::new(CreateMatrix)),
445            true => {
446                let mut data: Vec<T> = Vec::new();
447                for i in 0..self.data.len() -1 {
448                    data.push(self.data[i] - rhs.data[i]);
449                }
450                Ok(Self{
451                    num_rows: self.num_rows,
452                    num_cols: self.num_cols,
453                    data: data.into_boxed_slice(),
454                    is_square: self.is_square
455                })
456            }
457        }
458    }
459}
460
461impl<T> Sub for &NmlMatrix<T> where T: Num + Copy + Default + Signed + PartialOrd + MulAssign + AddAssign {
462    type Output = Result<NmlMatrix<T>, NmlError>;
463
464    fn sub(self, rhs: Self) -> Self::Output {
465        match self.num_rows == rhs.num_rows && self.num_cols == rhs.num_cols {
466            false => Err(NmlError::new(CreateMatrix)),
467            true => {
468                let mut data: Vec<T> = Vec::new();
469                for i in 0..self.data.len() -1 {
470                    data.push(self.data[i] - rhs.data[i]);
471                }
472                Ok(NmlMatrix{
473                    num_rows: self.num_rows,
474                    num_cols: self.num_cols,
475                    data: data.into_boxed_slice(),
476                    is_square: self.is_square
477                })
478            }
479        }
480    }
481}
482
483impl<T> Display for NmlMatrix<T> where T: Num + Copy + Default + Signed + PartialOrd + MulAssign + AddAssign + Display{
484    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
485        let mut output = String::new();
486        for i in 0..self.num_rows {
487            for j in 0..self.num_cols {
488                output.push_str(&self.data[(i * self.num_cols + j) as usize].to_string());
489                output.push_str(" ");
490            }
491            output.push_str("\n");
492        }
493        write!(f, "{}", output)
494    }
495}
496
497impl<T> Eq for NmlMatrix<T> where T: Num + Copy + Default + Signed + PartialOrd + MulAssign + AddAssign + SampleUniform + Display{}
498impl<T> PartialEq for NmlMatrix<T> where T: Num + Copy + Default + Signed + PartialOrd + MulAssign + AddAssign + SampleUniform + Display{
499    fn eq(&self, other: &Self) -> bool {
500        self.equality(other)
501    }
502}
503
504impl<T> Add for NmlMatrix<T> where T: Num + Copy + Default + Signed + PartialOrd + MulAssign + AddAssign{
505    type Output = Result<Self, NmlError>;
506
507    fn add(self, rhs: Self) -> Self::Output {
508        match self.num_rows == rhs.num_rows && self.num_cols == rhs.num_cols{
509            false => Err(NmlError::new(CreateMatrix)),
510            true => {
511                let mut data: Vec<T> = Vec::new();
512                for i in 0..self.data.len() {
513                    data.push(self.data[i] + rhs.data[i]);
514                }
515                Ok(Self{
516                    num_cols: self.num_cols,
517                    num_rows: self.num_rows,
518                    data: data.into_boxed_slice(),
519                    is_square: self.is_square
520                })
521            }
522        }
523    }
524}
525
526impl<T> Add for &NmlMatrix<T>  where T: Num + Copy + Default + Signed + PartialOrd + MulAssign + AddAssign{
527    type Output = Result<NmlMatrix<T>, NmlError>;
528
529    fn add(self, rhs: Self) -> Self::Output {
530        match self.num_rows == rhs.num_rows && self.num_cols == rhs.num_cols{
531            false => Err(NmlError::new(CreateMatrix)),
532            true => {
533                let mut data: Vec<T> = Vec::new();
534                for i in 0..self.data.len() {
535                    data.push(self.data[i] + rhs.data[i]);
536                }
537                Ok(NmlMatrix{
538                    num_cols: self.num_cols,
539                    num_rows: self.num_rows,
540                    data: data.into_boxed_slice(),
541                    is_square: self.is_square
542                })
543            }
544        }
545    }
546}
547
548impl<T> Mul for NmlMatrix<T> where T: Num + Copy + Default + Signed + PartialOrd + MulAssign + AddAssign + SampleUniform + Display{
549    type Output = Result<Self<>, NmlError>;
550
551    fn mul(self, rhs: Self) -> Self::Output {
552        return self.mul_naive(&rhs);
553    }
554}
555