1use std::cmp::min;
2
3use crate::algorithms::matmul::ComputeInnerProduct;
4use crate::field::{Field, FieldStore};
5use crate::integer::*;
6use crate::matrix::*;
7use crate::ring::*;
8use crate::rings::approx_real::{ApproxRealField, SqrtRing};
9use crate::rings::rational::*;
10
11#[stability::unstable(feature = "enable")]
12pub trait QRDecompositionField: Field {
13 fn scaled_qr_decomposition<V1, V2>(
34 &self,
35 matrix: SubmatrixMut<V1, Self::Element>,
36 q: SubmatrixMut<V2, Self::Element>,
37 ) -> Vec<Self::Element>
38 where
39 V1: AsPointerToSlice<Self::Element>,
40 V2: AsPointerToSlice<Self::Element>;
41
42 fn ldl_decomposition<V>(&self, matrix: SubmatrixMut<V, Self::Element>) -> Vec<Self::Element>
57 where
58 V: AsPointerToSlice<Self::Element>,
59 {
60 ldl_decomposition_impl(RingRef::new(self), matrix)
61 }
62
63 fn qr_decomposition<V1, V2>(
71 &self,
72 mut matrix: SubmatrixMut<V1, Self::Element>,
73 mut q: SubmatrixMut<V2, Self::Element>,
74 ) where
75 V1: AsPointerToSlice<Self::Element>,
76 V2: AsPointerToSlice<Self::Element>,
77 Self: SqrtRing,
78 {
79 let d = self.scaled_qr_decomposition(matrix.reborrow(), q.reborrow());
80 for (i, scale_sqr) in d.into_iter().enumerate() {
81 let scale = self.sqrt(scale_sqr);
82 let scale_inv = self.div(&self.one(), &scale);
83 for j in 0..matrix.col_count() {
84 self.mul_assign_ref(matrix.at_mut(i, j), &scale);
85 }
86 for k in 0..q.row_count() {
87 self.mul_assign_ref(q.at_mut(k, i), &scale_inv);
88 }
89 }
90 }
91}
92
93impl<I> QRDecompositionField for RationalFieldBase<I>
94where
95 I: RingStore,
96 I::Type: IntegerRing,
97{
98 fn scaled_qr_decomposition<V1, V2>(
99 &self,
100 mut matrix: SubmatrixMut<V1, Self::Element>,
101 mut q: SubmatrixMut<V2, Self::Element>,
102 ) -> Vec<Self::Element>
103 where
104 V1: AsPointerToSlice<Self::Element>,
105 V2: AsPointerToSlice<Self::Element>,
106 {
107 let ring = RingValue::from_ref(self);
109 let m = matrix.row_count();
110 let n = matrix.col_count();
111 assert_eq!(m, q.row_count());
112 assert_eq!(m, q.col_count());
113
114 let mut result = Vec::with_capacity(n);
115 let mut mus = Vec::with_capacity(n);
116 for i in 0..n {
117 mus.clear();
118 for j in 0..i {
119 mus.push(self.div(
120 &<_ as ComputeInnerProduct>::inner_product_ref(self, (0..m).map(|k| (matrix.at(k, i), q.at(k, j)))),
121 &result[j],
122 ));
123 }
124 let (mut target, orthogonalized) = q.reborrow().split_cols(i..(i + 1), 0..i);
125 for k in 0..m {
126 *target.at_mut(k, 0) = self.sub_ref_fst(
127 matrix.at(k, i),
128 <_ as ComputeInnerProduct>::inner_product_ref(
129 self,
130 (0..i).map(|j| (&mus[j], orthogonalized.at(k, j))),
131 ),
132 );
133 }
134 result.push(<_ as RingStore>::sum(
135 ring,
136 (0..m).map(|k| ring.pow(ring.clone_el(target.at(k, 0)), 2)),
137 ));
138 for (k, c) in mus.drain(..).enumerate() {
139 *matrix.at_mut(k, i) = c;
140 }
141 *matrix.at_mut(i, i) = self.one();
142 for k in (i + 1)..m {
143 *matrix.at_mut(k, i) = self.zero();
144 }
145 }
146
147 return result;
148 }
149}
150
151fn ldl_decomposition_impl<R, V>(ring: R, mut matrix: SubmatrixMut<V, El<R>>) -> Vec<El<R>>
152where
153 R: RingStore,
154 R::Type: Field,
155 V: AsPointerToSlice<El<R>>,
156{
157 assert_eq!(matrix.row_count(), matrix.col_count());
158 let n = matrix.row_count();
159 let mut result = Vec::with_capacity(n);
160 for i in 0..n {
161 let pivot = ring.clone_el(matrix.at(i, i));
162 if !ring.get_ring().is_approximate() && ring.is_zero(&pivot) {
163 panic!("matrix is singular")
164 }
165 let pivot_inv = ring.div(&ring.one(), matrix.at(i, i));
166 for j in i..n {
167 ring.mul_assign_ref(matrix.at_mut(j, i), &pivot_inv);
168 }
169 for k in (i + 1)..n {
170 for l in k..n {
171 let subtract = ring.mul_ref_snd(
172 ring.mul_ref(matrix.as_const().at(k, i), matrix.as_const().at(l, i)),
173 &pivot,
174 );
175 ring.sub_assign(matrix.at_mut(l, k), subtract);
176 }
177 }
178 result.push(pivot);
179 }
180 for i in 0..n {
181 for j in (i + 1)..n {
182 *matrix.at_mut(i, j) = ring.zero();
183 }
184 }
185 return result;
186}
187
188impl<R: ApproxRealField + SqrtRing> QRDecompositionField for R {
189 default fn scaled_qr_decomposition<V1, V2>(
190 &self,
191 mut matrix: SubmatrixMut<V1, Self::Element>,
192 mut q: SubmatrixMut<V2, Self::Element>,
193 ) -> Vec<Self::Element>
194 where
195 V1: AsPointerToSlice<Self::Element>,
196 V2: AsPointerToSlice<Self::Element>,
197 {
198 self.qr_decomposition(matrix.reborrow(), q.reborrow());
199 let mut result = Vec::with_capacity(matrix.row_count());
200 for i in 0..matrix.row_count() {
201 let mut scale = self.clone_el(matrix.at(i, i));
202 let scale_inv = self.div(&self.one(), &scale);
203 for j in i..matrix.col_count() {
204 self.mul_assign_ref(matrix.at_mut(i, j), &scale_inv);
205 }
206 for j in 0..q.row_count() {
207 self.mul_assign_ref(q.at_mut(j, i), &scale);
208 }
209 self.square(&mut scale);
210 result.push(scale);
211 }
212 return result;
213 }
214
215 default fn ldl_decomposition<V>(&self, matrix: SubmatrixMut<V, Self::Element>) -> Vec<Self::Element>
216 where
217 V: AsPointerToSlice<Self::Element>,
218 {
219 ldl_decomposition_impl(RingRef::new(self), matrix)
220 }
221
222 default fn qr_decomposition<V1, V2>(
223 &self,
224 mut matrix: SubmatrixMut<V1, Self::Element>,
225 mut q: SubmatrixMut<V2, Self::Element>,
226 ) where
227 V1: AsPointerToSlice<Self::Element>,
228 V2: AsPointerToSlice<Self::Element>,
229 {
230 let ring = RingRef::new(self);
231 let m = matrix.row_count();
232 let n = matrix.col_count();
233 assert_eq!(m, q.row_count());
234 assert_eq!(m, q.col_count());
235 for i in 0..m {
236 for j in 0..m {
237 *q.at_mut(i, j) = if i == j { self.one() } else { self.zero() };
238 }
239 }
240
241 let mut householder_vector = Vec::with_capacity(m);
242 for i in 0..min(n, m) {
243 let norm_sqr = <_ as RingStore>::sum(&ring, (i..m).map(|k| ring.pow(ring.clone_el(matrix.at(k, i)), 2)));
244 let norm = self.sqrt(self.clone_el(&norm_sqr));
245 let alpha = if self.is_neg(matrix.at(i, i)) {
246 self.clone_el(&norm)
247 } else {
248 self.negate(self.clone_el(&norm))
249 };
250 let scale = self.sqrt(self.sub(norm_sqr, self.mul_ref(&alpha, matrix.at(i, i))));
252 householder_vector.clear();
253 householder_vector.extend((i..m).map(|k| ring.clone_el(matrix.at(k, i))));
254 ring.sub_assign_ref(&mut householder_vector[0], &alpha);
255 for x in &mut householder_vector {
256 *x = self.div(x, &scale);
257 }
258
259 let mut rest = matrix.reborrow().submatrix(i..m, (i + 1)..n);
261 for j in 0..(n - i - 1) {
262 let inner_product = <_ as ComputeInnerProduct>::inner_product_ref(
263 self,
264 (0..(m - i)).map(|k| (&householder_vector[k], rest.at(k, j))),
265 );
266 for k in 0..(m - i) {
267 ring.sub_assign(rest.at_mut(k, j), ring.mul_ref(&inner_product, &householder_vector[k]));
268 }
269 }
270
271 let mut rest = q.reborrow().restrict_cols(i..m);
273 for j in 0..m {
274 let inner_product = <_ as ComputeInnerProduct>::inner_product_ref(
275 self,
276 (0..(m - i)).map(|k| (&householder_vector[k], rest.at(j, k))),
277 );
278 for k in 0..(m - i) {
279 ring.sub_assign(rest.at_mut(j, k), ring.mul_ref(&inner_product, &householder_vector[k]));
280 }
281 }
282
283 let mut pivot_col = matrix.reborrow().submatrix(i..m, i..(i + 1));
285 for k in 1..(m - i) {
286 *pivot_col.at_mut(k, 0) = self.zero();
287 }
288 *pivot_col.at_mut(0, 0) = alpha;
289 }
290 }
291}
292
293#[cfg(test)]
294use crate::algorithms::matmul::MatmulAlgorithm;
295#[cfg(test)]
296use crate::algorithms::matmul::STANDARD_MATMUL;
297#[cfg(test)]
298use crate::assert_matrix_eq;
299#[cfg(test)]
300use crate::homomorphism::Homomorphism;
301#[cfg(test)]
302use crate::matrix::format_matrix;
303#[cfg(test)]
304use crate::matrix::{TransposableSubmatrix, TransposableSubmatrixMut};
305#[cfg(test)]
306use crate::primitive_int::StaticRing;
307#[cfg(test)]
308use crate::rings::approx_real::float::Real64;
309#[cfg(test)]
310use crate::rings::fraction::FractionFieldStore;
311
312#[cfg(test)]
313fn assert_is_correct_qr<V1, V2, V3>(original: Submatrix<V1, f64>, q: Submatrix<V2, f64>, r: Submatrix<V3, f64>)
314where
315 V1: AsPointerToSlice<f64>,
316 V2: AsPointerToSlice<f64>,
317 V3: AsPointerToSlice<f64>,
318{
319 let m = q.row_count();
320 let n = r.col_count();
321 assert_eq!(m, original.row_count());
322 assert_eq!(n, original.col_count());
323 assert_eq!(m, r.row_count());
324 let mut product = OwnedMatrix::zero(m, n, Real64::RING);
325 STANDARD_MATMUL.matmul(
326 TransposableSubmatrix::from(q),
327 TransposableSubmatrix::from(r),
328 TransposableSubmatrixMut::from(product.data_mut()),
329 Real64::RING,
330 );
331 for i in 0..m {
332 for j in 0..n {
333 if !(Real64::RING
334 .get_ring()
335 .is_approx_eq(*original.at(i, j), *product.at(i, j), 100))
336 {
337 println!("product does not match; Q, R are");
338 println!("{}", format_matrix(m, m, |i, j| q.at(i, j), Real64::RING));
339 println!("and");
340 println!("{}", format_matrix(m, n, |i, j| r.at(i, j), Real64::RING));
341 println!("the product is");
342 println!("{}", format_matrix(m, n, |i, j| product.at(i, j), Real64::RING));
343 panic!();
344 }
345 }
346 }
347 let mut product = OwnedMatrix::zero(m, m, Real64::RING);
348 STANDARD_MATMUL.matmul(
349 TransposableSubmatrix::from(q).transpose(),
350 TransposableSubmatrix::from(q),
351 TransposableSubmatrixMut::from(product.data_mut()),
352 Real64::RING,
353 );
354 for i in 0..m {
355 for j in 0..m {
356 let expected = if i == j { 1.0 } else { 0.0 };
357 if !(Real64::RING.get_ring().is_approx_eq(expected, *product.at(i, j), 100)) {
358 println!("Q is not orthogonal");
359 println!("{}", format_matrix(m, m, |i, j| q.at(i, j), Real64::RING));
360 panic!();
361 }
362 }
363 }
364
365 for j in 0..n {
366 for i in (j + 1)..m {
367 if !(Real64::RING.get_ring().is_approx_eq(0.0, *r.at(i, j), 100)) {
368 println!("R is not upper triangular");
369 println!("{}", format_matrix(m, n, |i, j| r.at(i, j), Real64::RING));
370 panic!();
371 }
372 }
373 }
374}
375
376#[cfg(test)]
377fn assert_is_correct_ldl<V1, V2>(original: Submatrix<V1, f64>, l: Submatrix<V2, f64>, d: &[f64])
378where
379 V1: AsPointerToSlice<f64>,
380 V2: AsPointerToSlice<f64>,
381{
382 let n = l.col_count();
383 assert_eq!(n, l.row_count());
384 assert_eq!(n, original.col_count());
385 assert_eq!(n, original.row_count());
386 let l_scaled = OwnedMatrix::from_fn(n, n, |i, j| *l.at(i, j) * d[j]);
387 let mut product = OwnedMatrix::zero(n, n, Real64::RING);
388 STANDARD_MATMUL.matmul(
389 TransposableSubmatrix::from(l_scaled.data()),
390 TransposableSubmatrix::from(l).transpose(),
391 TransposableSubmatrixMut::from(product.data_mut()),
392 Real64::RING,
393 );
394 for i in 0..n {
395 for j in 0..n {
396 if !(Real64::RING
397 .get_ring()
398 .is_approx_eq(*original.at(i, j), *product.at(i, j), 100))
399 {
400 println!("product does not match; L is");
401 println!("{}", format_matrix(n, n, |i, j| l.at(i, j), Real64::RING));
402 println!("D is diag{:?} and the product LDL^T is", d);
403 println!("{}", format_matrix(n, n, |i, j| product.at(i, j), Real64::RING));
404 panic!();
405 }
406 }
407 }
408 for i in 0..n {
409 for j in (i + 1)..n {
410 if !(Real64::RING.get_ring().is_approx_eq(0.0, *l.at(i, j), 100)) {
411 println!("L is not lower triangular");
412 println!("{}", format_matrix(n, n, |i, j| l.at(i, j), Real64::RING));
413 panic!();
414 }
415 }
416 }
417}
418
419#[test]
420fn test_float_qr() {
421 let RR = Real64::RING;
422 let a = OwnedMatrix::new_with_shape(vec![0.0, 1.0, 1.0, 0.0], 2, 2);
423 let mut r = a.clone_matrix(RR);
424 let mut q = OwnedMatrix::zero(2, 2, RR);
425 RR.get_ring().qr_decomposition(r.data_mut(), q.data_mut());
426 assert_is_correct_qr(a.data(), q.data(), r.data());
427
428 let a = OwnedMatrix::new_with_shape(vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0], 3, 2);
429 let mut r = a.clone_matrix(RR);
430 let mut q = OwnedMatrix::zero(3, 3, RR);
431 RR.get_ring().qr_decomposition(r.data_mut(), q.data_mut());
432 assert_is_correct_qr(a.data(), q.data(), r.data());
433
434 let a = OwnedMatrix::new_with_shape(vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0], 2, 3);
435 let mut r = a.clone_matrix(RR);
436 let mut q = OwnedMatrix::zero(2, 2, RR);
437 RR.get_ring().qr_decomposition(r.data_mut(), q.data_mut());
438 assert_is_correct_qr(a.data(), q.data(), r.data());
439
440 let a = OwnedMatrix::new_with_shape(vec![1.0, 1.0, 1.0, 2.0, 2.0, 3.0, 0.0, 0.0, 1.0], 3, 3);
441 let mut r = a.clone_matrix(RR);
442 let mut q = OwnedMatrix::zero(3, 3, RR);
443 RR.get_ring().qr_decomposition(r.data_mut(), q.data_mut());
444 assert_is_correct_qr(a.data(), q.data(), r.data());
445
446 let a = OwnedMatrix::new_with_shape(
447 (1..31)
448 .map(|x| x as f64 * if x % 2 == 0 { -1.0 } else { 1.0 })
449 .collect::<Vec<_>>(),
450 6,
451 5,
452 );
453 let mut r = a.clone_matrix(RR);
454 let mut q = OwnedMatrix::zero(6, 6, RR);
455 RR.get_ring().qr_decomposition(r.data_mut(), q.data_mut());
456 assert_is_correct_qr(a.data(), q.data(), r.data());
457}
458
459#[test]
460fn test_float_qdr() {
461 let RR = Real64::RING;
462 let a = OwnedMatrix::new_with_shape((1..10).map(|c| c as f64).collect(), 3, 3);
463 let mut r = a.clone_matrix(RR);
464 let mut q = OwnedMatrix::zero(3, 3, RR);
465 let diags = RR.get_ring().scaled_qr_decomposition(r.data_mut(), q.data_mut());
466 for i in 0..3 {
467 for j in 0..3 {
468 if i == j {
469 assert!(RR.get_ring().is_approx_eq(1.0, *r.at(i, j), 100));
470 }
471 RR.mul_assign(r.at_mut(i, j), diags[i].sqrt());
472 RR.mul_assign(q.at_mut(i, j), 1.0 / diags[j].sqrt());
473 }
474 }
475 assert_is_correct_qr(a.data(), q.data(), r.data());
476}
477
478#[test]
479fn test_float_ldl() {
480 let RR = Real64::RING;
481 let a = OwnedMatrix::new_with_shape(vec![5.0, 1.0, 1.0, 5.0], 2, 2);
482 let mut l = a.clone_matrix(RR);
483 let d = RR.get_ring().ldl_decomposition(l.data_mut());
484 assert_is_correct_ldl(a.data(), l.data(), &d);
485
486 let a = OwnedMatrix::new_with_shape(vec![1.0, 2.0, 3.0, 2.0, 6.0, 5.0, 3.0, 5.0, 20.0], 3, 3);
487 let mut l = a.clone_matrix(RR);
488 let d = RR.get_ring().ldl_decomposition(l.data_mut());
489 assert_is_correct_ldl(a.data(), l.data(), &d);
490
491 let mut a = OwnedMatrix::zero(5, 5, RR);
492 let factor = OwnedMatrix::new((0..25).map(|c| (c as f64).powi(2)).collect(), 5);
493 STANDARD_MATMUL.matmul(
494 TransposableSubmatrix::from(factor.data()),
495 TransposableSubmatrix::from(factor.data()).transpose(),
496 TransposableSubmatrixMut::from(a.data_mut()),
497 RR,
498 );
499 let mut l = a.clone_matrix(RR);
500 let d = RR.get_ring().ldl_decomposition(l.data_mut());
501 assert_is_correct_ldl(a.data(), l.data(), &d);
502
503 let a = OwnedMatrix::new_with_shape(vec![1.0, 2.0, 3.0, 2.0, 6.0, 5.0, 3.0, 5.0, -20.0], 3, 3);
504 let mut l = a.clone_matrix(RR);
505 let d = RR.get_ring().ldl_decomposition(l.data_mut());
506 assert_is_correct_ldl(a.data(), l.data(), &d);
507}
508
509#[test]
510fn test_rational_qdr() {
511 let QQ = RationalField::new(StaticRing::<i64>::RING);
512 let mut actual_r = OwnedMatrix::new_with_shape((1..10).map(|x| QQ.pow(QQ.int_hom().map(x), 2)).collect(), 3, 3);
513 let mut actual_q = OwnedMatrix::zero(3, 3, &QQ);
514 let diags = QQ
515 .get_ring()
516 .scaled_qr_decomposition(actual_r.data_mut(), actual_q.data_mut());
517 assert_el_eq!(&QQ, QQ.from_fraction(2658, 1), &diags[0]);
518 assert_el_eq!(&QQ, QQ.from_fraction(9891, 443), &diags[1]);
519 assert_el_eq!(&QQ, QQ.from_fraction(864, 1099), &diags[2]);
520
521 let mut expected_r = OwnedMatrix::identity(3, 3, &QQ);
522 *expected_r.at_mut(0, 1) = QQ.from_fraction(590, 443);
523 *expected_r.at_mut(0, 2) = QQ.from_fraction(759, 443);
524 *expected_r.at_mut(1, 2) = QQ.from_fraction(2700, 1099);
525 assert_matrix_eq!(&QQ, expected_r, actual_r);
526
527 let expected_q_num = [
528 [486857, 1299018, 356172],
529 [7789712, 1796865, -233904],
530 [23855993, -613242, 69108],
531 ];
532 let expected_q_den = 443 * 1099;
533 let expected_q = OwnedMatrix::from_fn(3, 3, |i, j| QQ.from_fraction(expected_q_num[i][j], expected_q_den));
534 assert_matrix_eq!(&QQ, expected_q, actual_q);
535}