1use crate::assert;
2use crate::internal_prelude::*;
3use linalg::householder::*;
4use crate::utils::cost::{ParThreshold, rate};
5use crate::utils::thread::with_width;
6use linalg::matmul::{dot, matmul};
7
8const BLOCK_SIZE: usize = 16;
19const BLOCK_FLOOR: usize = 128;
23
24pub fn bidiag_in_place_scratch<T: ComplexField>(
27 nrows: usize,
28 ncols: usize,
29 par: ParShape,
30 params: Spec<BidiagParams, T>,
31) -> StackReq {
32 _ = par;
33 _ = params;
34 let tail = StackReq::all_of(&[
35 temp_mat_scratch::<T>(nrows, 1),
36 temp_mat_scratch::<T>(ncols, 1),
37 temp_mat_scratch::<T>(ncols, 1),
38 temp_mat_scratch::<T>(ncols, 1),
39 ]);
40 if Ord::min(nrows, ncols) > BLOCK_FLOOR {
41 let blocked = StackReq::all_of(&[
44 temp_mat_scratch::<T>(nrows, BLOCK_SIZE),
45 temp_mat_scratch::<T>(ncols, BLOCK_SIZE),
46 temp_mat_scratch::<T>(BLOCK_SIZE, 2),
47 temp_mat_scratch::<T>(ncols, 1),
48 ]);
49 StackReq::any_of(&[blocked, tail])
50 } else {
51 tail
52 }
53}
54#[derive(Debug, Copy, Clone)]
56pub struct BidiagParams {
57 pub par_threshold: ParThreshold,
61 #[doc(hidden)]
62 pub non_exhaustive: NonExhaustive,
63}
64impl<T: ComplexField> Auto<T> for BidiagParams {
65 fn auto() -> Self {
66 Self {
67 par_threshold: ParThreshold::Derived,
68 non_exhaustive: NonExhaustive(()),
69 }
70 }
71}
72pub fn bidiag_in_place<T: ComplexField>(
84 A: MatMut<'_, T>,
85 H_left: MatMut<'_, T>,
86 H_right: MatMut<'_, T>,
87 par: Par,
88 stack: &mut MemStack,
89 params: Spec<BidiagParams, T>,
90) {
91 let params = params.config;
92 let m = A.nrows();
93 let n = A.ncols();
94 let size = Ord::min(m, n);
95 let bl = H_left.nrows();
96 let br = H_right.nrows();
97 assert!(H_left.ncols() == size);
98 assert!(H_right.ncols() == size.saturating_sub(1));
99 let mut A = A;
100 let mut Hl = H_left;
101 let mut Hr = H_right;
102 let mut par = par;
103 if matches!(params.par_threshold, ParThreshold::Never) {
105 par = Par::Seq;
106 }
107 let mut p = 0;
113 if size > BLOCK_FLOOR {
114 p = bidiag_blocked_phase(
115 A.rb_mut(),
116 Hl.rb_mut().row_mut(0),
117 Hr.rb_mut().row_mut(0),
118 par.reborrow(),
119 params.par_threshold,
120 stack,
121 );
122 }
123
124 bidiag_unblocked(
127 A.rb_mut().get_mut(p.., p..),
128 Hl.rb_mut().row_mut(0).get_mut(p..),
129 Hr.rb_mut().row_mut(0).get_mut(p..),
130 par.reborrow(),
131 stack,
132 params.par_threshold,
133 p,
134 );
135
136 let mut j = 0;
137 while j < size {
138 let bl = Ord::min(bl, size - j);
139 let mut Hl = Hl.rb_mut().get_mut(..bl, j..j + bl);
140 for k in 0..bl {
141 Hl[(k, k)] = Hl[(0, k)].copy();
142 }
143 upgrade_householder_factor(
144 Hl.rb_mut(),
145 A.rb().get(j.., j..j + bl),
146 bl,
147 1,
148 par.reborrow(),
149 );
150 j += bl;
151 }
152 if size > 0 {
153 let size = size - 1;
154 let A = A.rb().get(..size, 1..);
155 let mut Hr = Hr.rb_mut().get_mut(.., ..size);
156 let mut j = 0;
157 while j < size {
158 let br = Ord::min(br, size - j);
159 let mut Hr = Hr.rb_mut().get_mut(..br, j..j + br);
160 for k in 0..br {
161 Hr[(k, k)] = Hr[(0, k)].copy();
162 }
163 upgrade_householder_factor(
164 Hr.rb_mut(),
165 A.transpose().get(j.., j..j + br),
166 br,
167 1,
168 par.reborrow(),
169 );
170 j += br;
171 }
172 }
173}
174
175fn bidiag_blocked_phase<T: ComplexField>(
178 A: MatMut<'_, T>,
179 Hl: RowMut<'_, T>,
180 Hr: RowMut<'_, T>,
181 mut par: Par,
182 par_threshold: ParThreshold,
183 stack: &mut MemStack,
184) -> usize {
185 let mut A = A;
186 let mut Hl = Hl;
187 let mut Hr = Hr;
188 let m = A.nrows();
189 let n = A.ncols();
190 let size = Ord::min(m, n);
191 let (mut X, stack) =
192 unsafe { temp_mat_uninit::<T, _, _>(m, BLOCK_SIZE, stack) };
193 let (mut Y, stack) =
194 unsafe { temp_mat_uninit::<T, _, _>(n, BLOCK_SIZE, stack) };
195 let (mut de, stack) =
196 unsafe { temp_mat_uninit::<T, _, _>(BLOCK_SIZE, 2, stack) };
197 let (mut w, _) = unsafe { temp_mat_uninit::<T, _, _>(n, 1, stack) };
198 let mut X = X.as_mat_mut();
199 let mut Y = Y.as_mat_mut();
200 let mut de = de.as_mat_mut();
201 let mut w = w.as_mat_mut();
202 let mut p = 0;
203 while size - p > BLOCK_FLOOR {
204 let width = par_threshold.region_width::<T>(&par, (m - p) * (n - p), rate::bidiag(), 2);
208 let width = if width > 1 { par.degree() } else { 1 };
209 with_width(par.reborrow(), width, |par| {
210 bidiag_blocked_panel(
211 A.rb_mut().get_mut(p.., p..),
212 Hl.rb_mut().get_mut(p..p + BLOCK_SIZE),
213 Hr.rb_mut().get_mut(p..p + BLOCK_SIZE),
214 X.rb_mut().get_mut(p.., ..),
215 Y.rb_mut().get_mut(p.., ..),
216 de.rb_mut(),
217 w.rb_mut().col_mut(0).get_mut(p..),
218 par,
219 );
220 });
221 p += BLOCK_SIZE;
222 }
223 p
224}
225
226fn bidiag_blocked_panel<T: ComplexField>(
247 A: MatMut<'_, T>,
248 Hl: RowMut<'_, T>,
249 Hr: RowMut<'_, T>,
250 X: MatMut<'_, T>,
251 Y: MatMut<'_, T>,
252 de: MatMut<'_, T>,
253 w: ColMut<'_, T>,
254 mut par: Par,
255) {
256 let mut A = A;
257 let mut w = w;
258 let mut Hl = Hl;
259 let mut Hr = Hr;
260 let mut X = X;
261 let mut Y = Y;
262 let mut de = de;
263 let m = A.nrows();
264 let n = A.ncols();
265 let ib = X.ncols();
266 assert!(all(
267 Y.ncols() == ib,
268 X.nrows() == m,
269 Y.nrows() == n,
270 Ord::min(m, n) > ib,
271 Hl.ncols() == ib,
272 Hr.ncols() == ib,
273 ));
274
275 for i in 0..ib {
276 let tl = {
279 let (left, right) = A.rb_mut().split_at_col_mut(i);
280 let (top, mut col) = right.col_mut(0).split_at_row_mut(i);
281 if i > 0 {
282 matmul(
283 col.rb_mut(),
284 Accum::Add,
285 left.rb().get(i.., ..),
286 Y.rb().row(i).get(..i).adjoint(),
287 -one::<T>(),
288 Par::Seq,
289 );
290 matmul(
291 col.rb_mut(),
292 Accum::Add,
293 X.rb().get(i.., ..i),
294 top.rb(),
295 -one::<T>(),
296 Par::Seq,
297 );
298 }
299 let (mut head, tail) = col.split_at_row_mut(1);
301 let head = &mut head[0];
302 let HouseholderInfo { tau: tl, .. } =
303 make_householder_in_place(head, tail);
304 de[(i, 0)] = head.copy();
305 *head = one();
306 Hl[i] = tl.to_cplx();
307 tl
308 };
309 let ref tl_inv = tl.recip();
310
311 #[cfg(feature = "rayon")]
315 {
316 let u = A.rb().col(i).get(i..);
317 let (y_prev, y_i) = Y.rb_mut().split_at_col_mut(i);
318 let (mut t, mut y) = y_i.col_mut(0).split_at_row_mut(i + 1);
319 matmul(
320 y.rb_mut(),
321 Accum::Replace,
322 A.rb().get(i.., i + 1..).adjoint(),
323 u,
324 one(),
325 par.reborrow(),
326 );
327 if i > 0 {
328 let mut t = t.rb_mut().get_mut(..i);
329 matmul(
330 t.rb_mut(),
331 Accum::Replace,
332 A.rb().get(i.., ..i).adjoint(),
333 u,
334 one(),
335 Par::Seq,
336 );
337 matmul(
338 y.rb_mut(),
339 Accum::Add,
340 y_prev.rb().get(i + 1.., ..),
341 t.rb(),
342 -one::<T>(),
343 Par::Seq,
344 );
345 matmul(
346 t.rb_mut(),
347 Accum::Replace,
348 X.rb().get(i.., ..i).adjoint(),
349 u,
350 one(),
351 Par::Seq,
352 );
353 matmul(
354 y.rb_mut(),
355 Accum::Add,
356 A.rb().get(..i, i + 1..).adjoint(),
357 t.rb(),
358 -one::<T>(),
359 Par::Seq,
360 );
361 }
362 z!(y.rb_mut()).for_each(|uz!(y)| *y = y.mul_real(tl_inv));
363 }
364
365 let tr = {
369 let (top, bot) = A.rb_mut().split_at_row_mut(i);
370 let (left, mut row) = bot.row_mut(0).split_at_col_mut(i + 1);
371 let mut w = w.rb_mut().get_mut(i + 1..);
372 w.copy_from(row.rb().transpose());
373 matmul(
374 w.rb_mut(),
375 Accum::Add,
376 Y.rb().get(i + 1.., ..i + 1).conjugate(),
377 left.rb().transpose(),
378 -one::<T>(),
379 Par::Seq,
380 );
381 if i > 0 {
382 matmul(
383 w.rb_mut(),
384 Accum::Add,
385 top.rb().get(.., i + 1..).transpose(),
386 X.rb().row(i).get(..i).transpose(),
387 -one::<T>(),
388 Par::Seq,
389 );
390 }
391 let (mut head, tail) = w.rb_mut().split_at_row_mut(1);
393 let head = &mut head[0];
394 let HouseholderInfo { tau: tr, .. } =
395 make_householder_in_place(head, tail);
396 de[(i, 1)] = head.copy();
397 *head = one();
398 Hr[i] = tr.to_cplx();
399 row.rb_mut().transpose_mut().copy_from(w.rb());
400 tr
401 };
402 let ref tr_inv = tr.recip();
403
404 {
407 let r = w.rb().get(i + 1..).conjugate();
408 let (x_prev, x_i) = X.rb_mut().split_at_col_mut(i);
409 let (mut t, mut x) = x_i.col_mut(0).split_at_row_mut(i + 1);
410 matmul(
411 x.rb_mut(),
412 Accum::Replace,
413 A.rb().get(i + 1.., i + 1..),
414 r,
415 one(),
416 par.reborrow(),
417 );
418 matmul(
419 t.rb_mut(),
420 Accum::Replace,
421 Y.rb().get(i + 1.., ..i + 1).adjoint(),
422 r,
423 one(),
424 Par::Seq,
425 );
426 matmul(
427 x.rb_mut(),
428 Accum::Add,
429 A.rb().get(i + 1.., ..i + 1),
430 t.rb(),
431 -one::<T>(),
432 Par::Seq,
433 );
434 if i > 0 {
435 let mut t = t.rb_mut().get_mut(..i);
436 matmul(
437 t.rb_mut(),
438 Accum::Replace,
439 A.rb().get(..i, i + 1..),
440 r,
441 one(),
442 Par::Seq,
443 );
444 matmul(
445 x.rb_mut(),
446 Accum::Add,
447 x_prev.rb().get(i + 1.., ..),
448 t.rb(),
449 -one::<T>(),
450 Par::Seq,
451 );
452 }
453 z!(x.rb_mut()).for_each(|uz!(x)| *x = x.mul_real(tr_inv));
454 }
455 }
456
457 {
459 let (top, bot) = A.rb_mut().split_at_row_mut(ib);
460 let (v, mut a22) = bot.split_at_col_mut(ib);
461 matmul(
462 a22.rb_mut(),
463 Accum::Add,
464 v.rb(),
465 Y.rb().get(ib.., ..).adjoint(),
466 -one::<T>(),
467 par.reborrow(),
468 );
469 matmul(
470 a22.rb_mut(),
471 Accum::Add,
472 X.rb().get(ib.., ..),
473 top.rb().get(.., ib..),
474 -one::<T>(),
475 par.reborrow(),
476 );
477 }
478 for i in 0..ib {
480 A[(i, i)] = de[(i, 0)].copy();
481 A[(i, i + 1)] = de[(i, 1)].copy();
482 }
483}
484
485fn bidiag_unblocked<T: ComplexField>(
495 A: MatMut<'_, T>,
496 Hl: RowMut<'_, T>,
497 Hr: RowMut<'_, T>,
498 par: Par,
499 stack: &mut MemStack,
500 par_threshold: ParThreshold,
501 row_offset: usize,
502) {
503 let m = A.nrows();
504 let n = A.ncols();
505 let size = Ord::min(m, n);
506 alloca!('stack: {
507 let mut y = unsafe { row![uninit::<T>, n] };
508 let mut z = unsafe { col![uninit::<T>, m] };
509 let mut w0 = unsafe { row![uninit::<T>, n] };
510 let mut w1 = unsafe { row![uninit::<T>, n] };
511 });
512 let mut A = A;
513 let mut Hl = Hl;
514 let mut Hr = Hr;
515 let mut par = par;
516 if matches!(par_threshold, ParThreshold::Never) {
518 par = Par::Seq;
519 }
520 let mut w = w0.rb_mut();
521 let mut wp = w1.rb_mut();
522 for k in 0..size {
523 let mut A = A.rb_mut();
524 let (_, _, A10, A11) = A.rb_mut().split_at_mut(k, k);
525 let (A10, A20) = A10.split_first_row_mut().unwrap();
526 let (mut A11, A12, A21, mut A22) = A11.split_at_mut(1, 1);
527 let mut A12 = A12.row_mut(0);
528 let mut A21 = A21.col_mut(0);
529 let a11 = &mut A11[(0, 0)];
530 let (y1, mut y2) = y.rb_mut().split_at_col_mut(k).1.split_at_col_mut(1);
531 let (z1, mut z2) = z.rb_mut().split_at_row_mut(k).1.split_at_row_mut(1);
532 let mut wk = w.rb_mut().split_at_col_mut(k + 1).1;
534 wk.copy_from(A12.rb());
535 let vp = wp.rb().split_at_col(k + 1).1;
536 if k > 0 {
537 let ref y1 = y1[0].copy();
538 let ref z1 = z1[0].copy();
539 let k1 = k - 1;
540 let ref up0 = A10[k1];
541 let up = A20.rb().col(k1);
542 *a11 -= up0 * y1 + z1;
543 z!(A21.rb_mut(), up.rb(), z2.rb()).for_each(
544 |uz!(a, u, z): Zip!(&mut T, &T, &T)| *a -= u * y1 + z,
545 );
546 z!(wk.rb_mut(), y2.rb(), vp).for_each(
547 |uz!(a, y, v): Zip!(&mut T, &T, &T)| {
548 *a -= up0 * y + z1 * v;
549 },
550 );
551 }
552 let HouseholderInfo { tau: tl, .. } =
553 make_householder_in_place(a11, A21.rb_mut());
554 let ref tl_inv = tl.recip();
555 Hl[k] = tl.to_cplx();
556 let width = par_threshold.region_width::<T>(&par, (m - k - 1) * (n - k - 1), rate::bidiag(), 2);
558 if k > 0 {
559 let k1 = k - 1;
560 let up = A20.rb().col(k1);
561 with_width(par.reborrow(), width, |mut par| {
562 match par.reborrow() {
563 Par::Seq => bidiag_fused_op(
564 A22.rb_mut(),
565 A21.rb(),
566 up.rb(),
567 z2.rb(),
568 y2.rb_mut(),
569 vp,
570 simd_align(row_offset + k + 1),
571 ),
572 #[cfg(feature = "rayon")]
573 _ => {
574 use rayon::prelude::*;
575 let nthreads =
576 core::num::NonZeroUsize::new(par.degree()).unwrap().get();
577 crate::utils::thread::for_each(
578 par.reborrow(),
579 nthreads,
580 A22.rb_mut()
581 .par_col_partition_mut(nthreads)
582 .zip_eq(y2.rb_mut().par_partition_mut(nthreads))
583 .zip_eq(vp.par_partition(nthreads)),
584 |((A22, y2), vp)| {
585 bidiag_fused_op(
586 A22,
587 A21.rb(),
588 up.rb(),
589 z2.rb(),
590 y2,
591 vp.rb(),
592 simd_align(row_offset + k + 1),
593 );
594 },
595 );
596 },
597 }
598 });
599 } else {
600 with_width(par.reborrow(), width, |par| {
601 matmul(
602 y2.rb_mut(),
603 Accum::Replace,
604 A21.rb().adjoint(),
605 A22.rb(),
606 one(),
607 par,
608 );
609 });
610 }
611 z!(y2.rb_mut(), wk.rb_mut()).for_each(|uz!(y, a)| {
612 *y = (&*y + &*a).mul_real(tl_inv);
613 *a -= &*y;
614 });
615 let norm = wk.rb().norm_l2();
619 if norm != zero() {
620 let ref norm_inv = if norm >= min_positive() {
621 (&norm).recip()
622 } else {
623 let ref shift = from_f64::<T::Real>(4503599627370496.0);
624 z!(wk.rb_mut()).for_each(|uz!(a)| *a = a.mul_real(shift));
625 (&norm * shift).recip()
626 };
627 z!(wk.rb_mut()).for_each(|uz!(a)| *a = a.mul_real(norm_inv));
628 }
629 with_width(par.reborrow(), width, |par| {
630 matmul(
631 z2.rb_mut(),
632 Accum::Replace,
633 A22.rb(),
634 wk.rb().adjoint(),
635 one(),
636 par,
637 );
638 });
639 if k + 1 == size {
640 A12.copy_from(wk.rb());
641 break;
642 }
643 let (mut wk_a, mut wk_b) = wk.rb_mut().split_at_col_mut(1);
644 let A22_a = A22.rb().col(0);
645 let (y2_a, y2_b) = y2.rb().split_at_col(1);
646 let y2_a = &y2_a[0];
647 let a12_a = &mut wk_a[0];
648 let HouseholderInfo {
649 tau: tr,
650 head_with_beta_inv: m,
651 ..
652 } = make_householder_in_place(a12_a, wk_b.rb_mut().transpose_mut());
653 let ref tr_inv = tr.recip();
654 Hr[k] = tr.to_cplx();
655 let beta = a12_a.copy();
656 *a12_a = a12_a.mul_real(norm);
657 let ref b = y2_a
658 + dot::inner_prod(y2_b, Conj::No, wk_b.rb().transpose(), Conj::Yes);
659 if m != infinity() {
660 z!(z2.rb_mut(), A21.rb(), A22_a.rb()).for_each(
661 |uz!(z, u, a): Zip!(&mut T, &T, &T)| {
662 let w = &*z - a * beta.conj();
663 let w = w * m.conj();
664 let w = w - u * b;
665 *z = w.mul_real(tr_inv);
666 },
667 );
668 } else {
669 z!(z2.rb_mut(), A21.rb(), A22_a.rb()).for_each(
670 |uz!(z, u, a): Zip!(&mut T, &T, &T)| {
671 let w = a - u * b;
672 *z = w.mul_real(tr_inv);
673 },
674 );
675 }
676 A12.copy_from(wk.rb());
679 core::mem::swap(&mut w, &mut wp);
680 }
681}
682fn bidiag_fused_op<T: ComplexField>(
683 A22: MatMut<'_, T>,
684 u: ColRef<'_, T>,
685 up: ColRef<'_, T>,
686 z: ColRef<'_, T>,
687 y: RowMut<'_, T>,
688 vp: RowRef<'_, T>,
689 align: usize,
690) {
691 let mut A22 = A22;
692 if const { T::SIMD_CAPABILITIES.is_simd() } {
693 if let (Some(A22), Some(u), Some(up), Some(z)) = (
694 A22.rb_mut().try_as_col_major_mut(),
695 u.try_as_col_major(),
696 up.try_as_col_major(),
697 z.try_as_col_major(),
698 ) {
699 bidiag_fused_op_simd(A22, u, up, z, y, vp, align);
700 } else {
701 bidiag_fused_op_fallback(A22, u, up, z, y, vp);
702 }
703 } else {
704 bidiag_fused_op_fallback(A22, u, up, z, y, vp);
705 }
706}
707fn bidiag_fused_op_fallback<T: ComplexField>(
708 A22: MatMut<'_, T>,
709 u: ColRef<'_, T>,
710 up: ColRef<'_, T>,
711 z: ColRef<'_, T>,
712 y: RowMut<'_, T>,
713 vp: RowRef<'_, T>,
714) {
715 let mut A22 = A22;
716 let mut y = y;
717 matmul(A22.rb_mut(), Accum::Add, up, y.rb(), -one::<T>(), Par::Seq);
718 matmul(A22.rb_mut(), Accum::Add, z, vp, -one::<T>(), Par::Seq);
719 matmul(
720 y.rb_mut(),
721 Accum::Replace,
722 u.adjoint(),
723 A22.rb(),
724 one(),
725 Par::Seq,
726 );
727}
728fn bidiag_fused_op_simd<'M, 'N, T: ComplexField>(
729 A22: MatMut<'_, T, usize, usize, ContiguousFwd>,
730 u: ColRef<'_, T, usize, ContiguousFwd>,
731 up: ColRef<'_, T, usize, ContiguousFwd>,
732 z: ColRef<'_, T, usize, ContiguousFwd>,
733 y: RowMut<'_, T, usize>,
734 vp: RowRef<'_, T, usize>,
735 align: usize,
736) {
737 struct Impl<'a, 'M, 'N, T: ComplexField> {
738 A22: MatMut<'a, T, Dim<'M>, Dim<'N>, ContiguousFwd>,
739 u: ColRef<'a, T, Dim<'M>, ContiguousFwd>,
740 up: ColRef<'a, T, Dim<'M>, ContiguousFwd>,
741 z: ColRef<'a, T, Dim<'M>, ContiguousFwd>,
742 y: RowMut<'a, T, Dim<'N>>,
743 vp: RowRef<'a, T, Dim<'N>>,
744 align: usize,
745 }
746 impl<'a, 'M, 'N, T: ComplexField> pulp::WithSimd for Impl<'a, 'M, 'N, T> {
747 type Output = ();
748
749 #[inline(always)]
750 fn with_simd<S: pulp::Simd>(self, simd: S) -> Self::Output {
751 let Self {
752 mut A22,
753 u,
754 up,
755 z,
756 mut y,
757 vp,
758 align,
759 } = self;
760 let m = A22.nrows();
761 let n = A22.ncols();
762 let simd = SimdCtx::<T, S>::new_align(T::simd_ctx(simd), m, align);
763 let indices = simd.batch_indices::<4>();
764 for j in n.indices() {
765 let mut A = A22.rb_mut().col_mut(j);
766 let mut acc = [simd.zero(); 4];
767 let yj = simd.splat(-&y[j]);
768 let vj = simd.splat(-&vp[j]);
769 simd_iter!(for (IDX, i) in [indices; 4] {
770 let mut a = simd.read(A.rb(), i);
771 a = simd.mul_add(simd.read(up, i), yj, a);
772 a = simd.mul_add(simd.read(z, i), vj, a);
773 simd.write(A.rb_mut(), i, a);
774 acc[IDX] = simd.conj_mul_add(simd.read(u, i), a, acc[IDX]);
775 });
776 let acc0 = simd.add(acc[0], acc[1]);
777 let acc2 = simd.add(acc[2], acc[3]);
778 let acc0 = simd.add(acc0, acc2);
779 y[j] = simd.reduce_sum(acc0);
780 }
781 }
782 }
783 with_dim!(M, A22.nrows());
784 with_dim!(N, A22.ncols());
785 dispatch!(
786 Impl {
787 A22: A22.as_shape_mut(M, N),
788 u: u.as_row_shape(M),
789 up: up.as_row_shape(M),
790 z: z.as_row_shape(M),
791 y: y.as_col_shape_mut(N),
792 vp: vp.as_col_shape(N),
793 align,
794 },
795 Impl,
796 T
797 )
798}
799#[cfg(test)]
800mod tests {
801 use super::*;
802 use crate::stats::prelude::*;
803 use crate::utils::approx::*;
804 use crate::{Mat, assert, c64};
805 use dyn_stack::MemBuffer;
806
807 fn reconstruct<T: ComplexField>(
809 A: &Mat<T>,
810 mut par: Par,
811 params: BidiagParams,
812 ) -> (Mat<T>, Mat<T>) {
813 let m = A.nrows();
814 let n = A.ncols();
815 let size = Ord::min(m, n);
816 let bl = 4;
817 let br = 3;
818 let mut Hl = Mat::zeros(bl, size);
819 let mut Hr = Mat::zeros(br, size - 1);
820 let mut UV = A.clone();
821 let mut mem =
822 MemBuffer::new(bidiag_in_place_scratch::<T>(m, n, par.shape(), params.into()));
823 bidiag_in_place(
824 UV.rb_mut(),
825 Hl.rb_mut(),
826 Hr.rb_mut(),
827 par.reborrow(),
828 MemStack::new(&mut mem),
829 params.into(),
830 );
831 let mut B = A.clone();
832 let mut B = B.as_mut();
833 apply_block_householder_sequence_transpose_on_the_left_in_place_with_conj(
834 UV.rb().get(.., ..size),
835 Hl.rb(),
836 Conj::Yes,
837 B.rb_mut(),
838 par.reborrow(),
839 MemStack::new(&mut MemBuffer::new(
840 apply_block_householder_sequence_transpose_on_the_left_in_place_scratch::<T>(
841 n - 1, 1, m,
842 ),
843 )),
844 );
845 let V = UV.rb().get(..size - 1, 1..size);
846 let B1 = B.rb_mut().get_mut(.., 1..size);
847 apply_block_householder_sequence_on_the_right_in_place_with_conj(
848 V.transpose(),
849 Hr.rb(),
850 Conj::Yes,
851 B1,
852 par.reborrow(),
853 MemStack::new(&mut MemBuffer::new(
854 apply_block_householder_sequence_on_the_right_in_place_scratch::<
855 T,
856 >(n - 1, 1, m),
857 )),
858 );
859 for j in 0..n {
860 for i in 0..m {
861 if i > j || j > i + 1 {
862 UV[(i, j)] = zero();
863 }
864 }
865 }
866 (UV, B.to_owned())
867 }
868
869 const SHAPES: [(usize, usize); 5] =
872 [(8, 4), (8, 8), (300, 300), (400, 300), (200, 200)];
873
874 fn for_each_budget(mut f: impl FnMut(Par<'_>, BidiagParams)) {
876 f(Par::Seq, <BidiagParams as Auto<f64>>::auto());
877 let mut threaded = <BidiagParams as Auto<f64>>::auto();
880 threaded.par_threshold = ParThreshold::Fixed(0);
881 crate::team::with_team(4, |par| f(par, threaded));
882 }
883
884 #[test]
885 fn test_bidiag_real() {
886 let rng = &mut StdRng::seed_from_u64(0);
887 for (m, n) in SHAPES {
888 let A = CwiseMatDistribution {
889 nrows: m,
890 ncols: n,
891 dist: StandardNormal,
892 }
893 .rand::<Mat<f64>>(rng);
894 for_each_budget(|par, params| {
895 let (UV, B) = reconstruct(&A, par, params);
896 let approx_eq =
897 CwiseMat(ApproxEq::<f64>::eps() * ((m * n) as f64).sqrt());
898 assert!(UV ~ B);
899 });
900 }
901 }
902 #[test]
907 fn bidiag_on_a_team_reconstructs() {
908 fn check<T: crate::ComplexField<Real = f64>>(a: Mat<T>, bl: usize, br: usize) {
909 let (m, n) = a.shape();
910 let size = Ord::min(m, n);
911 let mut params: BidiagParams = crate::Auto::<T>::auto();
912 params.par_threshold = crate::utils::cost::ParThreshold::Fixed(1);
913 if size < 2 {
914 return;
915 }
916 let mut hl = Mat::<T>::zeros(bl, size);
917 let mut hr = Mat::<T>::zeros(br, size - 1);
918 let mut uv = a.clone();
919 crate::team::with_team(4, |par| {
920 bidiag_in_place(
921 uv.rb_mut(),
922 hl.rb_mut(),
923 hr.rb_mut(),
924 par,
925 MemStack::new(&mut MemBuffer::new(bidiag_in_place_scratch::<T>(
926 m,
927 n,
928 ParShape::team(4),
929 params.into(),
930 ))),
931 params.into(),
932 );
933 });
934 let mut rebuilt = a.clone();
935 let mut rebuilt = rebuilt.as_mut();
936 apply_block_householder_sequence_transpose_on_the_left_in_place_with_conj(
937 uv.rb().get(.., ..size),
938 hl.rb(),
939 Conj::Yes,
940 rebuilt.rb_mut(),
941 Par::Seq,
942 MemStack::new(&mut MemBuffer::new(
943 apply_block_householder_sequence_transpose_on_the_left_in_place_scratch::<T>(n - 1, bl, m),
944 )),
945 );
946 let v = uv.rb().get(..size - 1, 1..size);
947 let a1 = rebuilt.rb_mut().get_mut(.., 1..size);
948 apply_block_householder_sequence_on_the_right_in_place_with_conj(
949 v.transpose(),
950 hr.as_ref(),
951 Conj::Yes,
952 a1,
953 Par::Seq,
954 MemStack::new(&mut MemBuffer::new(
955 apply_block_householder_sequence_on_the_right_in_place_scratch::<T>(n - 1, br, m),
956 )),
957 );
958 for j in 0..n {
959 for i in 0..m {
960 if i > j || j > i + 1 {
961 uv[(i, j)] = T::zero();
962 }
963 }
964 }
965 let mut err = 0.0f64;
966 let mut nonfinite = false;
967 for j in 0..n {
968 for i in 0..m {
969 let d = (uv[(i, j)].clone() - rebuilt[(i, j)].clone()).abs();
970 nonfinite |= !d.is_finite() || !uv[(i, j)].abs().is_finite();
971 if d.is_finite() {
972 err = err.max(d);
973 }
974 }
975 }
976 core::assert!(!nonfinite, "{m}x{n}: a non-finite factor or difference");
977 let scale = (0..n)
978 .flat_map(|j| (0..m).map(move |i| (i, j)))
979 .fold(0.0f64, |m, (i, j)| m.max(a[(i, j)].abs()));
980 core::assert!(err <= 1e-10 * scale.max(1.0), "{m}x{n}: {err} {scale}");
981 }
982 let rng = &mut StdRng::seed_from_u64(11);
983 let real_as_c64 = Mat::<c64>::from_fn(96, 96, |i, j| {
986 let h = (i * 2654435761usize).wrapping_add(j * 40503).wrapping_add(977) % 1009;
987 c64::new(h as f64 / 504.0 - 1.0, 0.0)
988 });
989 check::<c64>(real_as_c64, 8, 8);
990 for (bl, br) in [(4usize, 3usize), (8, 8)] {
991 for (m, n) in [(8usize, 4usize), (8, 8), (40, 40), (96, 96), (37, 13)] {
992 check::<f64>(Mat::from_fn(m, n, |_, _| rand::Rng::random::<f64>(rng)), bl, br);
993 check::<c64>(
994 Mat::from_fn(m, n, |_, _| {
995 c64::new(
996 rand::Rng::random::<f64>(rng),
997 rand::Rng::random::<f64>(rng),
998 )
999 }),
1000 bl,
1001 br,
1002 );
1003 }
1004 }
1005 }
1006 #[test]
1007 fn test_bidiag_cplx() {
1008 let rng = &mut StdRng::seed_from_u64(0);
1009 for (m, n) in SHAPES {
1010 let A = CwiseMatDistribution {
1011 nrows: m,
1012 ncols: n,
1013 dist: ComplexDistribution::new(StandardNormal, StandardNormal),
1014 }
1015 .rand::<Mat<c64>>(rng);
1016 for_each_budget(|par, params| {
1017 let (UV, B) = reconstruct(&A, par, params);
1018 let approx_eq =
1019 CwiseMat(ApproxEq::<f64>::eps() * ((m * n) as f64).sqrt());
1020 assert!(UV ~ B);
1021 });
1022 }
1023 }
1024}