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: Par,
70 params: SvdParams,
71 ) -> StackReq,
72 params: SvdParams,
73 par: Par,
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: Par,
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: Par,
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: Par,
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 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,
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,
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, 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: Par,
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 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,
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,
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: Par,
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 use super::*;
941 use crate::assert;
942 use crate::stats::prelude::*;
943 use crate::utils::approx::*;
944 use dyn_stack::MemBuffer;
945 #[track_caller]
946 fn test_svd<T: ComplexField>(mat: MatRef<'_, T>) {
947 let (m, n) = mat.shape();
948 let params = Spec::new(SvdParams {
949 recursion_threshold: 8,
950 qr_ratio_threshold: 1.0,
951 ..auto!(T)
952 });
953 let approx_eq = CwiseMat(
954 ApproxEq::<T::Real>::eps()
955 * from_f64::<T::Real>(8.0 * Ord::max(m, n) as f64).sqrt(),
956 );
957 {
958 let mut s = Mat::zeros(m, n);
959 let mut u = Mat::zeros(m, m);
960 let mut v = Mat::zeros(n, n);
961 svd(
962 mat.as_ref(),
963 s.as_mut().diagonal_mut(),
964 Some(u.as_mut()),
965 Some(v.as_mut()),
966 Par::Seq,
967 MemStack::new(&mut MemBuffer::new(svd_scratch::<T>(
968 m,
969 n,
970 ComputeSvdVectors::Full,
971 ComputeSvdVectors::Full,
972 Par::Seq,
973 params,
974 ))),
975 params,
976 )
977 .unwrap();
978 let reconstructed = &u * &s * v.adjoint();
979 assert!(reconstructed ~ mat);
980 }
981 let size = Ord::min(m, n);
982 let mut s = Mat::zeros(size, size);
983 let mut u = Mat::zeros(m, size);
984 let mut v = Mat::zeros(n, size);
985 {
986 svd(
987 mat.as_ref(),
988 s.as_mut().diagonal_mut(),
989 Some(u.as_mut()),
990 Some(v.as_mut()),
991 Par::Seq,
992 MemStack::new(&mut MemBuffer::new(svd_scratch::<T>(
993 m,
994 n,
995 ComputeSvdVectors::Thin,
996 ComputeSvdVectors::Thin,
997 Par::Seq,
998 params,
999 ))),
1000 params,
1001 )
1002 .unwrap();
1003 let reconstructed = &u * &s * v.adjoint();
1004 assert!(reconstructed ~ mat);
1005 }
1006 {
1007 let mut s2 = Mat::zeros(size, size);
1008 let mut u2 = Mat::zeros(m, size);
1009 svd(
1010 mat.as_ref(),
1011 s2.as_mut().diagonal_mut(),
1012 Some(u2.as_mut()),
1013 None,
1014 Par::Seq,
1015 MemStack::new(&mut MemBuffer::new(svd_scratch::<T>(
1016 m,
1017 n,
1018 ComputeSvdVectors::Thin,
1019 ComputeSvdVectors::No,
1020 Par::Seq,
1021 params,
1022 ))),
1023 params,
1024 )
1025 .unwrap();
1026 assert!(s2 ~ s);
1027 assert!(u2 ~ u);
1028 }
1029 {
1030 let mut s2 = Mat::zeros(size, size);
1031 let mut v2 = Mat::zeros(n, size);
1032 svd(
1033 mat.as_ref(),
1034 s2.as_mut().diagonal_mut(),
1035 None,
1036 Some(v2.as_mut()),
1037 Par::Seq,
1038 MemStack::new(&mut MemBuffer::new(svd_scratch::<T>(
1039 m,
1040 n,
1041 ComputeSvdVectors::No,
1042 ComputeSvdVectors::Thin,
1043 Par::Seq,
1044 params,
1045 ))),
1046 params,
1047 )
1048 .unwrap();
1049 assert!(s2 ~ s);
1050 assert!(v2 ~ v);
1051 }
1052 {
1053 let mut s2 = Mat::zeros(size, size);
1054 svd(
1055 mat.as_ref(),
1056 s2.as_mut().diagonal_mut(),
1057 None,
1058 None,
1059 Par::Seq,
1060 MemStack::new(&mut MemBuffer::new(svd_scratch::<T>(
1061 m,
1062 n,
1063 ComputeSvdVectors::No,
1064 ComputeSvdVectors::No,
1065 Par::Seq,
1066 params,
1067 ))),
1068 params,
1069 )
1070 .unwrap();
1071 assert!(s2 ~ s);
1072 }
1073 }
1074 fn check_tiny_split<T: ComplexField>(a: MatRef<'_, T>) {
1075 let (m, n) = a.shape();
1076 let size = Ord::min(m, n);
1077 let compute = |threshold, want_u, want_v| {
1078 let params = Spec::new(SvdParams {
1079 recursion_threshold: threshold,
1080 ..auto!(T)
1081 });
1082 let mut s = Col::zeros(size);
1083 let mut u = Mat::zeros(m, if want_u { size } else { 0 });
1084 let mut v = Mat::zeros(n, if want_v { size } else { 0 });
1085 let vectors = |want| {
1086 if want {
1087 ComputeSvdVectors::Thin
1088 } else {
1089 ComputeSvdVectors::No
1090 }
1091 };
1092 svd(
1093 a,
1094 s.as_mut().as_diagonal_mut(),
1095 if want_u { Some(u.as_mut()) } else { None },
1096 if want_v { Some(v.as_mut()) } else { None },
1097 Par::Seq,
1098 MemStack::new(&mut MemBuffer::new(svd_scratch::<T>(
1099 m,
1100 n,
1101 vectors(want_u),
1102 vectors(want_v),
1103 Par::Seq,
1104 params,
1105 ))),
1106 params,
1107 )
1108 .unwrap();
1109 (s, u, v)
1110 };
1111 let (reference, _, _) = compute(usize::MAX, true, true);
1112 for (want_u, want_v) in
1113 [(true, true), (false, false), (true, false), (false, true)]
1114 {
1115 let (s, u, v) = compute(128, want_u, want_v);
1116 assert!((&s - &reference).norm_max() < from_f64::<T::Real>(1e-13));
1117 if want_u {
1118 assert!(
1119 (u.adjoint() * &u - Mat::<T>::identity(size, size))
1120 .norm_l2() < from_f64::<T::Real>(1e-13)
1121 );
1122 }
1123 if want_v {
1124 assert!(
1125 (v.adjoint() * &v - Mat::<T>::identity(size, size))
1126 .norm_l2() < from_f64::<T::Real>(1e-13)
1127 );
1128 }
1129 if want_u && want_v {
1130 let back = &u * s.as_diagonal() * v.adjoint();
1131 assert!(
1132 (&back - a).norm_l2() / a.norm_l2()
1133 < from_f64::<T::Real>(1e-13)
1134 );
1135 }
1136 }
1137 }
1138 #[test]
1139 fn test_tiny_diagonal_at_split() {
1140 for n in [128, 256] {
1141 for tiny in [0.0, 1e-15, 1e-13, 1e-10, 1e-6] {
1142 let a = Mat::<f64>::from_fn(n, n, |i, j| {
1143 if (i == j && i < 64) || (j == i + 1 && i < 64) {
1144 1.0
1145 } else if i == 64 && j == 64 {
1146 tiny
1147 } else {
1148 0.0
1149 }
1150 });
1151 check_tiny_split(a.as_ref());
1152 let complex =
1153 Mat::<c64>::from_fn(n, n, |i, j| c64::new(0.0, a[(i, j)]));
1154 check_tiny_split(complex.as_ref());
1155 }
1156 }
1157 }
1158 #[test]
1162 fn test_zero_block_at_split() {
1163 fn check<T: ComplexField>(
1164 n: usize,
1165 threshold: usize,
1166 want: (bool, bool),
1167 tol: f64,
1168 ) {
1169 let (want_u, want_v) = want;
1170 let a = Mat::<T>::from_fn(n, n, |i, j| {
1171 if i == n / 2 && j == n / 2 {
1172 one()
1173 } else {
1174 zero()
1175 }
1176 });
1177 let params = Spec::new(SvdParams {
1178 recursion_threshold: threshold,
1179 ..auto!(T)
1180 });
1181 let vectors = |want| {
1182 if want {
1183 ComputeSvdVectors::Full
1184 } else {
1185 ComputeSvdVectors::No
1186 }
1187 };
1188 let mut s = Col::<T>::zeros(n);
1189 let mut u = Mat::<T>::zeros(n, if want_u { n } else { 0 });
1190 let mut v = Mat::<T>::zeros(n, if want_v { n } else { 0 });
1191 svd(
1192 a.as_ref(),
1193 s.as_mut().as_diagonal_mut(),
1194 if want_u { Some(u.as_mut()) } else { None },
1195 if want_v { Some(v.as_mut()) } else { None },
1196 Par::Seq,
1197 MemStack::new(&mut MemBuffer::new(svd_scratch::<T>(
1198 n,
1199 n,
1200 vectors(want_u),
1201 vectors(want_v),
1202 Par::Seq,
1203 params,
1204 ))),
1205 params,
1206 )
1207 .unwrap();
1208 assert!(s.is_all_finite());
1210 if want_u {
1211 assert!(u.is_all_finite());
1212 }
1213 if want_v {
1214 assert!(v.is_all_finite());
1215 }
1216 let expected =
1217 Col::<T>::from_fn(n, |i| if i == 0 { one() } else { zero() });
1218 let tol = from_f64::<T::Real>(tol);
1219 assert!((&s - &expected).norm_max() <= tol.copy());
1220 let identity = Mat::<T>::identity(n, n);
1221 if want_u && want_v {
1222 let back = &u * s.as_diagonal() * v.adjoint();
1223 assert!((&back - &a).norm_max() <= tol.copy());
1224 }
1225 if want_u {
1226 assert!(
1227 (u.adjoint() * &u - &identity).norm_max() <= tol.copy()
1228 );
1229 }
1230 if want_v {
1231 assert!(
1232 (v.adjoint() * &v - &identity).norm_max() <= tol.copy()
1233 );
1234 }
1235 }
1236 for n in [8usize, 16, 32] {
1237 for threshold in [4, 8] {
1238 for want in
1239 [(true, true), (false, false), (true, false), (false, true)]
1240 {
1241 check::<f32>(n, threshold, want, 1e-5);
1242 check::<c32>(n, threshold, want, 1e-5);
1243 check::<f64>(n, threshold, want, 1e-13);
1244 check::<c64>(n, threshold, want, 1e-13);
1245 }
1246 }
1247 }
1248 let threshold = <SvdParams as Auto<f64>>::auto().recursion_threshold;
1250 check::<f64>(256, threshold, (false, false), 1e-13);
1251 }
1252 #[test]
1256 fn test_extreme_scale() {
1257 fn check<T: ComplexField>(s: f64, tol: f64) {
1258 let scale = from_f64::<T>(s);
1259 let a = Mat::<T>::from_fn(2, 2, |i, j| match (i, j) {
1260 (0, 0) => scale.copy(),
1261 (0, 1) => scale.mul_real(from_f64::<T::Real>(0.5)),
1262 (1, 1) => scale.copy(),
1263 _ => zero(),
1264 });
1265 let params = Spec::new(SvdParams {
1266 recursion_threshold: 4,
1267 ..auto!(T)
1268 });
1269 let mut sigma = Mat::<T>::zeros(2, 2);
1270 let mut u = Mat::<T>::zeros(2, 2);
1271 let mut v = Mat::<T>::zeros(2, 2);
1272 svd(
1273 a.as_ref(),
1274 sigma.as_mut().diagonal_mut(),
1275 Some(u.as_mut()),
1276 Some(v.as_mut()),
1277 Par::Seq,
1278 MemStack::new(&mut MemBuffer::new(svd_scratch::<T>(
1279 2,
1280 2,
1281 ComputeSvdVectors::Thin,
1282 ComputeSvdVectors::Thin,
1283 Par::Seq,
1284 params,
1285 ))),
1286 params,
1287 )
1288 .unwrap();
1289 let rec = &u * &sigma * v.adjoint();
1290 let err = (&rec - &a).norm_max();
1291 assert!(rec.is_all_finite());
1292 assert!(err <= a.norm_max() * from_f64::<T::Real>(tol));
1293 }
1294 for s in [1.0f64, 1e160, 1e-300] {
1295 check::<f64>(s, 1e-14);
1296 check::<c64>(s, 1e-14);
1297 }
1298 for s in [1.0f64, 1e30, 1e-35] {
1299 check::<f32>(s, 1e-6);
1300 check::<c32>(s, 1e-6);
1301 }
1302 }
1303 #[test]
1306 fn test_rank_one_subnormal_cascade() {
1307 for n in [16usize, 32, 150] {
1308 test_svd(Mat::<f32>::full(n, n, 1.0).as_ref());
1309 test_svd(Mat::<c32>::full(n, n, c32::ONE).as_ref());
1310 }
1311 for n in [32usize, 150] {
1312 test_svd(Mat::<f64>::full(n, n, 1.0).as_ref());
1313 test_svd(Mat::<c64>::full(n, n, c64::ONE).as_ref());
1314 }
1315 }
1316 #[test]
1317 fn test_zero_child_at_split() {
1318 for n in [128, 256] {
1319 for pos in [0, n / 2, n - 1] {
1320 let a = Mat::<f64>::from_fn(n, n, |i, j| {
1321 if i == pos && j == pos { 1.0 } else { 0.0 }
1322 });
1323 check_tiny_split(a.as_ref());
1324 let complex =
1325 Mat::<c64>::from_fn(n, n, |i, j| c64::new(0.0, a[(i, j)]));
1326 check_tiny_split(complex.as_ref());
1327 }
1328 }
1329 }
1330 #[test]
1331 fn test_real() {
1332 let rng = &mut StdRng::seed_from_u64(1);
1333 for (m, n) in [
1334 (3, 2),
1335 (2, 2),
1336 (4, 4),
1337 (15, 10),
1338 (10, 10),
1339 (15, 15),
1340 (50, 50),
1341 (100, 100),
1342 (150, 150),
1343 (150, 20),
1344 (20, 150),
1345 ] {
1346 let mat = CwiseMatDistribution {
1347 nrows: m,
1348 ncols: n,
1349 dist: StandardNormal,
1350 }
1351 .rand::<Mat<f64>>(rng);
1352 test_svd(mat.as_ref());
1353 }
1354 }
1355 #[test]
1356 fn test_cplx() {
1357 let rng = &mut StdRng::seed_from_u64(1);
1358 for (m, n) in [
1359 (1, 1),
1360 (2, 2),
1361 (3, 2),
1362 (2, 2),
1363 (3, 3),
1364 (4, 4),
1365 (15, 10),
1366 (10, 10),
1367 (15, 15),
1368 (16, 16),
1369 (17, 17),
1370 (18, 18),
1371 (19, 19),
1372 (20, 20),
1373 (30, 30),
1374 (50, 50),
1375 (100, 100),
1376 (150, 150),
1377 (150, 20),
1378 (20, 150),
1379 ] {
1380 let mat = CwiseMatDistribution {
1381 nrows: m,
1382 ncols: n,
1383 dist: ComplexDistribution::new(StandardNormal, StandardNormal),
1384 }
1385 .rand::<Mat<c64>>(rng);
1386 test_svd(mat.as_ref());
1387 }
1388 }
1389 #[test]
1390 fn test_special() {
1391 for (m, n) in [
1392 (3, 2),
1393 (2, 2),
1394 (4, 4),
1395 (15, 10),
1396 (10, 10),
1397 (15, 15),
1398 (50, 50),
1399 (100, 100),
1400 (150, 150),
1401 (150, 20),
1402 (20, 150),
1403 ] {
1404 test_svd(Mat::<f64>::zeros(m, n).as_ref());
1405 test_svd(Mat::<c64>::zeros(m, n).as_ref());
1406 test_svd(Mat::<f64>::full(m, n, 1.0).as_ref());
1407 test_svd(Mat::<c64>::full(m, n, c64::ONE).as_ref());
1408 test_svd(Mat::<f64>::identity(m, n).as_ref());
1409 test_svd(Mat::<c64>::identity(m, n).as_ref());
1410 }
1411 }
1412 #[test]
1413 fn test_zink() {
1414 let diag = [
1415 -9.931833701529301,
1416 -10.920807536026027,
1417 -52.33647796311243,
1418 2.3685025127736967,
1419 2.421701994236093,
1420 -0.5051763005624579,
1421 -0.04808263896606017,
1422 -0.003875251886338955,
1423 -0.0006413264967716465,
1424 -0.003381944152463707,
1425 2.981152313236375e-5,
1426 5.4290648208388795e-6,
1427 -6.329275972084404e-7,
1428 -6.879142344209158e-7,
1429 -5.265228263479126e-9,
1430 -2.941999902335516e-9,
1431 -1.3060984997930294e-10,
1432 7.07516117218088e-12,
1433 1.8657003929029376e-12,
1434 -6.216080089659131e-14,
1435 ];
1436 let subdiag = [
1437 -57.8029649868477,
1438 17.67263066467847,
1439 8.884153814270894,
1440 -9.01998231080713,
1441 -1.028638150814966,
1442 0.22247719217200435,
1443 0.016389886745811315,
1444 -0.004090989452162578,
1445 0.00036818904090536926,
1446 -0.0031394146217732367,
1447 -7.571300829706796e-6,
1448 3.0045718718618155e-6,
1449 2.1329796886727743e-6,
1450 9.259701025627789e-8,
1451 2.2291214755992877e-9,
1452 -2.3017207713252894e-9,
1453 6.807967994979358e-11,
1454 2.1677299575405587e-12,
1455 -3.07282771050034e-13,
1456 0.0,
1457 ];
1458 let n = diag.len();
1459 let params = SvdParams {
1460 recursion_threshold: 8,
1461 qr_ratio_threshold: 1.0,
1462 ..auto!(f64)
1463 };
1464 let mut d = ColRef::from_slice(&diag).to_owned();
1465 let mut s = ColRef::from_slice(&subdiag).to_owned();
1466 compute_bidiag_real_svd(
1467 d.as_mut().try_as_col_major_mut().unwrap(),
1468 s.as_mut().try_as_col_major_mut().unwrap(),
1469 None,
1470 None,
1471 params,
1472 Par::Seq,
1473 MemStack::new(&mut MemBuffer::new(bidiag_real_svd_scratch::<f64>(
1474 n,
1475 false,
1476 false,
1477 Par::Seq,
1478 params,
1479 ))),
1480 )
1481 .unwrap();
1482 assert!(d[n - 1] != 0.0);
1483 }
1484 #[test]
1485 fn test_pinv() {
1486 let rng = &mut StdRng::seed_from_u64(0);
1487 let m = 6;
1488 let n = 36;
1489 let mat = CwiseMatDistribution {
1490 nrows: m,
1491 ncols: n,
1492 dist: StandardNormal,
1493 }
1494 .rand::<Mat<f64>>(rng);
1495 let pinv = mat.svd().unwrap().pseudoinverse();
1496 let err = &mat * &pinv - Mat::<f64>::identity(m, m);
1497 assert!(err.norm_max() < 1e-10);
1498 }
1499 #[test]
1500 fn test_pinv2() {
1501 let rng = &mut StdRng::seed_from_u64(0);
1502 let m = 36;
1503 let n = 6;
1504 let mat = CwiseMatDistribution {
1505 nrows: m,
1506 ncols: n,
1507 dist: StandardNormal,
1508 }
1509 .rand::<Mat<f64>>(rng);
1510 let pinv = mat.svd().unwrap().pseudoinverse();
1511 let err = &pinv * &mat - Mat::<f64>::identity(n, n);
1512 assert!(err.norm_max() < 1e-10);
1513 }
1514 fn cplx_phase_reduction_is_scale_exact<T: ComplexField>(
1519 make: fn(f64, f64) -> T,
1520 ) {
1521 let n = 3;
1522 let base_d = [make(1.8, -0.4), make(-0.3, 0.7), make(0.5, 0.5)];
1523 let base_e = [make(0.2, 0.6), make(-0.8, 0.1)];
1524 let params: SvdParams = auto!(T);
1525 let run = |s: &T::Real| {
1526 let mut d = Col::<T>::from_fn(n, |i| base_d[i].mul_real(s));
1527 let mut e = Col::<T>::from_fn(n, |i| {
1528 if i + 1 < n {
1529 base_e[i].mul_real(s)
1530 } else {
1531 zero()
1532 }
1533 });
1534 let mut u = Mat::<T>::zeros(n + 1, n + 1);
1535 let mut v = Mat::<T>::zeros(n, n);
1536 compute_bidiag_cplx_svd(
1537 d.as_mut().try_as_col_major_mut().unwrap(),
1538 e.as_mut().try_as_col_major_mut().unwrap(),
1539 Some(u.as_mut()),
1540 Some(v.as_mut()),
1541 params,
1542 Par::Seq,
1543 MemStack::new(&mut MemBuffer::new(
1544 bidiag_cplx_svd_scratch::<T>(
1545 n,
1546 true,
1547 true,
1548 Par::Seq,
1549 params,
1550 ),
1551 )),
1552 )
1553 .unwrap();
1554 assert!(u.is_all_finite() && v.is_all_finite());
1555 d
1556 };
1557 let reference = run(&one());
1558 let sml = min_positive::<T::Real>();
1559 for s in [sml.recip(), &sml * from_f64::<T::Real>(0.0625)] {
1560 let d = run(&s);
1561 for i in 0..n {
1562 let scaled_back = d[i].real() / &s;
1563 assert!(
1564 (scaled_back - reference[i].real()).abs()
1565 <= from_f64::<T::Real>(1e3)
1566 * eps::<T::Real>() * reference[0].real()
1567 );
1568 }
1569 }
1570 }
1571 #[test]
1572 fn cplx_phase_reduction_outside_normal_range() {
1573 cplx_phase_reduction_is_scale_exact::<c64>(c64::new);
1574 cplx_phase_reduction_is_scale_exact::<c32>(|r, i| {
1575 c32::new(r as f32, i as f32)
1576 });
1577 cplx_phase_reduction_is_scale_exact::<f64>(|r, _| r);
1578 cplx_phase_reduction_is_scale_exact::<f32>(|r, _| r as f32);
1579 }
1580 #[test]
1584 fn full_factors_ignore_old_output_entries() {
1585 for (m, n) in [(4, 2), (2, 4)] {
1586 let a = Mat::<f64>::identity(m, n);
1587 let size = Ord::min(m, n);
1588 let mut s = Diag::<f64>::zeros(size);
1589 let mut u = Mat::<f64>::full(m, m, 1.0);
1590 let mut v = Mat::<f64>::full(n, n, 1.0);
1591 svd(
1592 a.as_ref(),
1593 s.as_mut(),
1594 Some(u.as_mut()),
1595 Some(v.as_mut()),
1596 Par::Seq,
1597 MemStack::new(&mut MemBuffer::new(svd_scratch::<f64>(
1598 m,
1599 n,
1600 ComputeSvdVectors::Full,
1601 ComputeSvdVectors::Full,
1602 Par::Seq,
1603 default(),
1604 ))),
1605 default(),
1606 )
1607 .unwrap();
1608 let orth_u =
1609 (u.transpose() * &u - Mat::<f64>::identity(m, m)).norm_max();
1610 let orth_v =
1611 (v.transpose() * &v - Mat::<f64>::identity(n, n)).norm_max();
1612 assert!(orth_u < 1e-12, "U orthogonality error {orth_u}");
1613 assert!(orth_v < 1e-12, "V orthogonality error {orth_v}");
1614 let rec = u.as_ref().get(.., ..size) * s.as_ref()
1615 * v.as_ref().get(.., ..size).transpose();
1616 assert!((&rec - &a).norm_max() < 1e-12);
1617 }
1618 }
1619}