1use crate::assert;
13use crate::internal_prelude::*;
14use bidiag::BidiagParams;
15use linalg::qr::no_pivoting::factor::QrParams;
16pub mod bidiag;
18pub(crate) mod bidiag_svd;
19pub(crate) mod dqds;
20#[derive(Copy, Clone, Debug, PartialEq, Eq)]
22pub enum ComputeSvdVectors {
23 No,
25 Thin,
27 Full,
29}
30#[derive(Copy, Clone, Debug, PartialEq, Eq)]
32pub enum SvdError {
33 NoConvergence,
35}
36#[derive(Debug, Copy, Clone)]
38pub struct SvdParams {
39 pub bidiag: BidiagParams,
41 pub qr: QrParams,
43 pub recursion_threshold: usize,
45 pub qr_ratio_threshold: f64,
47 #[doc(hidden)]
48 pub non_exhaustive: NonExhaustive,
49}
50impl<T: ComplexField> Auto<T> for SvdParams {
51 fn auto() -> Self {
52 Self {
53 recursion_threshold: 128,
54 qr_ratio_threshold: 11.0 / 6.0,
55 bidiag: auto!(T),
56 qr: auto!(T),
57 non_exhaustive: NonExhaustive(()),
58 }
59 }
60}
61fn work_rows<T>(m: usize) -> usize {
69 let size = core::mem::size_of::<T>();
70 if size > 0 && (m * size) % 1024 == 0 {
71 m + 64usize.div_ceil(size)
72 } else {
73 m
74 }
75}
76fn svd_imp_scratch<T: ComplexField>(
77 m: usize,
78 n: usize,
79 compute_u: ComputeSvdVectors,
80 compute_v: ComputeSvdVectors,
81 bidiag_svd_scratch: fn(
82 n: usize,
83 compute_u: bool,
84 compute_v: bool,
85 par: ParShape,
86 params: SvdParams,
87 ) -> StackReq,
88 params: SvdParams,
89 par: ParShape,
90) -> StackReq {
91 assert!(m >= n);
92 let householder_block_size =
93 linalg::qr::no_pivoting::factor::recommended_block_size::<T>(m, n);
94 let bid = temp_mat_scratch::<T>(work_rows::<T>(m), n);
95 let householder_left = temp_mat_scratch::<T>(householder_block_size, n);
96 let householder_right = temp_mat_scratch::<T>(householder_block_size, n);
97 let compute_bidiag =
98 bidiag::bidiag_in_place_scratch::<T>(m, n, par, params.bidiag.into());
99 let diag = temp_mat_scratch::<T>(n, 1);
100 let subdiag = diag;
101 let compute_ub = compute_v != ComputeSvdVectors::No;
102 let compute_vb = compute_u != ComputeSvdVectors::No;
103 let u_b = temp_mat_scratch::<T>(if compute_ub { n + 1 } else { 2 }, n + 1);
104 let v_b = temp_mat_scratch::<T>(n, if compute_vb { n } else { 0 });
105 let compute_bidiag_svd =
106 bidiag_svd_scratch(n, compute_ub, compute_vb, par, params);
107 let apply_householder_u = linalg::householder::apply_block_householder_sequence_on_the_left_in_place_scratch::<T>(
108 m,
109 householder_block_size,
110 match compute_u {
111 ComputeSvdVectors::No => 0,
112 ComputeSvdVectors::Thin => n,
113 ComputeSvdVectors::Full => m,
114 },
115 );
116 let apply_householder_v = linalg::householder::apply_block_householder_sequence_on_the_left_in_place_scratch::<T>(
117 n - 1,
118 householder_block_size,
119 match compute_v {
120 ComputeSvdVectors::No => 0,
121 _ => n,
122 },
123 );
124 StackReq::all_of(&[
125 bid,
126 householder_left,
127 householder_right,
128 StackReq::any_of(&[
129 compute_bidiag,
130 StackReq::all_of(&[
131 diag,
132 subdiag,
133 u_b,
134 v_b,
135 StackReq::any_of(&[
136 compute_bidiag_svd,
137 StackReq::all_of(&[
138 apply_householder_u,
139 apply_householder_v,
140 ]),
141 ]),
142 ]),
143 ]),
144 ])
145}
146fn bidiag_cplx_svd_scratch<T: ComplexField>(
147 n: usize,
148 compute_u: bool,
149 compute_v: bool,
150 par: ParShape,
151 params: SvdParams,
152) -> StackReq {
153 StackReq::all_of(&[
154 temp_mat_scratch::<T>(n, 1).array(4),
155 temp_mat_scratch::<T::Real>(n + 1, if compute_u { n + 1 } else { 0 }),
156 temp_mat_scratch::<T::Real>(n, if compute_v { n } else { 0 }),
157 bidiag_real_svd_scratch::<T::Real>(
158 n, compute_u, compute_v, par, params,
159 ),
160 ])
161}
162fn bidiag_real_svd_scratch<T: RealField>(
163 n: usize,
164 compute_u: bool,
165 compute_v: bool,
166 par: ParShape,
167 params: SvdParams,
168) -> StackReq {
169 let dqds = if compute_u || compute_v { StackReq::EMPTY } else { dqds::singular_values_scratch::<T>(n) };
171 if n < params.recursion_threshold {
172 dqds
173 } else {
174 StackReq::any_of(&[dqds, StackReq::all_of(&[
175 temp_mat_scratch::<T>(2, if compute_u { 0 } else { n + 1 }),
176 bidiag_svd::divide_and_conquer_scratch::<T>(
177 n,
178 params.recursion_threshold,
179 compute_u,
180 compute_v,
181 par,
182 ),
183 ])])
184 }
185}
186fn compute_bidiag_cplx_svd<T: ComplexField>(
187 mut diag: ColMut<'_, T, usize, ContiguousFwd>,
188 subdiag: ColMut<'_, T, usize, ContiguousFwd>,
189 mut u: Option<MatMut<'_, T>>,
190 mut v: Option<MatMut<'_, T>>,
191 params: SvdParams,
192 par: Par,
193 stack: &mut MemStack,
194) -> Result<(), SvdError> {
195 let n = diag.nrows();
196 let (mut diag_real, stack) =
197 unsafe { temp_mat_uninit::<T::Real, _, _>(n, 1, stack) };
198 let (mut subdiag_real, stack) =
199 unsafe { temp_mat_uninit::<T::Real, _, _>(n, 1, stack) };
200 let (mut u_real, stack) = unsafe {
201 temp_mat_uninit::<T::Real, _, _>(
202 n + 1,
203 if u.is_some() { n + 1 } else { 0 },
204 stack,
205 )
206 };
207 let (mut v_real, stack) = unsafe {
208 temp_mat_uninit::<T::Real, _, _>(
209 n,
210 if v.is_some() { n } else { 0 },
211 stack,
212 )
213 };
214 let (mut col_mul, stack) =
215 unsafe { temp_mat_uninit::<T, _, _>(n, 1, stack) };
216 let (mut row_mul, stack) =
217 unsafe { temp_mat_uninit::<T, _, _>(n - 1, 1, stack) };
218 let mut u_real = u.rb().map(|_| u_real.as_mat_mut());
219 let mut v_real = v.rb().map(|_| v_real.as_mat_mut());
220 let mut diag_real = diag_real
221 .as_mat_mut()
222 .col_mut(0)
223 .try_as_col_major_mut()
224 .unwrap();
225 let mut subdiag_real = subdiag_real
226 .as_mat_mut()
227 .col_mut(0)
228 .try_as_col_major_mut()
229 .unwrap();
230 let mut col_mul = col_mul.as_mat_mut().col_mut(0);
231 let mut row_mul = row_mul.as_mat_mut().col_mut(0);
232 let normalized = |x: T| {
233 if x == zero() {
234 one()
235 } else {
236 let norm1 = x.real().abs().fmax(x.imag().abs());
237 let ref shift = if norm1 >= min_positive::<T::Real>() {
240 from_f64::<T::Real>(1.0)
241 } else {
242 from_f64::<T::Real>(4503599627370496.0)
243 };
244 let y = x.mul_real(shift).mul_real((&norm1 * shift).recip());
245 y.mul_real(y.abs().recip())
246 }
247 };
248 let magnitude = |x: &T| {
249 let sml = min_positive::<T::Real>();
255 let big = sml.recip();
256 let m = x.real().abs().fmax(x.imag().abs());
257 if m < sml && m != zero() {
258 x.mul_real(&big).abs() * sml
259 } else if m > big && m.is_finite() {
260 x.mul_real(&sml).abs() * big
261 } else {
262 x.abs()
263 }
264 };
265 let mut col_normalized = normalized(diag[0].conj());
266 col_mul[0] = col_normalized.copy();
267 diag_real[0] = magnitude(&diag[0]);
268 subdiag_real[n - 1] = zero();
269 for i in 1..n {
270 let row_normalized =
271 normalized((&subdiag[i - 1] * col_normalized).conj());
272 subdiag_real[i - 1] = magnitude(&subdiag[i - 1]);
273 row_mul[i - 1] = row_normalized.conj();
274 col_normalized = normalized((&diag[i] * row_normalized).conj());
275 diag_real[i] = magnitude(&diag[i]);
276 col_mul[i] = col_normalized.copy();
277 }
278 compute_bidiag_real_svd(
279 diag_real.rb_mut(),
280 subdiag_real.rb_mut(),
281 u_real.rb_mut(),
282 v_real.rb_mut(),
283 params,
284 par,
285 stack,
286 )?;
287 for i in 0..n {
288 diag[i] = diag_real[i].to_cplx();
289 }
290 let u_real = u_real.rb();
291 let v_real = v_real.rb();
292 if let (Some(mut u), Some(u_real)) = (u.rb_mut(), u_real) {
293 z!(u.rb_mut().row_mut(0), u_real.row(0))
294 .for_each(|uz!(u, r)| *u = r.to_cplx());
295 z!(u.rb_mut().row_mut(n), u_real.row(n))
296 .for_each(|uz!(u, r)| *u = r.to_cplx());
297 for j in 0..u.ncols() {
298 let mut u = u.rb_mut().col_mut(j).subrows_mut(1, n - 1);
299 let u_real = u_real.rb().col(j).subrows(1, n - 1);
300 z!(u.rb_mut(), u_real, row_mul.rb())
301 .for_each(|uz!(u, re, f)| *u = f.mul_real(re));
302 }
303 }
304 if let (Some(mut v), Some(v_real)) = (v.rb_mut(), v_real) {
305 for j in 0..v.ncols() {
306 let mut v = v.rb_mut().col_mut(j);
307 let v_real = v_real.rb().col(j);
308 z!(v.rb_mut(), v_real, col_mul.rb())
309 .for_each(|uz!(v, re, f)| *v = f.mul_real(re));
310 }
311 }
312 Ok(())
313}
314fn compute_bidiag_real_svd<T: RealField>(
315 mut diag: ColMut<'_, T, usize, ContiguousFwd>,
316 mut subdiag: ColMut<'_, T, usize, ContiguousFwd>,
317 mut u: Option<MatMut<'_, T, usize, usize>>,
318 mut v: Option<MatMut<'_, T, usize, usize>>,
319 params: SvdParams,
320 par: Par,
321 stack: &mut MemStack,
322) -> Result<(), SvdError> {
323 let n = diag.nrows();
324 for i in 0..n {
325 if !(diag[i].is_finite() && subdiag[i].is_finite()) {
326 return Err(SvdError::NoConvergence);
327 }
328 }
329 if u.is_none() && v.is_none() {
330 if !diag.rb().iter().any(|x| *x == zero())
346 && dqds::singular_values(diag.rb_mut().as_slice_mut(), subdiag.rb_mut().as_slice_mut(), stack).is_ok()
347 {
348 return Ok(());
349 }
350 }
351 if n < params.recursion_threshold {
352 if let Some(mut u) = u.rb_mut() {
353 u.fill(zero());
354 u.diagonal_mut().fill(one());
355 }
356 if let Some(mut v) = v.rb_mut() {
357 v.fill(zero());
358 v.diagonal_mut().fill(one());
359 }
360 bidiag_svd::qr_algorithm(
361 diag.rb_mut(),
362 subdiag.rb_mut(),
363 u.rb_mut().map(|u| u.submatrix_mut(0, 0, n, n)),
364 v.rb_mut(),
365 )?;
366 return Ok(());
367 } else {
368 let (mut u2, stack) = unsafe {
369 temp_mat_uninit::<T::Real, _, _>(
370 2,
371 if u.is_some() { 0 } else { n + 1 },
372 stack,
373 )
374 };
375 bidiag_svd::divide_and_conquer(
376 diag.as_row_shape_mut(n),
377 subdiag.as_row_shape_mut(n),
378 match u {
379 Some(u) => bidiag_svd::MatU::Full(u),
380 None => bidiag_svd::MatU::TwoRowsStorage(u2.as_mat_mut()),
381 },
382 v.map(|m| m.as_shape_mut(n, n)),
383 par,
384 stack,
385 params.recursion_threshold,
386 )
387 }
388}
389pub fn bidiagonal_svd_scratch<T: ComplexField>(
404 dim: usize,
405 par: ParShape,
406 params: Spec<SvdParams, T>,
407) -> StackReq {
408 let n = dim;
409 if n == 0 {
410 return StackReq::EMPTY;
411 }
412 StackReq::all_of(&[
413 temp_mat_scratch::<T>(n, 1).array(2),
414 temp_mat_scratch::<T>(n + 1, n + 1),
415 temp_mat_scratch::<T>(n, n),
416 bidiag_cplx_svd_scratch::<T>(n, true, true, par, *params),
417 ])
418}
419pub fn bidiagonal_svd<T: ComplexField>(
461 diag: &mut [T],
462 offdiag: &[T],
463 upper: bool,
464 mut u: MatMut<'_, T>,
465 mut v: MatMut<'_, T>,
466 par: Par,
467 stack: &mut MemStack,
468 params: Spec<SvdParams, T>,
469) -> Result<(), SvdError> {
470 let n = diag.len();
471 assert!(offdiag.len() == n.saturating_sub(1));
472 assert!(
473 u.nrows() == n && u.ncols() == n && v.nrows() == n && v.ncols() == n
474 );
475 if !diag.iter().chain(offdiag).all(|x| x.is_finite()) {
476 return Err(SvdError::NoConvergence);
477 }
478 if n == 0 {
479 return Ok(());
480 }
481 let (mut d, stack) = unsafe { temp_mat_uninit::<T, _, _>(n, 1, stack) };
484 let (mut e, stack) = temp_mat_zeroed::<T, _, _>(n, 1, stack);
485 let (mut left, stack) =
488 unsafe { temp_mat_uninit::<T, _, _>(n + 1, n + 1, stack) };
489 let (mut right, stack) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
491 let mut d = d.as_mat_mut().col_mut(0).try_as_col_major_mut().unwrap();
492 let mut e = e.as_mat_mut().col_mut(0).try_as_col_major_mut().unwrap();
493 for i in 0..n {
494 d[i] = if upper {
495 diag[i].conj()
496 } else {
497 diag[i].copy()
498 };
499 if i + 1 < n {
500 e[i] = if upper {
501 offdiag[i].conj()
502 } else {
503 offdiag[i].copy()
504 };
505 }
506 }
507 compute_bidiag_cplx_svd(
508 d.rb_mut(),
509 e.rb_mut(),
510 Some(left.as_mat_mut()),
511 Some(right.as_mat_mut()),
512 *params,
513 par,
514 stack,
515 )?;
516 if !(d.rb().is_all_finite()
518 && left.as_mat_ref().submatrix(0, 0, n, n).is_all_finite()
519 && right.as_mat_ref().is_all_finite())
520 {
521 return Err(SvdError::NoConvergence);
522 }
523 for i in 0..n {
524 diag[i] = d[i].copy();
525 }
526 if upper {
527 u.copy_from(right.as_mat_ref());
528 v.copy_from(left.as_mat_ref().submatrix(0, 0, n, n));
529 } else {
530 u.copy_from(left.as_mat_ref().submatrix(0, 0, n, n));
531 v.copy_from(right.as_mat_ref());
532 }
533 Ok(())
534}
535fn svd_imp<T: ComplexField>(
537 matrix: MatRef<'_, T>,
538 s: ColMut<'_, T>,
539 u: Option<MatMut<'_, T>>,
540 v: Option<MatMut<'_, T>>,
541 bidiag_svd: fn(
542 diag: ColMut<'_, T, usize, ContiguousFwd>,
543 subdiag: ColMut<'_, T, usize, ContiguousFwd>,
544 u: Option<MatMut<'_, T, usize, usize>>,
545 v: Option<MatMut<'_, T, usize, usize>>,
546 params: SvdParams,
547 par: Par,
548 stack: &mut MemStack,
549 ) -> Result<(), SvdError>,
550 mut par: Par,
551 stack: &mut MemStack,
552 params: SvdParams,
553) -> Result<(), SvdError> {
554 assert!(matrix.nrows() >= matrix.ncols());
555 let m = matrix.nrows();
556 let n = matrix.ncols();
557 let bs = linalg::qr::no_pivoting::factor::recommended_block_size::<T>(m, n);
558 let (mut bid_store, stack) =
559 unsafe { temp_mat_uninit::<T, _, _>(work_rows::<T>(m), n, stack) };
560 let mut bid = bid_store.as_mat_mut().subrows_mut(0, m);
561 let (mut Hl, stack) = unsafe { temp_mat_uninit::<T, _, _>(bs, n, stack) };
562 let (mut Hr, stack) =
563 unsafe { temp_mat_uninit::<T, _, _>(bs, n - 1, stack) };
564 let mut Hl = Hl.as_mat_mut();
565 let mut Hr = Hr.as_mat_mut();
566 bid.copy_from(matrix);
567 bidiag::bidiag_in_place(
568 bid.rb_mut(),
569 Hl.rb_mut(),
570 Hr.rb_mut(),
571 par.reborrow(),
572 stack,
573 params.bidiag.into(),
574 );
575 let (mut diag, stack) = unsafe { temp_mat_uninit::<T, _, _>(n, 1, stack) };
576 let (mut subdiag, stack) =
577 unsafe { temp_mat_uninit::<T, _, _>(n, 1, stack) };
578 let mut diag = diag.as_mat_mut().col_mut(0).try_as_col_major_mut().unwrap();
579 let mut subdiag = subdiag
580 .as_mat_mut()
581 .col_mut(0)
582 .try_as_col_major_mut()
583 .unwrap();
584 let (mut ub, stack) = unsafe {
585 temp_mat_uninit::<T, _, _>(
586 n + 1,
587 if v.is_some() { n + 1 } else { 0 },
588 stack,
589 )
590 };
591 let (mut vb, stack) = unsafe {
592 temp_mat_uninit::<T, _, _>(n, if u.is_some() { n } else { 0 }, stack)
593 };
594 let mut ub = ub.as_mat_mut();
595 let mut vb = vb.as_mat_mut();
596 for i in 0..n {
597 diag[i] = bid[(i, i)].conj();
598 if i + 1 < n {
599 subdiag[i] = bid[(i, i + 1)].conj();
600 } else {
601 subdiag[i] = zero();
602 }
603 }
604 bidiag_svd(
605 diag.rb_mut(),
606 subdiag.rb_mut(),
607 v.rb().map(|_| ub.rb_mut()),
608 u.rb().map(|_| vb.rb_mut()),
609 params,
610 par.reborrow(),
611 stack,
612 )?;
613 { s }.copy_from(diag);
614 if let Some(mut u) = u {
615 let ncols = u.ncols();
616 u.rb_mut().submatrix_mut(0, 0, n, n).copy_from(vb.rb());
617 u.rb_mut().submatrix_mut(n, 0, m - n, ncols).fill(zero());
618 u.rb_mut().submatrix_mut(0, n, n, ncols - n).fill(zero());
619 u.rb_mut()
620 .submatrix_mut(n, n, ncols - n, ncols - n)
621 .diagonal_mut()
622 .fill(one());
623 linalg::householder::apply_block_householder_sequence_on_the_left_in_place_with_conj(bid.rb(), Hl.rb(), Conj::No, u, par.reborrow(), stack);
624 }
625 if let Some(mut v) = v {
626 v.copy_from(ub.rb().submatrix(0, 0, n, n));
627 for j in 1..n {
628 for i in 0..j {
629 bid[(j, i)] = bid[(i, j)].copy();
630 }
631 }
632 linalg::householder::apply_block_householder_sequence_on_the_left_in_place_with_conj(
633 bid.rb().submatrix(1, 0, n - 1, n - 1),
634 Hr.rb(),
635 Conj::Yes,
636 v.subrows_mut(1, n - 1),
637 par,
638 stack,
639 );
640 }
641 Ok(())
642}
643fn compute_squareish_svd<T: ComplexField>(
644 matrix: MatRef<'_, T>,
645 s: ColMut<'_, T>,
646 u: Option<MatMut<'_, T>>,
647 v: Option<MatMut<'_, T>>,
648 par: Par,
649 stack: &mut MemStack,
650 params: SvdParams,
651) -> Result<(), SvdError> {
652 if const { T::IS_REAL } {
653 svd_imp::<T::Real>(
654 unsafe { core::mem::transmute(matrix) },
655 unsafe { core::mem::transmute(s) },
656 unsafe { core::mem::transmute(u) },
657 unsafe { core::mem::transmute(v) },
658 compute_bidiag_real_svd::<T::Real>,
659 par,
660 stack,
661 params,
662 )
663 } else {
664 svd_imp::<T>(
665 matrix,
666 s,
667 u,
668 v,
669 compute_bidiag_cplx_svd::<T>,
670 par,
671 stack,
672 params,
673 )
674 }
675}
676pub fn svd_scratch<T: ComplexField>(
678 nrows: usize,
679 ncols: usize,
680 compute_u: ComputeSvdVectors,
681 compute_v: ComputeSvdVectors,
682 par: ParShape,
683 params: Spec<SvdParams, T>,
684) -> StackReq {
685 let params = params.config;
686 let mut m = nrows;
687 let mut n = ncols;
688 let mut compute_u = compute_u;
689 let mut compute_v = compute_v;
690 if n > m {
691 core::mem::swap(&mut m, &mut n);
692 core::mem::swap(&mut compute_u, &mut compute_v);
693 }
694 if n == 0 {
695 return StackReq::EMPTY;
696 }
697 let bidiag_svd_scratch = if const { T::IS_REAL } {
698 bidiag_real_svd_scratch::<T::Real>
699 } else {
700 bidiag_cplx_svd_scratch::<T>
701 };
702 if m as f64 / n as f64 <= params.qr_ratio_threshold {
703 svd_imp_scratch::<T>(
704 m,
705 n,
706 compute_u,
707 compute_v,
708 bidiag_svd_scratch,
709 params,
710 par,
711 )
712 } else {
713 let bs =
714 linalg::qr::no_pivoting::factor::recommended_block_size::<T>(m, n);
715 StackReq::all_of(&[
716 temp_mat_scratch::<T>(m, n),
717 temp_mat_scratch::<T>(bs, n),
718 StackReq::any_of(&[
719 StackReq::all_of(&[
720 temp_mat_scratch::<T>(n, n),
721 svd_imp_scratch::<T>(n, n, compute_u, compute_v, bidiag_svd_scratch, params, par),
722 ]),
723 linalg::householder::apply_block_householder_sequence_on_the_left_in_place_scratch::<T>(
724 m,
725 bs,
726 match compute_u {
727 ComputeSvdVectors::No => 0,
728 ComputeSvdVectors::Thin => n,
729 ComputeSvdVectors::Full => m,
730 },
731 ),
732 ]),
733 ])
734 }
735}
736pub fn svd<T: ComplexField>(
742 A: MatRef<'_, T>,
743 s: DiagMut<'_, T>,
744 u: Option<MatMut<'_, T>>,
745 v: Option<MatMut<'_, T>>,
746 mut par: Par,
747 stack: &mut MemStack,
748 params: Spec<SvdParams, T>,
749) -> Result<(), SvdError> {
750 let params = params.config;
751 let (m, n) = A.shape();
752 let size = Ord::min(m, n);
753 assert!(s.dim() == size);
754 let s = s.column_vector_mut();
755 if let Some(u) = u.rb() {
756 assert!(all(
757 u.nrows() == A.nrows(),
758 any(u.ncols() == A.nrows(), u.ncols() == size),
759 ));
760 }
761 if let Some(v) = v.rb() {
762 assert!(all(
763 v.nrows() == A.ncols(),
764 any(v.ncols() == A.ncols(), v.ncols() == size),
765 ));
766 }
767 #[cfg(feature = "perf-warn")]
768 match (u.rb(), v.rb()) {
769 (Some(matrix), _) | (_, Some(matrix)) => {
770 if matrix.row_stride().unsigned_abs() != 1
771 && crate::__perf_warn!(QR_WARN)
772 {
773 if matrix.col_stride().unsigned_abs() == 1 {
774 log::warn!(
775 target : "faer_perf",
776 "SVD prefers column-major singular vector matrices. Found row-major matrix."
777 );
778 } else {
779 log::warn!(
780 target : "faer_perf",
781 "SVD prefers column-major singular vector matrices. Found matrix with generic strides."
782 );
783 }
784 }
785 },
786 _ => {},
787 }
788 let mut u = u;
789 let mut v = v;
790 let mut matrix = A;
791 let do_transpose = n > m;
792 if do_transpose {
793 matrix = matrix.transpose();
794 core::mem::swap(&mut u, &mut v)
795 }
796 let (m, n) = matrix.shape();
797 if n == 0 {
798 if let Some(mut u) = u {
799 u.fill(zero());
800 u.rb_mut().diagonal_mut().fill(one());
801 }
802 return Ok(());
803 }
804 if m as f64 / n as f64 <= params.qr_ratio_threshold {
805 compute_squareish_svd(
806 matrix,
807 s,
808 u.rb_mut(),
809 v.rb_mut(),
810 par,
811 stack,
812 params,
813 )?;
814 } else {
815 let bs =
816 linalg::qr::no_pivoting::factor::recommended_block_size::<T>(m, n);
817 let (mut qr, stack) =
818 unsafe { temp_mat_uninit::<T, _, _>(m, n, stack) };
819 let mut qr = qr.as_mat_mut();
820 let (mut householder, stack) =
821 unsafe { temp_mat_uninit::<T, _, _>(bs, n, stack) };
822 let mut householder = householder.as_mat_mut();
823 {
824 qr.copy_from(matrix.rb());
825 linalg::qr::no_pivoting::factor::qr_in_place(
826 qr.rb_mut(),
827 householder.rb_mut(),
828 par.reborrow(),
829 stack,
830 params.qr.into(),
831 );
832 }
833 {
834 let (mut r, stack) =
835 unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
836 let mut r = r.as_mat_mut();
837 z!(r.rb_mut()).for_each_triangular_lower(
838 linalg::zip::Diag::Skip,
839 |uz!(dst)| *dst = zero(),
840 );
841 z!(r.rb_mut(), qr.rb().submatrix(0, 0, n, n))
842 .for_each_triangular_upper(
843 linalg::zip::Diag::Include,
844 |uz!(dst, src)| *dst = src.copy(),
845 );
846 compute_squareish_svd(
847 r.rb(),
848 s,
849 u.rb_mut().map(|u| u.submatrix_mut(0, 0, n, n)),
850 v.rb_mut(),
851 par.reborrow(),
852 stack,
853 params,
854 )?;
855 }
856 if let Some(mut u) = u.rb_mut() {
857 u.rb_mut().subrows_mut(n, m - n).fill(zero());
858 if u.ncols() == m {
859 u.rb_mut().submatrix_mut(0, n, n, m - n).fill(zero());
862 u.rb_mut()
863 .submatrix_mut(n, n, m - n, m - n)
864 .diagonal_mut()
865 .fill(one());
866 }
867 linalg::householder::apply_block_householder_sequence_on_the_left_in_place_with_conj(
868 qr.rb(),
869 householder.rb(),
870 Conj::No,
871 u.rb_mut(),
872 par,
873 stack,
874 );
875 }
876 }
877 if do_transpose {
878 if let Some(u) = u.rb_mut() {
879 z!(u).for_each(|uz!(u)| *u = u.conj())
880 }
881 if let Some(v) = v.rb_mut() {
882 z!(v).for_each(|uz!(v)| *v = v.conj())
883 }
884 }
885 Ok(())
886}
887pub fn pseudoinverse_from_svd_scratch<T: ComplexField>(
890 nrows: usize,
891 ncols: usize,
892 par: ParShape,
893) -> StackReq {
894 _ = par;
895 let size = Ord::min(nrows, ncols);
896 StackReq::all_of(&[
897 temp_mat_scratch::<T>(nrows, size),
898 temp_mat_scratch::<T>(ncols, size),
899 ])
900}
901pub fn pseudoinverse_from_svd<T: ComplexField>(
904 pinv: MatMut<'_, T>,
905 s: DiagRef<'_, T>,
906 u: MatRef<'_, T>,
907 v: MatRef<'_, T>,
908 par: Par,
909 stack: &mut MemStack,
910) {
911 pseudoinverse_from_svd_with_tolerance(
912 pinv,
913 s,
914 u,
915 v,
916 zero(),
917 eps::<T::Real>()
918 * from_f64::<T::Real>(Ord::max(u.nrows(), v.nrows()) as f64),
919 par,
920 stack,
921 );
922}
923pub fn pseudoinverse_from_svd_with_tolerance<T: ComplexField>(
926 pinv: MatMut<'_, T>,
927 s: DiagRef<'_, T>,
928 u: MatRef<'_, T>,
929 v: MatRef<'_, T>,
930 abs_tol: T::Real,
931 rel_tol: T::Real,
932 par: Par,
933 stack: &mut MemStack,
934) {
935 let mut pinv = pinv;
936 let m = u.nrows();
937 let n = v.nrows();
938 let size = Ord::min(m, n);
939 assert!(all(
940 u.nrows() == m,
941 v.nrows() == n,
942 u.ncols() >= size,
943 v.ncols() >= size,
944 s.dim() >= size
945 ));
946 let s = s.column_vector();
947 let u = u.get(.., ..size);
948 let v = v.get(.., ..size);
949 let smax = s.norm_max();
950 let tol = abs_tol.fmax(rel_tol * smax);
951 let (mut u_trunc, stack) =
952 unsafe { temp_mat_uninit::<T, _, _>(m, size, stack) };
953 let (mut vp_trunc, _) =
954 unsafe { temp_mat_uninit::<T, _, _>(n, size, stack) };
955 let mut u_trunc = u_trunc.as_mat_mut();
956 let mut vp_trunc = vp_trunc.as_mat_mut();
957 let mut len = 0;
958 for j in 0..size {
959 let x = s[j].absmax();
960 if x > tol {
961 let ref p = s[j].real().recip();
962 u_trunc.rb_mut().col_mut(len).copy_from(u.col(j));
963 z!(vp_trunc.rb_mut().col_mut(len), v.col(j))
964 .for_each(|uz!(dst, src)| *dst = src.mul_real(p));
965 len += 1;
966 }
967 }
968 let u_trunc = u_trunc.get(.., ..len);
969 let vp_trunc = vp_trunc.get(.., ..len);
970 linalg::matmul::matmul(
971 pinv.rb_mut(),
972 Accum::Replace,
973 vp_trunc,
974 u_trunc.adjoint(),
975 one(),
976 par,
977 );
978}
979#[cfg(test)]
980mod tests {
981 mod qr_stall;
982 mod lapack_svd_f64 {
984 type Real = f64;
985 type Scalar = Real;
986 const GUARD: Scalar = -12345.0;
987 const ROTATION_PHASE: Scalar = 1.0;
988 fn diagonal_phase(i: usize) -> Scalar {
989 if i % 2 == 0 { 1.0 } else { -1.0 }
990 }
991 use super::super::{
992 bidiag_real_svd_scratch as bidiagonal_scratch,
993 compute_bidiag_real_svd as compute_bidiagonal,
994 };
995 mod lapack {
996 include!("tests/lapack.rs");
997 }
998 mod svd_views {
999 include!("tests/svd_views.rs");
1000 }
1001 }
1002 mod lapack_svd_f32 {
1003 type Real = f32;
1004 type Scalar = Real;
1005 const GUARD: Scalar = -12345.0;
1006 const ROTATION_PHASE: Scalar = 1.0;
1007 fn diagonal_phase(i: usize) -> Scalar {
1008 if i % 2 == 0 { 1.0 } else { -1.0 }
1009 }
1010 use super::super::{
1011 bidiag_real_svd_scratch as bidiagonal_scratch,
1012 compute_bidiag_real_svd as compute_bidiagonal,
1013 };
1014 mod lapack {
1015 include!("tests/lapack.rs");
1016 }
1017 mod svd_views {
1018 include!("tests/svd_views.rs");
1019 }
1020 #[test]
1021 fn f32_fixtures_use_single_precision_limits() {
1022 let (_, large) = lapack::diagonal_case(2, 2, 6);
1023 assert_eq!(large[0] as f64, f32::MAX.sqrt() as f64);
1024 let (_, small) = lapack::diagonal_case(2, 2, 7);
1025 assert_eq!(small[0] as f64, f32::MIN_POSITIVE.sqrt() as f64);
1026 let (_, geometric) = lapack::diagonal_case(3, 3, 4);
1027 assert_eq!(geometric[2] as f64, f32::EPSILON as f64);
1028 let (_, cluster) = lapack::diagonal_case(4, 4, 9);
1029 assert_eq!(cluster[3] as f64, (1.0 - 24.0 * f32::EPSILON) as f64);
1030 }
1031 }
1032 mod lapack_svd_c64 {
1033 type Real = f64;
1034 type Scalar = crate::c64;
1035 const GUARD: Scalar = Scalar::new(-12345.0, 6789.0);
1036 const ROTATION_PHASE: Scalar = Scalar::new(0.0, 1.0);
1037 fn diagonal_phase(i: usize) -> Scalar {
1038 match i % 4 {
1039 0 => Scalar::new(0.0, 1.0),
1040 1 => Scalar::new(0.6, 0.8),
1041 2 => Scalar::new(-0.8, 0.6),
1042 _ => Scalar::new(-1.0, 0.0),
1043 }
1044 }
1045 use super::super::{
1046 bidiag_cplx_svd_scratch as bidiagonal_scratch,
1047 compute_bidiag_cplx_svd as compute_bidiagonal,
1048 };
1049 mod lapack {
1050 include!("tests/lapack.rs");
1051 }
1052 mod svd_views {
1053 include!("tests/svd_views.rs");
1054 }
1055 mod complex_tests {
1056 include!("tests/complex.rs");
1057 }
1058 }
1059 mod lapack_svd_c32 {
1060 type Real = f32;
1061 type Scalar = crate::c32;
1062 const GUARD: Scalar = Scalar::new(-12345.0, 6789.0);
1063 const ROTATION_PHASE: Scalar = Scalar::new(0.0, 1.0);
1064 fn diagonal_phase(i: usize) -> Scalar {
1065 match i % 4 {
1066 0 => Scalar::new(0.0, 1.0),
1067 1 => Scalar::new(0.6, 0.8),
1068 2 => Scalar::new(-0.8, 0.6),
1069 _ => Scalar::new(-1.0, 0.0),
1070 }
1071 }
1072 use super::super::{
1073 bidiag_cplx_svd_scratch as bidiagonal_scratch,
1074 compute_bidiag_cplx_svd as compute_bidiagonal,
1075 };
1076 mod lapack {
1077 include!("tests/lapack.rs");
1078 }
1079 mod svd_views {
1080 include!("tests/svd_views.rs");
1081 }
1082 mod complex_tests {
1083 include!("tests/complex.rs");
1084 }
1085 #[test]
1086 fn c32_fixtures_use_single_precision_limits() {
1087 let (_, large) = lapack::diagonal_case(2, 2, 6);
1088 assert_eq!(large[0] as f64, f32::MAX.sqrt() as f64);
1089 let (_, small) = lapack::diagonal_case(2, 2, 7);
1090 assert_eq!(small[0] as f64, f32::MIN_POSITIVE.sqrt() as f64);
1091 let (_, geometric) = lapack::diagonal_case(3, 3, 4);
1092 assert_eq!(geometric[2] as f64, f32::EPSILON as f64);
1093 let (_, cluster) = lapack::diagonal_case(4, 4, 9);
1094 assert_eq!(cluster[3] as f64, (1.0 - 24.0 * f32::EPSILON) as f64);
1095 }
1096 }
1097 use super::*;
1098 use crate::assert;
1099 use crate::stats::prelude::*;
1100 use crate::utils::approx::*;
1101 use dyn_stack::MemBuffer;
1102 #[track_caller]
1103 fn test_svd<T: ComplexField>(mat: MatRef<'_, T>) {
1104 let (m, n) = mat.shape();
1105 let params = Spec::new(SvdParams {
1106 recursion_threshold: 8,
1107 qr_ratio_threshold: 1.0,
1108 ..auto!(T)
1109 });
1110 let approx_eq = CwiseMat(
1111 ApproxEq::<T::Real>::eps()
1112 * from_f64::<T::Real>(8.0 * Ord::max(m, n) as f64).sqrt(),
1113 );
1114 {
1115 let mut s = Mat::zeros(m, n);
1116 let mut u = Mat::zeros(m, m);
1117 let mut v = Mat::zeros(n, n);
1118 svd(
1119 mat.as_ref(),
1120 s.as_mut().diagonal_mut(),
1121 Some(u.as_mut()),
1122 Some(v.as_mut()),
1123 Par::Seq,
1124 MemStack::new(&mut MemBuffer::new(svd_scratch::<T>(
1125 m,
1126 n,
1127 ComputeSvdVectors::Full,
1128 ComputeSvdVectors::Full,
1129 ParShape::Seq,
1130 params,
1131 ))),
1132 params,
1133 )
1134 .unwrap();
1135 let reconstructed = &u * &s * v.adjoint();
1136 assert!(reconstructed ~ mat);
1137 }
1138 let size = Ord::min(m, n);
1139 let mut s = Mat::zeros(size, size);
1140 let mut u = Mat::zeros(m, size);
1141 let mut v = Mat::zeros(n, size);
1142 {
1143 svd(
1144 mat.as_ref(),
1145 s.as_mut().diagonal_mut(),
1146 Some(u.as_mut()),
1147 Some(v.as_mut()),
1148 Par::Seq,
1149 MemStack::new(&mut MemBuffer::new(svd_scratch::<T>(
1150 m,
1151 n,
1152 ComputeSvdVectors::Thin,
1153 ComputeSvdVectors::Thin,
1154 ParShape::Seq,
1155 params,
1156 ))),
1157 params,
1158 )
1159 .unwrap();
1160 let reconstructed = &u * &s * v.adjoint();
1161 assert!(reconstructed ~ mat);
1162 }
1163 {
1164 let mut s2 = Mat::zeros(size, size);
1165 let mut u2 = Mat::zeros(m, size);
1166 svd(
1167 mat.as_ref(),
1168 s2.as_mut().diagonal_mut(),
1169 Some(u2.as_mut()),
1170 None,
1171 Par::Seq,
1172 MemStack::new(&mut MemBuffer::new(svd_scratch::<T>(
1173 m,
1174 n,
1175 ComputeSvdVectors::Thin,
1176 ComputeSvdVectors::No,
1177 ParShape::Seq,
1178 params,
1179 ))),
1180 params,
1181 )
1182 .unwrap();
1183 assert!(s2 ~ s);
1184 assert!(u2 ~ u);
1185 }
1186 {
1187 let mut s2 = Mat::zeros(size, size);
1188 let mut v2 = Mat::zeros(n, size);
1189 svd(
1190 mat.as_ref(),
1191 s2.as_mut().diagonal_mut(),
1192 None,
1193 Some(v2.as_mut()),
1194 Par::Seq,
1195 MemStack::new(&mut MemBuffer::new(svd_scratch::<T>(
1196 m,
1197 n,
1198 ComputeSvdVectors::No,
1199 ComputeSvdVectors::Thin,
1200 ParShape::Seq,
1201 params,
1202 ))),
1203 params,
1204 )
1205 .unwrap();
1206 assert!(s2 ~ s);
1207 assert!(v2 ~ v);
1208 }
1209 {
1210 let mut s2 = Mat::zeros(size, size);
1211 svd(
1212 mat.as_ref(),
1213 s2.as_mut().diagonal_mut(),
1214 None,
1215 None,
1216 Par::Seq,
1217 MemStack::new(&mut MemBuffer::new(svd_scratch::<T>(
1218 m,
1219 n,
1220 ComputeSvdVectors::No,
1221 ComputeSvdVectors::No,
1222 ParShape::Seq,
1223 params,
1224 ))),
1225 params,
1226 )
1227 .unwrap();
1228 assert!(s2 ~ s);
1229 }
1230 }
1231 fn check_tiny_split<T: ComplexField>(a: MatRef<'_, T>) {
1232 let (m, n) = a.shape();
1233 let size = Ord::min(m, n);
1234 let compute = |threshold, want_u, want_v| {
1235 let params = Spec::new(SvdParams {
1236 recursion_threshold: threshold,
1237 ..auto!(T)
1238 });
1239 let mut s = Col::zeros(size);
1240 let mut u = Mat::zeros(m, if want_u { size } else { 0 });
1241 let mut v = Mat::zeros(n, if want_v { size } else { 0 });
1242 let vectors = |want| {
1243 if want {
1244 ComputeSvdVectors::Thin
1245 } else {
1246 ComputeSvdVectors::No
1247 }
1248 };
1249 svd(
1250 a,
1251 s.as_mut().as_diagonal_mut(),
1252 if want_u { Some(u.as_mut()) } else { None },
1253 if want_v { Some(v.as_mut()) } else { None },
1254 Par::Seq,
1255 MemStack::new(&mut MemBuffer::new(svd_scratch::<T>(
1256 m,
1257 n,
1258 vectors(want_u),
1259 vectors(want_v),
1260 ParShape::Seq,
1261 params,
1262 ))),
1263 params,
1264 )
1265 .unwrap();
1266 (s, u, v)
1267 };
1268 let (reference, _, _) = compute(usize::MAX, true, true);
1269 for (want_u, want_v) in
1270 [(true, true), (false, false), (true, false), (false, true)]
1271 {
1272 let (s, u, v) = compute(128, want_u, want_v);
1273 assert!((&s - &reference).norm_max() < from_f64::<T::Real>(1e-13));
1274 if want_u {
1275 assert!(
1276 (u.adjoint() * &u - Mat::<T>::identity(size, size))
1277 .norm_l2() < from_f64::<T::Real>(1e-13)
1278 );
1279 }
1280 if want_v {
1281 assert!(
1282 (v.adjoint() * &v - Mat::<T>::identity(size, size))
1283 .norm_l2() < from_f64::<T::Real>(1e-13)
1284 );
1285 }
1286 if want_u && want_v {
1287 let back = &u * s.as_diagonal() * v.adjoint();
1288 assert!(
1289 (&back - a).norm_l2() / a.norm_l2()
1290 < from_f64::<T::Real>(1e-13)
1291 );
1292 }
1293 }
1294 }
1295 #[test]
1296 fn test_v_only_rank_one_exact_scratch() {
1297 fn check<T: ComplexField>(entry: T, tol: f64) {
1298 let n = 8;
1299 let a = Mat::<T>::from_fn(n, n, |i, j| {
1300 if i == 0 && j == 0 {
1301 entry.copy()
1302 } else {
1303 zero()
1304 }
1305 });
1306 let mut params = Spec::<SvdParams, T>::default();
1307 params.recursion_threshold = 4;
1308 let mut s = Col::<T>::zeros(n);
1309 let mut v = Mat::<T>::zeros(n, n);
1310 let mut mem = MemBuffer::new(svd_scratch::<T>(
1311 n,
1312 n,
1313 ComputeSvdVectors::No,
1314 ComputeSvdVectors::Thin,
1315 ParShape::Seq,
1316 params,
1317 ));
1318 svd(
1319 a.as_ref(),
1320 s.as_mut().as_diagonal_mut(),
1321 None,
1322 Some(v.as_mut()),
1323 Par::Seq,
1324 MemStack::new(&mut mem),
1325 params,
1326 )
1327 .unwrap();
1328 assert!(s.is_all_finite() && v.is_all_finite());
1329 let expected =
1330 Col::<T>::from_fn(n, |i| if i == 0 { one() } else { zero() });
1331 let tol = from_f64::<T::Real>(tol);
1332 assert!((&s - &expected).norm_max() <= tol.copy());
1333 assert!(
1334 (v.adjoint() * &v - Mat::<T>::identity(n, n)).norm_max() <= tol
1335 );
1336 }
1337 check::<f32>(1.0, 1e-5);
1338 check::<c32>(c32::new(0.0, 1.0), 1e-5);
1339 check::<f64>(1.0, 1e-13);
1340 check::<c64>(c64::new(0.0, 1.0), 1e-13);
1341 }
1342 #[test]
1343 fn test_tiny_diagonal_at_split() {
1344 for n in [128, 256] {
1345 for tiny in [0.0, 1e-15, 1e-13, 1e-10, 1e-6] {
1346 let a = Mat::<f64>::from_fn(n, n, |i, j| {
1347 if (i == j && i < 64) || (j == i + 1 && i < 64) {
1348 1.0
1349 } else if i == 64 && j == 64 {
1350 tiny
1351 } else {
1352 0.0
1353 }
1354 });
1355 check_tiny_split(a.as_ref());
1356 let complex =
1357 Mat::<c64>::from_fn(n, n, |i, j| c64::new(0.0, a[(i, j)]));
1358 check_tiny_split(complex.as_ref());
1359 }
1360 }
1361 }
1362 #[test]
1366 fn test_zero_block_at_split() {
1367 fn check<T: ComplexField>(
1368 n: usize,
1369 threshold: usize,
1370 want: (bool, bool),
1371 tol: f64,
1372 ) {
1373 let (want_u, want_v) = want;
1374 let a = Mat::<T>::from_fn(n, n, |i, j| {
1375 if i == n / 2 && j == n / 2 {
1376 one()
1377 } else {
1378 zero()
1379 }
1380 });
1381 let params = Spec::new(SvdParams {
1382 recursion_threshold: threshold,
1383 ..auto!(T)
1384 });
1385 let vectors = |want| {
1386 if want {
1387 ComputeSvdVectors::Full
1388 } else {
1389 ComputeSvdVectors::No
1390 }
1391 };
1392 let mut s = Col::<T>::zeros(n);
1393 let mut u = Mat::<T>::zeros(n, if want_u { n } else { 0 });
1394 let mut v = Mat::<T>::zeros(n, if want_v { n } else { 0 });
1395 svd(
1396 a.as_ref(),
1397 s.as_mut().as_diagonal_mut(),
1398 if want_u { Some(u.as_mut()) } else { None },
1399 if want_v { Some(v.as_mut()) } else { None },
1400 Par::Seq,
1401 MemStack::new(&mut MemBuffer::new(svd_scratch::<T>(
1402 n,
1403 n,
1404 vectors(want_u),
1405 vectors(want_v),
1406 ParShape::Seq,
1407 params,
1408 ))),
1409 params,
1410 )
1411 .unwrap();
1412 assert!(s.is_all_finite());
1414 if want_u {
1415 assert!(u.is_all_finite());
1416 }
1417 if want_v {
1418 assert!(v.is_all_finite());
1419 }
1420 let expected =
1421 Col::<T>::from_fn(n, |i| if i == 0 { one() } else { zero() });
1422 let tol = from_f64::<T::Real>(tol);
1423 assert!((&s - &expected).norm_max() <= tol.copy());
1424 let identity = Mat::<T>::identity(n, n);
1425 if want_u && want_v {
1426 let back = &u * s.as_diagonal() * v.adjoint();
1427 assert!((&back - &a).norm_max() <= tol.copy());
1428 }
1429 if want_u {
1430 assert!(
1431 (u.adjoint() * &u - &identity).norm_max() <= tol.copy()
1432 );
1433 }
1434 if want_v {
1435 assert!(
1436 (v.adjoint() * &v - &identity).norm_max() <= tol.copy()
1437 );
1438 }
1439 }
1440 for n in [8usize, 16, 32] {
1441 for threshold in [4, 8] {
1442 for want in
1443 [(true, true), (false, false), (true, false), (false, true)]
1444 {
1445 check::<f32>(n, threshold, want, 1e-5);
1446 check::<c32>(n, threshold, want, 1e-5);
1447 check::<f64>(n, threshold, want, 1e-13);
1448 check::<c64>(n, threshold, want, 1e-13);
1449 }
1450 }
1451 }
1452 let threshold = <SvdParams as Auto<f64>>::auto().recursion_threshold;
1454 check::<f64>(256, threshold, (false, false), 1e-13);
1455 }
1456 #[test]
1460 fn test_extreme_scale() {
1461 fn check<T: ComplexField>(s: f64, tol: f64) {
1462 let scale = from_f64::<T>(s);
1463 let a = Mat::<T>::from_fn(2, 2, |i, j| match (i, j) {
1464 (0, 0) => scale.copy(),
1465 (0, 1) => scale.mul_real(from_f64::<T::Real>(0.5)),
1466 (1, 1) => scale.copy(),
1467 _ => zero(),
1468 });
1469 let params = Spec::new(SvdParams {
1470 recursion_threshold: 4,
1471 ..auto!(T)
1472 });
1473 let mut sigma = Mat::<T>::zeros(2, 2);
1474 let mut u = Mat::<T>::zeros(2, 2);
1475 let mut v = Mat::<T>::zeros(2, 2);
1476 svd(
1477 a.as_ref(),
1478 sigma.as_mut().diagonal_mut(),
1479 Some(u.as_mut()),
1480 Some(v.as_mut()),
1481 Par::Seq,
1482 MemStack::new(&mut MemBuffer::new(svd_scratch::<T>(
1483 2,
1484 2,
1485 ComputeSvdVectors::Thin,
1486 ComputeSvdVectors::Thin,
1487 ParShape::Seq,
1488 params,
1489 ))),
1490 params,
1491 )
1492 .unwrap();
1493 let rec = &u * &sigma * v.adjoint();
1494 let err = (&rec - &a).norm_max();
1495 assert!(rec.is_all_finite());
1496 assert!(err <= a.norm_max() * from_f64::<T::Real>(tol));
1497 }
1498 for s in [1.0f64, 1e160, 1e-300] {
1499 check::<f64>(s, 1e-14);
1500 check::<c64>(s, 1e-14);
1501 }
1502 for s in [1.0f64, 1e30, 1e-35] {
1503 check::<f32>(s, 1e-6);
1504 check::<c32>(s, 1e-6);
1505 }
1506 }
1507 #[test]
1510 fn test_rank_one_subnormal_cascade() {
1511 for n in [16usize, 32, 150] {
1512 test_svd(Mat::<f32>::full(n, n, 1.0).as_ref());
1513 test_svd(Mat::<c32>::full(n, n, c32::ONE).as_ref());
1514 }
1515 for n in [32usize, 150] {
1516 test_svd(Mat::<f64>::full(n, n, 1.0).as_ref());
1517 test_svd(Mat::<c64>::full(n, n, c64::ONE).as_ref());
1518 }
1519 }
1520 #[test]
1521 fn test_zero_child_at_split() {
1522 for n in [128, 256] {
1523 for pos in [0, n / 2, n - 1] {
1524 let a = Mat::<f64>::from_fn(n, n, |i, j| {
1525 if i == pos && j == pos { 1.0 } else { 0.0 }
1526 });
1527 check_tiny_split(a.as_ref());
1528 let complex =
1529 Mat::<c64>::from_fn(n, n, |i, j| c64::new(0.0, a[(i, j)]));
1530 check_tiny_split(complex.as_ref());
1531 }
1532 }
1533 }
1534 #[test]
1535 fn test_real() {
1536 let rng = &mut StdRng::seed_from_u64(1);
1537 for (m, n) in [
1538 (3, 2),
1539 (2, 2),
1540 (4, 4),
1541 (15, 10),
1542 (10, 10),
1543 (15, 15),
1544 (50, 50),
1545 (100, 100),
1546 (150, 150),
1547 (150, 20),
1548 (20, 150),
1549 ] {
1550 let mat = CwiseMatDistribution {
1551 nrows: m,
1552 ncols: n,
1553 dist: StandardNormal,
1554 }
1555 .rand::<Mat<f64>>(rng);
1556 test_svd(mat.as_ref());
1557 }
1558 }
1559 #[test]
1560 fn test_cplx() {
1561 let rng = &mut StdRng::seed_from_u64(1);
1562 for (m, n) in [
1563 (1, 1),
1564 (2, 2),
1565 (3, 2),
1566 (2, 2),
1567 (3, 3),
1568 (4, 4),
1569 (15, 10),
1570 (10, 10),
1571 (15, 15),
1572 (16, 16),
1573 (17, 17),
1574 (18, 18),
1575 (19, 19),
1576 (20, 20),
1577 (30, 30),
1578 (50, 50),
1579 (100, 100),
1580 (150, 150),
1581 (150, 20),
1582 (20, 150),
1583 ] {
1584 let mat = CwiseMatDistribution {
1585 nrows: m,
1586 ncols: n,
1587 dist: ComplexDistribution::new(StandardNormal, StandardNormal),
1588 }
1589 .rand::<Mat<c64>>(rng);
1590 test_svd(mat.as_ref());
1591 }
1592 }
1593 #[test]
1594 fn test_special() {
1595 for (m, n) in [
1596 (3, 2),
1597 (2, 2),
1598 (4, 4),
1599 (15, 10),
1600 (10, 10),
1601 (15, 15),
1602 (50, 50),
1603 (100, 100),
1604 (150, 150),
1605 (150, 20),
1606 (20, 150),
1607 ] {
1608 test_svd(Mat::<f64>::zeros(m, n).as_ref());
1609 test_svd(Mat::<c64>::zeros(m, n).as_ref());
1610 test_svd(Mat::<f64>::full(m, n, 1.0).as_ref());
1611 test_svd(Mat::<c64>::full(m, n, c64::ONE).as_ref());
1612 test_svd(Mat::<f64>::identity(m, n).as_ref());
1613 test_svd(Mat::<c64>::identity(m, n).as_ref());
1614 }
1615 }
1616 #[test]
1617 fn test_zink() {
1618 let diag = [
1619 -9.931833701529301,
1620 -10.920807536026027,
1621 -52.33647796311243,
1622 2.3685025127736967,
1623 2.421701994236093,
1624 -0.5051763005624579,
1625 -0.04808263896606017,
1626 -0.003875251886338955,
1627 -0.0006413264967716465,
1628 -0.003381944152463707,
1629 2.981152313236375e-5,
1630 5.4290648208388795e-6,
1631 -6.329275972084404e-7,
1632 -6.879142344209158e-7,
1633 -5.265228263479126e-9,
1634 -2.941999902335516e-9,
1635 -1.3060984997930294e-10,
1636 7.07516117218088e-12,
1637 1.8657003929029376e-12,
1638 -6.216080089659131e-14,
1639 ];
1640 let subdiag = [
1641 -57.8029649868477,
1642 17.67263066467847,
1643 8.884153814270894,
1644 -9.01998231080713,
1645 -1.028638150814966,
1646 0.22247719217200435,
1647 0.016389886745811315,
1648 -0.004090989452162578,
1649 0.00036818904090536926,
1650 -0.0031394146217732367,
1651 -7.571300829706796e-6,
1652 3.0045718718618155e-6,
1653 2.1329796886727743e-6,
1654 9.259701025627789e-8,
1655 2.2291214755992877e-9,
1656 -2.3017207713252894e-9,
1657 6.807967994979358e-11,
1658 2.1677299575405587e-12,
1659 -3.07282771050034e-13,
1660 0.0,
1661 ];
1662 let n = diag.len();
1663 let params = SvdParams {
1664 recursion_threshold: 8,
1665 qr_ratio_threshold: 1.0,
1666 ..auto!(f64)
1667 };
1668 let mut d = ColRef::from_slice(&diag).to_owned();
1669 let mut s = ColRef::from_slice(&subdiag).to_owned();
1670 compute_bidiag_real_svd(
1671 d.as_mut().try_as_col_major_mut().unwrap(),
1672 s.as_mut().try_as_col_major_mut().unwrap(),
1673 None,
1674 None,
1675 params,
1676 Par::Seq,
1677 MemStack::new(&mut MemBuffer::new(bidiag_real_svd_scratch::<f64>(
1678 n,
1679 false,
1680 false,
1681 ParShape::Seq,
1682 params,
1683 ))),
1684 )
1685 .unwrap();
1686 assert!(d[n - 1] != 0.0);
1687 }
1688 #[test]
1689 fn test_pinv() {
1690 let rng = &mut StdRng::seed_from_u64(0);
1691 let m = 6;
1692 let n = 36;
1693 let mat = CwiseMatDistribution {
1694 nrows: m,
1695 ncols: n,
1696 dist: StandardNormal,
1697 }
1698 .rand::<Mat<f64>>(rng);
1699 let pinv = mat.svd(Par::Seq).unwrap().pseudoinverse(Par::Seq);
1700 let err = &mat * &pinv - Mat::<f64>::identity(m, m);
1701 assert!(err.norm_max() < 1e-10);
1702 }
1703 #[test]
1704 fn test_pinv2() {
1705 let rng = &mut StdRng::seed_from_u64(0);
1706 let m = 36;
1707 let n = 6;
1708 let mat = CwiseMatDistribution {
1709 nrows: m,
1710 ncols: n,
1711 dist: StandardNormal,
1712 }
1713 .rand::<Mat<f64>>(rng);
1714 let pinv = mat.svd(Par::Seq).unwrap().pseudoinverse(Par::Seq);
1715 let err = &pinv * &mat - Mat::<f64>::identity(n, n);
1716 assert!(err.norm_max() < 1e-10);
1717 }
1718 fn cplx_phase_reduction_is_scale_exact<T: ComplexField>(
1723 make: fn(f64, f64) -> T,
1724 ) {
1725 let n = 3;
1726 let base_d = [make(1.8, -0.4), make(-0.3, 0.7), make(0.5, 0.5)];
1727 let base_e = [make(0.2, 0.6), make(-0.8, 0.1)];
1728 let params: SvdParams = auto!(T);
1729 let run = |s: &T::Real| {
1730 let mut d = Col::<T>::from_fn(n, |i| base_d[i].mul_real(s));
1731 let mut e = Col::<T>::from_fn(n, |i| {
1732 if i + 1 < n {
1733 base_e[i].mul_real(s)
1734 } else {
1735 zero()
1736 }
1737 });
1738 let mut u = Mat::<T>::zeros(n + 1, n + 1);
1739 let mut v = Mat::<T>::zeros(n, n);
1740 compute_bidiag_cplx_svd(
1741 d.as_mut().try_as_col_major_mut().unwrap(),
1742 e.as_mut().try_as_col_major_mut().unwrap(),
1743 Some(u.as_mut()),
1744 Some(v.as_mut()),
1745 params,
1746 Par::Seq,
1747 MemStack::new(&mut MemBuffer::new(
1748 bidiag_cplx_svd_scratch::<T>(
1749 n,
1750 true,
1751 true,
1752 ParShape::Seq,
1753 params,
1754 ),
1755 )),
1756 )
1757 .unwrap();
1758 assert!(u.is_all_finite() && v.is_all_finite());
1759 d
1760 };
1761 let reference = run(&one());
1762 let sml = min_positive::<T::Real>();
1763 for s in [sml.recip(), &sml * from_f64::<T::Real>(0.0625)] {
1764 let d = run(&s);
1765 for i in 0..n {
1766 let scaled_back = d[i].real() / &s;
1767 assert!(
1768 (scaled_back - reference[i].real()).abs()
1769 <= from_f64::<T::Real>(1e3)
1770 * eps::<T::Real>() * reference[0].real()
1771 );
1772 }
1773 }
1774 }
1775 #[test]
1776 fn cplx_phase_reduction_outside_normal_range() {
1777 cplx_phase_reduction_is_scale_exact::<c64>(c64::new);
1778 cplx_phase_reduction_is_scale_exact::<c32>(|r, i| {
1779 c32::new(r as f32, i as f32)
1780 });
1781 cplx_phase_reduction_is_scale_exact::<f64>(|r, _| r);
1782 cplx_phase_reduction_is_scale_exact::<f32>(|r, _| r as f32);
1783 }
1784 #[test]
1790 fn test_sorted_with_subnormal_at_split() {
1791 let n = 128;
1792 let entries = [
1795 (41, 80, -1.3368711811580607e-64, 3.48452997681476e-64),
1796 (67, 80, -5.871970588424443e-1, -1.065196368760547e-1),
1797 (5, 112, -2.8153454475366503e-4, 5.652970997533713e-4),
1798 ];
1799 let mut a = Mat::<c64>::zeros(n, n);
1800 for (row, col, re, im) in entries {
1801 a[(row, col)] = c64::new(re, im);
1802 }
1803 let svd = a.as_ref().svd(Par::Seq).unwrap();
1804 let s = svd.S().column_vector();
1805 let values: std::vec::Vec<f64> =
1806 (0..s.nrows()).map(|i| s[i].re).collect();
1807 for i in 0..n {
1808 assert!(values[i] >= 0.0);
1809 assert!(s[i].im == 0.0);
1810 if i + 1 < n {
1811 assert!(
1812 values[i] >= values[i + 1],
1813 "s[{i}] = {:e} < s[{}] = {:e}",
1814 values[i],
1815 i + 1,
1816 values[i + 1],
1817 );
1818 }
1819 }
1820 assert!((values[0] - 0.596780377486914).abs() <= 1e-14);
1822 assert!((values[1] - 6.315239590698253e-4).abs() <= 1e-17);
1823 let u = svd.U();
1824 let v = svd.V();
1825 let rec = &u * svd.S() * v.adjoint();
1826 assert!((&rec - &a).norm_max() <= 1e-14);
1827 assert!(
1828 (u.adjoint() * &u - Mat::<c64>::identity(n, n)).norm_max() <= 1e-14
1829 );
1830 assert!(
1831 (v.adjoint() * &v - Mat::<c64>::identity(n, n)).norm_max() <= 1e-14
1832 );
1833 }
1834 #[test]
1838 fn full_factors_ignore_old_output_entries() {
1839 for (m, n) in [(4, 2), (2, 4)] {
1840 let a = Mat::<f64>::identity(m, n);
1841 let size = Ord::min(m, n);
1842 let mut s = Diag::<f64>::zeros(size);
1843 let mut u = Mat::<f64>::full(m, m, 1.0);
1844 let mut v = Mat::<f64>::full(n, n, 1.0);
1845 svd(
1846 a.as_ref(),
1847 s.as_mut(),
1848 Some(u.as_mut()),
1849 Some(v.as_mut()),
1850 Par::Seq,
1851 MemStack::new(&mut MemBuffer::new(svd_scratch::<f64>(
1852 m,
1853 n,
1854 ComputeSvdVectors::Full,
1855 ComputeSvdVectors::Full,
1856 ParShape::Seq,
1857 default(),
1858 ))),
1859 default(),
1860 )
1861 .unwrap();
1862 let orth_u =
1863 (u.transpose() * &u - Mat::<f64>::identity(m, m)).norm_max();
1864 let orth_v =
1865 (v.transpose() * &v - Mat::<f64>::identity(n, n)).norm_max();
1866 assert!(orth_u < 1e-12, "U orthogonality error {orth_u}");
1867 assert!(orth_v < 1e-12, "V orthogonality error {orth_v}");
1868 let rec = u.as_ref().get(.., ..size) * s.as_ref()
1869 * v.as_ref().get(.., ..size).transpose();
1870 assert!((&rec - &a).norm_max() < 1e-12);
1871 }
1872 }
1873}