1use crate::assert;
2use crate::internal_prelude::*;
3use linalg::householder::*;
4use linalg::matmul::{dot, matmul};
5pub fn bidiag_in_place_scratch<T: ComplexField>(
8 nrows: usize,
9 ncols: usize,
10 par: Par,
11 params: Spec<BidiagParams, T>,
12) -> StackReq {
13 _ = par;
14 _ = params;
15 StackReq::all_of(&[
16 temp_mat_scratch::<T>(nrows, 1),
17 temp_mat_scratch::<T>(ncols, 1),
18 ])
19}
20#[derive(Debug, Copy, Clone)]
22pub struct BidiagParams {
23 pub par_threshold: usize,
25 #[doc(hidden)]
26 pub non_exhaustive: NonExhaustive,
27}
28impl<T: ComplexField> Auto<T> for BidiagParams {
29 fn auto() -> Self {
30 Self {
31 par_threshold: 192 * 256,
32 non_exhaustive: NonExhaustive(()),
33 }
34 }
35}
36pub fn bidiag_in_place<T: ComplexField>(
48 A: MatMut<'_, T>,
49 H_left: MatMut<'_, T>,
50 H_right: MatMut<'_, T>,
51 par: Par,
52 stack: &mut MemStack,
53 params: Spec<BidiagParams, T>,
54) {
55 let params = params.config;
56 let m = A.nrows();
57 let n = A.ncols();
58 let size = Ord::min(m, n);
59 let bl = H_left.nrows();
60 let br = H_right.nrows();
61 assert!(H_left.ncols() == size);
62 assert!(H_right.ncols() == size.saturating_sub(1));
63 alloca!('stack: {
64 let mut y = unsafe { row![uninit::<T>, n] };
65 let mut z = unsafe { col![uninit::<T>, m] };
66 });
67 let mut A = A;
68 let mut Hl = H_left;
69 let mut Hr = H_right;
70 let mut par = par;
71 {
72 let mut Hl = Hl.rb_mut().row_mut(0);
73 let mut Hr = Hr.rb_mut().row_mut(0);
74 for k in 0..size {
75 let mut A = A.rb_mut();
76 let (_, A01, A10, A11) = A.rb_mut().split_at_mut(k, k);
77 let (_, A02) = A01.split_first_col().unwrap();
78 let (A10, A20) = A10.split_first_row_mut().unwrap();
79 let (mut A11, A12, A21, mut A22) = A11.split_at_mut(1, 1);
80 let mut A12 = A12.row_mut(0);
81 let mut A21 = A21.col_mut(0);
82 let a11 = &mut A11[(0, 0)];
83 let (y1, mut y2) =
84 y.rb_mut().split_at_col_mut(k).1.split_at_col_mut(1);
85 let (z1, mut z2) =
86 z.rb_mut().split_at_row_mut(k).1.split_at_row_mut(1);
87 if k > 0 {
88 let ref y1 = y1[0].copy();
89 let ref z1 = z1[0].copy();
90 let k1 = k - 1;
91 let ref up0 = A10[k1];
92 let up = A20.rb().col(k1);
93 let vp = A02.rb().row(k1);
94 *a11 -= up0 * y1 + z1;
95 z!(A21.rb_mut(), up.rb(), z2.rb()).for_each(
96 |uz!(a, u, z): Zip!(&mut T, &T, &T)| *a -= u * y1 + z,
97 );
98 z!(A12.rb_mut(), y2.rb(), vp.rb()).for_each(
99 |uz!(a, y, v): Zip!(&mut T, &T, &T)| {
100 *a -= up0 * y + z1 * v;
101 },
102 );
103 }
104 let HouseholderInfo { tau: tl, .. } =
105 make_householder_in_place(a11, A21.rb_mut());
106 let ref tl_inv = tl.recip();
107 Hl[k] = tl.to_cplx();
108 if (m - k - 1) * (n - k - 1) < params.par_threshold {
109 par = Par::Seq;
110 }
111 if k > 0 {
112 let k1 = k - 1;
113 let up = A20.rb().col(k1);
114 let vp = A02.row(k1);
115 match par {
116 Par::Seq => bidiag_fused_op(
117 A22.rb_mut(),
118 A21.rb(),
119 up.rb(),
120 z2.rb(),
121 y2.rb_mut(),
122 vp.rb(),
123 simd_align(k + 1),
124 ),
125 #[cfg(feature = "rayon")]
126 Par::Rayon(nthreads) => {
127 use rayon::prelude::*;
128 let nthreads = nthreads.get();
129 spindle::for_each(
130 nthreads,
131 A22.rb_mut()
132 .par_col_partition_mut(nthreads)
133 .zip_eq(y2.rb_mut().par_partition_mut(nthreads))
134 .zip_eq(vp.par_partition(nthreads)),
135 |((A22, y2), vp)| {
136 bidiag_fused_op(
137 A22,
138 A21.rb(),
139 up.rb(),
140 z2.rb(),
141 y2,
142 vp.rb(),
143 simd_align(k + 1),
144 );
145 },
146 );
147 },
148 }
149 } else {
150 matmul(
151 y2.rb_mut(),
152 Accum::Replace,
153 A21.rb().adjoint(),
154 A22.rb(),
155 one(),
156 par,
157 );
158 }
159 z!(y2.rb_mut(), A12.rb_mut()).for_each(|uz!(y, a)| {
160 *y = (&*y + &*a).mul_real(tl_inv);
161 *a -= &*y;
162 });
163 let norm = A12.rb().norm_l2();
164 if norm != zero() {
165 let ref norm_inv = if norm >= min_positive() {
168 (&norm).recip()
169 } else {
170 let ref shift = from_f64::<T::Real>(4503599627370496.0);
171 z!(A12.rb_mut()).for_each(|uz!(a)| *a = a.mul_real(shift));
172 (&norm * shift).recip()
173 };
174 z!(A12.rb_mut()).for_each(|uz!(a)| *a = a.mul_real(norm_inv));
175 }
176 matmul(
177 z2.rb_mut(),
178 Accum::Replace,
179 A22.rb(),
180 A12.rb().adjoint(),
181 one(),
182 par,
183 );
184 if k + 1 == size {
185 break;
186 }
187 let (mut A12_a, mut A12_b) = A12.rb_mut().split_at_col_mut(1);
188 let A22_a = A22.rb().col(0);
189 let (y2_a, y2_b) = y2.rb().split_at_col(1);
190 let y2_a = &y2_a[0];
191 let a12_a = &mut A12_a[0];
192 let HouseholderInfo {
193 tau: tr,
194 head_with_beta_inv: m,
195 ..
196 } = make_householder_in_place(a12_a, A12_b.rb_mut().transpose_mut());
197 let ref tr_inv = tr.recip();
198 Hr[k] = tr.to_cplx();
199 let beta = a12_a.copy();
200 *a12_a = a12_a.mul_real(norm);
201 let ref b = y2_a
202 + dot::inner_prod(
203 y2_b,
204 Conj::No,
205 A12_b.rb().transpose(),
206 Conj::Yes,
207 );
208 if m != infinity() {
209 z!(z2.rb_mut(), A21.rb(), A22_a.rb()).for_each(
210 |uz!(z, u, a): Zip!(&mut T, &T, &T)| {
211 let w = &*z - a * beta.conj();
212 let w = w * m.conj();
213 let w = w - u * b;
214 *z = w.mul_real(tr_inv);
215 },
216 );
217 } else {
218 z!(z2.rb_mut(), A21.rb(), A22_a.rb()).for_each(
219 |uz!(z, u, a): Zip!(&mut T, &T, &T)| {
220 let w = a - u * b;
221 *z = w.mul_real(tr_inv);
222 },
223 );
224 }
225 }
226 }
227 let mut j = 0;
228 while j < size {
229 let bl = Ord::min(bl, size - j);
230 let mut Hl = Hl.rb_mut().get_mut(..bl, j..j + bl);
231 for k in 0..bl {
232 Hl[(k, k)] = Hl[(0, k)].copy();
233 }
234 upgrade_householder_factor(
235 Hl.rb_mut(),
236 A.rb().get(j.., j..j + bl),
237 bl,
238 1,
239 par,
240 );
241 j += bl;
242 }
243 if size > 0 {
244 let size = size - 1;
245 let A = A.rb().get(..size, 1..);
246 let mut Hr = Hr.rb_mut().get_mut(.., ..size);
247 let mut j = 0;
248 while j < size {
249 let br = Ord::min(br, size - j);
250 let mut Hr = Hr.rb_mut().get_mut(..br, j..j + br);
251 for k in 0..br {
252 Hr[(k, k)] = Hr[(0, k)].copy();
253 }
254 upgrade_householder_factor(
255 Hr.rb_mut(),
256 A.transpose().get(j.., j..j + br),
257 br,
258 1,
259 par,
260 );
261 j += br;
262 }
263 }
264}
265fn bidiag_fused_op<T: ComplexField>(
266 A22: MatMut<'_, T>,
267 u: ColRef<'_, T>,
268 up: ColRef<'_, T>,
269 z: ColRef<'_, T>,
270 y: RowMut<'_, T>,
271 vp: RowRef<'_, T>,
272 align: usize,
273) {
274 let mut A22 = A22;
275 if const { T::SIMD_CAPABILITIES.is_simd() } {
276 if let (Some(A22), Some(u), Some(up), Some(z)) = (
277 A22.rb_mut().try_as_col_major_mut(),
278 u.try_as_col_major(),
279 up.try_as_col_major(),
280 z.try_as_col_major(),
281 ) {
282 bidiag_fused_op_simd(A22, u, up, z, y, vp, align);
283 } else {
284 bidiag_fused_op_fallback(A22, u, up, z, y, vp);
285 }
286 } else {
287 bidiag_fused_op_fallback(A22, u, up, z, y, vp);
288 }
289}
290fn bidiag_fused_op_fallback<T: ComplexField>(
291 A22: MatMut<'_, T>,
292 u: ColRef<'_, T>,
293 up: ColRef<'_, T>,
294 z: ColRef<'_, T>,
295 y: RowMut<'_, T>,
296 vp: RowRef<'_, T>,
297) {
298 let mut A22 = A22;
299 let mut y = y;
300 matmul(A22.rb_mut(), Accum::Add, up, y.rb(), -one::<T>(), Par::Seq);
301 matmul(A22.rb_mut(), Accum::Add, z, vp, -one::<T>(), Par::Seq);
302 matmul(
303 y.rb_mut(),
304 Accum::Replace,
305 u.adjoint(),
306 A22.rb(),
307 one(),
308 Par::Seq,
309 );
310}
311fn bidiag_fused_op_simd<'M, 'N, T: ComplexField>(
312 A22: MatMut<'_, T, usize, usize, ContiguousFwd>,
313 u: ColRef<'_, T, usize, ContiguousFwd>,
314 up: ColRef<'_, T, usize, ContiguousFwd>,
315 z: ColRef<'_, T, usize, ContiguousFwd>,
316 y: RowMut<'_, T, usize>,
317 vp: RowRef<'_, T, usize>,
318 align: usize,
319) {
320 struct Impl<'a, 'M, 'N, T: ComplexField> {
321 A22: MatMut<'a, T, Dim<'M>, Dim<'N>, ContiguousFwd>,
322 u: ColRef<'a, T, Dim<'M>, ContiguousFwd>,
323 up: ColRef<'a, T, Dim<'M>, ContiguousFwd>,
324 z: ColRef<'a, T, Dim<'M>, ContiguousFwd>,
325 y: RowMut<'a, T, Dim<'N>>,
326 vp: RowRef<'a, T, Dim<'N>>,
327 align: usize,
328 }
329 impl<'a, 'M, 'N, T: ComplexField> pulp::WithSimd for Impl<'a, 'M, 'N, T> {
330 type Output = ();
331
332 #[inline(always)]
333 fn with_simd<S: pulp::Simd>(self, simd: S) -> Self::Output {
334 let Self {
335 mut A22,
336 u,
337 up,
338 z,
339 mut y,
340 vp,
341 align,
342 } = self;
343 let m = A22.nrows();
344 let n = A22.ncols();
345 let simd = SimdCtx::<T, S>::new_align(T::simd_ctx(simd), m, align);
346 let indices = simd.batch_indices::<4>();
347 for j in n.indices() {
348 let mut A = A22.rb_mut().col_mut(j);
349 let mut acc = [simd.zero(); 4];
350 let yj = simd.splat(-&y[j]);
351 let vj = simd.splat(-&vp[j]);
352 simd_iter!(for (IDX, i) in [indices; 4] {
353 let mut a = simd.read(A.rb(), i);
354 a = simd.mul_add(simd.read(up, i), yj, a);
355 a = simd.mul_add(simd.read(z, i), vj, a);
356 simd.write(A.rb_mut(), i, a);
357 acc[IDX] = simd.conj_mul_add(simd.read(u, i), a, acc[IDX]);
358 });
359 let acc0 = simd.add(acc[0], acc[1]);
360 let acc2 = simd.add(acc[2], acc[3]);
361 let acc0 = simd.add(acc0, acc2);
362 y[j] = simd.reduce_sum(acc0);
363 }
364 }
365 }
366 with_dim!(M, A22.nrows());
367 with_dim!(N, A22.ncols());
368 dispatch!(
369 Impl {
370 A22: A22.as_shape_mut(M, N),
371 u: u.as_row_shape(M),
372 up: up.as_row_shape(M),
373 z: z.as_row_shape(M),
374 y: y.as_col_shape_mut(N),
375 vp: vp.as_col_shape(N),
376 align,
377 },
378 Impl,
379 T
380 )
381}
382#[cfg(test)]
383mod tests {
384 use super::*;
385 use crate::stats::prelude::*;
386 use crate::utils::approx::*;
387 use crate::{Mat, assert, c64};
388 use dyn_stack::MemBuffer;
389 use std::mem::MaybeUninit;
390 #[test]
391 fn test_bidiag_real() {
392 let rng = &mut StdRng::seed_from_u64(0);
393 for (m, n) in [(8, 4), (8, 8)] {
394 let size = Ord::min(m, n);
395 let A = CwiseMatDistribution {
396 nrows: m,
397 ncols: n,
398 dist: StandardNormal,
399 }
400 .rand::<Mat<f64>>(rng);
401 let bl = 4;
402 let br = 3;
403 let mut Hl = Mat::zeros(bl, size);
404 let mut Hr = Mat::zeros(br, size - 1);
405 let mut UV = A.clone();
406 bidiag_in_place(
407 UV.rb_mut(),
408 Hl.rb_mut(),
409 Hr.rb_mut(),
410 Par::Seq,
411 MemStack::new(&mut [MaybeUninit::uninit(); 1024]),
412 default(),
413 );
414 let mut A = A.clone();
415 let mut A = A.as_mut();
416 apply_block_householder_sequence_transpose_on_the_left_in_place_with_conj(
417 UV.rb().get(.., ..size),
418 Hl.rb(),
419 Conj::Yes,
420 A.rb_mut(),
421 Par::Seq,
422 MemStack::new(&mut MemBuffer::new(
423 apply_block_householder_sequence_transpose_on_the_left_in_place_scratch::<f64>(n - 1, 1, m),
424 )),
425 );
426 let V = UV.rb().get(..size - 1, 1..size);
427 let A1 = A.rb_mut().get_mut(.., 1..size);
428 let Hr = Hr.as_ref();
429 apply_block_householder_sequence_on_the_right_in_place_with_conj(
430 V.transpose(),
431 Hr.as_ref(),
432 Conj::Yes,
433 A1,
434 Par::Seq,
435 MemStack::new(&mut MemBuffer::new(
436 apply_block_householder_sequence_on_the_right_in_place_scratch::<f64>(n - 1, 1, m),
437 )),
438 );
439 let approx_eq = CwiseMat(ApproxEq::<f64>::eps());
440 for j in 0..n {
441 for i in 0..m {
442 if i > j || j > i + 1 {
443 UV[(i, j)] = 0.0;
444 }
445 }
446 }
447 assert!(UV ~ A);
448 }
449 }
450 #[test]
451 fn test_bidiag_cplx() {
452 let rng = &mut StdRng::seed_from_u64(0);
453 for (m, n) in [(8, 4), (8, 8)] {
454 let size = Ord::min(m, n);
455 let A = CwiseMatDistribution {
456 nrows: m,
457 ncols: n,
458 dist: ComplexDistribution::new(StandardNormal, StandardNormal),
459 }
460 .rand::<Mat<c64>>(rng);
461 let bl = 4;
462 let br = 3;
463 let mut Hl = Mat::zeros(bl, size);
464 let mut Hr = Mat::zeros(br, size - 1);
465 let mut UV = A.clone();
466 let mut UV = UV.as_mut();
467 bidiag_in_place(
468 UV.rb_mut(),
469 Hl.rb_mut(),
470 Hr.rb_mut(),
471 Par::Seq,
472 MemStack::new(&mut [MaybeUninit::uninit(); 1024]),
473 default(),
474 );
475 let mut A = A.clone();
476 let mut A = A.as_mut();
477 apply_block_householder_sequence_transpose_on_the_left_in_place_with_conj(
478 UV.rb().subcols(0, size),
479 Hl.rb(),
480 Conj::Yes,
481 A.rb_mut(),
482 Par::Seq,
483 MemStack::new(&mut MemBuffer::new(
484 apply_block_householder_sequence_transpose_on_the_left_in_place_scratch::<c64>(n - 1, 1, m),
485 )),
486 );
487 let V = UV.rb().get(..size - 1, 1..size);
488 let A1 = A.rb_mut().get_mut(.., 1..size);
489 let Hr = Hr.rb();
490 apply_block_householder_sequence_on_the_right_in_place_with_conj(
491 V.transpose(),
492 Hr,
493 Conj::Yes,
494 A1,
495 Par::Seq,
496 MemStack::new(&mut MemBuffer::new(
497 apply_block_householder_sequence_on_the_right_in_place_scratch::<c64>(n - 1, 1, m),
498 )),
499 );
500 let approx_eq = CwiseMat(ApproxEq::eps());
501 for j in 0..n {
502 for i in 0..m {
503 if i > j || j > i + 1 {
504 UV[(i, j)] = c64::ZERO;
505 }
506 }
507 }
508 assert!(UV ~ A);
509 }
510 }
511}