numerical_linear_algebra/
matrix.rs1use std::{
2 fmt::Display,
3 iter::Sum,
4 ops::{Add, AddAssign, Index, IndexMut, Mul, MulAssign, Sub, SubAssign},
5};
6
7use num_complex::{Complex64, ComplexFloat};
8use rayon::iter::{IntoParallelRefMutIterator, ParallelBridge, ParallelIterator};
9use tabled::{
10 builder::Builder,
11 settings::{Margin, Style},
12};
13
14use crate::Vector;
15
16#[derive(Debug, Clone)]
17pub struct Matrix<const M: usize, const N: usize> {
18 data: [[Complex64; N]; M],
19}
20
21impl<const M: usize, const N: usize> Index<[usize; 2]> for Matrix<M, N> {
22 type Output = Complex64;
23
24 fn index(&self, [i, j]: [usize; 2]) -> &Self::Output {
25 &self.data[i][j]
26 }
27}
28
29impl<const M: usize, const N: usize> IndexMut<[usize; 2]> for Matrix<M, N> {
30 fn index_mut(&mut self, index: [usize; 2]) -> &mut Self::Output {
31 &mut self.data[index[0]][index[1]]
32 }
33}
34
35impl<const M: usize, const N: usize> AddAssign for Matrix<M, N> {
36 fn add_assign(&mut self, rhs: Self) {
37 self.iter_mut()
38 .zip(rhs.iter())
39 .par_bridge()
40 .for_each(|(left, right)| *left += right);
41 }
42}
43
44impl<const M: usize, const N: usize> Add for Matrix<M, N> {
45 type Output = Matrix<M, N>;
46
47 fn add(self, rhs: Self) -> Self::Output {
48 let mut result = self.clone();
49 result += rhs;
50 result
51 }
52}
53
54impl<const M: usize, const N: usize> Sum for Matrix<M, N> {
55 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
56 iter.reduce(|acc, x| acc + x).unwrap_or(Matrix::ZEROS)
57 }
58}
59
60impl<const M: usize, const N: usize> SubAssign for Matrix<M, N> {
61 fn sub_assign(&mut self, rhs: Self) {
62 self.iter_mut()
63 .zip(rhs.iter())
64 .par_bridge()
65 .for_each(|(left, right)| *left -= right);
66 }
67}
68
69impl<const M: usize, const N: usize> Sub for Matrix<M, N> {
70 type Output = Matrix<M, N>;
71
72 fn sub(self, rhs: Self) -> Self::Output {
73 let mut result = self.clone();
74 result -= rhs;
75 result
76 }
77}
78
79impl<const M: usize, const N: usize, const P: usize> Mul<Matrix<N, P>> for Matrix<M, N> {
80 type Output = Matrix<M, P>;
81
82 fn mul(self, rhs: Matrix<N, P>) -> Self::Output {
83 let mut result = Matrix::ZEROS;
84
85 for i in 0..M {
86 for j in 0..P {
87 for k in 0..N {
88 result[[i, j]] += self[[i, k]] * rhs[[k, j]];
89 }
90 }
91 }
92
93 result
94 }
95}
96
97impl<const M: usize, const N: usize> MulAssign<Complex64> for Matrix<M, N> {
98 fn mul_assign(&mut self, rhs: Complex64) {
99 self.iter_mut().par_bridge().for_each(|x| *x *= rhs);
100 }
101}
102
103impl<const M: usize, const N: usize> Mul<Complex64> for Matrix<M, N> {
104 type Output = Matrix<M, N>;
105
106 fn mul(self, rhs: Complex64) -> Self::Output {
107 let mut result = self.clone();
108 result *= rhs;
109 result
110 }
111}
112
113impl<const M: usize, const N: usize> Mul<Matrix<M, N>> for Complex64 {
114 type Output = Matrix<M, N>;
115
116 fn mul(self, rhs: Matrix<M, N>) -> Self::Output {
117 rhs * self
118 }
119}
120
121impl<const M: usize, const N: usize> PartialEq for Matrix<M, N> {
122 fn eq(&self, other: &Self) -> bool {
123 let epsilon = self.epsilon();
124 self.iter()
125 .zip(other.iter())
126 .par_bridge()
127 .all(|(a, b)| (a - b).abs() < epsilon)
128 }
129}
130
131impl<const M: usize, const N: usize> Matrix<M, N> {
132 #[inline]
133 pub const fn new(data: [[Complex64; N]; M]) -> Self {
134 Self { data }
135 }
136
137 pub fn from_column_vectors(columns: [Vector<M>; N]) -> Self {
138 Matrix::<N, M>::new(columns.map(|column| column.transpose().data[0])).transpose()
139 }
140
141 pub fn to_column_vectors(self) -> [Vector<M>; N] {
142 self.transpose()
143 .data
144 .map(|row| Matrix::new([row]).transpose())
145 }
146
147 pub const ZEROS: Self = Matrix::new([[Complex64::ZERO; N]; M]);
148
149 pub fn iter(&self) -> impl Iterator<Item = &Complex64> {
150 self.data.iter().flatten()
151 }
152
153 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Complex64> {
154 self.data.iter_mut().flatten()
155 }
156
157 pub fn epsilon(&self) -> f64 {
158 self.iter()
159 .par_bridge()
160 .map(|x| x.abs())
161 .max_by(|a, b| a.total_cmp(b))
162 .unwrap()
163 * self.data.len().max(self.data[0].len()) as f64
164 * f64::EPSILON
165 }
166
167 pub fn swap_row(&mut self, i: usize, j: usize) {
168 self.data.swap(i, j);
169 }
170
171 pub fn multiply_row_by_scalar(&mut self, i: usize, scalar: Complex64) {
172 self.data[i]
173 .par_iter_mut()
174 .for_each(|entry| *entry *= scalar);
175 }
176
177 pub fn add_row_with_scalar(&mut self, i: usize, j: usize, scalar: Complex64) {
178 for k in 0..N {
179 let value = scalar * self[[i, k]];
180 self[[j, k]] += value;
181 }
182 }
183
184 pub fn transpose(&self) -> Matrix<N, M> {
185 let mut result = Matrix::ZEROS;
186
187 for i in 0..M {
188 for j in 0..N {
189 result[[j, i]] = self[[i, j]];
190 }
191 }
192
193 result
194 }
195
196 pub fn gaussian_elimination(self) -> (Matrix<M, N>, Matrix<M, M>, usize) {
197 let mut row_echelon_matrix = self;
198 let mut operation_matrix = Matrix::<M, M>::identity();
199 let mut row_swap_count = 0;
200 let size = M.min(N);
201
202 for i in 0..size {
203 let max_entry_index = (i..size)
204 .max_by(|&j, &k| {
205 row_echelon_matrix[[j, i]]
206 .abs()
207 .total_cmp(&row_echelon_matrix[[k, i]].abs())
208 })
209 .unwrap();
210 if row_echelon_matrix[[max_entry_index, i]] == Complex64::ZERO {
211 continue;
212 }
213 if max_entry_index != i {
214 row_echelon_matrix.swap_row(i, max_entry_index);
215 operation_matrix.swap_row(i, max_entry_index);
216 row_swap_count += 1;
217 }
218
219 for j in i + 1..size {
220 let scalar = -row_echelon_matrix[[j, i]] / row_echelon_matrix[[i, i]];
221 row_echelon_matrix.add_row_with_scalar(i, j, scalar);
222 operation_matrix.add_row_with_scalar(i, j, scalar);
223 }
224 }
225
226 (row_echelon_matrix, operation_matrix, row_swap_count)
227 }
228
229 pub fn rank(&self) -> usize {
230 let epsilon = self.epsilon();
231 let (row_echelon_matrix, _, _) = self.clone().gaussian_elimination();
232 row_echelon_matrix
233 .data
234 .iter()
235 .map(|row| row.iter().any(|&entry| entry.abs() >= epsilon))
236 .filter(|&x| x)
237 .count()
238 }
239}
240
241impl<const M: usize> Matrix<M, M> {
242 pub fn diagonal(data: [Complex64; M]) -> Self {
243 let mut result = Matrix::ZEROS;
244 for i in 0..M {
245 result[[i, i]] = data[i];
246 }
247
248 result
249 }
250
251 pub fn identity() -> Self {
252 Self::diagonal([Complex64::ONE; M])
253 }
254
255 pub fn determinant(&self) -> Complex64 {
256 let (row_echelon_matrix, _, row_swap_count) = self.clone().gaussian_elimination();
257 let sign = if row_swap_count % 2 == 0 { 1. } else { -1. };
258 sign * (0..M)
259 .map(|i| row_echelon_matrix[[i, i]])
260 .product::<Complex64>()
261 }
262
263 pub fn gauss_jordan_elimination(self) -> (Matrix<M, M>, Matrix<M, M>) {
264 let epsilon = self.epsilon();
265 let (mut reduced_row_echelon_matrix, mut operation_matrix, _) = self.gaussian_elimination();
266
267 for i in 1..M {
268 if reduced_row_echelon_matrix[[i, i]].abs() <= epsilon {
269 continue;
270 }
271 for j in 0..=i - 1 {
272 let scalar =
273 -reduced_row_echelon_matrix[[j, i]] / reduced_row_echelon_matrix[[i, i]];
274
275 reduced_row_echelon_matrix.add_row_with_scalar(i, j, scalar);
276 operation_matrix.add_row_with_scalar(i, j, scalar);
277 }
278 }
279
280 for i in 0..M {
281 if reduced_row_echelon_matrix[[i, i]].abs() <= epsilon {
282 continue;
283 }
284
285 let scalar = 1. / reduced_row_echelon_matrix[[i, i]];
286 reduced_row_echelon_matrix.multiply_row_by_scalar(i, scalar);
287 operation_matrix.multiply_row_by_scalar(i, scalar);
288 }
289
290 (reduced_row_echelon_matrix, operation_matrix)
291 }
292
293 pub fn inverse(self) -> Option<Matrix<M, M>> {
294 let (reduced_row_echelon_matrix, operation_matrix) = self.gauss_jordan_elimination();
295 if reduced_row_echelon_matrix == Matrix::<M, M>::identity() {
296 Some(operation_matrix)
297 } else {
298 None
299 }
300 }
301
302 pub fn qr_decomposition(self) -> (Matrix<M, M>, Matrix<M, M>) {
303 let q = Matrix::from_column_vectors(Vector::<M>::gram_schimidt_process(
304 self.clone().to_column_vectors(),
305 ));
306 let r = q.transpose() * self;
307
308 (q, r)
309 }
310
311 pub fn schur_form(self, iterations: usize) -> Matrix<M, M> {
312 let mut result = self;
313
314 for _ in 0..iterations {
315 let (q, r) = result.qr_decomposition();
316 result = r * q;
317 }
318
319 result
320 }
321
322 pub fn eigenvalues(&self, iterations: usize) -> [Complex64; M] {
323 let schur_form = self.clone().schur_form(iterations);
324 let mut eigenvalues: [Complex64; M] = (0..M)
325 .map(|i| schur_form[[i, i]])
326 .collect::<Vec<_>>()
327 .try_into()
328 .unwrap();
329
330 eigenvalues.sort_by(|a, b| b.re().total_cmp(&a.abs()));
331
332 eigenvalues
333 }
334
335 pub fn eigenvectors(&self, iterations: usize) -> [Vector<M>; M] {
336 (0..iterations)
337 .fold(self.clone().qr_decomposition().0, |q, _| {
338 (self.clone() * q).qr_decomposition().0
339 })
340 .to_column_vectors()
341 }
342}
343
344impl<const M: usize, const N: usize> Display for Matrix<M, N> {
345 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
346 let mut table_builder = Builder::default();
347 for i in 0..M {
348 table_builder.push_record(self.data[i].iter().map(|x| x.to_string()));
349 }
350 let mut table = table_builder.build();
351 table.with(Style::blank());
352 table.with(Margin::new(4, 0, 0, 0));
353
354 write!(f, "[\n{}\n]", table)
355 }
356}