faer/linalg/cholesky/llt_pivoting/
factor.rs1use crate::assert;
2use crate::internal_prelude::*;
3pub use linalg::cholesky::llt::factor::LltError;
4use linalg::matmul::triangular::BlockStructure;
5#[derive(Copy, Clone, Debug)]
6pub struct PivLltParams {
7 pub block_size: usize,
8 #[doc(hidden)]
9 pub non_exhaustive: NonExhaustive,
10}
11impl Default for PivLltParams {
12 #[inline]
13 fn default() -> Self {
14 Self {
15 block_size: 128,
16 non_exhaustive: NonExhaustive(()),
17 }
18 }
19}
20impl<T> Auto<T> for PivLltParams {
21 #[inline]
22 fn auto() -> Self {
23 Self {
24 block_size: 128,
25 non_exhaustive: NonExhaustive(()),
26 }
27 }
28}
29#[derive(Copy, Clone, Debug)]
30pub struct PivLltInfo {
31 pub rank: usize,
33 pub transposition_count: usize,
35}
36#[inline]
37pub fn cholesky_in_place_scratch<I: Index, T: ComplexField>(
38 dim: usize,
39 par: Par,
40 params: Spec<PivLltParams, T>,
41) -> StackReq {
42 _ = par;
43 _ = params;
44 temp_mat_scratch::<T::Real>(dim, 2)
45}
46#[track_caller]
47pub fn cholesky_in_place<'out, I: Index, T: ComplexField>(
48 a: MatMut<'_, T>,
49 perm: &'out mut [I],
50 perm_inv: &'out mut [I],
51 par: Par,
52 stack: &mut MemStack,
53 params: Spec<PivLltParams, T>,
54) -> Result<(PivLltInfo, PermRef<'out, I>), LltError> {
55 assert!(a.nrows() == a.ncols());
56 let n = a.nrows();
57 assert!(n <= I::Signed::MAX.zx());
58 let mut rank = n;
59 let mut transposition_count = 0;
60 'exit: {
61 if n > 0 {
62 let mut a = a;
63 for (i, p) in perm.iter_mut().enumerate() {
64 *p = I::truncate(i);
65 }
66 let (mut work1, stack) =
67 unsafe { temp_mat_uninit::<T::Real, _, _>(n, 1, stack) };
68 let (mut work2, _) =
69 unsafe { temp_mat_uninit::<T::Real, _, _>(n, 1, stack) };
70 let work1 = work1.as_mat_mut();
71 let work2 = work2.as_mat_mut();
72 let mut dot_products = work1.col_mut(0);
73 let mut diagonals = work2.col_mut(0);
74 let mut ajj = zero::<T::Real>();
75 let mut pvt = 0usize;
76 for i in 0..n {
77 let aii = a[(i, i)].real();
78 if aii < zero::<T::Real>() || aii.is_nan() {
79 return Err(LltError::NonPositivePivot { index: 0 });
80 }
81 if aii > ajj {
82 ajj = aii;
83 pvt = i;
84 }
85 }
86 let tol = eps::<T::Real>() * from_f64::<T::Real>(n as f64) * &ajj;
87 let mut k = 0usize;
88 while k < n {
89 let bs = Ord::min(n - k, params.block_size);
90 for i in k..n {
91 dot_products[i] = zero::<T::Real>();
92 }
93 for j in k..k + bs {
94 if j == k {
95 for i in j..n {
96 diagonals[i] = a[(i, i)].real();
97 }
98 } else {
99 for i in j..n {
100 dot_products[i] =
101 &dot_products[i] + a[(i, j - 1)].abs2();
102 diagonals[i] = a[(i, i)].real() - &dot_products[i];
103 }
104 }
105 if j > 0 {
106 pvt = j;
107 ajj = zero::<T::Real>();
108 for i in j..n {
109 let aii = &diagonals[i];
110 if aii.is_nan() {
111 return Err(LltError::NonPositivePivot {
112 index: j,
113 });
114 }
115 if *aii > ajj {
116 pvt = i;
117 ajj = aii.copy();
118 }
119 }
120 if ajj < tol {
121 rank = j;
122 a.rb_mut().get_mut(.., j..).fill(zero());
126 break 'exit;
127 }
128 }
129 if pvt != j {
130 transposition_count += 1;
131 a[(pvt, pvt)] = a[(j, j)].copy();
132 crate::perm::swap_rows_idx(
133 a.rb_mut().get_mut(.., ..j),
134 j,
135 pvt,
136 );
137 crate::perm::swap_cols_idx(
138 a.rb_mut().get_mut(pvt + 1.., ..),
139 j,
140 pvt,
141 );
142 unsafe {
143 z!(
144 a.rb().get(j + 1..pvt, j).const_cast(),
145 a.rb()
146 .get(pvt, j + 1..pvt)
147 .const_cast()
148 .transpose_mut(),
149 )
150 }
151 .for_each(|uz!(a, b)| (*a, *b) = (b.conj(), a.conj()));
152 a[(pvt, j)] = a[(pvt, j)].conj();
153 let tmp = dot_products[j].copy();
154 dot_products[j] = dot_products[pvt].copy();
155 dot_products[pvt] = tmp;
156 perm.swap(j, pvt);
157 }
158 ajj = ajj.sqrt();
159 a[(j, j)] = ajj.to_cplx();
160 unsafe {
161 linalg::matmul::matmul(
162 a.rb().get(j + 1.., j).const_cast(),
163 Accum::Add,
164 a.rb().get(j + 1.., k..j),
165 a.rb().get(j, k..j).adjoint(),
166 -one::<T>(),
167 par,
168 );
169 }
170 let ajj = &ajj.recip();
171 z!(a.rb_mut().get_mut(j + 1.., j))
172 .for_each(|uz!(x)| *x = x.mul_real(ajj));
173 }
174 linalg::matmul::triangular::matmul(
175 unsafe { a.rb().get(k + bs.., k + bs..).const_cast() },
176 BlockStructure::TriangularLower,
177 Accum::Add,
178 a.rb().get(k + bs.., k..k + bs),
179 BlockStructure::Rectangular,
180 a.rb().get(k + bs.., k..k + bs).adjoint(),
181 BlockStructure::Rectangular,
182 -one::<T>(),
183 par,
184 );
185 k += bs;
186 }
187 rank = n;
188 }
189 }
190 for (i, p) in perm.iter().enumerate() {
191 perm_inv[p.zx()] = I::truncate(i);
192 }
193 unsafe {
194 Ok((
195 PivLltInfo {
196 rank,
197 transposition_count,
198 },
199 PermRef::new_unchecked(perm, perm_inv, n),
200 ))
201 }
202}