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