1pub mod compute;
6pub mod inverse;
7pub mod reconstruct;
8pub mod solve;
9pub mod update;
10
11#[derive(Debug, Clone, Copy)]
14pub struct CholeskyError {
15 pub non_positive_definite_minor: usize,
18}
19
20#[cfg(test)]
21mod tests {
22 use assert_approx_eq::assert_approx_eq;
23 use dyn_stack::{GlobalPodBuffer, PodStack};
24
25 use super::{compute::*, inverse::*, reconstruct::*, solve::*, update::*};
26 use faer_core::{c64, mul, ComplexField, Conj, Mat, MatRef, Parallelism};
27
28 type E = c64;
29
30 fn reconstruct_matrix(cholesky_factor: MatRef<'_, E>) -> Mat<E> {
31 let n = cholesky_factor.nrows();
32
33 let mut a_reconstructed = Mat::zeros(n, n);
34 reconstruct_lower(
35 a_reconstructed.as_mut(),
36 cholesky_factor,
37 Parallelism::Rayon(0),
38 PodStack::new(&mut GlobalPodBuffer::new(
39 reconstruct_lower_req::<E>(n).unwrap(),
40 )),
41 );
42
43 a_reconstructed
44 }
45
46 fn random() -> E {
47 E {
48 re: rand::random(),
49 im: rand::random(),
50 }
51 }
52
53 fn random_positive_definite(n: usize) -> Mat<E> {
54 let a = Mat::from_fn(n, n, |_, _| random());
55 let mut ata = Mat::zeros(n, n);
56
57 mul::matmul(
58 ata.as_mut(),
59 a.as_ref().adjoint(),
60 a.as_ref(),
61 None,
62 E::faer_one(),
63 Parallelism::Rayon(8),
64 );
65
66 ata
67 }
68
69 #[test]
70 fn test_roundtrip() {
71 for n in (0..32).chain((2..32).map(|i| i * 16)) {
72 let mut a = random_positive_definite(n);
73 let a_orig = a.clone();
74 cholesky_in_place(
75 a.as_mut(),
76 Default::default(),
77 Parallelism::Rayon(8),
78 PodStack::new(&mut []),
79 Default::default(),
80 )
81 .unwrap();
82 let mut a_reconstructed = reconstruct_matrix(a.as_ref());
83 let mut inv = Mat::zeros(n, n);
84 invert_lower(
85 inv.as_mut(),
86 a.as_ref(),
87 Parallelism::Rayon(0),
88 PodStack::new(&mut GlobalPodBuffer::new(
89 invert_lower_req::<E>(n, Parallelism::Rayon(0)).unwrap(),
90 )),
91 );
92
93 for j in 0..n {
94 for i in 0..j {
95 a_reconstructed.write(i, j, a_reconstructed.read(j, i).faer_conj());
96 inv.write(i, j, inv.read(j, i).faer_conj());
97 }
98 }
99
100 let mut prod = Mat::zeros(n, n);
101 mul::matmul(
102 prod.as_mut(),
103 a_reconstructed.as_ref(),
104 inv.as_ref(),
105 None,
106 E::faer_one(),
107 Parallelism::Rayon(0),
108 );
109
110 for j in 0..n {
111 for i in j..n {
112 assert_approx_eq!(a_reconstructed.read(i, j), a_orig.read(i, j));
113 }
114 }
115
116 for j in 0..n {
117 for i in 0..n {
118 let target = if i == j {
119 E::faer_one()
120 } else {
121 E::faer_zero()
122 };
123 assert_approx_eq!(prod.read(i, j), target);
124 }
125 }
126 }
127 }
128
129 #[test]
130 fn test_solve() {
131 for n in 0..20 {
132 let k = 5;
133 let mut a = random_positive_definite(n);
134 let mut rhs = Mat::from_fn(n, k, |_, _| random());
135 let a_orig = a.clone();
136 let rhs_orig = rhs.clone();
137
138 cholesky_in_place(
139 a.as_mut(),
140 Default::default(),
141 Parallelism::Rayon(8),
142 PodStack::new(&mut []),
143 Default::default(),
144 )
145 .unwrap();
146 solve_in_place_with_conj(
147 a.as_ref(),
148 Conj::No,
149 rhs.as_mut(),
150 Parallelism::Rayon(8),
151 PodStack::new(&mut []),
152 );
153
154 let mut result = Mat::zeros(n, k);
155 use mul::triangular::BlockStructure::*;
156 mul::triangular::matmul(
157 result.as_mut(),
158 Rectangular,
159 a_orig.as_ref(),
160 TriangularLower,
161 rhs.as_ref(),
162 Rectangular,
163 None,
164 E::faer_one(),
165 Parallelism::Rayon(8),
166 );
167
168 mul::triangular::matmul(
169 result.as_mut(),
170 Rectangular,
171 a_orig.as_ref().adjoint(),
172 StrictTriangularUpper,
173 rhs.as_ref(),
174 Rectangular,
175 Some(E::faer_one()),
176 E::faer_one(),
177 Parallelism::Rayon(8),
178 );
179
180 for j in 0..k {
181 for i in 0..n {
182 assert_approx_eq!(result.read(i, j), rhs_orig.read(i, j), 1e-3);
183 }
184 }
185 }
186 }
187
188 #[test]
189 fn test_update() {
190 use mul::triangular::BlockStructure::*;
191 for k in [0, 1, 2, 3, 4, 5] {
192 let n = 4;
193 let mut a = random_positive_definite(n);
194 let mut a_updated = a.clone();
195 let mut w = Mat::from_fn(n, k, |_, _| random());
196 let mut alpha = Mat::from_fn(k, 1, |_, _| E::faer_from_real(rand::random()));
197 let alpha = alpha.as_mut().col_mut(0).as_2d_mut();
198
199 let mut w_alpha = Mat::zeros(n, k);
200 for j in 0..k {
201 for i in 0..n {
202 w_alpha.write(i, j, alpha.read(j, 0).faer_mul(w.read(i, j)));
203 }
204 }
205
206 mul::triangular::matmul(
207 a_updated.as_mut(),
208 TriangularLower,
209 w_alpha.as_ref(),
210 Rectangular,
211 w.as_ref().adjoint(),
212 Rectangular,
213 Some(E::faer_one()),
214 E::faer_one(),
215 Parallelism::Rayon(8),
216 );
217
218 cholesky_in_place(
219 a.as_mut(),
220 Default::default(),
221 Parallelism::Rayon(8),
222 PodStack::new(&mut []),
223 Default::default(),
224 )
225 .unwrap();
226
227 rank_r_update_clobber(a.as_mut(), w.as_mut(), alpha).unwrap();
228
229 let a_reconstructed = reconstruct_matrix(a.as_ref());
230
231 for j in 0..n {
232 for i in j..n {
233 assert_approx_eq!(a_reconstructed.read(i, j), a_updated.read(i, j), 1e-4);
234 }
235 }
236 }
237 }
238
239 #[test]
240 fn test_delete() {
241 let a_orig = random_positive_definite(4);
242
243 {
244 let mut a = a_orig.clone();
245 let n = a.nrows();
246 let r = 2;
247
248 cholesky_in_place(
249 a.as_mut(),
250 Default::default(),
251 Parallelism::Rayon(8),
252 PodStack::new(&mut []),
253 Default::default(),
254 )
255 .unwrap();
256
257 delete_rows_and_cols_clobber(
258 a.as_mut(),
259 &mut [1, 3],
260 Parallelism::Rayon(8),
261 PodStack::new(&mut GlobalPodBuffer::new(
262 delete_rows_and_cols_clobber_req::<E>(n, r, Parallelism::Rayon(8)).unwrap(),
263 )),
264 );
265
266 let a_reconstructed = reconstruct_matrix(a.as_ref().submatrix(0, 0, n - r, n - r));
267 assert_approx_eq!(a_reconstructed.read(0, 0), a_orig.read(0, 0));
268 assert_approx_eq!(a_reconstructed.read(1, 0), a_orig.read(2, 0));
269 assert_approx_eq!(a_reconstructed.read(1, 1), a_orig.read(2, 2));
270 }
271
272 {
273 let mut a = a_orig.clone();
274 let n = a.nrows();
275 let r = 2;
276
277 cholesky_in_place(
278 a.as_mut(),
279 Default::default(),
280 Parallelism::Rayon(8),
281 PodStack::new(&mut []),
282 Default::default(),
283 )
284 .unwrap();
285
286 delete_rows_and_cols_clobber(
287 a.as_mut(),
288 &mut [0, 2],
289 Parallelism::Rayon(8),
290 PodStack::new(&mut GlobalPodBuffer::new(
291 delete_rows_and_cols_clobber_req::<E>(n, r, Parallelism::Rayon(8)).unwrap(),
292 )),
293 );
294
295 let a_reconstructed = reconstruct_matrix(a.as_ref().submatrix(0, 0, n - r, n - r));
296 assert_approx_eq!(a_reconstructed.read(0, 0), a_orig.read(1, 1));
297 assert_approx_eq!(a_reconstructed.read(1, 0), a_orig.read(3, 1));
298 assert_approx_eq!(a_reconstructed.read(1, 1), a_orig.read(3, 3));
299 }
300
301 {
302 let mut a = a_orig.clone();
303 let n = a.nrows();
304 let r = 3;
305
306 cholesky_in_place(
307 a.as_mut(),
308 Default::default(),
309 Parallelism::Rayon(8),
310 PodStack::new(&mut []),
311 Default::default(),
312 )
313 .unwrap();
314
315 delete_rows_and_cols_clobber(
316 a.as_mut(),
317 &mut [0, 2, 3],
318 Parallelism::Rayon(8),
319 PodStack::new(&mut GlobalPodBuffer::new(
320 delete_rows_and_cols_clobber_req::<E>(n, r, Parallelism::Rayon(8)).unwrap(),
321 )),
322 );
323
324 let a_reconstructed = reconstruct_matrix(a.as_ref().submatrix(0, 0, n - r, n - r));
325 assert_approx_eq!(a_reconstructed.read(0, 0), a_orig.read(1, 1));
326 }
327 }
328
329 #[test]
330 fn test_insert() {
331 let n = 4;
332 let r = 2;
333
334 let a_orig = random_positive_definite(n + r);
335
336 for position in [0, 2, 4] {
337 let mut a = a_orig.clone();
338 let mut w = Mat::zeros(n + r, r);
339
340 for j in 0..r {
341 for i in 0..n + r {
342 w.write(i, j, a.read(i, j + position));
343 }
344 }
345
346 cholesky_in_place(
347 a.as_mut(),
348 Default::default(),
349 Parallelism::Rayon(8),
350 PodStack::new(&mut []),
351 Default::default(),
352 )
353 .unwrap();
354
355 delete_rows_and_cols_clobber(
356 a.as_mut(),
357 &mut [position, position + 1],
358 Parallelism::Rayon(8),
359 PodStack::new(&mut GlobalPodBuffer::new(
360 delete_rows_and_cols_clobber_req::<f64>(n + r, r, Parallelism::Rayon(8))
361 .unwrap(),
362 )),
363 );
364
365 insert_rows_and_cols_clobber(
366 a.as_mut(),
367 position,
368 w.as_mut(),
369 Parallelism::Rayon(8),
370 PodStack::new(&mut GlobalPodBuffer::new(
371 insert_rows_and_cols_clobber_req::<E>(r, Parallelism::Rayon(8)).unwrap(),
372 )),
373 )
374 .unwrap();
375
376 let a_reconstructed = reconstruct_matrix(a.as_ref());
377 for j in 0..n + r {
378 for i in j..n + r {
379 assert_approx_eq!(a_reconstructed.read(i, j), a_orig.read(i, j), 1e-4);
380 }
381 }
382 }
383 }
384}