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: ParShape,
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 mut 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 if ajj == zero::<T::Real>() {
90 rank = 0;
91 a.fill(zero());
92 break 'exit;
93 }
94 let tol = eps::<T::Real>() * from_f64::<T::Real>(n as f64) * &ajj;
95 let mut k = 0usize;
96 while k < n {
97 let bs = Ord::min(n - k, params.block_size);
98 for i in k..n {
99 dot_products[i] = zero::<T::Real>();
100 }
101 for j in k..k + bs {
102 if j == k {
103 for i in j..n {
104 diagonals[i] = a[(i, i)].real();
105 }
106 } else {
107 for i in j..n {
108 dot_products[i] =
109 &dot_products[i] + a[(i, j - 1)].abs2();
110 diagonals[i] = a[(i, i)].real() - &dot_products[i];
111 }
112 }
113 if j > 0 {
114 pvt = j;
115 ajj = zero::<T::Real>();
116 for i in j..n {
117 let aii = &diagonals[i];
118 if aii.is_nan() {
119 return Err(LltError::NonPositivePivot {
120 index: j,
121 });
122 }
123 if *aii > ajj {
124 pvt = i;
125 ajj = aii.copy();
126 }
127 }
128 if ajj < tol {
129 rank = j;
130 a.rb_mut().get_mut(.., j..).fill(zero());
134 break 'exit;
135 }
136 }
137 if pvt != j {
138 transposition_count += 1;
139 a[(pvt, pvt)] = a[(j, j)].copy();
140 crate::perm::swap_rows_idx(
141 a.rb_mut().get_mut(.., ..j),
142 j,
143 pvt,
144 );
145 crate::perm::swap_cols_idx(
146 a.rb_mut().get_mut(pvt + 1.., ..),
147 j,
148 pvt,
149 );
150 unsafe {
151 z!(
152 a.rb().get(j + 1..pvt, j).const_cast(),
153 a.rb()
154 .get(pvt, j + 1..pvt)
155 .const_cast()
156 .transpose_mut(),
157 )
158 }
159 .for_each(|uz!(a, b)| (*a, *b) = (b.conj(), a.conj()));
160 a[(pvt, j)] = a[(pvt, j)].conj();
161 let tmp = dot_products[j].copy();
162 dot_products[j] = dot_products[pvt].copy();
163 dot_products[pvt] = tmp;
164 perm.swap(j, pvt);
165 }
166 ajj = ajj.sqrt();
167 a[(j, j)] = ajj.to_cplx();
168 unsafe {
169 linalg::matmul::matmul(
170 a.rb().get(j + 1.., j).const_cast(),
171 Accum::Add,
172 a.rb().get(j + 1.., k..j),
173 a.rb().get(j, k..j).adjoint(),
174 -one::<T>(),
175 par.reborrow(),
176 );
177 }
178 let ajj = &ajj.recip();
179 z!(a.rb_mut().get_mut(j + 1.., j))
180 .for_each(|uz!(x)| *x = x.mul_real(ajj));
181 }
182 linalg::matmul::triangular::matmul(
183 unsafe { a.rb().get(k + bs.., k + bs..).const_cast() },
184 BlockStructure::TriangularLower,
185 Accum::Add,
186 a.rb().get(k + bs.., k..k + bs),
187 BlockStructure::Rectangular,
188 a.rb().get(k + bs.., k..k + bs).adjoint(),
189 BlockStructure::Rectangular,
190 -one::<T>(),
191 par.reborrow(),
192 );
193 k += bs;
194 }
195 rank = n;
196 }
197 }
198 for (i, p) in perm.iter().enumerate() {
199 perm_inv[p.zx()] = I::truncate(i);
200 }
201 unsafe {
202 Ok((
203 PivLltInfo {
204 rank,
205 transposition_count,
206 },
207 PermRef::new_unchecked(perm, perm_inv, n),
208 ))
209 }
210}