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;
355 let mut H = Q_coeff;
356 let size = H.ncols();
357 let block_size = H.nrows();
358 let ret = qr_in_place_unblocked(
359 A.rb_mut(),
360 H.rb_mut().row_mut(0),
361 col_perm,
362 col_perm_inv,
363 par,
364 stack,
365 params,
366 );
367 let mut j = 0;
368 while j < size {
369 let block_size = Ord::min(block_size, size - j);
370 let mut H = H
371 .rb_mut()
372 .subcols_mut(j, block_size)
373 .subrows_mut(0, block_size);
374 for j in 0..block_size {
375 H[(j, j)] = H[(0, j)].copy();
376 }
377 let A = A.rb().get(j.., j..j + block_size);
378 householder::upgrade_householder_factor(
379 H.rb_mut(),
380 A,
381 block_size,
382 1,
383 par,
384 );
385 j += block_size;
386 }
387 ret
388}
389#[cfg(test)]
390mod tests {
391 use super::*;
392 use crate::stats::prelude::*;
393 use crate::utils::approx::*;
394 use crate::{Mat, assert, c64};
395 use dyn_stack::MemBuffer;
396 #[test]
397 fn test_unblocked_qr() {
398 let rng = &mut StdRng::seed_from_u64(0);
399 for par in [Par::Seq, Par::rayon(8)] {
400 for n in [2, 3, 4, 8, 16, 24, 32, 128, 255] {
401 let bs = 15;
402 let approx_eq = CwiseMat(ApproxEq {
403 abs_tol: 1e-10,
404 rel_tol: 1e-10,
405 });
406 let A = CwiseMatDistribution {
407 nrows: n,
408 ncols: n,
409 dist: ComplexDistribution::new(
410 StandardNormal,
411 StandardNormal,
412 ),
413 }
414 .rand::<Mat<c64>>(rng);
415 let A = A.as_ref();
416 let mut QR = A.cloned();
417 let mut H = Mat::zeros(bs, n);
418 let col_perm = &mut *vec![0usize; n];
419 let col_perm_inv = &mut *vec![0usize; n];
420 let q = qr_in_place(
421 QR.as_mut(),
422 H.as_mut(),
423 col_perm,
424 col_perm_inv,
425 par,
426 MemStack::new(&mut MemBuffer::new(qr_in_place_scratch::<
427 usize,
428 c64,
429 >(
430 n,
431 n,
432 bs,
433 par,
434 default(),
435 ))),
436 default(),
437 )
438 .1;
439 let mut Q = Mat::<c64, _, _>::zeros(n, n);
440 let mut R = QR.as_ref().cloned();
441 for j in 0..n {
442 Q[(j, j)] = c64::ONE;
443 }
444 householder::apply_block_householder_sequence_on_the_left_in_place_with_conj(
445 QR.as_ref(),
446 H.as_ref(),
447 Conj::No,
448 Q.as_mut(),
449 Par::Seq,
450 MemStack::new(&mut MemBuffer::new(
451 householder::apply_block_householder_sequence_on_the_left_in_place_scratch::<c64>(n, bs, n),
452 )),
453 );
454 for j in 0..n {
455 for i in j + 1..n {
456 R[(i, j)] = c64::ZERO;
457 }
458 }
459 assert!(Q * R * q ~ A);
460 }
461 let n = 20;
462 for m in [2, 3, 4, 8, 16, 24, 32, 128, 255] {
463 let bs = 15;
464 let size = Ord::min(m, n);
465 let approx_eq = CwiseMat(ApproxEq {
466 abs_tol: 1e-10,
467 rel_tol: 1e-10,
468 });
469 let A = CwiseMatDistribution {
470 nrows: m,
471 ncols: n,
472 dist: ComplexDistribution::new(
473 StandardNormal,
474 StandardNormal,
475 ),
476 }
477 .rand::<Mat<c64>>(rng);
478 let A = A.as_ref();
479 let mut QR = A.cloned();
480 let mut H = Mat::zeros(bs, size);
481 let col_perm = &mut *vec![0usize; n];
482 let col_perm_inv = &mut *vec![0usize; n];
483 let q = qr_in_place(
484 QR.as_mut(),
485 H.as_mut(),
486 col_perm,
487 col_perm_inv,
488 par,
489 MemStack::new(&mut MemBuffer::new(qr_in_place_scratch::<
490 usize,
491 c64,
492 >(
493 m,
494 n,
495 bs,
496 par,
497 default(),
498 ))),
499 default(),
500 )
501 .1;
502 let mut Q = Mat::<c64, _, _>::zeros(m, m);
503 let mut R = QR.as_ref().cloned();
504 for j in 0..m {
505 Q[(j, j)] = c64::ONE;
506 }
507 householder::apply_block_householder_sequence_on_the_left_in_place_with_conj(
508 QR.as_ref().subcols(0, size),
509 H.as_ref(),
510 Conj::No,
511 Q.as_mut(),
512 Par::Seq,
513 MemStack::new(&mut MemBuffer::new(
514 householder::apply_block_householder_sequence_on_the_left_in_place_scratch::<c64>(m, bs, m),
515 )),
516 );
517 for j in 0..n {
518 for i in j + 1..m {
519 R[(i, j)] = c64::ZERO;
520 }
521 }
522 assert!(Q * R * q ~ A);
523 }
524 }
525 }
526}