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[(j, j)] = ajj.to_cplx();
123 break 'exit;
124 }
125 }
126 if pvt != j {
127 transposition_count += 1;
128 a[(pvt, pvt)] = a[(j, j)].copy();
129 crate::perm::swap_rows_idx(
130 a.rb_mut().get_mut(.., ..j),
131 j,
132 pvt,
133 );
134 crate::perm::swap_cols_idx(
135 a.rb_mut().get_mut(pvt + 1.., ..),
136 j,
137 pvt,
138 );
139 unsafe {
140 z!(
141 a.rb().get(j + 1..pvt, j).const_cast(),
142 a.rb()
143 .get(pvt, j + 1..pvt)
144 .const_cast()
145 .transpose_mut(),
146 )
147 }
148 .for_each(|uz!(a, b)| (*a, *b) = (b.conj(), a.conj()));
149 a[(pvt, j)] = a[(pvt, j)].conj();
150 let tmp = dot_products[j].copy();
151 dot_products[j] = dot_products[pvt].copy();
152 dot_products[pvt] = tmp;
153 perm.swap(j, pvt);
154 }
155 ajj = ajj.sqrt();
156 a[(j, j)] = ajj.to_cplx();
157 unsafe {
158 linalg::matmul::matmul(
159 a.rb().get(j + 1.., j).const_cast(),
160 Accum::Add,
161 a.rb().get(j + 1.., k..j),
162 a.rb().get(j, k..j).adjoint(),
163 -one::<T>(),
164 par,
165 );
166 }
167 let ajj = &ajj.recip();
168 z!(a.rb_mut().get_mut(j + 1.., j))
169 .for_each(|uz!(x)| *x = x.mul_real(ajj));
170 }
171 linalg::matmul::triangular::matmul(
172 unsafe { a.rb().get(k + bs.., k + bs..).const_cast() },
173 BlockStructure::TriangularLower,
174 Accum::Add,
175 a.rb().get(k + bs.., k..k + bs),
176 BlockStructure::Rectangular,
177 a.rb().get(k + bs.., k..k + bs).adjoint(),
178 BlockStructure::Rectangular,
179 -one::<T>(),
180 par,
181 );
182 k += bs;
183 }
184 rank = n;
185 }
186 }
187 for (i, p) in perm.iter().enumerate() {
188 perm_inv[p.zx()] = I::truncate(i);
189 }
190 unsafe {
191 Ok((
192 PivLltInfo {
193 rank,
194 transposition_count,
195 },
196 PermRef::new_unchecked(perm, perm_inv, n),
197 ))
198 }
199}