mathhook_core/matrices/
decomposition.rs

1//! Matrix decomposition algorithms
2//!
3//! This module provides various matrix decomposition methods including LU, QR,
4//! Cholesky, and SVD decompositions optimized for the unified matrix system.
5
6pub mod cholesky;
7pub mod decomposition_tests;
8pub mod lu;
9pub mod qr;
10pub mod svd;
11use crate::core::Expression;
12use crate::matrices::types::*;
13use crate::matrices::unified::Matrix;
14
15/// Matrix decomposition operations trait
16///
17/// This trait provides a unified interface for all matrix decomposition methods.
18/// Each method returns an Option to handle cases where decomposition is not possible.
19pub trait MatrixDecomposition {
20    /// Perform LU decomposition with partial pivoting
21    ///
22    /// Decomposes matrix A into PA = LU where:
23    /// - P is a permutation matrix
24    /// - L is lower triangular with 1s on diagonal
25    /// - U is upper triangular
26    ///
27    /// # Examples
28    ///
29    /// ```rust
30    /// use mathhook_core::matrices::{Matrix, MatrixDecomposition};
31    /// use mathhook_core::Expression;
32    ///
33    /// let matrix = Matrix::from_arrays([
34    ///     [2, 1, 1],
35    ///     [4, 3, 3],
36    ///     [8, 7, 9]
37    /// ]);
38    ///
39    /// let lu = matrix.lu_decomposition().unwrap();
40    /// // Verify: P*A = L*U
41    /// ```
42    fn lu_decomposition(&self) -> Option<LUDecomposition>;
43
44    /// Perform QR decomposition using Gram-Schmidt process
45    ///
46    /// Decomposes matrix A into A = QR where:
47    /// - Q is orthogonal (Q^T * Q = I)
48    /// - R is upper triangular
49    ///
50    /// # Examples
51    ///
52    /// ```rust
53    /// use mathhook_core::matrices::{Matrix, MatrixDecomposition};
54    ///
55    /// let matrix = Matrix::from_arrays([
56    ///     [1, 1, 0],
57    ///     [1, 0, 1],
58    ///     [0, 1, 1]
59    /// ]);
60    ///
61    /// let qr = matrix.qr_decomposition().unwrap();
62    /// // Verify: A = Q*R and Q^T*Q = I
63    /// ```
64    fn qr_decomposition(&self) -> Option<QRDecomposition>;
65
66    /// Perform Cholesky decomposition for positive definite matrices
67    ///
68    /// Decomposes symmetric positive definite matrix A into A = LL^T where:
69    /// - L is lower triangular with positive diagonal elements
70    ///
71    /// # Examples
72    ///
73    /// ```rust
74    /// use mathhook_core::matrices::{Matrix, MatrixDecomposition};
75    ///
76    /// let matrix = Matrix::from_arrays([
77    ///     [4, 2, 1],
78    ///     [2, 3, 0],
79    ///     [1, 0, 2]
80    /// ]);
81    ///
82    /// if let Some(chol) = matrix.cholesky_decomposition() {
83    ///     // Verify: A = L*L^T
84    /// }
85    /// ```
86    fn cholesky_decomposition(&self) -> Option<CholeskyDecomposition>;
87
88    /// Perform Singular Value Decomposition
89    ///
90    /// Decomposes matrix A into A = UΣV^T where:
91    /// - U contains left singular vectors (orthogonal)
92    /// - Σ contains singular values (diagonal, non-negative)
93    /// - V^T contains right singular vectors (orthogonal)
94    ///
95    /// # Examples
96    ///
97    /// ```rust
98    /// use mathhook_core::matrices::{Matrix, MatrixDecomposition};
99    ///
100    /// let matrix = Matrix::from_arrays([
101    ///     [1, 2],
102    ///     [3, 4],
103    ///     [5, 6]
104    /// ]);
105    ///
106    /// let svd = matrix.svd_decomposition().unwrap();
107    /// // Verify: A = U*Σ*V^T
108    /// ```
109    fn svd_decomposition(&self) -> Option<SVDDecomposition>;
110
111    /// Get matrix rank using SVD
112    fn rank(&self) -> usize;
113
114    /// Check if matrix is positive definite
115    fn is_positive_definite(&self) -> bool;
116
117    /// Get condition number (ratio of largest to smallest singular value)
118    fn condition_number(&self) -> Expression;
119}
120
121/// Implementation of MatrixDecomposition trait for Matrix
122impl MatrixDecomposition for Matrix {
123    fn lu_decomposition(&self) -> Option<LUDecomposition> {
124        // Delegate to lu module implementation
125        self.lu_decomposition()
126    }
127
128    fn qr_decomposition(&self) -> Option<QRDecomposition> {
129        // Delegate to qr module implementation
130        self.qr_decomposition()
131    }
132
133    fn cholesky_decomposition(&self) -> Option<CholeskyDecomposition> {
134        // Delegate to cholesky module implementation
135        self.cholesky_decomposition()
136    }
137
138    fn svd_decomposition(&self) -> Option<SVDDecomposition> {
139        // Delegate to svd module implementation
140        self.svd_decomposition()
141    }
142
143    fn rank(&self) -> usize {
144        // Delegate to svd module implementation
145        self.rank_via_svd()
146    }
147
148    fn is_positive_definite(&self) -> bool {
149        // Delegate to cholesky module implementation
150        self.is_positive_definite_cholesky()
151    }
152
153    fn condition_number(&self) -> Expression {
154        // Delegate to svd module implementation
155        self.condition_number_via_svd()
156    }
157}
158
159/// Helper methods for matrix operations
160impl Matrix {
161    /// Convert any matrix to dense representation
162    pub(crate) fn to_dense_matrix(&self) -> Matrix {
163        let (rows, cols) = self.dimensions();
164        let mut dense_rows = Vec::with_capacity(rows);
165
166        for i in 0..rows {
167            let mut row = Vec::with_capacity(cols);
168            for j in 0..cols {
169                row.push(self.get_element(i, j));
170            }
171            dense_rows.push(row);
172        }
173
174        Matrix::dense(dense_rows)
175    }
176
177    /// Swap two rows in a matrix
178    pub(crate) fn swap_rows(&self, row1: usize, row2: usize) -> Matrix {
179        if row1 == row2 {
180            return self.clone();
181        }
182
183        let (rows, cols) = self.dimensions();
184        let mut new_rows = Vec::with_capacity(rows);
185
186        for i in 0..rows {
187            let mut row = Vec::with_capacity(cols);
188            let source_row = if i == row1 {
189                row2
190            } else if i == row2 {
191                row1
192            } else {
193                i
194            };
195
196            for j in 0..cols {
197                row.push(self.get_element(source_row, j));
198            }
199            new_rows.push(row);
200        }
201
202        Matrix::dense(new_rows)
203    }
204
205    /// Set element at position (i, j) - returns new matrix
206    pub(crate) fn set_element(&self, i: usize, j: usize, value: &Expression) -> Matrix {
207        let (rows, cols) = self.dimensions();
208        let mut new_rows = Vec::with_capacity(rows);
209
210        for row_idx in 0..rows {
211            let mut row = Vec::with_capacity(cols);
212            for col_idx in 0..cols {
213                if row_idx == i && col_idx == j {
214                    row.push(value.clone());
215                } else {
216                    row.push(self.get_element(row_idx, col_idx));
217                }
218            }
219            new_rows.push(row);
220        }
221
222        Matrix::dense(new_rows)
223    }
224}