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 let ref norm_inv = norm.recip();
165 if norm != zero() {
166 z!(A12.rb_mut()).for_each(|uz!(a)| *a = a.mul_real(norm_inv));
167 }
168 matmul(
169 z2.rb_mut(),
170 Accum::Replace,
171 A22.rb(),
172 A12.rb().adjoint(),
173 one(),
174 par,
175 );
176 if k + 1 == size {
177 break;
178 }
179 let (mut A12_a, mut A12_b) = A12.rb_mut().split_at_col_mut(1);
180 let A22_a = A22.rb().col(0);
181 let (y2_a, y2_b) = y2.rb().split_at_col(1);
182 let y2_a = &y2_a[0];
183 let a12_a = &mut A12_a[0];
184 let HouseholderInfo {
185 tau: tr,
186 head_with_beta_inv: m,
187 ..
188 } = make_householder_in_place(a12_a, A12_b.rb_mut().transpose_mut());
189 let ref tr_inv = tr.recip();
190 Hr[k] = tr.to_cplx();
191 let beta = a12_a.copy();
192 *a12_a = a12_a.mul_real(norm);
193 let ref b = y2_a
194 + dot::inner_prod(
195 y2_b,
196 Conj::No,
197 A12_b.rb().transpose(),
198 Conj::Yes,
199 );
200 if m != infinity() {
201 z!(z2.rb_mut(), A21.rb(), A22_a.rb()).for_each(
202 |uz!(z, u, a): Zip!(&mut T, &T, &T)| {
203 let w = &*z - a * beta.conj();
204 let w = w * m.conj();
205 let w = w - u * b;
206 *z = w.mul_real(tr_inv);
207 },
208 );
209 } else {
210 z!(z2.rb_mut(), A21.rb(), A22_a.rb()).for_each(
211 |uz!(z, u, a): Zip!(&mut T, &T, &T)| {
212 let w = a - u * b;
213 *z = w.mul_real(tr_inv);
214 },
215 );
216 }
217 }
218 }
219 let mut j = 0;
220 while j < size {
221 let bl = Ord::min(bl, size - j);
222 let mut Hl = Hl.rb_mut().get_mut(..bl, j..j + bl);
223 for k in 0..bl {
224 Hl[(k, k)] = Hl[(0, k)].copy();
225 }
226 upgrade_householder_factor(
227 Hl.rb_mut(),
228 A.rb().get(j.., j..j + bl),
229 bl,
230 1,
231 par,
232 );
233 j += bl;
234 }
235 if size > 0 {
236 let size = size - 1;
237 let A = A.rb().get(..size, 1..);
238 let mut Hr = Hr.rb_mut().get_mut(.., ..size);
239 let mut j = 0;
240 while j < size {
241 let br = Ord::min(br, size - j);
242 let mut Hr = Hr.rb_mut().get_mut(..br, j..j + br);
243 for k in 0..br {
244 Hr[(k, k)] = Hr[(0, k)].copy();
245 }
246 upgrade_householder_factor(
247 Hr.rb_mut(),
248 A.transpose().get(j.., j..j + br),
249 br,
250 1,
251 par,
252 );
253 j += br;
254 }
255 }
256}
257fn bidiag_fused_op<T: ComplexField>(
258 A22: MatMut<'_, T>,
259 u: ColRef<'_, T>,
260 up: ColRef<'_, T>,
261 z: ColRef<'_, T>,
262 y: RowMut<'_, T>,
263 vp: RowRef<'_, T>,
264 align: usize,
265) {
266 let mut A22 = A22;
267 if const { T::SIMD_CAPABILITIES.is_simd() } {
268 if let (Some(A22), Some(u), Some(up), Some(z)) = (
269 A22.rb_mut().try_as_col_major_mut(),
270 u.try_as_col_major(),
271 up.try_as_col_major(),
272 z.try_as_col_major(),
273 ) {
274 bidiag_fused_op_simd(A22, u, up, z, y, vp, align);
275 } else {
276 bidiag_fused_op_fallback(A22, u, up, z, y, vp);
277 }
278 } else {
279 bidiag_fused_op_fallback(A22, u, up, z, y, vp);
280 }
281}
282fn bidiag_fused_op_fallback<T: ComplexField>(
283 A22: MatMut<'_, T>,
284 u: ColRef<'_, T>,
285 up: ColRef<'_, T>,
286 z: ColRef<'_, T>,
287 y: RowMut<'_, T>,
288 vp: RowRef<'_, T>,
289) {
290 let mut A22 = A22;
291 let mut y = y;
292 matmul(A22.rb_mut(), Accum::Add, up, y.rb(), -one::<T>(), Par::Seq);
293 matmul(A22.rb_mut(), Accum::Add, z, vp, -one::<T>(), Par::Seq);
294 matmul(
295 y.rb_mut(),
296 Accum::Replace,
297 u.adjoint(),
298 A22.rb(),
299 one(),
300 Par::Seq,
301 );
302}
303fn bidiag_fused_op_simd<'M, 'N, T: ComplexField>(
304 A22: MatMut<'_, T, usize, usize, ContiguousFwd>,
305 u: ColRef<'_, T, usize, ContiguousFwd>,
306 up: ColRef<'_, T, usize, ContiguousFwd>,
307 z: ColRef<'_, T, usize, ContiguousFwd>,
308 y: RowMut<'_, T, usize>,
309 vp: RowRef<'_, T, usize>,
310 align: usize,
311) {
312 struct Impl<'a, 'M, 'N, T: ComplexField> {
313 A22: MatMut<'a, T, Dim<'M>, Dim<'N>, ContiguousFwd>,
314 u: ColRef<'a, T, Dim<'M>, ContiguousFwd>,
315 up: ColRef<'a, T, Dim<'M>, ContiguousFwd>,
316 z: ColRef<'a, T, Dim<'M>, ContiguousFwd>,
317 y: RowMut<'a, T, Dim<'N>>,
318 vp: RowRef<'a, T, Dim<'N>>,
319 align: usize,
320 }
321 impl<'a, 'M, 'N, T: ComplexField> pulp::WithSimd for Impl<'a, 'M, 'N, T> {
322 type Output = ();
323
324 #[inline(always)]
325 fn with_simd<S: pulp::Simd>(self, simd: S) -> Self::Output {
326 let Self {
327 mut A22,
328 u,
329 up,
330 z,
331 mut y,
332 vp,
333 align,
334 } = self;
335 let m = A22.nrows();
336 let n = A22.ncols();
337 let simd = SimdCtx::<T, S>::new_align(T::simd_ctx(simd), m, align);
338 let indices = simd.batch_indices::<4>();
339 for j in n.indices() {
340 let mut A = A22.rb_mut().col_mut(j);
341 let mut acc = [simd.zero(); 4];
342 let yj = simd.splat(-&y[j]);
343 let vj = simd.splat(-&vp[j]);
344 simd_iter!(for (IDX, i) in [indices; 4] {
345 let mut a = simd.read(A.rb(), i);
346 a = simd.mul_add(simd.read(up, i), yj, a);
347 a = simd.mul_add(simd.read(z, i), vj, a);
348 simd.write(A.rb_mut(), i, a);
349 acc[IDX] = simd.conj_mul_add(simd.read(u, i), a, acc[IDX]);
350 });
351 let acc0 = simd.add(acc[0], acc[1]);
352 let acc2 = simd.add(acc[2], acc[3]);
353 let acc0 = simd.add(acc0, acc2);
354 y[j] = simd.reduce_sum(acc0);
355 }
356 }
357 }
358 with_dim!(M, A22.nrows());
359 with_dim!(N, A22.ncols());
360 dispatch!(
361 Impl {
362 A22: A22.as_shape_mut(M, N),
363 u: u.as_row_shape(M),
364 up: up.as_row_shape(M),
365 z: z.as_row_shape(M),
366 y: y.as_col_shape_mut(N),
367 vp: vp.as_col_shape(N),
368 align,
369 },
370 Impl,
371 T
372 )
373}
374#[cfg(test)]
375mod tests {
376 use super::*;
377 use crate::stats::prelude::*;
378 use crate::utils::approx::*;
379 use crate::{Mat, assert, c64};
380 use dyn_stack::MemBuffer;
381 use std::mem::MaybeUninit;
382 #[test]
383 fn test_bidiag_real() {
384 let rng = &mut StdRng::seed_from_u64(0);
385 for (m, n) in [(8, 4), (8, 8)] {
386 let size = Ord::min(m, n);
387 let A = CwiseMatDistribution {
388 nrows: m,
389 ncols: n,
390 dist: StandardNormal,
391 }
392 .rand::<Mat<f64>>(rng);
393 let bl = 4;
394 let br = 3;
395 let mut Hl = Mat::zeros(bl, size);
396 let mut Hr = Mat::zeros(br, size - 1);
397 let mut UV = A.clone();
398 bidiag_in_place(
399 UV.rb_mut(),
400 Hl.rb_mut(),
401 Hr.rb_mut(),
402 Par::Seq,
403 MemStack::new(&mut [MaybeUninit::uninit(); 1024]),
404 default(),
405 );
406 let mut A = A.clone();
407 let mut A = A.as_mut();
408 apply_block_householder_sequence_transpose_on_the_left_in_place_with_conj(
409 UV.rb().get(.., ..size),
410 Hl.rb(),
411 Conj::Yes,
412 A.rb_mut(),
413 Par::Seq,
414 MemStack::new(&mut MemBuffer::new(
415 apply_block_householder_sequence_transpose_on_the_left_in_place_scratch::<f64>(n - 1, 1, m),
416 )),
417 );
418 let V = UV.rb().get(..size - 1, 1..size);
419 let A1 = A.rb_mut().get_mut(.., 1..size);
420 let Hr = Hr.as_ref();
421 apply_block_householder_sequence_on_the_right_in_place_with_conj(
422 V.transpose(),
423 Hr.as_ref(),
424 Conj::Yes,
425 A1,
426 Par::Seq,
427 MemStack::new(&mut MemBuffer::new(
428 apply_block_householder_sequence_on_the_right_in_place_scratch::<f64>(n - 1, 1, m),
429 )),
430 );
431 let approx_eq = CwiseMat(ApproxEq::<f64>::eps());
432 for j in 0..n {
433 for i in 0..m {
434 if i > j || j > i + 1 {
435 UV[(i, j)] = 0.0;
436 }
437 }
438 }
439 assert!(UV ~ A);
440 }
441 }
442 #[test]
443 fn test_bidiag_cplx() {
444 let rng = &mut StdRng::seed_from_u64(0);
445 for (m, n) in [(8, 4), (8, 8)] {
446 let size = Ord::min(m, n);
447 let A = CwiseMatDistribution {
448 nrows: m,
449 ncols: n,
450 dist: ComplexDistribution::new(StandardNormal, StandardNormal),
451 }
452 .rand::<Mat<c64>>(rng);
453 let bl = 4;
454 let br = 3;
455 let mut Hl = Mat::zeros(bl, size);
456 let mut Hr = Mat::zeros(br, size - 1);
457 let mut UV = A.clone();
458 let mut UV = UV.as_mut();
459 bidiag_in_place(
460 UV.rb_mut(),
461 Hl.rb_mut(),
462 Hr.rb_mut(),
463 Par::Seq,
464 MemStack::new(&mut [MaybeUninit::uninit(); 1024]),
465 default(),
466 );
467 let mut A = A.clone();
468 let mut A = A.as_mut();
469 apply_block_householder_sequence_transpose_on_the_left_in_place_with_conj(
470 UV.rb().subcols(0, size),
471 Hl.rb(),
472 Conj::Yes,
473 A.rb_mut(),
474 Par::Seq,
475 MemStack::new(&mut MemBuffer::new(
476 apply_block_householder_sequence_transpose_on_the_left_in_place_scratch::<c64>(n - 1, 1, m),
477 )),
478 );
479 let V = UV.rb().get(..size - 1, 1..size);
480 let A1 = A.rb_mut().get_mut(.., 1..size);
481 let Hr = Hr.rb();
482 apply_block_householder_sequence_on_the_right_in_place_with_conj(
483 V.transpose(),
484 Hr,
485 Conj::Yes,
486 A1,
487 Par::Seq,
488 MemStack::new(&mut MemBuffer::new(
489 apply_block_householder_sequence_on_the_right_in_place_scratch::<c64>(n - 1, 1, m),
490 )),
491 );
492 let approx_eq = CwiseMat(ApproxEq::eps());
493 for j in 0..n {
494 for i in 0..m {
495 if i > j || j > i + 1 {
496 UV[(i, j)] = c64::ZERO;
497 }
498 }
499 }
500 assert!(UV ~ A);
501 }
502 }
503}