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