1#[allow(unused_imports)]
8use crate::prelude::*;
9use core::fmt;
10use num_rational::Rational64;
11use num_traits::Signed;
12
13#[derive(Debug, Clone, PartialEq)]
15pub struct Matrix {
16 rows: usize,
17 cols: usize,
18 data: Vec<Rational64>,
19}
20
21impl Matrix {
22 pub fn zeros(rows: usize, cols: usize) -> Self {
24 Self {
25 rows,
26 cols,
27 data: vec![Rational64::from(0); rows * cols],
28 }
29 }
30
31 pub fn identity(size: usize) -> Self {
33 let mut matrix = Self::zeros(size, size);
34 for i in 0..size {
35 matrix.set(i, i, Rational64::from(1));
36 }
37 matrix
38 }
39
40 pub fn from_rows(rows: Vec<Vec<Rational64>>) -> Self {
42 if rows.is_empty() {
43 return Self::zeros(0, 0);
44 }
45 let nrows = rows.len();
46 let ncols = rows[0].len();
47 let mut data = Vec::with_capacity(nrows * ncols);
48 for row in rows {
49 assert_eq!(row.len(), ncols, "All rows must have the same length");
50 data.extend(row);
51 }
52 Self {
53 rows: nrows,
54 cols: ncols,
55 data,
56 }
57 }
58
59 pub fn from_vec(rows: usize, cols: usize, data: Vec<Rational64>) -> Self {
61 assert_eq!(data.len(), rows * cols, "Data size must match dimensions");
62 Self { rows, cols, data }
63 }
64
65 pub fn nrows(&self) -> usize {
67 self.rows
68 }
69
70 pub fn ncols(&self) -> usize {
72 self.cols
73 }
74
75 pub fn get(&self, row: usize, col: usize) -> Rational64 {
77 assert!(row < self.rows && col < self.cols);
78 self.data[row * self.cols + col]
79 }
80
81 pub fn set(&mut self, row: usize, col: usize, value: Rational64) {
83 assert!(row < self.rows && col < self.cols);
84 self.data[row * self.cols + col] = value;
85 }
86
87 pub fn row(&self, row: usize) -> &[Rational64] {
89 assert!(row < self.rows);
90 let start = row * self.cols;
91 &self.data[start..start + self.cols]
92 }
93
94 pub fn swap_rows(&mut self, i: usize, j: usize) {
96 assert!(i < self.rows && j < self.rows);
97 if i == j {
98 return;
99 }
100 for k in 0..self.cols {
101 let idx_i = i * self.cols + k;
102 let idx_j = j * self.cols + k;
103 self.data.swap(idx_i, idx_j);
104 }
105 }
106
107 pub fn add_row_multiple(&mut self, i: usize, j: usize, scalar: Rational64) {
109 assert!(i < self.rows && j < self.rows);
110 if scalar == Rational64::from(0) {
111 return;
112 }
113 for k in 0..self.cols {
114 let idx_i = i * self.cols + k;
115 let idx_j = j * self.cols + k;
116 let val_j = self.data[idx_j];
117 self.data[idx_i] += scalar * val_j;
118 }
119 }
120
121 pub fn scale_row(&mut self, row: usize, scalar: Rational64) {
123 assert!(row < self.rows);
124 for k in 0..self.cols {
125 let idx = row * self.cols + k;
126 self.data[idx] *= scalar;
127 }
128 }
129
130 #[allow(clippy::needless_range_loop)]
132 pub fn mul_vec(&self, v: &[Rational64]) -> Vec<Rational64> {
133 assert_eq!(
134 v.len(),
135 self.cols,
136 "Vector dimension must match matrix columns"
137 );
138 let mut result = vec![Rational64::from(0); self.rows];
139 for i in 0..self.rows {
140 for j in 0..self.cols {
141 result[i] += self.get(i, j) * v[j];
142 }
143 }
144 result
145 }
146
147 pub fn mul(&self, other: &Matrix) -> Matrix {
149 assert_eq!(
150 self.cols, other.rows,
151 "Matrix dimensions must be compatible for multiplication"
152 );
153 let mut result = Matrix::zeros(self.rows, other.cols);
154 for i in 0..self.rows {
155 for j in 0..other.cols {
156 let mut sum = Rational64::from(0);
157 for k in 0..self.cols {
158 sum += self.get(i, k) * other.get(k, j);
159 }
160 result.set(i, j, sum);
161 }
162 }
163 result
164 }
165
166 pub fn transpose(&self) -> Matrix {
168 let mut result = Matrix::zeros(self.cols, self.rows);
169 for i in 0..self.rows {
170 for j in 0..self.cols {
171 result.set(j, i, self.get(i, j));
172 }
173 }
174 result
175 }
176
177 pub fn gaussian_elimination(&self) -> (Matrix, usize) {
180 let mut result = self.clone();
181 let mut rank = 0;
182
183 for col in 0..self.cols.min(self.rows) {
184 let mut pivot_row = None;
186 for row in rank..self.rows {
187 if result.get(row, col) != Rational64::from(0) {
188 pivot_row = Some(row);
189 break;
190 }
191 }
192
193 let pivot_row = match pivot_row {
194 Some(r) => r,
195 None => continue, };
197
198 if pivot_row != rank {
200 result.swap_rows(rank, pivot_row);
201 }
202
203 let pivot = result.get(rank, col);
205 for row in rank + 1..self.rows {
206 let factor = result.get(row, col) / pivot;
207 result.add_row_multiple(row, rank, -factor);
208 }
209
210 rank += 1;
211 }
212
213 (result, rank)
214 }
215
216 pub fn lu_decomposition(&self) -> Option<(Matrix, Matrix, Vec<usize>)> {
219 if self.rows != self.cols {
220 return None; }
222 let n = self.rows;
223
224 let mut l = Matrix::zeros(n, n);
225 let mut u = self.clone();
226 let mut p: Vec<usize> = (0..n).collect();
227
228 for i in 0..n {
229 let mut max_row = i;
231 let mut max_val = u.get(i, i).abs();
232 for k in i + 1..n {
233 let val = u.get(k, i).abs();
234 if val > max_val {
235 max_val = val;
236 max_row = k;
237 }
238 }
239
240 if max_row != i {
242 u.swap_rows(i, max_row);
243 p.swap(i, max_row);
244 for k in 0..i {
246 let tmp = l.get(i, k);
247 l.set(i, k, l.get(max_row, k));
248 l.set(max_row, k, tmp);
249 }
250 }
251
252 let pivot = u.get(i, i);
253 if pivot == Rational64::from(0) {
254 return None; }
256
257 l.set(i, i, Rational64::from(1));
258
259 for k in i + 1..n {
260 let factor = u.get(k, i) / pivot;
261 l.set(k, i, factor);
262 for j in i..n {
263 let val = u.get(k, j) - factor * u.get(i, j);
264 u.set(k, j, val);
265 }
266 }
267 }
268
269 Some((l, u, p))
270 }
271
272 #[allow(clippy::needless_range_loop)]
275 pub fn solve(&self, b: &[Rational64]) -> Option<Vec<Rational64>> {
276 assert_eq!(
277 b.len(),
278 self.rows,
279 "Right-hand side dimension must match matrix rows"
280 );
281
282 if self.rows != self.cols {
283 return None; }
285
286 let mut aug = Matrix::zeros(self.rows, self.cols + 1);
288 for i in 0..self.rows {
289 for j in 0..self.cols {
290 aug.set(i, j, self.get(i, j));
291 }
292 aug.set(i, self.cols, b[i]);
293 }
294
295 for col in 0..self.cols {
297 let mut pivot_row = None;
299 for row in col..self.rows {
300 if aug.get(row, col) != Rational64::from(0) {
301 pivot_row = Some(row);
302 break;
303 }
304 }
305
306 let pivot_row = pivot_row?; if pivot_row != col {
309 aug.swap_rows(col, pivot_row);
310 }
311
312 let pivot = aug.get(col, col);
313 for row in col + 1..self.rows {
314 let factor = aug.get(row, col) / pivot;
315 aug.add_row_multiple(row, col, -factor);
316 }
317 }
318
319 let mut x = vec![Rational64::from(0); self.cols];
321 for i in (0..self.rows).rev() {
322 let mut sum = aug.get(i, self.cols);
323 for j in i + 1..self.cols {
324 sum -= aug.get(i, j) * x[j];
325 }
326 let pivot = aug.get(i, i);
327 if pivot == Rational64::from(0) {
328 return None; }
330 x[i] = sum / pivot;
331 }
332
333 Some(x)
334 }
335
336 pub fn determinant(&self) -> Option<Rational64> {
338 if self.rows != self.cols {
339 return None;
340 }
341
342 let (_, u, p) = self.lu_decomposition()?;
343
344 let mut det = Rational64::from(1);
346 for i in 0..self.rows {
347 det *= u.get(i, i);
348 }
349
350 let mut swaps = 0;
352 let mut visited = vec![false; p.len()];
353 for i in 0..p.len() {
354 if visited[i] {
355 continue;
356 }
357 let mut j = i;
358 let mut cycle_len = 0;
359 while !visited[j] {
360 visited[j] = true;
361 j = p[j];
362 cycle_len += 1;
363 }
364 if cycle_len > 1 {
365 swaps += cycle_len - 1;
366 }
367 }
368
369 if swaps % 2 == 1 {
370 det = -det;
371 }
372
373 Some(det)
374 }
375
376 pub fn qr_decomposition(&self) -> Option<(Matrix, Matrix)> {
386 let m = self.rows;
387 let n = self.cols;
388
389 if m < n {
390 return None; }
392
393 let mut q = Matrix::zeros(m, n);
394 let mut r = Matrix::zeros(n, n);
395
396 for j in 0..n {
398 for i in 0..m {
400 q.set(i, j, self.get(i, j));
401 }
402
403 for k in 0..j {
405 let mut dot_qk_aj = Rational64::from(0);
407 let mut dot_qk_qk = Rational64::from(0);
408
409 for i in 0..m {
410 dot_qk_aj += q.get(i, k) * self.get(i, j);
411 dot_qk_qk += q.get(i, k) * q.get(i, k);
412 }
413
414 if dot_qk_qk == Rational64::from(0) {
415 return None;
416 }
417
418 let proj_coeff = dot_qk_aj / dot_qk_qk;
419 r.set(k, j, proj_coeff);
420
421 for i in 0..m {
423 let new_val = q.get(i, j) - proj_coeff * q.get(i, k);
424 q.set(i, j, new_val);
425 }
426 }
427
428 let mut norm_sq = Rational64::from(0);
430 for i in 0..m {
431 let val = q.get(i, j);
432 norm_sq += val * val;
433 }
434
435 if norm_sq == Rational64::from(0) {
436 return None;
438 }
439
440 r.set(j, j, Rational64::from(1));
442 }
443
444 Some((q, r))
445 }
446
447 pub fn cholesky_decomposition(&self) -> Option<Matrix> {
458 if self.rows != self.cols {
459 return None; }
461
462 let n = self.rows;
463
464 for i in 0..n {
466 for j in i + 1..n {
467 if self.get(i, j) != self.get(j, i) {
468 return None; }
470 }
471 }
472
473 let mut l = Matrix::zeros(n, n);
474
475 for i in 0..n {
476 for j in 0..=i {
477 let mut sum = self.get(i, j);
478
479 for k in 0..j {
480 sum -= l.get(i, k) * l.get(j, k);
481 }
482
483 if i == j {
484 if sum <= Rational64::from(0) {
486 return None; }
488
489 let sqrt_val = rational_sqrt(sum)?;
492 l.set(i, i, sqrt_val);
493 } else {
494 let l_jj = l.get(j, j);
496 if l_jj == Rational64::from(0) {
497 return None;
498 }
499 l.set(i, j, sum / l_jj);
500 }
501 }
502 }
503
504 Some(l)
505 }
506
507 pub fn inverse(&self) -> Option<Matrix> {
515 if self.rows != self.cols {
516 return None; }
518
519 let n = self.rows;
520
521 let (l, u, p) = self.lu_decomposition()?;
523
524 let mut inv = Matrix::zeros(n, n);
526
527 for col in 0..n {
529 let mut b = vec![Rational64::from(0); n];
531 b[col] = Rational64::from(1);
532
533 let mut pb = vec![Rational64::from(0); n];
535 for i in 0..n {
536 pb[i] = b[p[i]];
537 }
538
539 let mut y = vec![Rational64::from(0); n];
541 for i in 0..n {
542 let mut sum = pb[i];
543 for (j, &y_j) in y.iter().enumerate().take(i) {
544 sum -= l.get(i, j) * y_j;
545 }
546 y[i] = sum; }
548
549 let mut x = vec![Rational64::from(0); n];
551 for i in (0..n).rev() {
552 let mut sum = y[i];
553 for (j, &x_j) in x.iter().enumerate().skip(i + 1).take(n - i - 1) {
554 sum -= u.get(i, j) * x_j;
555 }
556 let u_ii = u.get(i, i);
557 if u_ii == Rational64::from(0) {
558 return None; }
560 x[i] = sum / u_ii;
561 }
562
563 for (i, &x_i) in x.iter().enumerate() {
565 inv.set(i, col, x_i);
566 }
567 }
568
569 Some(inv)
570 }
571
572 pub fn is_identity(&self) -> bool {
574 if self.rows != self.cols {
575 return false;
576 }
577
578 for i in 0..self.rows {
579 for j in 0..self.cols {
580 let expected = if i == j {
581 Rational64::from(1)
582 } else {
583 Rational64::from(0)
584 };
585 if self.get(i, j) != expected {
586 return false;
587 }
588 }
589 }
590 true
591 }
592}
593
594fn rational_sqrt(r: Rational64) -> Option<Rational64> {
597 use num_integer::Roots;
598 use num_traits::Zero;
599
600 if r < Rational64::zero() {
601 return None;
602 }
603 if r == Rational64::zero() {
604 return Some(Rational64::zero());
605 }
606
607 let numer = r.numer();
608 let denom = r.denom();
609
610 let sqrt_numer = numer.sqrt();
612 let sqrt_denom = denom.sqrt();
613
614 if sqrt_numer * sqrt_numer == *numer && sqrt_denom * sqrt_denom == *denom {
615 Some(Rational64::new(sqrt_numer, sqrt_denom))
616 } else {
617 None
618 }
619}
620
621impl fmt::Display for Matrix {
622 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
623 writeln!(f, "[")?;
624 for i in 0..self.rows {
625 write!(f, " [")?;
626 for j in 0..self.cols {
627 if j > 0 {
628 write!(f, ", ")?;
629 }
630 write!(f, "{}", self.get(i, j))?;
631 }
632 writeln!(f, "]")?;
633 }
634 write!(f, "]")
635 }
636}
637
638#[derive(Debug, Clone, PartialEq)]
641pub struct SparseMatrix {
642 rows: usize,
643 cols: usize,
644 row_ptr: Vec<usize>,
647 col_indices: Vec<usize>,
649 values: Vec<Rational64>,
651}
652
653impl SparseMatrix {
654 pub fn new(rows: usize, cols: usize) -> Self {
656 Self {
657 rows,
658 cols,
659 row_ptr: vec![0; rows + 1],
660 col_indices: Vec::new(),
661 values: Vec::new(),
662 }
663 }
664
665 pub fn identity(size: usize) -> Self {
667 let mut matrix = Self::new(size, size);
668 matrix.row_ptr = (0..=size).collect();
669 matrix.col_indices = (0..size).collect();
670 matrix.values = vec![Rational64::from(1); size];
671 matrix
672 }
673
674 pub fn from_dense(dense: &Matrix) -> Self {
676 let mut matrix = Self::new(dense.nrows(), dense.ncols());
677 let mut col_indices = Vec::new();
678 let mut values = Vec::new();
679
680 for i in 0..dense.nrows() {
681 matrix.row_ptr[i] = values.len();
682 for j in 0..dense.ncols() {
683 let val = dense.get(i, j);
684 if val != Rational64::from(0) {
685 col_indices.push(j);
686 values.push(val);
687 }
688 }
689 }
690 matrix.row_ptr[dense.nrows()] = values.len();
691 matrix.col_indices = col_indices;
692 matrix.values = values;
693 matrix
694 }
695
696 pub fn nrows(&self) -> usize {
698 self.rows
699 }
700
701 pub fn ncols(&self) -> usize {
703 self.cols
704 }
705
706 pub fn nnz(&self) -> usize {
708 self.values.len()
709 }
710
711 pub fn get(&self, row: usize, col: usize) -> Rational64 {
713 assert!(row < self.rows && col < self.cols);
714 let start = self.row_ptr[row];
715 let end = self.row_ptr[row + 1];
716
717 for i in start..end {
719 if self.col_indices[i] == col {
720 return self.values[i];
721 } else if self.col_indices[i] > col {
722 break;
723 }
724 }
725 Rational64::from(0)
726 }
727
728 pub fn set(&mut self, row: usize, col: usize, value: Rational64) {
732 assert!(row < self.rows && col < self.cols);
733
734 if value == Rational64::from(0) {
735 let start = self.row_ptr[row];
737 let end = self.row_ptr[row + 1];
738
739 for i in start..end {
740 if self.col_indices[i] == col {
741 self.col_indices.remove(i);
742 self.values.remove(i);
743 for r in row + 1..=self.rows {
745 self.row_ptr[r] -= 1;
746 }
747 return;
748 }
749 }
750 return;
751 }
752
753 let start = self.row_ptr[row];
755 let end = self.row_ptr[row + 1];
756
757 for i in start..end {
758 if self.col_indices[i] == col {
759 self.values[i] = value;
761 return;
762 } else if self.col_indices[i] > col {
763 self.col_indices.insert(i, col);
765 self.values.insert(i, value);
766 for r in row + 1..=self.rows {
768 self.row_ptr[r] += 1;
769 }
770 return;
771 }
772 }
773
774 self.col_indices.insert(end, col);
776 self.values.insert(end, value);
777 for r in row + 1..=self.rows {
778 self.row_ptr[r] += 1;
779 }
780 }
781
782 #[allow(clippy::needless_range_loop)]
784 pub fn mul_vec(&self, v: &[Rational64]) -> Vec<Rational64> {
785 assert_eq!(
786 v.len(),
787 self.cols,
788 "Vector dimension must match matrix columns"
789 );
790 let mut result = vec![Rational64::from(0); self.rows];
791
792 for i in 0..self.rows {
793 let start = self.row_ptr[i];
794 let end = self.row_ptr[i + 1];
795 let mut sum = Rational64::from(0);
796
797 for k in start..end {
798 sum += self.values[k] * v[self.col_indices[k]];
799 }
800 result[i] = sum;
801 }
802
803 result
804 }
805
806 pub fn mul(&self, other: &SparseMatrix) -> SparseMatrix {
808 assert_eq!(
809 self.cols, other.rows,
810 "Matrix dimensions must be compatible for multiplication"
811 );
812
813 let mut result = SparseMatrix::new(self.rows, other.cols);
814 let mut row_data: Vec<Rational64> = vec![Rational64::from(0); other.cols];
815 let mut col_indices = Vec::new();
816 let mut values = Vec::new();
817
818 for i in 0..self.rows {
819 result.row_ptr[i] = values.len();
820
821 row_data.fill(Rational64::from(0));
823
824 let start = self.row_ptr[i];
825 let end = self.row_ptr[i + 1];
826
827 for k in start..end {
828 let a_val = &self.values[k];
829 let k_col = self.col_indices[k];
830
831 let b_start = other.row_ptr[k_col];
833 let b_end = other.row_ptr[k_col + 1];
834
835 for j in b_start..b_end {
836 let b_col = other.col_indices[j];
837 let b_val = &other.values[j];
838 row_data[b_col] += a_val * b_val;
839 }
840 }
841
842 for (j, val) in row_data.iter().enumerate() {
844 if val != &Rational64::from(0) {
845 col_indices.push(j);
846 values.push(*val);
847 }
848 }
849 }
850
851 result.row_ptr[self.rows] = values.len();
852 result.col_indices = col_indices;
853 result.values = values;
854 result
855 }
856
857 pub fn transpose(&self) -> SparseMatrix {
859 let mut result = SparseMatrix::new(self.cols, self.rows);
860 let mut col_counts = vec![0; self.cols];
861
862 for &col in &self.col_indices {
864 col_counts[col] += 1;
865 }
866
867 result.row_ptr[0] = 0;
869 #[allow(clippy::needless_range_loop)]
870 for i in 0..self.cols {
871 result.row_ptr[i + 1] = result.row_ptr[i] + col_counts[i];
872 }
873
874 result.col_indices = vec![0; self.nnz()];
876 result.values = vec![Rational64::from(0); self.nnz()];
877
878 let mut current_pos = result.row_ptr.clone();
880 for i in 0..self.rows {
881 let start = self.row_ptr[i];
882 let end = self.row_ptr[i + 1];
883
884 for k in start..end {
885 let col = self.col_indices[k];
886 let pos = current_pos[col];
887 result.col_indices[pos] = i;
888 result.values[pos] = self.values[k];
889 current_pos[col] += 1;
890 }
891 }
892
893 result
894 }
895
896 pub fn to_dense(&self) -> Matrix {
898 let mut dense = Matrix::zeros(self.rows, self.cols);
899 for i in 0..self.rows {
900 let start = self.row_ptr[i];
901 let end = self.row_ptr[i + 1];
902 for k in start..end {
903 dense.set(i, self.col_indices[k], self.values[k]);
904 }
905 }
906 dense
907 }
908}
909
910impl fmt::Display for SparseMatrix {
911 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
912 writeln!(
913 f,
914 "SparseMatrix({}x{}, {} non-zeros):",
915 self.rows,
916 self.cols,
917 self.nnz()
918 )?;
919 for i in 0..self.rows {
920 write!(f, " Row {}: ", i)?;
921 let start = self.row_ptr[i];
922 let end = self.row_ptr[i + 1];
923 for k in start..end {
924 write!(f, "({}, {}) ", self.col_indices[k], self.values[k])?;
925 }
926 writeln!(f)?;
927 }
928 Ok(())
929 }
930}
931
932#[cfg(test)]
933mod tests {
934 use super::*;
935
936 #[test]
937 fn test_matrix_zeros() {
938 let m = Matrix::zeros(2, 3);
939 assert_eq!(m.nrows(), 2);
940 assert_eq!(m.ncols(), 3);
941 assert_eq!(m.get(0, 0), Rational64::from(0));
942 assert_eq!(m.get(1, 2), Rational64::from(0));
943 }
944
945 #[test]
946 fn test_matrix_identity() {
947 let m = Matrix::identity(3);
948 assert_eq!(m.get(0, 0), Rational64::from(1));
949 assert_eq!(m.get(1, 1), Rational64::from(1));
950 assert_eq!(m.get(2, 2), Rational64::from(1));
951 assert_eq!(m.get(0, 1), Rational64::from(0));
952 assert_eq!(m.get(1, 2), Rational64::from(0));
953 }
954
955 #[test]
956 fn test_matrix_from_rows() {
957 let m = Matrix::from_rows(vec![
958 vec![Rational64::from(1), Rational64::from(2)],
959 vec![Rational64::from(3), Rational64::from(4)],
960 ]);
961 assert_eq!(m.get(0, 0), Rational64::from(1));
962 assert_eq!(m.get(0, 1), Rational64::from(2));
963 assert_eq!(m.get(1, 0), Rational64::from(3));
964 assert_eq!(m.get(1, 1), Rational64::from(4));
965 }
966
967 #[test]
968 fn test_matrix_swap_rows() {
969 let mut m = Matrix::from_rows(vec![
970 vec![Rational64::from(1), Rational64::from(2)],
971 vec![Rational64::from(3), Rational64::from(4)],
972 ]);
973 m.swap_rows(0, 1);
974 assert_eq!(m.get(0, 0), Rational64::from(3));
975 assert_eq!(m.get(0, 1), Rational64::from(4));
976 assert_eq!(m.get(1, 0), Rational64::from(1));
977 assert_eq!(m.get(1, 1), Rational64::from(2));
978 }
979
980 #[test]
981 fn test_matrix_add_row_multiple() {
982 let mut m = Matrix::from_rows(vec![
983 vec![Rational64::from(1), Rational64::from(2)],
984 vec![Rational64::from(3), Rational64::from(4)],
985 ]);
986 m.add_row_multiple(1, 0, Rational64::from(2));
987 assert_eq!(m.get(0, 0), Rational64::from(1));
988 assert_eq!(m.get(0, 1), Rational64::from(2));
989 assert_eq!(m.get(1, 0), Rational64::from(5)); assert_eq!(m.get(1, 1), Rational64::from(8)); }
992
993 #[test]
994 fn test_matrix_mul_vec() {
995 let m = Matrix::from_rows(vec![
996 vec![Rational64::from(1), Rational64::from(2)],
997 vec![Rational64::from(3), Rational64::from(4)],
998 ]);
999 let v = vec![Rational64::from(5), Rational64::from(6)];
1000 let result = m.mul_vec(&v);
1001 assert_eq!(result[0], Rational64::from(17)); assert_eq!(result[1], Rational64::from(39)); }
1004
1005 #[test]
1006 fn test_matrix_mul() {
1007 let a = Matrix::from_rows(vec![
1008 vec![Rational64::from(1), Rational64::from(2)],
1009 vec![Rational64::from(3), Rational64::from(4)],
1010 ]);
1011 let b = Matrix::from_rows(vec![
1012 vec![Rational64::from(5), Rational64::from(6)],
1013 vec![Rational64::from(7), Rational64::from(8)],
1014 ]);
1015 let c = a.mul(&b);
1016 assert_eq!(c.get(0, 0), Rational64::from(19)); assert_eq!(c.get(0, 1), Rational64::from(22)); assert_eq!(c.get(1, 0), Rational64::from(43)); assert_eq!(c.get(1, 1), Rational64::from(50)); }
1021
1022 #[test]
1023 fn test_matrix_transpose() {
1024 let m = Matrix::from_rows(vec![
1025 vec![
1026 Rational64::from(1),
1027 Rational64::from(2),
1028 Rational64::from(3),
1029 ],
1030 vec![
1031 Rational64::from(4),
1032 Rational64::from(5),
1033 Rational64::from(6),
1034 ],
1035 ]);
1036 let t = m.transpose();
1037 assert_eq!(t.nrows(), 3);
1038 assert_eq!(t.ncols(), 2);
1039 assert_eq!(t.get(0, 0), Rational64::from(1));
1040 assert_eq!(t.get(1, 0), Rational64::from(2));
1041 assert_eq!(t.get(2, 0), Rational64::from(3));
1042 assert_eq!(t.get(0, 1), Rational64::from(4));
1043 assert_eq!(t.get(1, 1), Rational64::from(5));
1044 assert_eq!(t.get(2, 1), Rational64::from(6));
1045 }
1046
1047 #[test]
1048 fn test_gaussian_elimination() {
1049 let m = Matrix::from_rows(vec![
1050 vec![
1051 Rational64::from(2),
1052 Rational64::from(1),
1053 Rational64::from(-1),
1054 ],
1055 vec![
1056 Rational64::from(-3),
1057 Rational64::from(-1),
1058 Rational64::from(2),
1059 ],
1060 vec![
1061 Rational64::from(-2),
1062 Rational64::from(1),
1063 Rational64::from(2),
1064 ],
1065 ]);
1066 let (ref_form, rank) = m.gaussian_elimination();
1067 assert_eq!(rank, 3);
1068 assert_ne!(ref_form.get(0, 0), Rational64::from(0));
1070 assert_eq!(ref_form.get(1, 0), Rational64::from(0));
1071 assert_eq!(ref_form.get(2, 0), Rational64::from(0));
1072 }
1073
1074 #[test]
1075 fn test_solve_linear_system() {
1076 let a = Matrix::from_rows(vec![
1080 vec![
1081 Rational64::from(2),
1082 Rational64::from(1),
1083 Rational64::from(-1),
1084 ],
1085 vec![
1086 Rational64::from(-3),
1087 Rational64::from(-1),
1088 Rational64::from(2),
1089 ],
1090 vec![
1091 Rational64::from(-2),
1092 Rational64::from(1),
1093 Rational64::from(2),
1094 ],
1095 ]);
1096 let b = vec![
1097 Rational64::from(8),
1098 Rational64::from(-11),
1099 Rational64::from(-3),
1100 ];
1101 let x = a.solve(&b).expect("test operation should succeed");
1102 assert_eq!(x[0], Rational64::from(2));
1103 assert_eq!(x[1], Rational64::from(3));
1104 assert_eq!(x[2], Rational64::from(-1));
1105 }
1106
1107 #[test]
1108 fn test_lu_decomposition() {
1109 let m = Matrix::from_rows(vec![
1110 vec![Rational64::from(4), Rational64::from(3)],
1111 vec![Rational64::from(6), Rational64::from(3)],
1112 ]);
1113 let (l, u, _p) = m.lu_decomposition().expect("test operation should succeed");
1114
1115 assert_eq!(l.get(0, 0), Rational64::from(1));
1117 assert_eq!(l.get(1, 1), Rational64::from(1));
1118 assert_eq!(l.get(0, 1), Rational64::from(0));
1119
1120 assert_eq!(u.get(1, 0), Rational64::from(0));
1122 }
1123
1124 #[test]
1125 fn test_determinant() {
1126 let m = Matrix::from_rows(vec![
1127 vec![Rational64::from(1), Rational64::from(2)],
1128 vec![Rational64::from(3), Rational64::from(4)],
1129 ]);
1130 let det = m.determinant().expect("test operation should succeed");
1131 assert_eq!(det, Rational64::from(-2)); }
1133
1134 #[test]
1135 fn test_determinant_3x3() {
1136 let m = Matrix::from_rows(vec![
1137 vec![
1138 Rational64::from(6),
1139 Rational64::from(1),
1140 Rational64::from(1),
1141 ],
1142 vec![
1143 Rational64::from(4),
1144 Rational64::from(-2),
1145 Rational64::from(5),
1146 ],
1147 vec![
1148 Rational64::from(2),
1149 Rational64::from(8),
1150 Rational64::from(7),
1151 ],
1152 ]);
1153 let det = m.determinant().expect("test operation should succeed");
1154 assert_eq!(det, Rational64::from(-306));
1155 }
1156
1157 #[test]
1158 fn test_sparse_matrix_identity() {
1159 let m = SparseMatrix::identity(3);
1160 assert_eq!(m.nrows(), 3);
1161 assert_eq!(m.ncols(), 3);
1162 assert_eq!(m.nnz(), 3);
1163 assert_eq!(m.get(0, 0), Rational64::from(1));
1164 assert_eq!(m.get(1, 1), Rational64::from(1));
1165 assert_eq!(m.get(2, 2), Rational64::from(1));
1166 assert_eq!(m.get(0, 1), Rational64::from(0));
1167 assert_eq!(m.get(1, 2), Rational64::from(0));
1168 }
1169
1170 #[test]
1171 fn test_sparse_from_dense() {
1172 let dense = Matrix::from_rows(vec![
1173 vec![
1174 Rational64::from(1),
1175 Rational64::from(0),
1176 Rational64::from(2),
1177 ],
1178 vec![
1179 Rational64::from(0),
1180 Rational64::from(3),
1181 Rational64::from(0),
1182 ],
1183 vec![
1184 Rational64::from(4),
1185 Rational64::from(0),
1186 Rational64::from(5),
1187 ],
1188 ]);
1189 let sparse = SparseMatrix::from_dense(&dense);
1190 assert_eq!(sparse.nnz(), 5); assert_eq!(sparse.get(0, 0), Rational64::from(1));
1192 assert_eq!(sparse.get(0, 2), Rational64::from(2));
1193 assert_eq!(sparse.get(1, 1), Rational64::from(3));
1194 assert_eq!(sparse.get(2, 0), Rational64::from(4));
1195 assert_eq!(sparse.get(2, 2), Rational64::from(5));
1196 assert_eq!(sparse.get(0, 1), Rational64::from(0));
1197 }
1198
1199 #[test]
1200 fn test_sparse_to_dense() {
1201 let sparse = SparseMatrix::identity(3);
1202 let dense = sparse.to_dense();
1203 assert_eq!(dense.get(0, 0), Rational64::from(1));
1204 assert_eq!(dense.get(1, 1), Rational64::from(1));
1205 assert_eq!(dense.get(2, 2), Rational64::from(1));
1206 assert_eq!(dense.get(0, 1), Rational64::from(0));
1207 }
1208
1209 #[test]
1210 fn test_sparse_mul_vec() {
1211 let dense = Matrix::from_rows(vec![
1212 vec![
1213 Rational64::from(1),
1214 Rational64::from(0),
1215 Rational64::from(2),
1216 ],
1217 vec![
1218 Rational64::from(0),
1219 Rational64::from(3),
1220 Rational64::from(0),
1221 ],
1222 ]);
1223 let sparse = SparseMatrix::from_dense(&dense);
1224 let v = vec![
1225 Rational64::from(1),
1226 Rational64::from(2),
1227 Rational64::from(3),
1228 ];
1229 let result = sparse.mul_vec(&v);
1230 assert_eq!(result[0], Rational64::from(7)); assert_eq!(result[1], Rational64::from(6)); }
1233
1234 #[test]
1235 fn test_sparse_transpose() {
1236 let dense = Matrix::from_rows(vec![
1237 vec![Rational64::from(1), Rational64::from(2)],
1238 vec![Rational64::from(3), Rational64::from(4)],
1239 vec![Rational64::from(5), Rational64::from(6)],
1240 ]);
1241 let sparse = SparseMatrix::from_dense(&dense);
1242 let transposed = sparse.transpose();
1243 assert_eq!(transposed.nrows(), 2);
1244 assert_eq!(transposed.ncols(), 3);
1245 assert_eq!(transposed.get(0, 0), Rational64::from(1));
1246 assert_eq!(transposed.get(0, 1), Rational64::from(3));
1247 assert_eq!(transposed.get(0, 2), Rational64::from(5));
1248 assert_eq!(transposed.get(1, 0), Rational64::from(2));
1249 assert_eq!(transposed.get(1, 1), Rational64::from(4));
1250 assert_eq!(transposed.get(1, 2), Rational64::from(6));
1251 }
1252
1253 #[test]
1254 fn test_sparse_mul() {
1255 let a_dense = Matrix::from_rows(vec![
1256 vec![Rational64::from(1), Rational64::from(2)],
1257 vec![Rational64::from(3), Rational64::from(4)],
1258 ]);
1259 let b_dense = Matrix::from_rows(vec![
1260 vec![Rational64::from(5), Rational64::from(6)],
1261 vec![Rational64::from(7), Rational64::from(8)],
1262 ]);
1263 let a = SparseMatrix::from_dense(&a_dense);
1264 let b = SparseMatrix::from_dense(&b_dense);
1265 let c = a.mul(&b);
1266 let c_dense = c.to_dense();
1267 assert_eq!(c_dense.get(0, 0), Rational64::from(19)); assert_eq!(c_dense.get(0, 1), Rational64::from(22)); assert_eq!(c_dense.get(1, 0), Rational64::from(43)); assert_eq!(c_dense.get(1, 1), Rational64::from(50)); }
1272
1273 #[test]
1274 fn test_qr_decomposition_simple() {
1275 let m = Matrix::from_rows(vec![
1277 vec![Rational64::from(1), Rational64::from(1)],
1278 vec![Rational64::from(0), Rational64::from(1)],
1279 ]);
1280
1281 let (q, r) = m.qr_decomposition().expect("test operation should succeed");
1282
1283 let reconstructed = q.mul(&r);
1285
1286 for i in 0..m.nrows() {
1288 for j in 0..m.ncols() {
1289 assert_eq!(
1290 m.get(i, j),
1291 reconstructed.get(i, j),
1292 "QR reconstruction failed at ({}, {})",
1293 i,
1294 j
1295 );
1296 }
1297 }
1298 }
1299
1300 #[test]
1301 fn test_qr_decomposition_3x2() {
1302 let m = Matrix::from_rows(vec![
1304 vec![Rational64::from(1), Rational64::from(0)],
1305 vec![Rational64::from(1), Rational64::from(1)],
1306 vec![Rational64::from(0), Rational64::from(1)],
1307 ]);
1308
1309 let (q, r) = m.qr_decomposition().expect("test operation should succeed");
1310
1311 assert_eq!(q.nrows(), 3);
1313 assert_eq!(q.ncols(), 2);
1314 assert_eq!(r.nrows(), 2);
1315 assert_eq!(r.ncols(), 2);
1316
1317 let reconstructed = q.mul(&r);
1319 for i in 0..m.nrows() {
1320 for j in 0..m.ncols() {
1321 assert_eq!(m.get(i, j), reconstructed.get(i, j));
1322 }
1323 }
1324 }
1325
1326 #[test]
1327 fn test_cholesky_decomposition_simple() {
1328 let m = Matrix::from_rows(vec![
1341 vec![Rational64::from(4), Rational64::from(2)],
1342 vec![Rational64::from(2), Rational64::from(2)],
1343 ]);
1344
1345 let l = m
1346 .cholesky_decomposition()
1347 .expect("test operation should succeed");
1348
1349 let lt = l.transpose();
1351 let reconstructed = l.mul(<);
1352
1353 for i in 0..m.nrows() {
1354 for j in 0..m.ncols() {
1355 assert_eq!(m.get(i, j), reconstructed.get(i, j));
1356 }
1357 }
1358
1359 assert_eq!(l.get(0, 1), Rational64::from(0));
1361 }
1362
1363 #[test]
1364 fn test_cholesky_decomposition_identity() {
1365 let m = Matrix::identity(3);
1367 let l = m
1368 .cholesky_decomposition()
1369 .expect("test operation should succeed");
1370
1371 for i in 0..3 {
1372 for j in 0..3 {
1373 assert_eq!(l.get(i, j), m.get(i, j));
1374 }
1375 }
1376 }
1377
1378 #[test]
1379 fn test_cholesky_decomposition_3x3() {
1380 let m = Matrix::from_rows(vec![
1386 vec![
1387 Rational64::from(9),
1388 Rational64::from(3),
1389 Rational64::from(3),
1390 ],
1391 vec![
1392 Rational64::from(3),
1393 Rational64::from(5),
1394 Rational64::from(1),
1395 ],
1396 vec![
1397 Rational64::from(3),
1398 Rational64::from(1),
1399 Rational64::from(5),
1400 ],
1401 ]);
1402
1403 let l = m
1404 .cholesky_decomposition()
1405 .expect("test operation should succeed");
1406
1407 let lt = l.transpose();
1409 let reconstructed = l.mul(<);
1410
1411 for i in 0..m.nrows() {
1412 for j in 0..m.ncols() {
1413 assert_eq!(
1414 m.get(i, j),
1415 reconstructed.get(i, j),
1416 "Cholesky reconstruction failed at ({}, {})",
1417 i,
1418 j
1419 );
1420 }
1421 }
1422
1423 for i in 0..3 {
1425 for j in i + 1..3 {
1426 assert_eq!(l.get(i, j), Rational64::from(0));
1427 }
1428 }
1429 }
1430
1431 #[test]
1432 fn test_cholesky_decomposition_not_symmetric() {
1433 let m = Matrix::from_rows(vec![
1435 vec![Rational64::from(4), Rational64::from(1)],
1436 vec![Rational64::from(2), Rational64::from(3)],
1437 ]);
1438
1439 assert!(m.cholesky_decomposition().is_none());
1440 }
1441
1442 #[test]
1443 fn test_cholesky_decomposition_not_positive_definite() {
1444 let m = Matrix::from_rows(vec![
1446 vec![Rational64::from(1), Rational64::from(2)],
1447 vec![Rational64::from(2), Rational64::from(1)],
1448 ]);
1449
1450 assert!(m.cholesky_decomposition().is_none());
1452 }
1453
1454 #[test]
1455 fn test_rational_sqrt_perfect_squares() {
1456 assert_eq!(
1457 rational_sqrt(Rational64::from(0)),
1458 Some(Rational64::from(0))
1459 );
1460 assert_eq!(
1461 rational_sqrt(Rational64::from(1)),
1462 Some(Rational64::from(1))
1463 );
1464 assert_eq!(
1465 rational_sqrt(Rational64::from(4)),
1466 Some(Rational64::from(2))
1467 );
1468 assert_eq!(
1469 rational_sqrt(Rational64::from(9)),
1470 Some(Rational64::from(3))
1471 );
1472 assert_eq!(
1473 rational_sqrt(Rational64::from(16)),
1474 Some(Rational64::from(4))
1475 );
1476 assert_eq!(
1477 rational_sqrt(Rational64::from(25)),
1478 Some(Rational64::from(5))
1479 );
1480
1481 assert_eq!(
1483 rational_sqrt(Rational64::new(1, 4)),
1484 Some(Rational64::new(1, 2))
1485 );
1486 assert_eq!(
1487 rational_sqrt(Rational64::new(9, 16)),
1488 Some(Rational64::new(3, 4))
1489 );
1490 }
1491
1492 #[test]
1493 fn test_rational_sqrt_not_perfect_squares() {
1494 assert!(rational_sqrt(Rational64::from(2)).is_none());
1495 assert!(rational_sqrt(Rational64::from(3)).is_none());
1496 assert!(rational_sqrt(Rational64::from(5)).is_none());
1497 assert!(rational_sqrt(Rational64::new(1, 2)).is_none());
1498 assert!(rational_sqrt(Rational64::new(2, 3)).is_none());
1499 assert!(rational_sqrt(Rational64::from(-1)).is_none()); }
1501
1502 #[test]
1503 fn test_matrix_inverse_2x2() {
1504 let a = Matrix::from_rows(vec![
1510 vec![Rational64::from(1), Rational64::from(2)],
1511 vec![Rational64::from(3), Rational64::from(4)],
1512 ]);
1513
1514 let a_inv = a.inverse().expect("test operation should succeed");
1515
1516 let product = a.mul(&a_inv);
1518 assert!(product.is_identity(), "A * A^-1 should be identity");
1519
1520 let product2 = a_inv.mul(&a);
1522 assert!(product2.is_identity(), "A^-1 * A should be identity");
1523 }
1524
1525 #[test]
1526 fn test_matrix_inverse_identity() {
1527 let i = Matrix::identity(3);
1529 let i_inv = i.inverse().expect("test operation should succeed");
1530 assert!(i_inv.is_identity());
1531 }
1532
1533 #[test]
1534 fn test_matrix_inverse_3x3() {
1535 let a = Matrix::from_rows(vec![
1537 vec![
1538 Rational64::from(2),
1539 Rational64::from(1),
1540 Rational64::from(1),
1541 ],
1542 vec![
1543 Rational64::from(1),
1544 Rational64::from(2),
1545 Rational64::from(1),
1546 ],
1547 vec![
1548 Rational64::from(1),
1549 Rational64::from(1),
1550 Rational64::from(2),
1551 ],
1552 ]);
1553
1554 let a_inv = a.inverse().expect("test operation should succeed");
1555
1556 let product = a.mul(&a_inv);
1558 for i in 0..3 {
1559 for j in 0..3 {
1560 let expected = if i == j {
1561 Rational64::from(1)
1562 } else {
1563 Rational64::from(0)
1564 };
1565 assert_eq!(
1566 product.get(i, j),
1567 expected,
1568 "A * A^-1 failed at ({}, {})",
1569 i,
1570 j
1571 );
1572 }
1573 }
1574 }
1575
1576 #[test]
1577 fn test_matrix_inverse_singular() {
1578 let a = Matrix::from_rows(vec![
1580 vec![Rational64::from(1), Rational64::from(2)],
1581 vec![Rational64::from(2), Rational64::from(4)], ]);
1583
1584 assert!(
1585 a.inverse().is_none(),
1586 "Singular matrix should not have inverse"
1587 );
1588 }
1589
1590 #[test]
1591 fn test_matrix_inverse_non_square() {
1592 let a = Matrix::from_rows(vec![
1594 vec![
1595 Rational64::from(1),
1596 Rational64::from(2),
1597 Rational64::from(3),
1598 ],
1599 vec![
1600 Rational64::from(4),
1601 Rational64::from(5),
1602 Rational64::from(6),
1603 ],
1604 ]);
1605
1606 assert!(
1607 a.inverse().is_none(),
1608 "Non-square matrix should not have inverse"
1609 );
1610 }
1611
1612 #[test]
1613 fn test_is_identity() {
1614 let i = Matrix::identity(3);
1615 assert!(i.is_identity());
1616
1617 let not_i = Matrix::from_rows(vec![
1618 vec![Rational64::from(1), Rational64::from(0)],
1619 vec![Rational64::from(0), Rational64::from(2)],
1620 ]);
1621 assert!(!not_i.is_identity());
1622
1623 let also_not_i = Matrix::from_rows(vec![
1624 vec![Rational64::from(1), Rational64::from(1)],
1625 vec![Rational64::from(0), Rational64::from(1)],
1626 ]);
1627 assert!(!also_not_i.is_identity());
1628 }
1629}