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