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 break 'next_iter;
631 }
632 if npiv == 1 {
633 let W0 = Wr.rb_mut().col_mut(0);
634 let diag = &W0[0].real();
635 let diag_inv = &diag.recip();
636 subdiag[0] = zero();
637 let (_, _, L, mut A) = Ar.rb_mut().split_at_mut(1, 1);
638 let W0 = W0.rb().get(1..);
639 let n = A.nrows();
640 let mut L = L.col_mut(0);
641 zip!(W0, L.rb_mut()).for_each(
642 |unzip!(w, a): Zip!(&T, &mut T)| {
643 *a = w.mul_real(diag_inv);
644 },
645 );
646 for j in 0..n {
647 A[(j, j)] =
648 (A[(j, j)].real() - diag * &L[j].abs2()).to_cplx();
649 }
650 } else {
651 let a00 = Wr[(0, 0)].real();
652 let a11 = Wr[(1, 1)].real();
653 let a10 = Wr[(1, 0)].copy();
654 subdiag[0] = a10.copy();
655 subdiag[1] = zero();
656 Wr[(1, 0)] = zero();
657 Ar[(1, 0)] = zero();
658 let d10 = a10.abs();
659 let d10_inv = &d10.recip();
660 let d00 = a00 * d10_inv;
661 let d11 = a11 * d10_inv;
662 let t = (&d00 * &d11 - one::<T::Real>()).recip();
663 let d10 = a10.mul_real(d10_inv);
664 let d = t * d10_inv;
665 let (_, _, L, mut A) = Ar.rb_mut().split_at_mut(2, 2);
666 let (mut L0, mut L1) = L.two_cols_mut(0, 1);
667 let Wr = Wr.rb().get(2.., ..);
668 let W0 = Wr.col(0);
669 let W1 = Wr.col(1);
670 let n = A.nrows();
671 for j in 0..n {
672 let x0 = &W0[j];
673 let x1 = &W1[j];
674 let w0 = (x0.mul_real(&d11) - x1 * &d10).mul_real(&d);
675 let w1 = (x1.mul_real(&d00) - x0 * d10.conj()).mul_real(&d);
676 A[(j, j)] =
677 (&A[(j, j)] - &W0[j] * w0.conj() - &W1[j] * w1.conj())
678 .as_real();
679 L0[j] = w0;
680 L1[j] = w1;
681 }
682 }
683 }
684 let offset = A_left.ncols();
685 if npiv == 2 {
686 pivots[k] = (offset + i0 + k) | TOP_BIT;
687 pivots[k + 1] = (offset + i1 + k) | TOP_BIT;
688 } else {
689 pivots[k] = offset + i0 + k;
690 }
691 k += npiv;
692 }
693 let W = W.rb().get(k.., ..k);
694 let (_, _, Al, mut Ar) = A.rb_mut().split_at_mut(k, k);
695 let Al = Al.rb();
696 linalg::matmul::triangular::matmul(
697 Ar.rb_mut(),
698 BlockStructure::StrictTriangularLower,
699 Accum::Add,
700 W,
701 BlockStructure::Rectangular,
702 Al.adjoint(),
703 BlockStructure::Rectangular,
704 -one::<T>(),
705 par,
706 );
707 for j in 0..n - k {
708 Ar[(j, j)] = Ar[(j, j)].as_real();
709 }
710 k
711}
712fn lblt_blocked<T: ComplexField>(
713 A: MatMut<'_, T>,
714 subdiag: DiagMut<'_, T>,
715 pivots: &mut [usize],
716 block_size: usize,
717 rook: bool,
718 diagonal: bool,
719 mut par: Par,
720 stack: &mut MemStack,
721) {
722 let ref alpha = (one::<T::Real>() + from_f64::<T::Real>(17.0).sqrt())
723 * from_f64::<T::Real>(0.125);
724 let mut A = A;
725 let mut subdiag = subdiag.column_vector_mut();
726 let n = A.nrows();
727 let mut k = 0;
728 while k < n {
729 let (_, _, mut A_left, A_right) = A.rb_mut().split_at_mut(k, k);
730 let (mut W, _) =
731 unsafe { temp_mat_uninit::<T, _, _>(n - k, block_size, stack) };
732 let W = W.as_mat_mut();
733 let next;
734 if block_size < 2 || n - k <= block_size {
735 lblt_unblocked(
736 alpha.copy(),
737 A_left.rb_mut(),
738 A_right,
739 subdiag.rb_mut().get_mut(k..).as_diagonal_mut(),
740 &mut pivots[k..],
741 rook,
742 diagonal,
743 par.reborrow(),
744 );
745 next = n;
746 } else {
747 let block_size = lblt_blocked_step(
748 alpha.copy(),
749 W,
750 A_left.rb_mut(),
751 A_right,
752 subdiag.rb_mut().get_mut(k..).as_diagonal_mut(),
753 &mut pivots[k..],
754 rook,
755 diagonal,
756 par.reborrow(),
757 );
758 next = k + block_size;
759 }
760 let pivots = &pivots[k..next];
761 let A_left = A.rb_mut().get_mut(.., ..k);
762 if A_left.ncols() > 0 {
763 match par {
764 Par::Seq => {
765 for mut col in A_left.col_iter_mut() {
766 for (i, &j) in core::iter::zip(k..next, pivots) {
767 let j = j & !TOP_BIT;
768 linalg::lu::partial_pivoting::factor::swap_elems(
769 col.rb_mut(),
770 i,
771 j,
772 );
773 }
774 }
775 },
776 #[cfg(feature = "rayon")]
777 _ => {
778 let nthreads = core::num::NonZeroUsize::new(par.degree()).unwrap();
779 let nthreads = nthreads.get();
780 crate::utils::thread::for_each(par.reborrow(),
781 nthreads,
782 A_left.par_col_iter_mut(),
783 |mut col| {
784 for (i, &j) in core::iter::zip(k..next, pivots) {
785 let j = j & !TOP_BIT;
786 linalg::lu::partial_pivoting::factor::swap_elems(col.rb_mut(), i, j);
787 }
788 },
789 );
790 },
791 }
792 }
793 k = next;
794 }
795}
796#[inline(never)]
797fn lblt_unblocked<T: ComplexField>(
798 alpha: T::Real,
799 A_left: MatMut<'_, T>,
800 A: MatMut<'_, T>,
801 subdiag: DiagMut<'_, T>,
802 pivots: &mut [usize],
803 rook: bool,
804 diagonal: bool,
805 par: Par,
806) {
807 let _ = par;
808 let mut A = A;
809 let mut A_left = A_left;
810 let mut subdiag = subdiag;
811 let n = A.nrows();
812 assert!(all(A.nrows() == n, A.ncols() == n, subdiag.dim() == n));
813 let mut k = 0usize;
814 while k < n {
815 let (_, _, mut L_prev, mut A) = A.rb_mut().split_at_mut(k, k);
816 let mut subdiag = subdiag.rb_mut().column_vector_mut().get_mut(k..);
817 let A_left = A_left.rb_mut().get_mut(k.., ..);
818 let npiv;
819 let mut i0 = if diagonal {
820 l1_argmax(A.rb().diagonal().column_vector()).0.unwrap()
821 } else {
822 0
823 };
824 let mut i1 = usize::MAX;
825 let (r, mut gamma_i) = offdiag_argmax(A.rb(), i0);
826 let mut nothing_to_do = false;
827 if k + 1 == n || gamma_i == zero() {
828 nothing_to_do = true;
829 npiv = 1;
830 } else if A[(i0, i0)].real().abs() >= &alpha * &gamma_i {
831 npiv = 1;
832 } else {
833 i1 = r.unwrap();
834 if rook {
835 loop {
836 let (s, gamma_r) = offdiag_argmax(A.rb(), i1);
837 if A[(i1, i1)].abs1() >= &alpha * &gamma_r {
838 npiv = 1;
839 i0 = i1;
840 i1 = usize::MAX;
841 break;
842 } else if gamma_i == gamma_r {
843 npiv = 2;
844 break;
845 } else {
846 i0 = i1;
847 i1 = s.unwrap();
848 gamma_i = gamma_r;
849 }
850 }
851 } else {
852 let (_, gamma_r) = offdiag_argmax(A.rb(), i1);
853 if A[(i0, i0)].real().abs()
854 >= (&alpha * &gamma_r) * (&gamma_r / &gamma_i)
855 {
856 npiv = 1;
857 } else if A[(i1, i1)].real().abs() >= &alpha * &gamma_r {
858 npiv = 1;
859 i0 = i1;
860 } else {
861 npiv = 2;
862 }
863 }
864 }
865 if npiv == 2 && i0 > i1 {
866 (i0, i1) = (i1, i0);
867 }
868 'next_iter: {
869 if i0 != 0 {
870 swap_self_adjoint(A.rb_mut(), 0, i0);
871 perm::swap_rows_idx(L_prev.rb_mut(), 0, i0);
872 }
873 if npiv == 2 && i1 != 1 {
874 swap_self_adjoint(A.rb_mut(), 1, i1);
875 perm::swap_rows_idx(L_prev.rb_mut(), 1, i1);
876 }
877 if nothing_to_do {
878 break 'next_iter;
879 }
880 if npiv == 1 {
881 let diag = A[(0, 0)].real();
882 let diag_inv = diag.recip();
883 subdiag[0] = zero();
884 let (_, _, L, A) = A.rb_mut().split_at_mut(1, 1);
885 let L = L.col_mut(0);
886 rank1_update(A, L, diag_inv);
887 } else {
888 let a00 = A[(0, 0)].real();
889 let a11 = A[(1, 1)].real();
890 let a10 = A[(1, 0)].copy();
891 subdiag[0] = a10.copy();
892 subdiag[1] = zero();
893 A[(1, 0)] = zero();
894 let d10 = a10.abs();
895 let d10_inv = &d10.recip();
896 let d00 = a00 * d10_inv;
897 let d11 = a11 * d10_inv;
898 let t = (&d00 * &d11 - one::<T::Real>()).recip();
899 let d10 = a10.mul_real(d10_inv);
900 let d = t * d10_inv;
901 let (_, _, L, A) = A.rb_mut().split_at_mut(2, 2);
902 let (L0, L1) = L.two_cols_mut(0, 1);
903 rank2_update(A, L0, L1, d, d00, d10, d11);
904 }
905 }
906 let offset = A_left.ncols();
907 if npiv == 2 {
908 pivots[k] = (offset + i0 + k) | TOP_BIT;
909 pivots[k + 1] = (offset + i1 + k) | TOP_BIT;
910 } else {
911 pivots[k] = offset + i0 + k;
912 }
913 k += npiv;
914 }
915}
916impl<T: ComplexField> Auto<T> for LbltParams {
917 fn auto() -> Self {
918 Self {
919 pivoting: PivotingStrategy::PartialDiag,
920 block_size: 64,
921 par_threshold: ParThreshold::Derived,
922 non_exhaustive: NonExhaustive(()),
923 }
924 }
925}
926pub fn rank2_update<'a, T: ComplexField>(
927 mut A: MatMut<'a, T>,
928 mut L0: ColMut<'a, T>,
929 mut L1: ColMut<'a, T>,
930 d: T::Real,
931 d00: T::Real,
932 d10: T,
933 d11: T::Real,
934) {
935 if const { T::SIMD_CAPABILITIES.is_simd() } {
936 if let (Some(A), Some(L0), Some(L1)) = (
937 A.rb_mut().try_as_col_major_mut(),
938 L0.rb_mut().try_as_col_major_mut(),
939 L1.rb_mut().try_as_col_major_mut(),
940 ) {
941 rank2_update_simd(A, L0, L1, d, d00, d10, d11);
942 } else {
943 rank2_update_fallback(A, L0, L1, d, d00, d10, d11);
944 }
945 } else {
946 rank2_update_fallback(A, L0, L1, d, d00, d10, d11);
947 }
948}
949pub fn rank2_update_simd<'a, T: ComplexField>(
950 A: MatMut<'a, T, usize, usize, ContiguousFwd>,
951 L0: ColMut<'a, T, usize, ContiguousFwd>,
952 L1: ColMut<'a, T, usize, ContiguousFwd>,
953 d: T::Real,
954 d00: T::Real,
955 d10: T,
956 d11: T::Real,
957) {
958 struct Impl<'a, T: ComplexField> {
959 A: MatMut<'a, T, usize, usize, ContiguousFwd>,
960 L0: ColMut<'a, T, usize, ContiguousFwd>,
961 L1: ColMut<'a, T, usize, ContiguousFwd>,
962 d: T::Real,
963 d00: T::Real,
964 d10: T,
965 d11: T::Real,
966 }
967 impl<T: ComplexField> pulp::WithSimd for Impl<'_, T> {
968 type Output = ();
969
970 #[inline(always)]
971 fn with_simd<S: pulp::Simd>(self, simd: S) {
972 let Self {
973 mut A,
974 mut L0,
975 mut L1,
976 d,
977 d00,
978 d10,
979 d11,
980 } = self;
981 let n = A.nrows();
982 for j in 0..n {
983 let x0 = &L0[j];
984 let x1 = &L1[j];
985 let w0 = (x0.mul_real(&d11) - x1 * &d10).mul_real(&d);
986 let w1 = (x1.mul_real(&d00) - x0 * d10.conj()).mul_real(&d);
987 with_dim!({
988 let subrange_len = n - j;
989 });
990 {
991 let mut A = A
992 .rb_mut()
993 .get_mut(j.., j)
994 .as_row_shape_mut(subrange_len);
995 let L0 = L0.rb().get(j..).as_row_shape(subrange_len);
996 let L1 = L1.rb().get(j..).as_row_shape(subrange_len);
997 let simd =
998 SimdCtx::<T, S>::new(T::simd_ctx(simd), subrange_len);
999 let w0_conj = w0.conj();
1000 let w1_conj = w1.conj();
1001 let w0_conj_neg = -w0_conj;
1002 let w1_conj_neg = -w1_conj;
1003 let w0_splat = simd.splat(&w0_conj_neg);
1004 let w1_splat = simd.splat(&w1_conj_neg);
1005 simd_iter!(for i in [simd.indices()] {
1006 let mut acc = simd.read(A.rb(), i);
1007 let l0_val = simd.read(L0, i);
1008 let l1_val = simd.read(L1, i);
1009 acc = simd.mul_add(l0_val, w0_splat, acc);
1010 acc = simd.mul_add(l1_val, w1_splat, acc);
1011 simd.write(A.rb_mut(), i, acc);
1012 });
1013 }
1014 A[(j, j)] = A[(j, j)].real().to_cplx();
1015 L0[j] = w0;
1016 L1[j] = w1;
1017 }
1018 }
1019 }
1020 dispatch!(
1021 Impl {
1022 A,
1023 L0,
1024 L1,
1025 d,
1026 d00,
1027 d10,
1028 d11
1029 },
1030 Impl,
1031 T
1032 )
1033}
1034pub fn rank2_update_fallback<'a, T: ComplexField>(
1035 mut A: MatMut<'a, T>,
1036 mut L0: ColMut<'a, T>,
1037 mut L1: ColMut<'a, T>,
1038 d: T::Real,
1039 d00: T::Real,
1040 d10: T,
1041 d11: T::Real,
1042) {
1043 let n = A.nrows();
1044 for j in 0..n {
1045 let x0 = &L0[j].copy();
1046 let x1 = &L1[j].copy();
1047 let w0 = (x0.mul_real(&d11) - x1 * &d10).mul_real(&d);
1048 let w1 = (x1.mul_real(&d00) - x0 * d10.conj()).mul_real(&d);
1049 for i in j..n {
1050 A[(i, j)] = &A[(i, j)] - &L0[i] * w0.conj() - &L1[i] * w1.conj();
1051 }
1052 A[(j, j)] = A[(j, j)].as_real();
1053 L0[j] = w0;
1054 L1[j] = w1;
1055 }
1056}
1057pub fn rank1_update<'a, T: ComplexField>(
1058 mut A: MatMut<'a, T>,
1059 mut L0: ColMut<'a, T>,
1060 d: T::Real,
1061) {
1062 if const { T::SIMD_CAPABILITIES.is_simd() } {
1063 if let (Some(A), Some(L0)) = (
1064 A.rb_mut().try_as_col_major_mut(),
1065 L0.rb_mut().try_as_col_major_mut(),
1066 ) {
1067 rank1_update_simd(A, L0, d);
1068 } else {
1069 rank1_update_fallback(A, L0, d);
1070 }
1071 } else {
1072 rank1_update_fallback(A, L0, d);
1073 }
1074}
1075pub fn rank1_update_simd<'a, T: ComplexField>(
1076 A: MatMut<'a, T, usize, usize, ContiguousFwd>,
1077 L0: ColMut<'a, T, usize, ContiguousFwd>,
1078 d: T::Real,
1079) {
1080 struct Impl<'a, T: ComplexField> {
1081 A: MatMut<'a, T, usize, usize, ContiguousFwd>,
1082 L0: ColMut<'a, T, usize, ContiguousFwd>,
1083 d: T::Real,
1084 }
1085 impl<T: ComplexField> pulp::WithSimd for Impl<'_, T> {
1086 type Output = ();
1087
1088 #[inline(always)]
1089 fn with_simd<S: pulp::Simd>(self, simd: S) {
1090 let Self { mut A, mut L0, d } = self;
1091 let n = A.nrows();
1092 for j in 0..n {
1093 let x0 = &L0[j];
1094 let w0 = x0.mul_real(&d);
1095 with_dim!({
1096 let subrange_len = n - j;
1097 });
1098 {
1099 let mut A = A
1100 .rb_mut()
1101 .get_mut(j.., j)
1102 .as_row_shape_mut(subrange_len);
1103 let L0 = L0.rb().get(j..).as_row_shape(subrange_len);
1104 let simd =
1105 SimdCtx::<T, S>::new(T::simd_ctx(simd), subrange_len);
1106 let w0_conj = w0.conj();
1107 let w0_conj_neg = -w0_conj;
1108 let w0_splat = simd.splat(&w0_conj_neg);
1109 simd_iter!(for i in [simd.indices()] {
1110 let mut acc = simd.read(A.rb(), i);
1111 let l0_val = simd.read(L0, i);
1112 acc = simd.mul_add(l0_val, w0_splat, acc);
1113 simd.write(A.rb_mut(), i, acc);
1114 });
1115 }
1116 A[(j, j)] = A[(j, j)].real().to_cplx();
1117 L0[j] = w0;
1118 }
1119 }
1120 }
1121 dispatch!(Impl { A, L0, d }, Impl, T)
1122}
1123pub fn rank1_update_fallback<'a, T: ComplexField>(
1124 mut A: MatMut<'a, T>,
1125 mut L0: ColMut<'a, T>,
1126 d: T::Real,
1127) {
1128 let n = A.nrows();
1129 for j in 0..n {
1130 let x0 = &L0[j];
1131 let w0 = x0.mul_real(&d);
1132 for i in j..n {
1133 A[(i, j)] = &A[(i, j)] - &L0[i] * w0.conj();
1134 }
1135 A[(j, j)] = A[(j, j)].real().to_cplx();
1136 L0[j] = w0;
1137 }
1138}
1139pub fn cholesky_in_place_scratch<I: Index, T: ComplexField>(
1142 dim: usize,
1143 par: ParShape,
1144 params: Spec<LbltParams, T>,
1145) -> StackReq {
1146 let params = params.config;
1147 let _ = par;
1148 let mut bs = params.block_size;
1149 if bs < 2 || dim <= bs {
1150 bs = 0;
1151 }
1152 StackReq::new::<usize>(dim).and(temp_mat_scratch::<T>(dim, bs))
1153}
1154#[derive(Copy, Clone, Debug)]
1156pub struct LbltInfo {
1157 pub transposition_count: usize,
1159}
1160#[track_caller]
1174pub fn cholesky_in_place<'out, I: Index, T: ComplexField>(
1175 A: MatMut<'_, T>,
1176 subdiag: DiagMut<'_, T>,
1177 perm: &'out mut [I],
1178 perm_inv: &'out mut [I],
1179 par: Par,
1180 stack: &mut MemStack,
1181 params: Spec<LbltParams, T>,
1182) -> (LbltInfo, PermRef<'out, I>) {
1183 let params = params.config;
1184 let truncate = <I::Signed as SignedIndex>::truncate;
1185 let n = A.nrows();
1186 assert!(all(
1187 A.nrows() == A.ncols(),
1188 subdiag.dim() == n,
1189 perm.len() == n,
1190 perm_inv.len() == n
1191 ));
1192 #[cfg(feature = "perf-warn")]
1193 if A.row_stride().unsigned_abs() != 1 && crate::__perf_warn!(CHOLESKY_WARN)
1194 {
1195 if A.col_stride().unsigned_abs() == 1 {
1196 log::warn!(
1197 target : "faer_perf",
1198 "$LBL^\top$ decomposition prefers column-major
1199 matrix. Found row-major matrix."
1200 );
1201 } else {
1202 log::warn!(
1203 target : "faer_perf",
1204 "$LBL^\top$ decomposition prefers column-major
1205 matrix. Found matrix with generic strides."
1206 );
1207 }
1208 }
1209 let (mut pivots, stack) = stack.make_with::<usize>(n, |_| 0);
1210 let pivots = &mut *pivots;
1211 let mut bs = params.block_size;
1212 if bs < 2 || n <= bs {
1213 bs = 0;
1214 }
1215 let (rook, diagonal) = match params.pivoting {
1216 PivotingStrategy::Partial => (false, false),
1217 PivotingStrategy::PartialDiag => (false, true),
1218 PivotingStrategy::Rook => (true, false),
1219 PivotingStrategy::RookDiag => (true, true),
1220 _ => (false, false),
1221 };
1222 if params.pivoting == PivotingStrategy::Full {
1223 lblt_full_piv(A, subdiag, pivots, par, params);
1224 } else {
1225 lblt_blocked(A, subdiag, pivots, bs, rook, diagonal, par, stack);
1226 }
1227 for (i, p) in perm.iter_mut().enumerate() {
1228 *p = I::from_signed(truncate(i));
1229 }
1230 let mut transposition_count = 0usize;
1231 for i in 0..n {
1232 let p = pivots[i] & !TOP_BIT;
1233 if i != p {
1234 transposition_count += 1;
1235 }
1236 perm.swap(i, p);
1237 }
1238 for (i, &p) in perm.iter().enumerate() {
1239 perm_inv[p.to_signed().zx()] = I::from_signed(truncate(i));
1240 }
1241 (
1242 LbltInfo {
1243 transposition_count,
1244 },
1245 unsafe { PermRef::new_unchecked(perm, perm_inv, n) },
1246 )
1247}