advanced_algorithms/numerical/
qr_decomposition.rs1use crate::{AlgorithmError, Result};
27
28pub struct QRDecomposition {
32 q: Vec<Vec<f64>>,
33 r: Vec<Vec<f64>>,
34}
35
36impl QRDecomposition {
37 pub fn decompose(matrix: &[Vec<f64>]) -> Result<Self> {
54 if matrix.is_empty() || matrix[0].is_empty() {
55 return Err(AlgorithmError::InvalidInput(
56 "Matrix cannot be empty".to_string()
57 ));
58 }
59
60 let m = matrix.len();
61 let n = matrix[0].len();
62
63 for row in matrix.iter() {
65 if row.len() != n {
66 return Err(AlgorithmError::InvalidInput(
67 "All rows must have the same length".to_string()
68 ));
69 }
70 }
71
72 if m < n {
73 return Err(AlgorithmError::InvalidInput(
74 "Matrix must have at least as many rows as columns".to_string()
75 ));
76 }
77
78 let mut r = matrix.to_vec();
80
81 let mut q = identity_matrix(m);
83
84 for k in 0..n.min(m - 1) {
86 let h = householder_matrix(&r, k, m)?;
87 r = matrix_multiply(&h, &r)?;
88 q = matrix_multiply(&h, &q)?;
89 }
90
91 q = transpose(&q);
93
94 Ok(QRDecomposition { q, r })
95 }
96
97 pub fn q(&self) -> &[Vec<f64>] {
99 &self.q
100 }
101
102 pub fn r(&self) -> &[Vec<f64>] {
104 &self.r
105 }
106
107 pub fn solve(&self, b: &[f64]) -> Result<Vec<f64>> {
117 if b.len() != self.q.len() {
118 return Err(AlgorithmError::DimensionMismatch(
119 format!("Vector length {} doesn't match matrix dimension {}",
120 b.len(), self.q.len())
121 ));
122 }
123
124 let qt_b = matrix_vector_multiply(&transpose(&self.q), b)?;
126
127 back_substitution(&self.r, &qt_b)
129 }
130}
131
132fn householder_matrix(a: &[Vec<f64>], k: usize, m: usize) -> Result<Vec<Vec<f64>>> {
134 let _n = a[0].len();
135
136 let mut x: Vec<f64> = (k..m).map(|i| a[i][k]).collect();
138
139 let norm_x: f64 = x.iter().map(|&val| val * val).sum::<f64>().sqrt();
141
142 if norm_x < 1e-10 {
143 return Ok(identity_matrix(m));
145 }
146
147 x[0] += if x[0] >= 0.0 { norm_x } else { -norm_x };
149 let norm_v: f64 = x.iter().map(|&val| val * val).sum::<f64>().sqrt();
150
151 if norm_v < 1e-10 {
152 return Ok(identity_matrix(m));
153 }
154
155 for val in x.iter_mut() {
157 *val /= norm_v;
158 }
159
160 let mut h = identity_matrix(m);
162
163 for i in 0..(m - k) {
164 for j in 0..(m - k) {
165 h[i + k][j + k] -= 2.0 * x[i] * x[j];
166 }
167 }
168
169 Ok(h)
170}
171
172fn identity_matrix(n: usize) -> Vec<Vec<f64>> {
174 let mut matrix = vec![vec![0.0; n]; n];
175 for (i, row) in matrix.iter_mut().enumerate() {
176 row[i] = 1.0;
177 }
178 matrix
179}
180
181fn matrix_multiply(a: &[Vec<f64>], b: &[Vec<f64>]) -> Result<Vec<Vec<f64>>> {
183 let m = a.len();
184 let n = b[0].len();
185 let p = a[0].len();
186
187 if p != b.len() {
188 return Err(AlgorithmError::DimensionMismatch(
189 "Matrix dimensions incompatible for multiplication".to_string()
190 ));
191 }
192
193 let mut result = vec![vec![0.0; n]; m];
194
195 for i in 0..m {
196 for j in 0..n {
197 for k in 0..p {
198 result[i][j] += a[i][k] * b[k][j];
199 }
200 }
201 }
202
203 Ok(result)
204}
205
206fn matrix_vector_multiply(a: &[Vec<f64>], b: &[f64]) -> Result<Vec<f64>> {
208 let m = a.len();
209 let n = a[0].len();
210
211 if n != b.len() {
212 return Err(AlgorithmError::DimensionMismatch(
213 "Matrix and vector dimensions incompatible".to_string()
214 ));
215 }
216
217 let mut result = vec![0.0; m];
218
219 for i in 0..m {
220 for j in 0..n {
221 result[i] += a[i][j] * b[j];
222 }
223 }
224
225 Ok(result)
226}
227
228fn transpose(matrix: &[Vec<f64>]) -> Vec<Vec<f64>> {
230 let m = matrix.len();
231 let n = matrix[0].len();
232
233 let mut result = vec![vec![0.0; m]; n];
234
235 for i in 0..m {
236 for j in 0..n {
237 result[j][i] = matrix[i][j];
238 }
239 }
240
241 result
242}
243
244fn back_substitution(r: &[Vec<f64>], b: &[f64]) -> Result<Vec<f64>> {
246 let n = r[0].len();
247 let mut x = vec![0.0; n];
248
249 for i in (0..n).rev() {
250 if r[i][i].abs() < 1e-10 {
251 return Err(AlgorithmError::NumericalInstability(
252 "Matrix is singular or nearly singular".to_string()
253 ));
254 }
255
256 let mut sum = b[i];
257 for j in (i + 1)..n {
258 sum -= r[i][j] * x[j];
259 }
260 x[i] = sum / r[i][i];
261 }
262
263 Ok(x)
264}
265
266#[cfg(test)]
267mod tests {
268 use super::*;
269
270 #[test]
271 fn test_qr_decomposition() {
272 let matrix = vec![
273 vec![12.0, -51.0, 4.0],
274 vec![6.0, 167.0, -68.0],
275 vec![-4.0, 24.0, -41.0],
276 ];
277
278 let qr = QRDecomposition::decompose(&matrix).unwrap();
279 let q = qr.q();
280 let r = qr.r();
281
282 let qt = transpose(q);
284 let qtq = matrix_multiply(&qt, q).unwrap();
285
286 for i in 0..3 {
287 for j in 0..3 {
288 let expected = if i == j { 1.0 } else { 0.0 };
289 assert!((qtq[i][j] - expected).abs() < 1e-10);
290 }
291 }
292
293 for i in 0..3 {
295 for j in 0..i {
296 assert!(r[i][j].abs() < 1e-10);
297 }
298 }
299 }
300
301 #[test]
302 fn test_solve() {
303 let matrix = vec![
304 vec![1.0, 2.0],
305 vec![3.0, 4.0],
306 vec![5.0, 6.0],
307 ];
308
309 let b = vec![1.0, 2.0, 3.0];
310
311 let qr = QRDecomposition::decompose(&matrix).unwrap();
312 let x = qr.solve(&b).unwrap();
313
314 assert_eq!(x.len(), 2);
316 }
317}