1use crate::internal_prelude::*;
2use crate::utils::cost::{ParThreshold, rate};
3use crate::{assert, perm};
4use linalg::matmul::triangular::BlockStructure;
5const TOP_BIT: usize = 1 << (usize::BITS - 1);
6#[derive(Copy, Clone, Debug, PartialEq, Eq)]
8#[non_exhaustive]
9pub enum PivotingStrategy {
10 #[deprecated]
12 Diagonal,
13 Partial,
15 PartialDiag,
18 Rook,
20 RookDiag,
23 Full,
25}
26#[derive(Copy, Clone, Debug)]
28pub struct LbltParams {
29 pub pivoting: PivotingStrategy,
31 pub block_size: usize,
33 pub par_threshold: ParThreshold,
38 #[doc(hidden)]
39 pub non_exhaustive: NonExhaustive,
40}
41fn swap_self_adjoint<T: ComplexField>(A: MatMut<'_, T>, i: usize, j: usize) {
42 assert_ne!(i, j);
43 let mut A = A;
44 let (i, j) = (Ord::min(i, j), Ord::max(i, j));
45 perm::swap_cols_idx(A.rb_mut().get_mut(j + 1.., ..), i, j);
46 perm::swap_rows_idx(A.rb_mut().get_mut(.., ..i), i, j);
47 let tmp = A[(i, i)].real();
48 A[(i, i)] = A[(j, j)].as_real();
49 A[(j, j)] = tmp.to_cplx();
50 A[(j, i)] = A[(j, i)].conj();
51 let (Ai, Aj) = A.split_at_row_mut(j);
52 let Ai = Ai.get_mut(i + 1..j, i);
53 let Aj = Aj.get_mut(0, i + 1..j).transpose_mut();
54 zip!(Ai, Aj).for_each(|unzip!(x, y): Zip!(&mut _, &mut _)| {
55 (*x, *y) = (y.conj(), x.conj());
56 });
57}
58fn rank_1_update_and_argmax_fallback<'M, 'N, T: ComplexField>(
59 A: MatMut<'_, T, Dim<'N>, Dim<'N>>,
60 L: ColRef<'_, T, Dim<'N>>,
61 d: T::Real,
62 start: IdxInc<'N>,
63 end: IdxInc<'N>,
64) -> (usize, usize, T::Real) {
65 let mut A = A;
66 let n = A.nrows();
67 let mut max_j = n.idx(0);
68 let mut max_i = n.idx(0);
69 let mut max_offdiag = zero();
70 for j in start.to(end) {
71 for i in j.next().to(n.end()) {
72 A[(i, j)] = &A[(i, j)] - (&L[i] * L[j].conj()).mul_real(&d);
73 let val = A[(i, j)].abs2();
74 if val > max_offdiag {
75 max_offdiag = val;
76 max_i = i;
77 max_j = j;
78 }
79 }
80 }
81 (*max_i, *max_j, max_offdiag)
82}
83fn rank_2_update_and_argmax_fallback<'N, T: ComplexField>(
84 A: MatMut<'_, T, Dim<'N>, Dim<'N>>,
85 L0: ColRef<'_, T, Dim<'N>>,
86 L1: ColRef<'_, T, Dim<'N>>,
87 d: T::Real,
88 d00: T::Real,
89 d11: T::Real,
90 d10: T,
91 start: IdxInc<'N>,
92 end: IdxInc<'N>,
93) -> (usize, usize, T::Real) {
94 let mut A = A;
95 let n = A.nrows();
96 let mut max_j = n.idx(0);
97 let mut max_i = n.idx(0);
98 let mut max_offdiag = zero();
99 for j in start.to(end) {
100 let x0 = L0[j].copy();
101 let x1 = L1[j].copy();
102 let w0 = (x0.mul_real(&d11) - &x1 * &d10).mul_real(&d);
103 let w1 = (x1.mul_real(&d00) - &x0 * d10.conj()).mul_real(&d);
104 for i in j.next().to(n.end()) {
105 A[(i, j)] = &A[(i, j)] - &L0[i] * w0.conj() - &L1[i] * w1.conj();
106 let val = A[(i, j)].abs2();
107 if val > max_offdiag {
108 max_offdiag = val;
109 max_i = i;
110 max_j = j;
111 }
112 }
113 }
114 (*max_i, *max_j, max_offdiag)
115}
116fn rank_1_update_and_argmax_seq<'M, 'N, T: ComplexField>(
117 A: MatMut<'_, T, Dim<'N>, Dim<'N>>,
118 L: ColRef<'_, T, Dim<'N>>,
119 d: T::Real,
120 start: IdxInc<'N>,
121 end: IdxInc<'N>,
122) -> (usize, usize, T::Real) {
123 rank_1_update_and_argmax_fallback(A, L, d, start, end)
124}
125fn rank_2_update_and_argmax_seq<'N, T: ComplexField>(
126 A: MatMut<'_, T, Dim<'N>, Dim<'N>>,
127 L0: ColRef<'_, T, Dim<'N>>,
128 L1: ColRef<'_, T, Dim<'N>>,
129 d: T::Real,
130 d00: T::Real,
131 d11: T::Real,
132 d10: T,
133 start: IdxInc<'N>,
134 end: IdxInc<'N>,
135) -> (usize, usize, T::Real) {
136 rank_2_update_and_argmax_fallback(A, L0, L1, d, d00, d11, d10, start, end)
137}
138fn rank_1_update_and_argmax<T: ComplexField>(
139 A: MatMut<'_, T>,
140 L: ColRef<'_, T>,
141 d: T::Real,
142 par: Par,
143) -> (usize, usize, T::Real) {
144 with_dim!(N, A.nrows());
145 match par {
146 Par::Seq => rank_1_update_and_argmax_seq(
147 A.as_shape_mut(N, N),
148 L.as_row_shape(N),
149 d,
150 IdxInc::ZERO,
151 N.end(),
152 ),
153 #[cfg(feature = "rayon")]
154 _ => {
155 let nthreads = core::num::NonZeroUsize::new(par.degree()).unwrap();
156 use rayon::prelude::*;
157 let nthreads = nthreads.get();
158 let n = *N;
159 assert!((n as u64) < (1u64 << 50));
160 let idx_to_col_start = |idx: usize| {
161 let idx_as_percent = idx as f64 / nthreads as f64;
162 let col_start_percent =
163 1.0f64 - libm::sqrt(1.0f64 - idx_as_percent);
164 (col_start_percent * n as f64) as usize
165 };
166 let mut r =
167 alloc::vec![(0usize, 0usize, zero::< T::Real > ()); nthreads];
168 crate::utils::thread::for_each(par,
169 nthreads,
170 r.par_iter_mut().enumerate(),
171 |(idx, out)| {
172 let A = unsafe { A.rb().const_cast() };
173 let start = N.idx_inc(idx_to_col_start(idx));
174 let end = N.idx_inc(idx_to_col_start(idx + 1));
175 *out = rank_1_update_and_argmax_seq(
176 A.as_shape_mut(N, N),
177 L.as_row_shape(N),
178 d.copy(),
179 start,
180 end,
181 );
182 },
183 );
184 r.into_iter()
185 .max_by(|(_, _, a), (_, _, b)| {
186 if a == b {
187 core::cmp::Ordering::Equal
188 } else if a > b {
189 core::cmp::Ordering::Greater
190 } else {
191 core::cmp::Ordering::Less
192 }
193 })
194 .unwrap()
195 },
196 }
197}
198fn rank_2_update_and_argmax<'N, T: ComplexField>(
199 A: MatMut<'_, T>,
200 L0: ColRef<'_, T>,
201 L1: ColRef<'_, T>,
202 d: T::Real,
203 d00: T::Real,
204 d11: T::Real,
205 d10: T,
206 par: Par,
207) -> (usize, usize, T::Real) {
208 with_dim!(N, A.nrows());
209 match par {
210 Par::Seq => rank_2_update_and_argmax_seq(
211 A.as_shape_mut(N, N),
212 L0.as_row_shape(N),
213 L1.as_row_shape(N),
214 d,
215 d00,
216 d11,
217 d10,
218 IdxInc::ZERO,
219 N.end(),
220 ),
221 #[cfg(feature = "rayon")]
222 _ => {
223 let nthreads = core::num::NonZeroUsize::new(par.degree()).unwrap();
224 use rayon::prelude::*;
225 let nthreads = nthreads.get();
226 let n = *N;
227 assert!((n as u64) < (1u64 << 50));
228 let idx_to_col_start = |idx: usize| {
229 let idx_as_percent = idx as f64 / nthreads as f64;
230 let col_start_percent =
231 1.0f64 - libm::sqrt(1.0f64 - idx_as_percent);
232 (col_start_percent * n as f64) as usize
233 };
234 let mut r =
235 alloc::vec![(0usize, 0usize, zero::< T::Real > ()); nthreads];
236 crate::utils::thread::for_each(par,
237 nthreads,
238 r.par_iter_mut().enumerate(),
239 |(idx, out)| {
240 let A = unsafe { A.rb().const_cast() };
241 let start = N.idx_inc(idx_to_col_start(idx));
242 let end = N.idx_inc(idx_to_col_start(idx + 1));
243 *out = rank_2_update_and_argmax_seq(
244 A.as_shape_mut(N, N),
245 L0.as_row_shape(N),
246 L1.as_row_shape(N),
247 d.copy(),
248 d00.copy(),
249 d11.copy(),
250 d10.copy(),
251 start,
252 end,
253 );
254 },
255 );
256 r.into_iter()
257 .max_by(|(_, _, a), (_, _, b)| {
258 if a == b {
259 core::cmp::Ordering::Equal
260 } else if a < b {
261 core::cmp::Ordering::Less
262 } else {
263 core::cmp::Ordering::Greater
264 }
265 })
266 .unwrap()
267 },
268 }
269}
270fn lblt_full_piv<T: ComplexField>(
271 A: MatMut<'_, T>,
272 subdiag: DiagMut<'_, T>,
273 pivots: &mut [usize],
274 par: Par,
275 params: LbltParams,
276) {
277 let alpha = ((one::<T::Real>() + from_f64::<T::Real>(17.0).sqrt())
278 * from_f64::<T::Real>(0.125))
279 .abs2();
280 let mut A = A;
281 let mut subdiag = subdiag.column_vector_mut();
282 let mut par = par;
283 let n = A.nrows();
284 let scale_fwd = &A.norm_max();
285 let scale_bwd = &scale_fwd.recip();
286 zip!(A.rb_mut()).for_each(|unzip!(x)| *x = x.mul_real(scale_bwd));
287 let mut max_i = 0;
288 let mut max_j = 0;
289 let mut max_offdiag = zero();
290 for j in 0..n {
291 for i in j + 1..n {
292 let val = A[(i, j)].abs2();
293 if val > max_offdiag {
294 max_offdiag = val;
295 max_i = i;
296 max_j = j;
297 }
298 }
299 }
300 let mut k = 0;
301 while k < n {
302 if max_offdiag == zero() {
303 break;
304 }
305 let (mut Aprev, mut A) =
306 A.rb_mut().get_mut(k.., ..).split_at_col_mut(k);
307 let mut subdiag = subdiag.rb_mut().get_mut(k..);
308 let pivots = &mut pivots[k..];
309 let n = A.nrows();
310 let mut max_s = 0;
311 let mut max_diag = zero();
312 for s in 0..n {
313 let val = A[(s, s)].abs2();
314 if val > max_diag {
315 max_diag = val;
316 max_s = s;
317 }
318 }
319 let npiv;
320 let i0;
321 let i1;
322 if max_diag >= &alpha * &max_offdiag {
323 npiv = 1;
324 i0 = max_s;
325 i1 = usize::MAX;
326 } else {
327 npiv = 2;
328 i0 = max_j;
329 i1 = max_i;
330 }
331 let rem = n - npiv;
332 if !params.par_threshold.region_pays::<T>(
335 par.shape(),
336 rem * rem,
337 rate::bunch_kaufman(),
338 1,
339 ) {
340 par = Par::Seq;
341 }
342 if i0 != 0 {
343 swap_self_adjoint(A.rb_mut(), 0, i0);
344 perm::swap_rows_idx(Aprev.rb_mut(), 0, i0);
345 }
346 if npiv == 2 && i1 != 1 {
347 swap_self_adjoint(A.rb_mut(), 1, i1);
348 perm::swap_rows_idx(Aprev.rb_mut(), 1, i1);
349 }
350 if npiv == 1 {
351 let diag = A[(0, 0)].real();
352 let diag_inv = &diag.recip();
353 subdiag[0] = zero();
354 let (_, _, L, mut A) = A.rb_mut().split_at_mut(1, 1);
355 let n = A.nrows();
356 let mut L = L.col_mut(0);
357 zip!(L.rb_mut()).for_each(|unzip!(x)| *x = x.mul_real(diag_inv));
358 for i in 0..n {
359 A[(i, i)] = (A[(i, i)].real() - &diag * L[i].abs2()).to_cplx();
360 }
361 if n != 0 {
362 (max_i, max_j, max_offdiag) =
363 rank_1_update_and_argmax(A.rb_mut(), L.rb(), diag, par.reborrow());
364 }
365 } else {
366 let a00 = A[(0, 0)].real();
367 let a11 = A[(1, 1)].real();
368 let a10 = A[(1, 0)].copy();
369 subdiag[0] = a10.copy();
370 subdiag[1] = zero();
371 A[(1, 0)] = zero();
372 let d10 = a10.abs();
373 let d10_inv = &d10.recip();
374 let d00 = a00 * d10_inv;
375 let d11 = a11 * d10_inv;
376 let t = (&d00 * &d11 - one::<T::Real>()).recip();
377 let d10 = a10.mul_real(d10_inv);
378 let d = t * d10_inv;
379 let (_, _, L, mut A) = A.rb_mut().split_at_mut(2, 2);
380 let (mut L0, mut L1) = L.two_cols_mut(0, 1);
381 let n = A.nrows();
382 if n != 0 {
383 (max_i, max_j, max_offdiag) = rank_2_update_and_argmax(
384 A.rb_mut(),
385 L0.rb(),
386 L1.rb(),
387 d.copy(),
388 d00.copy(),
389 d11.copy(),
390 d10.copy(),
391 par.reborrow(),
392 );
393 }
394 for j in 0..n {
395 let x0 = &L0[j].copy();
396 let x1 = &L1[j].copy();
397 let w0 = (x0.mul_real(&d11) - x1 * &d10).mul_real(&d);
398 let w1 = (x1.mul_real(&d00) - x0 * d10.conj()).mul_real(&d);
399 A[(j, j)] =
400 (&A[(j, j)] - &L0[j] * w0.conj() - &L1[j] * w1.conj())
401 .as_real();
402 L0[j] = w0;
403 L1[j] = w1;
404 }
405 }
406 if npiv == 2 {
407 pivots[0] = (i0 + k) | TOP_BIT;
408 pivots[1] = (i1 + k) | TOP_BIT;
409 } else {
410 pivots[0] = i0 + k;
411 }
412 k += npiv;
413 }
414 while k < n {
415 let (mut Aprev, mut A) =
416 A.rb_mut().get_mut(k.., ..).split_at_col_mut(k);
417 let mut subdiag = subdiag.rb_mut().get_mut(k..);
418 let pivots = &mut pivots[k..];
419 let n = A.nrows();
420 let mut max_s = 0;
421 let mut max_diag = zero();
422 for s in 0..n {
423 let val = A[(s, s)].abs2();
424 if val > max_diag {
425 max_diag = val;
426 max_s = s;
427 }
428 }
429 if max_s != 0 {
430 let (mut A0, mut As) = A.rb_mut().two_cols_mut(0, max_s);
431 core::mem::swap(&mut A0[0], &mut As[max_s]);
432 perm::swap_rows_idx(Aprev.rb_mut(), 0, max_s);
433 }
434 subdiag[0] = zero();
435 pivots[0] = max_s + k;
436 k += 1;
437 }
438 zip!(A.rb_mut().diagonal_mut().column_vector_mut())
439 .for_each(|unzip!(x)| *x = x.mul_real(scale_fwd));
440 zip!(subdiag.rb_mut()).for_each(|unzip!(x)| *x = x.mul_real(scale_fwd));
441}
442#[track_caller]
443fn l1_argmax<T: ComplexField>(col: ColRef<'_, T>) -> (Option<usize>, T::Real) {
444 let n = col.nrows();
445 if n == 0 {
446 return (None, zero());
447 }
448 let mut i = 0;
449 let mut best = zero();
450 for j in 0..n {
451 let val = col[j].abs1();
452 if val > best {
453 best = val;
454 i = j;
455 }
456 }
457 (Some(i), best)
458}
459#[track_caller]
460fn offdiag_argmax<T: ComplexField>(
461 A: MatRef<'_, T>,
462 idx: usize,
463) -> (Option<usize>, T::Real) {
464 let (mut col_argmax, col_max) = l1_argmax(A.rb().get(idx + 1.., idx));
465 col_argmax.as_mut().map(|col_argmax| *col_argmax += idx + 1);
466 let (row_argmax, row_max) = l1_argmax(A.rb().get(idx, ..idx).transpose());
467 if col_max > row_max {
468 (col_argmax, col_max)
469 } else {
470 (row_argmax, row_max)
471 }
472}
473fn update_and_offdiag_argmax<T: ComplexField>(
474 mut dst: ColMut<'_, T>,
475 Wl: MatRef<'_, T>,
476 Al: MatRef<'_, T>,
477 Ar: MatRef<'_, T>,
478 i0: usize,
479 par: Par,
480) -> (Option<usize>, T::Real) {
481 let n = Al.nrows();
482 for j in 0..i0 {
483 dst[j] = Ar[(i0, j)].conj();
484 }
485 dst[i0] = zero();
486 for j in i0 + 1..n {
487 dst[j] = Ar[(j, i0)].copy();
488 }
489 linalg::matmul::matmul(
490 dst.rb_mut(),
491 Accum::Add,
492 Al.rb(),
493 Wl.row(i0).adjoint(),
494 -one::<T>(),
495 par,
496 );
497 dst[i0] = zero();
498 let ret = l1_argmax(dst.rb());
499 dst[i0] = Ar[(i0, i0)].as_real();
500 if n == 1 { (None, zero()) } else { ret }
501}
502#[inline(never)]
503fn lblt_blocked_step<T: ComplexField>(
504 alpha: T::Real,
505 W: MatMut<'_, T>,
506 A_left: MatMut<'_, T>,
507 A: MatMut<'_, T>,
508 subdiag: DiagMut<'_, T>,
509 pivots: &mut [usize],
510 rook: bool,
511 diagonal: bool,
512 mut par: Par,
513) -> usize {
514 let mut A = A;
515 let mut A_left = A_left;
516 let mut subdiag = subdiag;
517 let mut W = W;
518 let n = A.nrows();
519 let block_size = W.ncols();
520 assert!(all(
521 A.nrows() == n,
522 A.ncols() == n,
523 W.nrows() == n,
524 subdiag.dim() == n,
525 block_size >= 2,
526 ));
527 let kmax = Ord::min(block_size - 1, n);
528 let mut k = 0usize;
529 while k < kmax {
530 let mut A = A.rb_mut();
531 let mut W = W.rb_mut();
532 let mut subdiag = subdiag.rb_mut().column_vector_mut().get_mut(k..);
533 let A_left = A_left.rb_mut().get_mut(k.., ..);
534 let (mut Wl, mut Wr) = W.rb_mut().get_mut(k.., ..).split_at_col_mut(k);
535 let (mut Al, mut Ar) = A.rb_mut().get_mut(k.., ..).split_at_col_mut(k);
536 let mut Al = Al.rb_mut();
537 let mut Wr = Wr.rb_mut().get_mut(.., ..2);
538 let npiv;
539 let mut i0 = if diagonal {
540 l1_argmax(Ar.rb().diagonal().column_vector()).0.unwrap()
541 } else {
542 0
543 };
544 let mut i1 = usize::MAX;
545 let mut nothing_to_do = false;
546 let (mut Wr0, mut Wr1) = Wr.rb_mut().two_cols_mut(0, 1);
547 let (r, mut gamma_i) = update_and_offdiag_argmax(
548 Wr0.rb_mut(),
549 Wl.rb(),
550 Al.rb(),
551 Ar.rb(),
552 i0,
553 par.reborrow(),
554 );
555 if k + 1 == n || gamma_i == zero() {
556 nothing_to_do = true;
557 npiv = 1;
558 } else if Ar[(i0, i0)].real().abs() >= &alpha * &gamma_i {
559 npiv = 1;
560 } else {
561 i1 = r.unwrap();
562 if rook {
563 loop {
564 let (s, gamma_r) = update_and_offdiag_argmax(
565 Wr1.rb_mut(),
566 Wl.rb(),
567 Al.rb(),
568 Ar.rb(),
569 i1,
570 par.reborrow(),
571 );
572 if Ar[(i1, i1)].abs1() >= &alpha * &gamma_r {
573 npiv = 1;
574 i0 = i1;
575 i1 = usize::MAX;
576 Wr0.copy_from(&Wr1);
577 break;
578 } else if s == Some(i0) || gamma_i == gamma_r {
579 npiv = 2;
580 break;
581 } else {
582 i0 = i1;
583 i1 = s.unwrap();
584 gamma_i = gamma_r;
585 Wr0.copy_from(&Wr1);
586 }
587 }
588 } else {
589 let (_, gamma_r) = update_and_offdiag_argmax(
590 Wr1.rb_mut(),
591 Wl.rb(),
592 Al.rb(),
593 Ar.rb(),
594 i1,
595 par.reborrow(),
596 );
597 if Ar[(i0, i0)].real().abs()
598 >= (&alpha * &gamma_r) * (&gamma_r / &gamma_i)
599 {
600 npiv = 1;
601 } else if Ar[(i1, i1)].real().abs() >= &alpha * &gamma_r {
602 npiv = 1;
603 i0 = i1;
604 i1 = usize::MAX;
605 Wr0.copy_from(&Wr1);
606 } else {
607 npiv = 2;
608 }
609 }
610 }
611 if npiv == 2 && i0 > i1 {
612 perm::swap_cols_idx(Wr.rb_mut(), 0, 1);
613 (i0, i1) = (i1, i0);
614 }
615 let mut Wr = Wr.rb_mut().get_mut(.., ..npiv);
616 'next_iter: {
617 if i0 != 0 {
618 swap_self_adjoint(Ar.rb_mut(), 0, i0);
619 perm::swap_rows_idx(Al.rb_mut(), 0, i0);
620 perm::swap_rows_idx(Wl.rb_mut(), 0, i0);
621 perm::swap_rows_idx(Wr.rb_mut(), 0, i0);
622 }
623 if npiv == 2 && i1 != 1 {
624 swap_self_adjoint(Ar.rb_mut(), 1, i1);
625 perm::swap_rows_idx(Al.rb_mut(), 1, i1);
626 perm::swap_rows_idx(Wl.rb_mut(), 1, i1);
627 perm::swap_rows_idx(Wr.rb_mut(), 1, i1);
628 }
629 if nothing_to_do {
630 subdiag[0] = zero();
631 break 'next_iter;
632 }
633 if npiv == 1 {
634 let W0 = Wr.rb_mut().col_mut(0);
635 let diag = &W0[0].real();
636 let diag_inv = &diag.recip();
637 subdiag[0] = zero();
638 let (_, _, L, mut A) = Ar.rb_mut().split_at_mut(1, 1);
639 let W0 = W0.rb().get(1..);
640 let n = A.nrows();
641 let mut L = L.col_mut(0);
642 zip!(W0, L.rb_mut()).for_each(
643 |unzip!(w, a): Zip!(&T, &mut T)| {
644 *a = w.mul_real(diag_inv);
645 },
646 );
647 for j in 0..n {
648 A[(j, j)] =
649 (A[(j, j)].real() - diag * &L[j].abs2()).to_cplx();
650 }
651 } else {
652 let a00 = Wr[(0, 0)].real();
653 let a11 = Wr[(1, 1)].real();
654 let a10 = Wr[(1, 0)].copy();
655 subdiag[0] = a10.copy();
656 subdiag[1] = zero();
657 Wr[(1, 0)] = zero();
658 Ar[(1, 0)] = zero();
659 let d10 = a10.abs();
660 let d10_inv = &d10.recip();
661 let d00 = a00 * d10_inv;
662 let d11 = a11 * d10_inv;
663 let t = (&d00 * &d11 - one::<T::Real>()).recip();
664 let d10 = a10.mul_real(d10_inv);
665 let d = t * d10_inv;
666 let (_, _, L, mut A) = Ar.rb_mut().split_at_mut(2, 2);
667 let (mut L0, mut L1) = L.two_cols_mut(0, 1);
668 let Wr = Wr.rb().get(2.., ..);
669 let W0 = Wr.col(0);
670 let W1 = Wr.col(1);
671 let n = A.nrows();
672 for j in 0..n {
673 let x0 = &W0[j];
674 let x1 = &W1[j];
675 let w0 = (x0.mul_real(&d11) - x1 * &d10).mul_real(&d);
676 let w1 = (x1.mul_real(&d00) - x0 * d10.conj()).mul_real(&d);
677 A[(j, j)] =
678 (&A[(j, j)] - &W0[j] * w0.conj() - &W1[j] * w1.conj())
679 .as_real();
680 L0[j] = w0;
681 L1[j] = w1;
682 }
683 }
684 }
685 let offset = A_left.ncols();
686 if npiv == 2 {
687 pivots[k] = (offset + i0 + k) | TOP_BIT;
688 pivots[k + 1] = (offset + i1 + k) | TOP_BIT;
689 } else {
690 pivots[k] = offset + i0 + k;
691 }
692 k += npiv;
693 }
694 let W = W.rb().get(k.., ..k);
695 let (_, _, Al, mut Ar) = A.rb_mut().split_at_mut(k, k);
696 let Al = Al.rb();
697 linalg::matmul::triangular::matmul(
698 Ar.rb_mut(),
699 BlockStructure::StrictTriangularLower,
700 Accum::Add,
701 W,
702 BlockStructure::Rectangular,
703 Al.adjoint(),
704 BlockStructure::Rectangular,
705 -one::<T>(),
706 par,
707 );
708 for j in 0..n - k {
709 Ar[(j, j)] = Ar[(j, j)].as_real();
710 }
711 k
712}
713fn lblt_blocked<T: ComplexField>(
714 A: MatMut<'_, T>,
715 subdiag: DiagMut<'_, T>,
716 pivots: &mut [usize],
717 block_size: usize,
718 rook: bool,
719 diagonal: bool,
720 mut par: Par,
721 stack: &mut MemStack,
722) {
723 let ref alpha = (one::<T::Real>() + from_f64::<T::Real>(17.0).sqrt())
724 * from_f64::<T::Real>(0.125);
725 let mut A = A;
726 let mut subdiag = subdiag.column_vector_mut();
727 let n = A.nrows();
728 let mut k = 0;
729 while k < n {
730 let (_, _, mut A_left, A_right) = A.rb_mut().split_at_mut(k, k);
731 let (mut W, _) =
732 unsafe { temp_mat_uninit::<T, _, _>(n - k, block_size, stack) };
733 let W = W.as_mat_mut();
734 let next;
735 if block_size < 2 || n - k <= block_size {
736 lblt_unblocked(
737 alpha.copy(),
738 A_left.rb_mut(),
739 A_right,
740 subdiag.rb_mut().get_mut(k..).as_diagonal_mut(),
741 &mut pivots[k..],
742 rook,
743 diagonal,
744 par.reborrow(),
745 );
746 next = n;
747 } else {
748 let block_size = lblt_blocked_step(
749 alpha.copy(),
750 W,
751 A_left.rb_mut(),
752 A_right,
753 subdiag.rb_mut().get_mut(k..).as_diagonal_mut(),
754 &mut pivots[k..],
755 rook,
756 diagonal,
757 par.reborrow(),
758 );
759 next = k + block_size;
760 }
761 let pivots = &pivots[k..next];
762 let A_left = A.rb_mut().get_mut(.., ..k);
763 if A_left.ncols() > 0 {
764 match par {
765 Par::Seq => {
766 for mut col in A_left.col_iter_mut() {
767 for (i, &j) in core::iter::zip(k..next, pivots) {
768 let j = j & !TOP_BIT;
769 linalg::lu::partial_pivoting::factor::swap_elems(
770 col.rb_mut(),
771 i,
772 j,
773 );
774 }
775 }
776 },
777 #[cfg(feature = "rayon")]
778 _ => {
779 let nthreads = core::num::NonZeroUsize::new(par.degree()).unwrap();
780 let nthreads = nthreads.get();
781 crate::utils::thread::for_each(par.reborrow(),
782 nthreads,
783 A_left.par_col_iter_mut(),
784 |mut col| {
785 for (i, &j) in core::iter::zip(k..next, pivots) {
786 let j = j & !TOP_BIT;
787 linalg::lu::partial_pivoting::factor::swap_elems(col.rb_mut(), i, j);
788 }
789 },
790 );
791 },
792 }
793 }
794 k = next;
795 }
796}
797#[inline(never)]
798fn lblt_unblocked<T: ComplexField>(
799 alpha: T::Real,
800 A_left: MatMut<'_, T>,
801 A: MatMut<'_, T>,
802 subdiag: DiagMut<'_, T>,
803 pivots: &mut [usize],
804 rook: bool,
805 diagonal: bool,
806 par: Par,
807) {
808 let _ = par;
809 let mut A = A;
810 let mut A_left = A_left;
811 let mut subdiag = subdiag;
812 let n = A.nrows();
813 assert!(all(A.nrows() == n, A.ncols() == n, subdiag.dim() == n));
814 let mut k = 0usize;
815 while k < n {
816 let (_, _, mut L_prev, mut A) = A.rb_mut().split_at_mut(k, k);
817 let mut subdiag = subdiag.rb_mut().column_vector_mut().get_mut(k..);
818 let A_left = A_left.rb_mut().get_mut(k.., ..);
819 let npiv;
820 let mut i0 = if diagonal {
821 l1_argmax(A.rb().diagonal().column_vector()).0.unwrap()
822 } else {
823 0
824 };
825 let mut i1 = usize::MAX;
826 let (r, mut gamma_i) = offdiag_argmax(A.rb(), i0);
827 let mut nothing_to_do = false;
828 if k + 1 == n || gamma_i == zero() {
829 nothing_to_do = true;
830 npiv = 1;
831 } else if A[(i0, i0)].real().abs() >= &alpha * &gamma_i {
832 npiv = 1;
833 } else {
834 i1 = r.unwrap();
835 if rook {
836 loop {
837 let (s, gamma_r) = offdiag_argmax(A.rb(), i1);
838 if A[(i1, i1)].abs1() >= &alpha * &gamma_r {
839 npiv = 1;
840 i0 = i1;
841 i1 = usize::MAX;
842 break;
843 } else if gamma_i == gamma_r {
844 npiv = 2;
845 break;
846 } else {
847 i0 = i1;
848 i1 = s.unwrap();
849 gamma_i = gamma_r;
850 }
851 }
852 } else {
853 let (_, gamma_r) = offdiag_argmax(A.rb(), i1);
854 if A[(i0, i0)].real().abs()
855 >= (&alpha * &gamma_r) * (&gamma_r / &gamma_i)
856 {
857 npiv = 1;
858 } else if A[(i1, i1)].real().abs() >= &alpha * &gamma_r {
859 npiv = 1;
860 i0 = i1;
861 } else {
862 npiv = 2;
863 }
864 }
865 }
866 if npiv == 2 && i0 > i1 {
867 (i0, i1) = (i1, i0);
868 }
869 'next_iter: {
870 if i0 != 0 {
871 swap_self_adjoint(A.rb_mut(), 0, i0);
872 perm::swap_rows_idx(L_prev.rb_mut(), 0, i0);
873 }
874 if npiv == 2 && i1 != 1 {
875 swap_self_adjoint(A.rb_mut(), 1, i1);
876 perm::swap_rows_idx(L_prev.rb_mut(), 1, i1);
877 }
878 if nothing_to_do {
879 subdiag[0] = zero();
880 break 'next_iter;
881 }
882 if npiv == 1 {
883 let diag = A[(0, 0)].real();
884 let diag_inv = diag.recip();
885 subdiag[0] = zero();
886 let (_, _, L, A) = A.rb_mut().split_at_mut(1, 1);
887 let L = L.col_mut(0);
888 rank1_update(A, L, diag_inv);
889 } else {
890 let a00 = A[(0, 0)].real();
891 let a11 = A[(1, 1)].real();
892 let a10 = A[(1, 0)].copy();
893 subdiag[0] = a10.copy();
894 subdiag[1] = zero();
895 A[(1, 0)] = zero();
896 let d10 = a10.abs();
897 let d10_inv = &d10.recip();
898 let d00 = a00 * d10_inv;
899 let d11 = a11 * d10_inv;
900 let t = (&d00 * &d11 - one::<T::Real>()).recip();
901 let d10 = a10.mul_real(d10_inv);
902 let d = t * d10_inv;
903 let (_, _, L, A) = A.rb_mut().split_at_mut(2, 2);
904 let (L0, L1) = L.two_cols_mut(0, 1);
905 rank2_update(A, L0, L1, d, d00, d10, d11);
906 }
907 }
908 let offset = A_left.ncols();
909 if npiv == 2 {
910 pivots[k] = (offset + i0 + k) | TOP_BIT;
911 pivots[k + 1] = (offset + i1 + k) | TOP_BIT;
912 } else {
913 pivots[k] = offset + i0 + k;
914 }
915 k += npiv;
916 }
917}
918impl<T: ComplexField> Auto<T> for LbltParams {
919 fn auto() -> Self {
920 Self {
921 pivoting: PivotingStrategy::PartialDiag,
922 block_size: 64,
923 par_threshold: ParThreshold::Derived,
924 non_exhaustive: NonExhaustive(()),
925 }
926 }
927}
928pub fn rank2_update<'a, T: ComplexField>(
929 mut A: MatMut<'a, T>,
930 mut L0: ColMut<'a, T>,
931 mut L1: ColMut<'a, T>,
932 d: T::Real,
933 d00: T::Real,
934 d10: T,
935 d11: T::Real,
936) {
937 if const { T::SIMD_CAPABILITIES.is_simd() } {
938 if let (Some(A), Some(L0), Some(L1)) = (
939 A.rb_mut().try_as_col_major_mut(),
940 L0.rb_mut().try_as_col_major_mut(),
941 L1.rb_mut().try_as_col_major_mut(),
942 ) {
943 rank2_update_simd(A, L0, L1, d, d00, d10, d11);
944 } else {
945 rank2_update_fallback(A, L0, L1, d, d00, d10, d11);
946 }
947 } else {
948 rank2_update_fallback(A, L0, L1, d, d00, d10, d11);
949 }
950}
951pub fn rank2_update_simd<'a, T: ComplexField>(
952 A: MatMut<'a, T, usize, usize, ContiguousFwd>,
953 L0: ColMut<'a, T, usize, ContiguousFwd>,
954 L1: ColMut<'a, T, usize, ContiguousFwd>,
955 d: T::Real,
956 d00: T::Real,
957 d10: T,
958 d11: T::Real,
959) {
960 struct Impl<'a, T: ComplexField> {
961 A: MatMut<'a, T, usize, usize, ContiguousFwd>,
962 L0: ColMut<'a, T, usize, ContiguousFwd>,
963 L1: ColMut<'a, T, usize, ContiguousFwd>,
964 d: T::Real,
965 d00: T::Real,
966 d10: T,
967 d11: T::Real,
968 }
969 impl<T: ComplexField> pulp::WithSimd for Impl<'_, T> {
970 type Output = ();
971
972 #[inline(always)]
973 fn with_simd<S: pulp::Simd>(self, simd: S) {
974 let Self {
975 mut A,
976 mut L0,
977 mut L1,
978 d,
979 d00,
980 d10,
981 d11,
982 } = self;
983 let n = A.nrows();
984 for j in 0..n {
985 let x0 = &L0[j];
986 let x1 = &L1[j];
987 let w0 = (x0.mul_real(&d11) - x1 * &d10).mul_real(&d);
988 let w1 = (x1.mul_real(&d00) - x0 * d10.conj()).mul_real(&d);
989 with_dim!({
990 let subrange_len = n - j;
991 });
992 {
993 let mut A = A
994 .rb_mut()
995 .get_mut(j.., j)
996 .as_row_shape_mut(subrange_len);
997 let L0 = L0.rb().get(j..).as_row_shape(subrange_len);
998 let L1 = L1.rb().get(j..).as_row_shape(subrange_len);
999 let simd =
1000 SimdCtx::<T, S>::new(T::simd_ctx(simd), subrange_len);
1001 let w0_conj = w0.conj();
1002 let w1_conj = w1.conj();
1003 let w0_conj_neg = -w0_conj;
1004 let w1_conj_neg = -w1_conj;
1005 let w0_splat = simd.splat(&w0_conj_neg);
1006 let w1_splat = simd.splat(&w1_conj_neg);
1007 simd_iter!(for i in [simd.indices()] {
1008 let mut acc = simd.read(A.rb(), i);
1009 let l0_val = simd.read(L0, i);
1010 let l1_val = simd.read(L1, i);
1011 acc = simd.mul_add(l0_val, w0_splat, acc);
1012 acc = simd.mul_add(l1_val, w1_splat, acc);
1013 simd.write(A.rb_mut(), i, acc);
1014 });
1015 }
1016 A[(j, j)] = A[(j, j)].real().to_cplx();
1017 L0[j] = w0;
1018 L1[j] = w1;
1019 }
1020 }
1021 }
1022 dispatch!(
1023 Impl {
1024 A,
1025 L0,
1026 L1,
1027 d,
1028 d00,
1029 d10,
1030 d11
1031 },
1032 Impl,
1033 T
1034 )
1035}
1036pub fn rank2_update_fallback<'a, T: ComplexField>(
1037 mut A: MatMut<'a, T>,
1038 mut L0: ColMut<'a, T>,
1039 mut L1: ColMut<'a, T>,
1040 d: T::Real,
1041 d00: T::Real,
1042 d10: T,
1043 d11: T::Real,
1044) {
1045 let n = A.nrows();
1046 for j in 0..n {
1047 let x0 = &L0[j].copy();
1048 let x1 = &L1[j].copy();
1049 let w0 = (x0.mul_real(&d11) - x1 * &d10).mul_real(&d);
1050 let w1 = (x1.mul_real(&d00) - x0 * d10.conj()).mul_real(&d);
1051 for i in j..n {
1052 A[(i, j)] = &A[(i, j)] - &L0[i] * w0.conj() - &L1[i] * w1.conj();
1053 }
1054 A[(j, j)] = A[(j, j)].as_real();
1055 L0[j] = w0;
1056 L1[j] = w1;
1057 }
1058}
1059pub fn rank1_update<'a, T: ComplexField>(
1060 mut A: MatMut<'a, T>,
1061 mut L0: ColMut<'a, T>,
1062 d: T::Real,
1063) {
1064 if const { T::SIMD_CAPABILITIES.is_simd() } {
1065 if let (Some(A), Some(L0)) = (
1066 A.rb_mut().try_as_col_major_mut(),
1067 L0.rb_mut().try_as_col_major_mut(),
1068 ) {
1069 rank1_update_simd(A, L0, d);
1070 } else {
1071 rank1_update_fallback(A, L0, d);
1072 }
1073 } else {
1074 rank1_update_fallback(A, L0, d);
1075 }
1076}
1077pub fn rank1_update_simd<'a, T: ComplexField>(
1078 A: MatMut<'a, T, usize, usize, ContiguousFwd>,
1079 L0: ColMut<'a, T, usize, ContiguousFwd>,
1080 d: T::Real,
1081) {
1082 struct Impl<'a, T: ComplexField> {
1083 A: MatMut<'a, T, usize, usize, ContiguousFwd>,
1084 L0: ColMut<'a, T, usize, ContiguousFwd>,
1085 d: T::Real,
1086 }
1087 impl<T: ComplexField> pulp::WithSimd for Impl<'_, T> {
1088 type Output = ();
1089
1090 #[inline(always)]
1091 fn with_simd<S: pulp::Simd>(self, simd: S) {
1092 let Self { mut A, mut L0, d } = self;
1093 let n = A.nrows();
1094 for j in 0..n {
1095 let x0 = &L0[j];
1096 let w0 = x0.mul_real(&d);
1097 with_dim!({
1098 let subrange_len = n - j;
1099 });
1100 {
1101 let mut A = A
1102 .rb_mut()
1103 .get_mut(j.., j)
1104 .as_row_shape_mut(subrange_len);
1105 let L0 = L0.rb().get(j..).as_row_shape(subrange_len);
1106 let simd =
1107 SimdCtx::<T, S>::new(T::simd_ctx(simd), subrange_len);
1108 let w0_conj = w0.conj();
1109 let w0_conj_neg = -w0_conj;
1110 let w0_splat = simd.splat(&w0_conj_neg);
1111 simd_iter!(for i in [simd.indices()] {
1112 let mut acc = simd.read(A.rb(), i);
1113 let l0_val = simd.read(L0, i);
1114 acc = simd.mul_add(l0_val, w0_splat, acc);
1115 simd.write(A.rb_mut(), i, acc);
1116 });
1117 }
1118 A[(j, j)] = A[(j, j)].real().to_cplx();
1119 L0[j] = w0;
1120 }
1121 }
1122 }
1123 dispatch!(Impl { A, L0, d }, Impl, T)
1124}
1125pub fn rank1_update_fallback<'a, T: ComplexField>(
1126 mut A: MatMut<'a, T>,
1127 mut L0: ColMut<'a, T>,
1128 d: T::Real,
1129) {
1130 let n = A.nrows();
1131 for j in 0..n {
1132 let x0 = &L0[j];
1133 let w0 = x0.mul_real(&d);
1134 for i in j..n {
1135 A[(i, j)] = &A[(i, j)] - &L0[i] * w0.conj();
1136 }
1137 A[(j, j)] = A[(j, j)].real().to_cplx();
1138 L0[j] = w0;
1139 }
1140}
1141pub fn cholesky_in_place_scratch<I: Index, T: ComplexField>(
1144 dim: usize,
1145 par: ParShape,
1146 params: Spec<LbltParams, T>,
1147) -> StackReq {
1148 let params = params.config;
1149 let _ = par;
1150 let mut bs = params.block_size;
1151 if bs < 2 || dim <= bs {
1152 bs = 0;
1153 }
1154 StackReq::new::<usize>(dim).and(temp_mat_scratch::<T>(dim, bs))
1155}
1156#[derive(Copy, Clone, Debug)]
1158pub struct LbltInfo {
1159 pub transposition_count: usize,
1161}
1162#[track_caller]
1176pub fn cholesky_in_place<'out, I: Index, T: ComplexField>(
1177 A: MatMut<'_, T>,
1178 subdiag: DiagMut<'_, T>,
1179 perm: &'out mut [I],
1180 perm_inv: &'out mut [I],
1181 par: Par,
1182 stack: &mut MemStack,
1183 params: Spec<LbltParams, T>,
1184) -> (LbltInfo, PermRef<'out, I>) {
1185 let params = params.config;
1186 let truncate = <I::Signed as SignedIndex>::truncate;
1187 let n = A.nrows();
1188 assert!(all(
1189 A.nrows() == A.ncols(),
1190 subdiag.dim() == n,
1191 perm.len() == n,
1192 perm_inv.len() == n
1193 ));
1194 #[cfg(feature = "perf-warn")]
1195 if A.row_stride().unsigned_abs() != 1 && crate::__perf_warn!(CHOLESKY_WARN)
1196 {
1197 if A.col_stride().unsigned_abs() == 1 {
1198 log::warn!(
1199 target : "faer_perf",
1200 "$LBL^\top$ decomposition prefers column-major
1201 matrix. Found row-major matrix."
1202 );
1203 } else {
1204 log::warn!(
1205 target : "faer_perf",
1206 "$LBL^\top$ decomposition prefers column-major
1207 matrix. Found matrix with generic strides."
1208 );
1209 }
1210 }
1211 let (mut pivots, stack) = stack.make_with::<usize>(n, |_| 0);
1212 let pivots = &mut *pivots;
1213 let mut bs = params.block_size;
1214 if bs < 2 || n <= bs {
1215 bs = 0;
1216 }
1217 let (rook, diagonal) = match params.pivoting {
1218 PivotingStrategy::Partial => (false, false),
1219 PivotingStrategy::PartialDiag => (false, true),
1220 PivotingStrategy::Rook => (true, false),
1221 PivotingStrategy::RookDiag => (true, true),
1222 _ => (false, false),
1223 };
1224 if params.pivoting == PivotingStrategy::Full {
1225 lblt_full_piv(A, subdiag, pivots, par, params);
1226 } else {
1227 lblt_blocked(A, subdiag, pivots, bs, rook, diagonal, par, stack);
1228 }
1229 for (i, p) in perm.iter_mut().enumerate() {
1230 *p = I::from_signed(truncate(i));
1231 }
1232 let mut transposition_count = 0usize;
1233 for i in 0..n {
1234 let p = pivots[i] & !TOP_BIT;
1235 if i != p {
1236 transposition_count += 1;
1237 }
1238 perm.swap(i, p);
1239 }
1240 for (i, &p) in perm.iter().enumerate() {
1241 perm_inv[p.to_signed().zx()] = I::from_signed(truncate(i));
1242 }
1243 (
1244 LbltInfo {
1245 transposition_count,
1246 },
1247 unsafe { PermRef::new_unchecked(perm, perm_inv, n) },
1248 )
1249}