1pub use super::super::no_pivoting::factor::recommended_block_size;
2use crate::assert;
3use crate::internal_prelude::*;
4use crate::perm::swap_cols_idx;
5use linalg::householder::{self, HouseholderInfo};
6use pulp::Simd;
7fn update_mat_and_dot_simd<T: ComplexField>(
8 norm: RowMut<'_, T>,
9 dot: RowMut<'_, T>,
10 B01: RowMut<'_, T>,
11 B11: MatMut<'_, T, usize, usize, ContiguousFwd>,
12 A10: ColRef<'_, T, usize, ContiguousFwd>,
13 B10: ColRef<'_, T, usize, ContiguousFwd>,
14 l: T,
15 tau_inv: T::Real,
16 align: usize,
17) {
18 struct Impl<'a, 'M, 'N, T: ComplexField> {
19 norm: RowMut<'a, T, Dim<'N>>,
20 dot: RowMut<'a, T, Dim<'N>>,
21 B01: RowMut<'a, T, Dim<'N>>,
22 B11: MatMut<'a, T, Dim<'M>, Dim<'N>, ContiguousFwd>,
23 A10: ColRef<'a, T, Dim<'M>, ContiguousFwd>,
24 B10: ColRef<'a, T, Dim<'M>, ContiguousFwd>,
25 l: T,
26 tau_inv: T::Real,
27 align: usize,
28 }
29 impl<'M, 'N, T: ComplexField> pulp::WithSimd for Impl<'_, 'M, 'N, T> {
30 type Output = ();
31
32 #[inline(always)]
33 fn with_simd<S: Simd>(self, simd: S) -> Self::Output {
34 let Self {
35 mut norm,
36 mut dot,
37 B01: mut u,
38 mut B11,
39 A10,
40 B10,
41 l,
42 ref tau_inv,
43 align,
44 } = self;
45 let m = B11.nrows();
46 let n = B11.ncols();
47 let simd =
48 SimdCtx::<'_, T, S>::new_align(T::simd_ctx(simd), m, align);
49 let indices4 = simd.batch_indices::<4>();
50
51 for j in n.indices() {
52 let b0 = dot[j].copy();
53 let rhs0 = simd.splat(&b0);
54 let mut acc = [simd.zero(); 4];
55 simd_iter!(for (IDX, i) in [indices4; 4] {
56 let lhs0 = simd.read(A10, i);
57 let lhs1 = simd.read(B10, i);
58 let mut dst0 = simd.read(B11.rb().col(j), i);
59 dst0 = simd.mul_add(lhs0, rhs0, dst0);
60 acc[IDX] = simd.conj_mul_add(lhs1, dst0, acc[IDX]);
61 simd.write(B11.rb_mut().col_mut(j), i, dst0);
62 });
63 let acc = simd
64 .add(simd.add(acc[0], acc[2]), simd.add(acc[1], acc[3]));
65 let tmp = &u[j] + &l * b0;
66 let d0 = (&tmp + simd.reduce_sum(acc)).mul_real(-tau_inv);
67 u[j] = tmp + &d0;
68 dot[j] = d0;
69 norm[j] = (norm[j].abs2() - u[j].abs2()).sqrt().to_cplx();
70 }
71 }
72 }
73 with_dim!(M, B11.nrows());
74 with_dim!(N, B11.ncols());
75 dispatch!(
76 Impl {
77 norm: norm.as_col_shape_mut(N),
78 dot: dot.as_col_shape_mut(N),
79 B01: B01.as_col_shape_mut(N),
80 B11: B11.as_shape_mut(M, N),
81 A10: A10.as_row_shape(M),
82 B10: B10.as_row_shape(M),
83 l,
84 tau_inv,
85 align
86 },
87 Impl,
88 T
89 )
90}
91#[derive(Copy, Clone, Debug)]
93pub struct ColPivQrParams {
94 pub blocking_threshold: usize,
96 pub par_threshold: usize,
98 #[doc(hidden)]
99 pub non_exhaustive: NonExhaustive,
100}
101impl<T: ComplexField> Auto<T> for ColPivQrParams {
102 #[inline]
103 fn auto() -> Self {
104 Self {
105 blocking_threshold: 48 * 48,
106 par_threshold: 192 * 256,
107 non_exhaustive: NonExhaustive(()),
108 }
109 }
110}
111#[track_caller]
112fn qr_in_place_unblocked<'out, I: Index, T: ComplexField>(
113 A: MatMut<'_, T>,
114 H: RowMut<'_, T>,
115 col_perm: &'out mut [I],
116 col_perm_inv: &'out mut [I],
117 par: Par,
118 stack: &mut MemStack,
119 params: Spec<ColPivQrParams, T>,
120) -> (ColPivQrInfo, PermRef<'out, I>) {
121 let m = A.nrows();
122 let n = A.ncols();
123 let size = H.ncols();
124 let params = params.config;
125 let mut A = A;
126 let mut H = H;
127 let mut par = par;
128 assert!(size == Ord::min(m, n));
129 for j in 0..n {
130 col_perm[j] = I::truncate(j);
131 }
132 let mut n_trans = 0;
133 'main: {
134 if size == 0 {
135 break 'main;
136 }
137 let (mut dot, stack) = temp_mat_zeroed::<T, _, _>(n, 1, stack);
138 let (mut norm, stack) = temp_mat_zeroed::<T, _, _>(n, 1, stack);
139 let _ = stack;
140 let mut dot = dot.as_mat_mut().col_mut(0).transpose_mut();
141 let mut norm = norm.as_mat_mut().col_mut(0).transpose_mut();
142 let mut best = zero();
143 let ref threshold = eps::<T::Real>().sqrt();
144 for j in 0..n {
145 let val = A.rb().col(j).norm_l2();
146 norm[j] = val.to_cplx();
147 if val > best {
148 best = val;
149 }
150 }
151 let ref scale_fwd = best.copy();
152 let ref scale_bwd = best.recip();
153 zip!(A.rb_mut()).for_each(|unzip!(a)| *a = a.mul_real(scale_bwd));
154 for j in 0..n {
155 norm[j] = (norm[j].real() * scale_bwd).to_cplx();
156 }
157 best = best * scale_bwd;
158 let mut best_threshold = best * threshold;
159 'unscale: {
160 for k in 0..size {
161 let mut new_best = zero::<T::Real>();
162 let mut best_col = k;
163 for j in k..n {
164 let val = norm[j].real();
165 if val > new_best {
166 new_best = val;
167 best_col = j;
168 }
169 }
170 let delayed_update = T::SIMD_CAPABILITIES.is_simd()
171 && A.row_stride() == 1
172 && k > 0 && new_best >= best_threshold;
173 if k > 0 && !delayed_update {
174 let (_, _, A10, mut A11) = A.rb_mut().split_at_mut(k, k);
175 let dot = dot.rb().get(k..);
176 let A10 = A10.rb().col(k - 1);
177 linalg::matmul::matmul(
178 A11.rb_mut(),
179 Accum::Add,
180 A10,
181 dot,
182 one(),
183 par,
184 );
185 best = zero();
186 for j in k..n {
187 let val = A11.rb().col(j - k).norm_l2();
188 norm[j] = val.to_cplx();
189 if val > best {
190 best = val;
191 best_col = j;
192 }
193 }
194 best_threshold = best * threshold;
195 }
196 if best_col != k {
197 n_trans += 1;
198 col_perm.as_mut().swap(best_col, k);
199 swap_cols_idx(A.rb_mut(), best_col, k);
200 swap_cols_idx(dot.rb_mut().as_mat_mut(), best_col, k);
201 swap_cols_idx(norm.rb_mut().as_mat_mut(), best_col, k);
202 }
203 let (_, _, A10, mut A11) = A.rb_mut().split_at_mut(k, k);
204 let A10 = A10.rb();
205 let dot0 = dot.rb_mut().get_mut(k..);
206 let (mut B00, B01, B10, mut B11) =
207 A11.rb_mut().split_at_mut(1, 1);
208 let B00 = &mut B00[(0, 0)];
209 let mut B01 = B01.row_mut(0);
210 let mut B10 = B10.col_mut(0);
211 let l = if delayed_update {
212 let A10 = A10.col(k - 1);
213 A10[0].copy()
214 } else {
215 zero()
216 };
217 let r = dot0[0].copy();
218 let mut dot = dot.rb_mut().get_mut(k + 1..);
219 let mut norm = norm.rb_mut().get_mut(k + 1..);
220 if delayed_update {
221 let A10 = A10.col(k - 1).get(1..);
222 *B00 += &l * &r;
223 zip!(B10.rb_mut(), A10).for_each(|unzip!(x, y)| {
224 *x += &r * y;
225 });
226 }
227 let HouseholderInfo { tau, .. } =
228 householder::make_householder_in_place(B00, B10.rb_mut());
229 let ref tau_inv = tau.recip();
230 H[k] = tau.to_cplx();
231 if k + 1 == size {
232 if delayed_update {
233 zip!(B01.rb_mut(), dot.rb()).for_each(
234 |unzip!(x, y)| {
235 *x += &l * y;
236 },
237 );
238 }
239 break 'unscale;
240 }
241 if (m - k - 1) * (n - k - 1) < params.par_threshold {
242 par = Par::Seq;
243 }
244 if delayed_update {
245 let A10 = A10.col(k - 1).get(1..);
246 match par {
247 Par::Seq => {
248 update_mat_and_dot_simd(
249 norm.rb_mut(),
250 dot.rb_mut(),
251 B01.rb_mut(),
252 B11.rb_mut().try_as_col_major_mut().unwrap(),
253 A10.try_as_col_major().unwrap(),
254 B10.rb().try_as_col_major().unwrap(),
255 l.copy(),
256 tau_inv.copy(),
257 simd_align(k + 1),
258 );
259 },
260 #[cfg(feature = "rayon")]
261 Par::Rayon(nthreads) => {
262 let nthreads = nthreads.get();
263 use rayon::prelude::*;
264 spindle::for_each(
265 nthreads,
266 norm.par_partition_mut(nthreads)
267 .zip(dot.par_partition_mut(nthreads))
268 .zip(B01.par_partition_mut(nthreads))
269 .zip(B11.par_col_partition_mut(nthreads)),
270 |(((norm, dot), B01), B11)| {
271 update_mat_and_dot_simd(
272 norm,
273 dot,
274 B01,
275 B11.try_as_col_major_mut().unwrap(),
276 A10.try_as_col_major().unwrap(),
277 B10.rb().try_as_col_major().unwrap(),
278 l.copy(),
279 tau_inv.copy(),
280 simd_align(k + 1),
281 );
282 },
283 );
284 },
285 }
286 } else {
287 dot.copy_from(B01.rb());
288 linalg::matmul::matmul(
289 dot.rb_mut(),
290 Accum::Add,
291 B10.rb().adjoint(),
292 B11.rb(),
293 one(),
294 par,
295 );
296 zip!(B01.rb_mut(), dot.rb_mut(), norm.rb_mut()).for_each(
297 |unzip!(a, dot, norm)| {
298 *dot = -dot.mul_real(tau_inv);
299 *a += &*dot;
300 *norm = (norm.abs2() - a.abs2()).sqrt().to_cplx();
301 },
302 );
303 }
304 }
305 }
306 zip!(A.rb_mut()).for_each_triangular_upper(
307 linalg::zip::Diag::Include,
308 |unzip!(a)| *a = a.mul_real(scale_fwd),
309 );
310 }
311 for j in 0..n {
312 col_perm_inv[col_perm[j].zx()] = I::truncate(j);
313 }
314 (
315 ColPivQrInfo {
316 transposition_count: n_trans,
317 },
318 unsafe { PermRef::new_unchecked(col_perm, col_perm_inv, n) },
319 )
320}
321pub fn qr_in_place_scratch<I: Index, T: ComplexField>(
324 nrows: usize,
325 ncols: usize,
326 block_size: usize,
327 par: Par,
328 params: Spec<ColPivQrParams, T>,
329) -> StackReq {
330 let _ = nrows;
331 let _ = ncols;
332 let _ = par;
333 let _ = block_size;
334 let _ = ¶ms;
335 linalg::temp_mat_scratch::<T>(ncols, 2)
336}
337#[derive(Copy, Clone, Debug)]
339pub struct ColPivQrInfo {
340 pub transposition_count: usize,
343}
344#[track_caller]
345pub fn qr_in_place<'out, I: Index, T: ComplexField>(
346 A: MatMut<'_, T>,
347 Q_coeff: MatMut<'_, T>,
348 col_perm: &'out mut [I],
349 col_perm_inv: &'out mut [I],
350 par: Par,
351 stack: &mut MemStack,
352 params: Spec<ColPivQrParams, T>,
353) -> (ColPivQrInfo, PermRef<'out, I>) {
354 let mut A = A;
365 if A.row_stride().unsigned_abs() != 1
366 && A.nrows() > 1
367 && A.ncols() > 1
368 && linalg::row_major_copy_pays(A.nrows(), A.ncols())
369 {
370 let (m, n) = A.shape();
371 let mut work = alloc::vec![zero::<T>(); m * n];
372 let mut work = MatMut::from_column_major_slice_mut(&mut work, m, n);
373 linalg::copy_across_layouts(work.rb_mut(), A.rb(), par);
374 let ret = qr_in_place(
375 work.rb_mut(),
376 Q_coeff,
377 col_perm,
378 col_perm_inv,
379 par,
380 stack,
381 params,
382 );
383 linalg::copy_across_layouts(A, work.rb(), par);
384 return ret;
385 }
386 let mut H = Q_coeff;
387 let size = H.ncols();
388 let block_size = H.nrows();
389 let ret = qr_in_place_unblocked(
390 A.rb_mut(),
391 H.rb_mut().row_mut(0),
392 col_perm,
393 col_perm_inv,
394 par,
395 stack,
396 params,
397 );
398 let mut j = 0;
399 while j < size {
400 let block_size = Ord::min(block_size, size - j);
401 let mut H = H
402 .rb_mut()
403 .subcols_mut(j, block_size)
404 .subrows_mut(0, block_size);
405 for j in 0..block_size {
406 H[(j, j)] = H[(0, j)].copy();
407 }
408 let A = A.rb().get(j.., j..j + block_size);
409 householder::upgrade_householder_factor(
410 H.rb_mut(),
411 A,
412 block_size,
413 1,
414 par,
415 );
416 j += block_size;
417 }
418 ret
419}
420#[cfg(test)]
421mod tests {
422 use super::*;
423 use crate::stats::prelude::*;
424 use crate::utils::approx::*;
425 use crate::{Mat, assert, c64};
426 use dyn_stack::MemBuffer;
427 #[test]
428 fn test_unblocked_qr() {
429 let rng = &mut StdRng::seed_from_u64(0);
430 for par in [Par::Seq, Par::rayon(8)] {
431 for n in [2, 3, 4, 8, 16, 24, 32, 128, 255] {
432 let bs = 15;
433 let approx_eq = CwiseMat(ApproxEq {
434 abs_tol: 1e-10,
435 rel_tol: 1e-10,
436 });
437 let A = CwiseMatDistribution {
438 nrows: n,
439 ncols: n,
440 dist: ComplexDistribution::new(
441 StandardNormal,
442 StandardNormal,
443 ),
444 }
445 .rand::<Mat<c64>>(rng);
446 let A = A.as_ref();
447 let mut QR = A.cloned();
448 let mut H = Mat::zeros(bs, n);
449 let col_perm = &mut *vec![0usize; n];
450 let col_perm_inv = &mut *vec![0usize; n];
451 let q = qr_in_place(
452 QR.as_mut(),
453 H.as_mut(),
454 col_perm,
455 col_perm_inv,
456 par,
457 MemStack::new(&mut MemBuffer::new(qr_in_place_scratch::<
458 usize,
459 c64,
460 >(
461 n,
462 n,
463 bs,
464 par,
465 default(),
466 ))),
467 default(),
468 )
469 .1;
470 let mut Q = Mat::<c64, _, _>::zeros(n, n);
471 let mut R = QR.as_ref().cloned();
472 for j in 0..n {
473 Q[(j, j)] = c64::ONE;
474 }
475 householder::apply_block_householder_sequence_on_the_left_in_place_with_conj(
476 QR.as_ref(),
477 H.as_ref(),
478 Conj::No,
479 Q.as_mut(),
480 Par::Seq,
481 MemStack::new(&mut MemBuffer::new(
482 householder::apply_block_householder_sequence_on_the_left_in_place_scratch::<c64>(n, bs, n),
483 )),
484 );
485 for j in 0..n {
486 for i in j + 1..n {
487 R[(i, j)] = c64::ZERO;
488 }
489 }
490 assert!(Q * R * q ~ A);
491 }
492 let n = 20;
493 for m in [2, 3, 4, 8, 16, 24, 32, 128, 255] {
494 let bs = 15;
495 let size = Ord::min(m, n);
496 let approx_eq = CwiseMat(ApproxEq {
497 abs_tol: 1e-10,
498 rel_tol: 1e-10,
499 });
500 let A = CwiseMatDistribution {
501 nrows: m,
502 ncols: n,
503 dist: ComplexDistribution::new(
504 StandardNormal,
505 StandardNormal,
506 ),
507 }
508 .rand::<Mat<c64>>(rng);
509 let A = A.as_ref();
510 let mut QR = A.cloned();
511 let mut H = Mat::zeros(bs, size);
512 let col_perm = &mut *vec![0usize; n];
513 let col_perm_inv = &mut *vec![0usize; n];
514 let q = qr_in_place(
515 QR.as_mut(),
516 H.as_mut(),
517 col_perm,
518 col_perm_inv,
519 par,
520 MemStack::new(&mut MemBuffer::new(qr_in_place_scratch::<
521 usize,
522 c64,
523 >(
524 m,
525 n,
526 bs,
527 par,
528 default(),
529 ))),
530 default(),
531 )
532 .1;
533 let mut Q = Mat::<c64, _, _>::zeros(m, m);
534 let mut R = QR.as_ref().cloned();
535 for j in 0..m {
536 Q[(j, j)] = c64::ONE;
537 }
538 householder::apply_block_householder_sequence_on_the_left_in_place_with_conj(
539 QR.as_ref().subcols(0, size),
540 H.as_ref(),
541 Conj::No,
542 Q.as_mut(),
543 Par::Seq,
544 MemStack::new(&mut MemBuffer::new(
545 householder::apply_block_householder_sequence_on_the_left_in_place_scratch::<c64>(m, bs, m),
546 )),
547 );
548 for j in 0..n {
549 for i in j + 1..m {
550 R[(i, j)] = c64::ZERO;
551 }
552 }
553 assert!(Q * R * q ~ A);
554 }
555 }
556 }
557}