1use crate::assert;
11use crate::internal_prelude_sp::*;
12use crate::sparse::utils;
13use linalg::lu::partial_pivoting::factor::PartialPivLuParams;
14use linalg_sp::cholesky::simplicial::EliminationTreeRef;
15use linalg_sp::{
16 LuError, SupernodalThreshold, SymbolicSupernodalParams, colamd,
17};
18#[inline(never)]
19fn resize_vec<T: Clone>(
20 v: &mut alloc::vec::Vec<T>,
21 n: usize,
22 exact: bool,
23 reserve_only: bool,
24 value: T,
25) -> Result<(), FaerError> {
26 let reserve = if exact {
27 alloc::vec::Vec::try_reserve_exact
28 } else {
29 alloc::vec::Vec::try_reserve
30 };
31 reserve(v, n.saturating_sub(v.len()))
32 .map_err(|_| FaerError::OutOfMemory)?;
33 if !reserve_only {
34 v.resize(Ord::max(n, v.len()), value);
35 }
36 Ok(())
37}
38pub mod supernodal {
44 use super::*;
45 use crate::assert;
46
47 pub fn solve_in_place_scratch<I: Index, T: ComplexField>(
48 dim: usize,
49 rhs_ncols: usize,
50 par: ParShape,
51 ) -> StackReq {
52 let _ = par;
53 crate::perm::permute_rows_in_place_scratch::<I, T>(dim, rhs_ncols)
54 .or(temp_mat_scratch::<T>(dim, rhs_ncols))
55 }
56
57 pub fn solve_transpose_in_place_scratch<I: Index, T: ComplexField>(
58 dim: usize,
59 rhs_ncols: usize,
60 par: ParShape,
61 ) -> StackReq {
62 solve_in_place_scratch::<I, T>(dim, rhs_ncols, par)
63 }
64
65 #[derive(Debug, Clone)]
67 pub struct SymbolicSupernodalLu<I: Index> {
68 pub(super) supernode_ptr: alloc::vec::Vec<I>,
69 pub(super) super_etree: alloc::vec::Vec<I>,
70 pub(super) nrows: usize,
71 pub(super) ncols: usize,
72 }
73 #[derive(Debug, Clone)]
76 pub struct SupernodalLu<I: Index, T> {
77 nrows: usize,
78 ncols: usize,
79 nsupernodes: usize,
80 supernode_ptr: alloc::vec::Vec<I>,
81 l_col_ptr_for_row_idx: alloc::vec::Vec<I>,
82 l_col_ptr_for_val: alloc::vec::Vec<I>,
83 l_row_idx: alloc::vec::Vec<I>,
84 l_val: alloc::vec::Vec<T>,
85 ut_col_ptr_for_row_idx: alloc::vec::Vec<I>,
86 ut_col_ptr_for_val: alloc::vec::Vec<I>,
87 ut_row_idx: alloc::vec::Vec<I>,
88 ut_val: alloc::vec::Vec<T>,
89
90 u_compress_cols: alloc::vec::Vec<alloc::vec::Vec<I>>,
91 l_compress_rows: alloc::vec::Vec<alloc::vec::Vec<I>>,
92 }
93 impl<I: Index, T> Default for SupernodalLu<I, T> {
94 fn default() -> Self {
95 Self::new()
96 }
97 }
98 impl<I: Index, T> SupernodalLu<I, T> {
99 #[inline]
101 pub fn new() -> Self {
102 Self {
103 nrows: 0,
104 ncols: 0,
105 nsupernodes: 0,
106 supernode_ptr: alloc::vec::Vec::new(),
107 l_col_ptr_for_row_idx: alloc::vec::Vec::new(),
108 ut_col_ptr_for_row_idx: alloc::vec::Vec::new(),
109 l_col_ptr_for_val: alloc::vec::Vec::new(),
110 ut_col_ptr_for_val: alloc::vec::Vec::new(),
111 l_row_idx: alloc::vec::Vec::new(),
112 ut_row_idx: alloc::vec::Vec::new(),
113 l_val: alloc::vec::Vec::new(),
114 ut_val: alloc::vec::Vec::new(),
115 u_compress_cols: alloc::vec::Vec::new(),
116 l_compress_rows: alloc::vec::Vec::new(),
117 }
118 }
119
120 #[inline]
122 pub fn nrows(&self) -> usize {
123 self.nrows
124 }
125
126 #[inline]
128 pub fn ncols(&self) -> usize {
129 self.ncols
130 }
131
132 #[inline]
134 pub fn n_supernodes(&self) -> usize {
135 self.nsupernodes
136 }
137
138 #[track_caller]
145 pub fn solve_in_place_with_conj(
146 &self,
147 row_perm: PermRef<'_, I>,
148 col_perm: PermRef<'_, I>,
149 conj_lhs: Conj,
150 rhs: MatMut<'_, T>,
151 mut par: Par,
152 stack: &mut MemStack,
153 ) where
154 T: ComplexField,
155 {
156 assert!(all(
157 self.nrows() == self.ncols(),
158 self.nrows() == rhs.nrows()
159 ));
160 let mut X = rhs;
161 crate::perm::permute_rows_in_place(X.rb_mut(), row_perm, stack);
162 self.l_solve_in_place_with_conj(conj_lhs, X.rb_mut(), par.reborrow(), stack);
163 self.u_solve_in_place_with_conj(conj_lhs, X.rb_mut(), par, stack);
164 crate::perm::permute_rows_in_place(
165 X.rb_mut(),
166 col_perm.inverse(),
167 stack,
168 );
169 }
170
171 #[track_caller]
178 pub fn solve_transpose_in_place_with_conj(
179 &self,
180 row_perm: PermRef<'_, I>,
181 col_perm: PermRef<'_, I>,
182 conj_lhs: Conj,
183 rhs: MatMut<'_, T>,
184 mut par: Par,
185 stack: &mut MemStack,
186 ) where
187 T: ComplexField,
188 {
189 assert!(all(
190 self.nrows() == self.ncols(),
191 self.nrows() == rhs.nrows()
192 ));
193 let mut X = rhs;
194 crate::perm::permute_rows_in_place(X.rb_mut(), col_perm, stack);
195 self.u_solve_transpose_in_place_with_conj(
196 conj_lhs,
197 X.rb_mut(),
198 par.reborrow(),
199 stack,
200 );
201 self.l_solve_transpose_in_place_with_conj(
202 conj_lhs,
203 X.rb_mut(),
204 par,
205 stack,
206 );
207 crate::perm::permute_rows_in_place(
208 X.rb_mut(),
209 row_perm.inverse(),
210 stack,
211 );
212 }
213
214 #[track_caller]
215 pub(crate) fn l_solve_in_place_with_conj(
216 &self,
217 conj_lhs: Conj,
218 rhs: MatMut<'_, T>,
219 mut par: Par,
220 stack: &mut MemStack,
221 ) where
222 T: ComplexField,
223 {
224 let lu = self;
225 assert!(lu.nrows() == lu.ncols());
226 assert!(lu.nrows() == rhs.nrows());
227 let mut X = rhs;
228 let nrhs = X.ncols();
229 let supernode_ptr = &*lu.supernode_ptr;
230 alloca!('stack: {
231 let mut work =
232 unsafe { mat![uninit::<T>, X.nrows(), X.ncols()] };
233 });
234 for s in 0..lu.nsupernodes {
235 let s_begin = supernode_ptr[s].zx();
236 let s_end = supernode_ptr[s + 1].zx();
237 let s_size = s_end - s_begin;
238 let s_row_idx_count = lu.l_col_ptr_for_row_idx[s + 1].zx()
239 - lu.l_col_ptr_for_row_idx[s].zx();
240 let L = &lu.l_val[lu.l_col_ptr_for_val[s].zx()
241 ..lu.l_col_ptr_for_val[s + 1].zx()];
242 let L =
243 MatRef::from_column_major_slice(L, s_row_idx_count, s_size);
244 let (L_top, L_bot) = L.split_at_row(s_size);
245 linalg::triangular_solve::solve_unit_lower_triangular_in_place_with_conj(
246 L_top,
247 conj_lhs,
248 X.rb_mut().subrows_mut(s_begin, s_size),
249 par.reborrow(),
250 );
251 linalg::matmul::matmul_with_conj(
252 work.rb_mut().subrows_mut(0, s_row_idx_count - s_size),
253 Accum::Replace,
254 L_bot,
255 conj_lhs,
256 X.rb().subrows(s_begin, s_size),
257 Conj::No,
258 one::<T>(),
259 par.reborrow(),
260 );
261 for j in 0..nrhs {
262 for (idx, &i) in lu.l_row_idx[lu.l_col_ptr_for_row_idx[s]
263 .zx()
264 ..lu.l_col_ptr_for_row_idx[s + 1].zx()][s_size..]
265 .iter()
266 .enumerate()
267 {
268 let i = i.zx();
269 X[(i, j)] -= &work[(idx, j)];
270 }
271 }
272 }
273 }
274
275 #[track_caller]
276 pub(crate) fn l_solve_transpose_in_place_with_conj(
277 &self,
278 conj_lhs: Conj,
279 rhs: MatMut<'_, T>,
280 mut par: Par,
281 stack: &mut MemStack,
282 ) where
283 T: ComplexField,
284 {
285 let lu = self;
286 assert!(lu.nrows() == lu.ncols());
287 assert!(lu.nrows() == rhs.nrows());
288 let mut X = rhs;
289 let nrhs = X.ncols();
290 let supernode_ptr = &*lu.supernode_ptr;
291 alloca!('stack: {
292 let mut work =
293 unsafe { mat![uninit::<T>, X.nrows(), X.ncols()] };
294 });
295 for s in (0..lu.nsupernodes).rev() {
296 let s_begin = supernode_ptr[s].zx();
297 let s_end = supernode_ptr[s + 1].zx();
298 let s_size = s_end - s_begin;
299 let s_row_idx_count = lu.l_col_ptr_for_row_idx[s + 1].zx()
300 - lu.l_col_ptr_for_row_idx[s].zx();
301 let L = &lu.l_val[lu.l_col_ptr_for_val[s].zx()
302 ..lu.l_col_ptr_for_val[s + 1].zx()];
303 let L =
304 MatRef::from_column_major_slice(L, s_row_idx_count, s_size);
305 let (L_top, L_bot) = L.split_at_row(s_size);
306 for j in 0..nrhs {
307 for (idx, &i) in lu.l_row_idx[lu.l_col_ptr_for_row_idx[s]
308 .zx()
309 ..lu.l_col_ptr_for_row_idx[s + 1].zx()][s_size..]
310 .iter()
311 .enumerate()
312 {
313 let i = i.zx();
314 work[(idx, j)] = X[(i, j)].copy();
315 }
316 }
317 linalg::matmul::matmul_with_conj(
318 X.rb_mut().subrows_mut(s_begin, s_size),
319 Accum::Add,
320 L_bot.transpose(),
321 conj_lhs,
322 work.rb().subrows(0, s_row_idx_count - s_size),
323 Conj::No,
324 -one::<T>(),
325 par.reborrow(),
326 );
327 linalg::triangular_solve::solve_unit_upper_triangular_in_place_with_conj(
328 L_top.transpose(),
329 conj_lhs,
330 X.rb_mut().subrows_mut(s_begin, s_size),
331 par.reborrow(),
332 );
333 }
334 }
335
336 #[track_caller]
337 pub(crate) fn u_solve_in_place_with_conj(
338 &self,
339 conj_lhs: Conj,
340 rhs: MatMut<'_, T>,
341 mut par: Par,
342 stack: &mut MemStack,
343 ) where
344 T: ComplexField,
345 {
346 let lu = self;
347 assert!(lu.nrows() == lu.ncols());
348 assert!(lu.nrows() == rhs.nrows());
349 let mut X = rhs;
350 let nrhs = X.ncols();
351 let supernode_ptr = &*lu.supernode_ptr;
352 alloca!('stack: {
353 let mut work =
354 unsafe { mat![uninit::<T>, X.nrows(), X.ncols()] };
355 });
356 for s in (0..lu.nsupernodes).rev() {
357 let s_begin = supernode_ptr[s].zx();
358 let s_end = supernode_ptr[s + 1].zx();
359 let s_size = s_end - s_begin;
360 let s_row_idx_count = lu.l_col_ptr_for_row_idx[s + 1].zx()
361 - lu.l_col_ptr_for_row_idx[s].zx();
362 let s_col_index_count = lu.ut_col_ptr_for_row_idx[s + 1].zx()
363 - lu.ut_col_ptr_for_row_idx[s].zx();
364 let L = &lu.l_val[lu.l_col_ptr_for_val[s].zx()
365 ..lu.l_col_ptr_for_val[s + 1].zx()];
366 let L =
367 MatRef::from_column_major_slice(L, s_row_idx_count, s_size);
368 let U = &lu.ut_val[lu.ut_col_ptr_for_val[s].zx()
369 ..lu.ut_col_ptr_for_val[s + 1].zx()];
370 let U_right = MatRef::from_column_major_slice(
371 U,
372 s_col_index_count,
373 s_size,
374 )
375 .transpose();
376 for j in 0..nrhs {
377 for (idx, &i) in lu.ut_row_idx[lu.ut_col_ptr_for_row_idx[s]
378 .zx()
379 ..lu.ut_col_ptr_for_row_idx[s + 1].zx()]
380 .iter()
381 .enumerate()
382 {
383 let i = i.zx();
384 work[(idx, j)] = X[(i, j)].copy();
385 }
386 }
387 let (U_left, _) = L.split_at_row(s_size);
388 linalg::matmul::matmul_with_conj(
389 X.rb_mut().subrows_mut(s_begin, s_size),
390 Accum::Add,
391 U_right,
392 conj_lhs,
393 work.rb().subrows(0, s_col_index_count),
394 Conj::No,
395 -one::<T>(),
396 par.reborrow(),
397 );
398 linalg::triangular_solve::solve_upper_triangular_in_place_with_conj(U_left, conj_lhs, X.rb_mut().subrows_mut(s_begin, s_size), par.reborrow());
399 }
400 }
401
402 #[track_caller]
403 pub(crate) fn u_solve_transpose_in_place_with_conj(
404 &self,
405 conj_lhs: Conj,
406 rhs: MatMut<'_, T>,
407 mut par: Par,
408 stack: &mut MemStack,
409 ) where
410 T: ComplexField,
411 {
412 let lu = self;
413 assert!(lu.nrows() == lu.ncols());
414 assert!(lu.nrows() == rhs.nrows());
415 let mut X = rhs;
416 let nrhs = X.ncols();
417 let supernode_ptr = &*lu.supernode_ptr;
418 alloca!('stack: {
419 let mut work =
420 unsafe { mat![uninit::<T>, X.nrows(), X.ncols()] };
421 });
422 for s in 0..lu.nsupernodes {
423 let s_begin = supernode_ptr[s].zx();
424 let s_end = supernode_ptr[s + 1].zx();
425 let s_size = s_end - s_begin;
426 let s_row_idx_count = lu.l_col_ptr_for_row_idx[s + 1].zx()
427 - lu.l_col_ptr_for_row_idx[s].zx();
428 let s_col_index_count = lu.ut_col_ptr_for_row_idx[s + 1].zx()
429 - lu.ut_col_ptr_for_row_idx[s].zx();
430 let L = &lu.l_val[lu.l_col_ptr_for_val[s].zx()
431 ..lu.l_col_ptr_for_val[s + 1].zx()];
432 let L =
433 MatRef::from_column_major_slice(L, s_row_idx_count, s_size);
434 let U = &lu.ut_val[lu.ut_col_ptr_for_val[s].zx()
435 ..lu.ut_col_ptr_for_val[s + 1].zx()];
436 let U_right = MatRef::from_column_major_slice(
437 U,
438 s_col_index_count,
439 s_size,
440 )
441 .transpose();
442 let (U_left, _) = L.split_at_row(s_size);
443 linalg::triangular_solve::solve_lower_triangular_in_place_with_conj(
444 U_left.transpose(),
445 conj_lhs,
446 X.rb_mut().subrows_mut(s_begin, s_size),
447 par.reborrow(),
448 );
449 linalg::matmul::matmul_with_conj(
450 work.rb_mut().subrows_mut(0, s_col_index_count),
451 Accum::Replace,
452 U_right.transpose(),
453 conj_lhs,
454 X.rb().subrows(s_begin, s_size),
455 Conj::No,
456 one::<T>(),
457 par.reborrow(),
458 );
459 for j in 0..nrhs {
460 for (idx, &i) in lu.ut_row_idx[lu.ut_col_ptr_for_row_idx[s]
461 .zx()
462 ..lu.ut_col_ptr_for_row_idx[s + 1].zx()]
463 .iter()
464 .enumerate()
465 {
466 let i = i.zx();
467 X[(i, j)] -= &work[(idx, j)];
468 }
469 }
470 }
471 }
472 }
473 pub fn factorize_supernodal_symbolic_lu_scratch<I: Index>(
476 nrows: usize,
477 ncols: usize,
478 ) -> StackReq {
479 let _ = nrows;
480 linalg_sp::cholesky::supernodal::factorize_supernodal_symbolic_cholesky_scratch::<I>(ncols)
481 }
482 #[track_caller]
484 pub fn factorize_supernodal_symbolic_lu<I: Index>(
485 A: SymbolicSparseColMatRef<'_, I>,
486 col_perm: Option<PermRef<'_, I>>,
487 min_col: &[I],
488 etree: EliminationTreeRef<'_, I>,
489 col_counts: &[I],
490 stack: &mut MemStack,
491 params: SymbolicSupernodalParams<'_>,
492 ) -> Result<SymbolicSupernodalLu<I>, FaerError> {
493 let m = A.nrows();
494 let n = A.ncols();
495 with_dim!(M, m);
496 with_dim!(N, n);
497 let I = I::truncate;
498 let A = A.as_shape(M, N);
499 let min_col = Array::from_ref(
500 MaybeIdx::from_slice_ref_checked(bytemuck::cast_slice(min_col), N),
501 M,
502 );
503 let etree = etree.as_bound(N);
504 let L = linalg_sp::cholesky::supernodal::ghost_factorize_supernodal_symbolic(
505 A,
506 col_perm.map(|perm| perm.as_shape(N)),
507 Some(min_col),
508 linalg_sp::cholesky::supernodal::CholeskyInput::ATA,
509 etree,
510 Array::from_ref(col_counts, N),
511 stack,
512 params,
513 )?;
514 let n_supernodes = L.n_supernodes();
515 let mut super_etree = try_zeroed::<I>(n_supernodes)?;
516 let (index_to_super, _) = unsafe { stack.make_raw::<I>(*N) };
517 for s in 0..n_supernodes {
518 index_to_super
519 [L.supernode_begin[s].zx()..L.supernode_begin[s + 1].zx()]
520 .fill(I(s));
521 }
522 for s in 0..n_supernodes {
523 let last = L.supernode_begin[s + 1].zx() - 1;
524 if let Some(parent) = etree[N.check(last)].idx() {
525 super_etree[s] = index_to_super[*parent.zx()];
526 } else {
527 super_etree[s] = I(NONE);
528 }
529 }
530 Ok(SymbolicSupernodalLu {
531 supernode_ptr: L.supernode_begin,
532 super_etree,
533 nrows: *A.nrows(),
534 ncols: *A.ncols(),
535 })
540 }
541 struct MatU8 {
542 data: alloc::vec::Vec<u8>,
543 nrows: usize,
544 }
545 impl MatU8 {
546 fn new() -> Self {
547 Self {
548 data: alloc::vec::Vec::new(),
549 nrows: 0,
550 }
551 }
552
553 fn with_dims(nrows: usize, ncols: usize) -> Result<Self, FaerError> {
554 Ok(Self {
555 data: try_collect((0..(nrows * ncols)).map(|_| 1u8))?,
556 nrows,
557 })
558 }
559 }
560 impl core::ops::Index<(usize, usize)> for MatU8 {
561 type Output = u8;
562
563 #[inline(always)]
564 fn index(&self, (row, col): (usize, usize)) -> &Self::Output {
565 &self.data[row + col * self.nrows]
566 }
567 }
568 impl core::ops::IndexMut<(usize, usize)> for MatU8 {
569 #[inline(always)]
570 fn index_mut(
571 &mut self,
572 (row, col): (usize, usize),
573 ) -> &mut Self::Output {
574 &mut self.data[row + col * self.nrows]
575 }
576 }
577 struct Front;
578 struct LPanel;
579 struct UPanel;
580 #[inline(never)]
581 fn noinline<T, R>(_: T, f: impl FnOnce() -> R) -> R {
582 f()
583 }
584 pub fn factorize_supernodal_numeric_lu_scratch<
587 I: Index,
588 T: ComplexField,
589 >(
590 symbolic: &SymbolicSupernodalLu<I>,
591 params: Spec<PartialPivLuParams, T>,
592 ) -> StackReq {
593 let m = StackReq::new::<I>(symbolic.nrows);
594 let n = StackReq::new::<I>(symbolic.ncols);
595 _ = params;
596 StackReq::all_of(&[
597 n.array(2),
598 m.array(5),
599 StackReq::new::<I>(symbolic.supernode_ptr.len()),
600 ])
601 }
602 pub fn factorize_supernodal_numeric_lu<I: Index, T: ComplexField>(
606 row_perm: &mut [I],
607 row_perm_inv: &mut [I],
608 lu: &mut SupernodalLu<I, T>,
609 A: SparseColMatRef<'_, I, T>,
610 AT: SparseColMatRef<'_, I, T>,
611 col_perm: PermRef<'_, I>,
612 symbolic: &SymbolicSupernodalLu<I>,
613 mut par: Par,
614 stack: &mut MemStack,
615 params: Spec<PartialPivLuParams, T>,
616 ) -> Result<(), LuError> {
617 use linalg_sp::cholesky::supernodal::partition_fn;
618 let SymbolicSupernodalLu {
619 supernode_ptr,
620 super_etree,
621 nrows: _,
622 ncols: _,
623 ..
624 } = symbolic;
625
626 let I = I::truncate;
627 let I_checked = |x: usize| -> Result<I, FaerError> {
628 if x > I::Signed::MAX.zx() {
629 Err(FaerError::IndexOverflow)
630 } else {
631 Ok(I(x))
632 }
633 };
634 let to_wide = |x: I| -> u128 { x.zx() as _ };
635 let from_wide_checked = |x: u128| -> Result<I, FaerError> {
636 if x > I::Signed::MAX.zx() as u128 {
637 Err(FaerError::IndexOverflow)
638 } else {
639 Ok(I(x as _))
640 }
641 };
642 let m = A.nrows();
643 let n = A.ncols();
644 assert!(m >= n);
645 assert!(all(AT.nrows() == n, AT.ncols() == m));
646 assert!(all(row_perm.len() == m, row_perm_inv.len() == m));
647 let n_supernodes = super_etree.len();
648 assert!(supernode_ptr.len() == n_supernodes + 1);
649 assert!(supernode_ptr[n_supernodes].zx() == n);
650 lu.nrows = 0;
651 lu.ncols = 0;
652 lu.nsupernodes = 0;
653 lu.supernode_ptr.clear();
654
655 let (index_to_super, stack) = unsafe { stack.make_raw::<I>(n) };
656 let (supernode_visited, stack) =
657 unsafe { stack.make_raw::<I>(n_supernodes) };
658 let u_compress_cols = &mut lu.u_compress_cols;
659 let l_compress_rows = &mut lu.l_compress_rows;
660
661 supernode_visited.fill(I(0));
662
663 u_compress_cols
664 .try_reserve_exact(n_supernodes)
665 .map_err(|_| FaerError::OutOfMemory)?;
666 l_compress_rows
667 .try_reserve_exact(m)
668 .map_err(|_| FaerError::OutOfMemory)?;
669
670 for v in u_compress_cols.iter_mut() {
671 v.clear();
672 }
673 for v in l_compress_rows.iter_mut() {
674 v.clear();
675 }
676
677 u_compress_cols.resize_with(n_supernodes, alloc::vec::Vec::new);
678 l_compress_rows.resize_with(m, alloc::vec::Vec::new);
679
680 for s in 0..n_supernodes {
681 index_to_super[supernode_ptr[s].zx()..supernode_ptr[s + 1].zx()]
682 .fill(I(s));
683 }
684
685 let (col_global_to_local, stack) = unsafe { stack.make_raw::<I>(n) };
686 let (row_global_to_local, stack) = unsafe { stack.make_raw::<I>(m) };
687 let (marked, stack) = unsafe { stack.make_raw::<I>(m) };
688 let (indices, stack) = unsafe { stack.make_raw::<I>(m) };
689 let (transpositions, stack) = unsafe { stack.make_raw::<I>(m) };
690 let (d_active_rows, _) = unsafe { stack.make_raw::<I>(m) };
691 col_global_to_local.fill(I(NONE));
692 row_global_to_local.fill(I(NONE));
693 marked.fill(I(0));
694 resize_vec(
695 &mut lu.l_col_ptr_for_row_idx,
696 n_supernodes + 1,
697 true,
698 false,
699 I(0),
700 )?;
701 resize_vec(
702 &mut lu.ut_col_ptr_for_row_idx,
703 n_supernodes + 1,
704 true,
705 false,
706 I(0),
707 )?;
708 resize_vec(
709 &mut lu.l_col_ptr_for_val,
710 n_supernodes + 1,
711 true,
712 false,
713 I(0),
714 )?;
715 resize_vec(
716 &mut lu.ut_col_ptr_for_val,
717 n_supernodes + 1,
718 true,
719 false,
720 I(0),
721 )?;
722 lu.l_col_ptr_for_row_idx[0] = I(0);
723 lu.ut_col_ptr_for_row_idx[0] = I(0);
724 lu.l_col_ptr_for_val[0] = I(0);
725 lu.ut_col_ptr_for_val[0] = I(0);
726 for i in 0..m {
727 row_perm[i] = I(i);
728 }
729 for i in 0..m {
730 row_perm_inv[i] = I(i);
731 }
732 let (col_perm, col_perm_inv) = col_perm.arrays();
733 let mut contrib_work = try_collect((0..n_supernodes).map(|_| {
734 (
735 alloc::vec::Vec::<T>::new(),
736 alloc::vec::Vec::<I>::new(),
737 0usize,
738 MatU8::new(),
739 )
740 }))?;
741 let work_to_mat_mut =
742 |v: &mut alloc::vec::Vec<T>, nrows: usize, ncols: usize| unsafe {
743 MatMut::from_raw_parts_mut(
744 v.as_mut_ptr(),
745 nrows,
746 ncols,
747 1,
748 nrows as isize,
749 )
750 };
751 let mut A_leftover = A.compute_nnz();
752 for s in 0..n_supernodes {
753 let s_begin = supernode_ptr[s].zx();
754 let s_end = supernode_ptr[s + 1].zx();
755 let s_size = s_end - s_begin;
756 let mut s_row_idx_count = 0usize;
757 let (left_contrib, right_contrib) = contrib_work.split_at_mut(s);
758 let s_row_idx = &mut *indices;
759 for j in s_begin..s_end {
760 let pj = col_perm[j].zx();
761 let row_idx = A.row_idx_of_col_raw(pj);
762 for i in row_idx {
763 let i = i.zx();
764 let pi = row_perm_inv[i].zx();
765 if pi < s_begin {
766 continue;
767 }
768 if marked[i] < I(2 * s + 1) {
769 s_row_idx[s_row_idx_count] = I(i);
770 s_row_idx_count += 1;
771 marked[i] = I(2 * s + 1);
772 }
773 }
774 }
775
776 for d in &u_compress_cols[s] {
777 let d = d.zx();
778 let d_begin = supernode_ptr[d].zx();
779 let d_end = supernode_ptr[d + 1].zx();
780 let d_size = d_end - d_begin;
781 let d_row_idx = &lu.l_row_idx[lu.l_col_ptr_for_row_idx[d].zx()
782 ..lu.l_col_ptr_for_row_idx[d + 1].zx()][d_size..];
783 let d_col_idx = &lu.ut_row_idx[lu.ut_col_ptr_for_row_idx[d].zx()
784 ..lu.ut_col_ptr_for_row_idx[d + 1].zx()];
785 let d_col_start =
786 d_col_idx.partition_point(partition_fn(s_begin));
787 if d_col_start < d_col_idx.len()
788 && d_col_idx[d_col_start].zx() < s_end
789 {
790 for i in d_row_idx.iter() {
791 let i = i.zx();
792 let pi = row_perm_inv[i].zx();
793 if pi < s_begin {
794 continue;
795 }
796 if marked[i] < I(2 * s + 1) {
797 s_row_idx[s_row_idx_count] = I(i);
798 s_row_idx_count += 1;
799 marked[i] = I(2 * s + 1);
800 }
801 }
802 }
803 }
804 lu.l_col_ptr_for_row_idx[s + 1] =
805 I_checked(lu.l_col_ptr_for_row_idx[s].zx() + s_row_idx_count)?;
806 lu.l_col_ptr_for_val[s + 1] = from_wide_checked(
807 to_wide(lu.l_col_ptr_for_val[s])
808 + (s_row_idx_count as u128 * s_size as u128),
809 )?;
810 resize_vec(
811 &mut lu.l_row_idx,
812 lu.l_col_ptr_for_row_idx[s + 1].zx(),
813 false,
814 false,
815 I(0),
816 )?;
817 resize_vec::<T>(
818 &mut lu.l_val,
819 lu.l_col_ptr_for_val[s + 1].zx(),
820 false,
821 false,
822 zero::<T>(),
823 )?;
824 lu.l_row_idx[lu.l_col_ptr_for_row_idx[s].zx()
825 ..lu.l_col_ptr_for_row_idx[s + 1].zx()]
826 .copy_from_slice(&s_row_idx[..s_row_idx_count]);
827 lu.l_row_idx[lu.l_col_ptr_for_row_idx[s].zx()
828 ..lu.l_col_ptr_for_row_idx[s + 1].zx()]
829 .sort_unstable();
830 let (left_row_idx, right_row_idx) =
831 lu.l_row_idx.split_at_mut(lu.l_col_ptr_for_row_idx[s].zx());
832 let s_row_idx =
833 &mut right_row_idx[0..lu.l_col_ptr_for_row_idx[s + 1].zx()
834 - lu.l_col_ptr_for_row_idx[s].zx()];
835 for (idx, i) in s_row_idx.iter().enumerate() {
836 let i = i.zx();
837 row_global_to_local[i] = I(idx);
838 }
839 let s_L = &mut lu.l_val[lu.l_col_ptr_for_val[s].zx()
840 ..lu.l_col_ptr_for_val[s + 1].zx()];
841 let mut s_L = MatMut::from_column_major_slice_mut(
842 s_L,
843 s_row_idx_count,
844 s_size,
845 );
846 s_L.fill(zero());
847 for j in s_begin..s_end {
848 let pj = col_perm[j].zx();
849 let row_idx = A.row_idx_of_col(pj);
850 let val = A.val_of_col(pj);
851 for (i, val) in iter::zip(row_idx, val) {
852 let pi = row_perm_inv[i].zx();
853 if pi < s_begin {
854 continue;
855 }
856 assert!(A_leftover > 0);
857 A_leftover -= 1;
858 let ix = row_global_to_local[i].zx();
859 let iy = j - s_begin;
860 s_L[(ix, iy)] += val;
861 }
862 }
863 noinline(LPanel, || {
864 for d in &u_compress_cols[s] {
865 let d = d.zx();
866 if left_contrib[d].0.is_empty() {
867 continue;
868 }
869 let d_begin = supernode_ptr[d].zx();
870 let d_end = supernode_ptr[d + 1].zx();
871 let d_size = d_end - d_begin;
872 let d_row_idx = &left_row_idx[lu.l_col_ptr_for_row_idx[d]
873 .zx()
874 ..lu.l_col_ptr_for_row_idx[d + 1].zx()][d_size..];
875 let d_col_idx = &lu.ut_row_idx[lu.ut_col_ptr_for_row_idx[d]
876 .zx()
877 ..lu.ut_col_ptr_for_row_idx[d + 1].zx()];
878 let d_col_start =
879 d_col_idx.partition_point(partition_fn(s_begin));
880 if d_col_start < d_col_idx.len()
881 && d_col_idx[d_col_start].zx() < s_end
882 {
883 let d_col_mid = d_col_start
884 + d_col_idx[d_col_start..]
885 .partition_point(partition_fn(s_end));
886 let mut d_LU_cols = work_to_mat_mut(
887 &mut left_contrib[d].0,
888 d_row_idx.len(),
889 d_col_idx.len(),
890 )
891 .subcols_mut(d_col_start, d_col_mid - d_col_start);
892 let left_contrib = &mut left_contrib[d];
893 let d_active = &mut left_contrib.1[d_col_start..];
894 let d_active_count = &mut left_contrib.2;
895 let d_active_mat = &mut left_contrib.3;
896 for (d_j, j) in
897 d_col_idx[d_col_start..d_col_mid].iter().enumerate()
898 {
899 if d_active[d_j] > I(0) {
900 let mut taken_rows = 0usize;
901 let j = j.zx();
902 let s_j = j - s_begin;
903 for (d_i, i) in d_row_idx.iter().enumerate() {
904 let i = i.zx();
905 let pi = row_perm_inv[i].zx();
906 if pi < s_begin {
907 continue;
908 }
909 let s_i = row_global_to_local[i].zx();
910 s_L[(s_i, s_j)] -= &d_LU_cols[(d_i, d_j)];
911 d_LU_cols[(d_i, d_j)] = zero::<T>();
912 taken_rows += d_active_mat
913 [(d_i, d_j + d_col_start)]
914 as usize;
915 d_active_mat[(d_i, d_j + d_col_start)] = 0;
916 }
917 assert!(d_active[d_j] >= I(taken_rows));
918 d_active[d_j] -= I(taken_rows);
919 if d_active[d_j] == I(0) {
920 assert!(*d_active_count > 0);
921 *d_active_count -= 1;
922 }
923 }
924 }
925 if *d_active_count == 0 {
926 left_contrib.0.clear();
927 left_contrib.1 = alloc::vec::Vec::new();
928 left_contrib.2 = 0;
929 left_contrib.3 = MatU8::new();
930 }
931 }
932 }
933 });
934 if s_L.nrows() < s_L.ncols() {
935 return Err(LuError::SymbolicSingular {
936 index: s_begin + s_L.nrows(),
937 });
938 }
939 let transpositions = &mut transpositions[s_begin..s_end];
940 crate::linalg::lu::partial_pivoting::factor::lu_in_place_recursion(
941 s_L.rb_mut(),
942 0,
943 s_size,
944 transpositions,
945 par.reborrow(),
946 params,
947 );
948 for i in 0..s_size {
949 if s_L[(i, i)].abs1() == zero::<T::Real>() {
950 return Err(LuError::NumericSingular {
951 index: s_begin + i,
952 });
953 }
954 }
955 for (idx, t) in transpositions.iter().enumerate() {
956 let i_t = s_row_idx[idx + t.zx()].zx();
957 let kk = row_perm_inv[i_t].zx();
958 row_perm.swap(s_begin + idx, kk);
959 row_perm_inv
960 .swap(row_perm[s_begin + idx].zx(), row_perm[kk].zx());
961 s_row_idx.swap(idx, idx + t.zx());
962 }
963 for (idx, t) in transpositions.iter().enumerate().rev() {
964 row_global_to_local
965 .swap(s_row_idx[idx].zx(), s_row_idx[idx + t.zx()].zx());
966 }
967 for i in s_row_idx.iter() {
968 let i = i.zx();
969 let pi = row_perm_inv[i].zx();
970
971 if pi >= s_end {
972 l_compress_rows
973 .try_reserve(1)
974 .ok()
975 .ok_or(FaerError::OutOfMemory)?;
976 l_compress_rows[i].push(I(s));
977 }
978 }
979 for (idx, i) in s_row_idx.iter().enumerate() {
980 assert!(row_global_to_local[i.zx()] == I(idx));
981 }
982 let s_col_idx = &mut indices[..n];
983 let mut s_col_index_count = 0usize;
984 for i in s_begin..s_end {
985 let pi = row_perm[i].zx();
986 for j in AT.row_idx_of_col(pi) {
987 let pj = col_perm_inv[j].zx();
988 if pj < s_end {
989 continue;
990 }
991 if marked[pj] < I(2 * s + 2) {
992 s_col_idx[s_col_index_count] = I(pj);
993 s_col_index_count += 1;
994 marked[pj] = I(2 * s + 2);
995 }
996 }
997 }
998
999 for i in s_begin..s_end {
1000 for d in &l_compress_rows[row_perm[i].zx()] {
1001 let d = d.zx();
1002 if supernode_visited[d].zx() < 2 * s + 1 {
1003 supernode_visited[d] = I(2 * s + 1);
1004
1005 if !left_contrib[d].0.is_empty() {
1006 let d_col_idx = &lu.ut_row_idx[lu
1007 .ut_col_ptr_for_row_idx[d]
1008 .zx()
1009 ..lu.ut_col_ptr_for_row_idx[d + 1].zx()];
1010 let d_col_start =
1011 d_col_idx.partition_point(partition_fn(s_end));
1012 for j in &d_col_idx[d_col_start..] {
1013 let j = j.zx();
1014 if marked[j] < I(2 * s + 2) {
1015 s_col_idx[s_col_index_count] = I(j);
1016 s_col_index_count += 1;
1017 marked[j] = I(2 * s + 2);
1018 }
1019 }
1020 }
1021 }
1022 }
1023 }
1024
1025 lu.ut_col_ptr_for_row_idx[s + 1] = I_checked(
1026 lu.ut_col_ptr_for_row_idx[s].zx() + s_col_index_count,
1027 )?;
1028 lu.ut_col_ptr_for_val[s + 1] = from_wide_checked(
1029 to_wide(lu.ut_col_ptr_for_val[s])
1030 + (s_col_index_count as u128 * s_size as u128),
1031 )?;
1032 resize_vec(
1033 &mut lu.ut_row_idx,
1034 lu.ut_col_ptr_for_row_idx[s + 1].zx(),
1035 false,
1036 false,
1037 I(0),
1038 )?;
1039 resize_vec::<T>(
1040 &mut lu.ut_val,
1041 lu.ut_col_ptr_for_val[s + 1].zx(),
1042 false,
1043 false,
1044 zero::<T>(),
1045 )?;
1046 lu.ut_row_idx[lu.ut_col_ptr_for_row_idx[s].zx()
1047 ..lu.ut_col_ptr_for_row_idx[s + 1].zx()]
1048 .copy_from_slice(&s_col_idx[..s_col_index_count]);
1049 lu.ut_row_idx[lu.ut_col_ptr_for_row_idx[s].zx()
1050 ..lu.ut_col_ptr_for_row_idx[s + 1].zx()]
1051 .sort_unstable();
1052 let s_col_idx = &lu.ut_row_idx[lu.ut_col_ptr_for_row_idx[s].zx()
1053 ..lu.ut_col_ptr_for_row_idx[s + 1].zx()];
1054 for (idx, j) in s_col_idx.iter().enumerate() {
1055 let j = j.zx();
1056 col_global_to_local[j] = I(idx);
1057
1058 let j_node = index_to_super[j].zx();
1059 if j_node > s
1060 && u_compress_cols[j_node].last().copied() != Some(I(s))
1061 {
1062 u_compress_cols
1063 .try_reserve(1)
1064 .ok()
1065 .ok_or(FaerError::OutOfMemory)?;
1066 u_compress_cols[j_node].push(I(s));
1067 }
1068 }
1069 let s_U = &mut lu.ut_val[lu.ut_col_ptr_for_val[s].zx()
1070 ..lu.ut_col_ptr_for_val[s + 1].zx()];
1071 let mut s_U = MatMut::from_column_major_slice_mut(
1072 s_U,
1073 s_col_index_count,
1074 s_size,
1075 )
1076 .transpose_mut();
1077 s_U.fill(zero());
1078 for i in s_begin..s_end {
1079 let pi = row_perm[i].zx();
1080 for (j, val) in
1081 iter::zip(AT.row_idx_of_col(pi), AT.val_of_col(pi))
1082 {
1083 let pj = col_perm_inv[j].zx();
1084 if pj < s_end {
1085 continue;
1086 }
1087 assert!(A_leftover > 0);
1088 A_leftover -= 1;
1089 let ix = i - s_begin;
1090 let iy = col_global_to_local[pj].zx();
1091 s_U[(ix, iy)] += val;
1092 }
1093 }
1094 noinline(UPanel, || {
1095 for i in s_begin..s_end {
1096 for d in &l_compress_rows[row_perm[i].zx()] {
1097 let d = d.zx();
1098 if supernode_visited[d].zx() < 2 * s + 2 {
1099 supernode_visited[d] = I(2 * s + 2);
1100
1101 let d = d.zx();
1102 if left_contrib[d].0.is_empty() {
1103 continue;
1104 }
1105 let d_begin = supernode_ptr[d].zx();
1106 let d_end = supernode_ptr[d + 1].zx();
1107 let d_size = d_end - d_begin;
1108 let d_row_idx =
1109 &left_row_idx[lu.l_col_ptr_for_row_idx[d].zx()
1110 ..lu.l_col_ptr_for_row_idx[d + 1].zx()][d_size..];
1111 let d_col_idx = &lu.ut_row_idx[lu
1112 .ut_col_ptr_for_row_idx[d]
1113 .zx()
1114 ..lu.ut_col_ptr_for_row_idx[d + 1].zx()];
1115
1116 let d_col_start =
1117 d_col_idx.partition_point(partition_fn(s_end));
1118 let d_LU = work_to_mat_mut(
1119 &mut left_contrib[d].0,
1120 d_row_idx.len(),
1121 d_col_idx.len(),
1122 );
1123 let mut d_LU = d_LU.get_mut(.., d_col_start..);
1124 let left_contrib = &mut left_contrib[d];
1125 let d_active = &mut left_contrib.1[d_col_start..];
1126 let d_active_count = &mut left_contrib.2;
1127 let d_active_mat = &mut left_contrib.3;
1128 for (d_j, j) in
1129 d_col_idx[d_col_start..].iter().enumerate()
1130 {
1131 if d_active[d_j] > I(0) {
1132 let mut taken_rows = 0usize;
1133 let j = j.zx();
1134 let s_j = col_global_to_local[j].zx();
1135 for (d_i, i) in d_row_idx.iter().enumerate()
1136 {
1137 let i = i.zx();
1138 let pi = row_perm_inv[i].zx();
1139 if pi >= s_begin && pi < s_end {
1140 let s_i =
1141 row_global_to_local[i].zx();
1142 s_U[(s_i, s_j)] -=
1143 &d_LU[(d_i, d_j)];
1144 d_LU[(d_i, d_j)] = zero::<T>();
1145 taken_rows += d_active_mat
1146 [(d_i, d_j + d_col_start)]
1147 as usize;
1148 d_active_mat
1149 [(d_i, d_j + d_col_start)] = 0;
1150 }
1151 }
1152 assert!(d_active[d_j] >= I(taken_rows));
1153 d_active[d_j] -= I(taken_rows);
1154 if d_active[d_j] == I(0) {
1155 assert!(*d_active_count > 0);
1156 *d_active_count -= 1;
1157 }
1158 }
1159 }
1160 if *d_active_count == 0 {
1161 left_contrib.0.clear();
1162 left_contrib.1 = alloc::vec::Vec::new();
1163 left_contrib.2 = 0;
1164 left_contrib.3 = MatU8::new();
1165 }
1166 }
1167 }
1168 }
1169 });
1170 linalg::triangular_solve::solve_unit_lower_triangular_in_place(
1171 s_L.rb().subrows(0, s_size),
1172 s_U.rb_mut(),
1173 par.reborrow(),
1174 );
1175 if s_row_idx_count > s_size && s_col_index_count > 0 {
1176 resize_vec::<T>(
1177 &mut right_contrib[0].0,
1178 from_wide_checked(
1179 to_wide(I(s_row_idx_count - s_size))
1180 * to_wide(I(s_col_index_count)),
1181 )?
1182 .zx(),
1183 false,
1184 false,
1185 zero::<T>(),
1186 )?;
1187 right_contrib[0]
1188 .1
1189 .try_reserve_exact(s_col_index_count)
1190 .ok()
1191 .ok_or(FaerError::OutOfMemory)?;
1192 right_contrib[0]
1193 .1
1194 .resize(s_col_index_count, I(s_row_idx_count - s_size));
1195 right_contrib[0].2 = s_col_index_count;
1196 right_contrib[0].3 = MatU8::with_dims(
1197 s_row_idx_count - s_size,
1198 s_col_index_count,
1199 )?;
1200 let mut s_LU = work_to_mat_mut(
1201 &mut right_contrib[0].0,
1202 s_row_idx_count - s_size,
1203 s_col_index_count,
1204 );
1205 linalg::matmul::matmul(
1206 s_LU.rb_mut(),
1207 Accum::Replace,
1208 s_L.rb().get(s_size.., ..),
1209 s_U.rb(),
1210 one::<T>(),
1211 par.reborrow(),
1212 );
1213 noinline(Front, || {
1214 for d in &u_compress_cols[s] {
1215 let d = d.zx();
1216 if left_contrib[d].0.is_empty() {
1217 continue;
1218 }
1219 let d_begin = supernode_ptr[d].zx();
1220 let d_end = supernode_ptr[d + 1].zx();
1221 let d_size = d_end - d_begin;
1222 let d_row_idx =
1223 &left_row_idx[lu.l_col_ptr_for_row_idx[d].zx()
1224 ..lu.l_col_ptr_for_row_idx[d + 1].zx()][d_size..];
1225 let d_col_idx =
1226 &lu.ut_row_idx[lu.ut_col_ptr_for_row_idx[d].zx()
1227 ..lu.ut_col_ptr_for_row_idx[d + 1].zx()];
1228 let contributes_to_front = d_row_idx
1229 .iter()
1230 .any(|&i| row_perm_inv[i.zx()].zx() >= s_end);
1231 if contributes_to_front {
1232 let d_col_start =
1233 d_col_idx.partition_point(partition_fn(s_end));
1234 let d_LU = work_to_mat_mut(
1235 &mut left_contrib[d].0,
1236 d_row_idx.len(),
1237 d_col_idx.len(),
1238 );
1239 let mut d_LU = d_LU.get_mut(.., d_col_start..);
1240 let left_contrib = &mut left_contrib[d];
1241 let d_active = &mut left_contrib.1[d_col_start..];
1242 let d_active_count = &mut left_contrib.2;
1243 let d_active_mat = &mut left_contrib.3;
1244 let mut d_active_row_count = 0usize;
1245 let mut first_iter = true;
1246 for (d_j, j) in
1247 d_col_idx[d_col_start..].iter().enumerate()
1248 {
1249 if d_active[d_j] > I(0) {
1250 if first_iter {
1251 first_iter = false;
1252 for (d_i, i) in
1253 d_row_idx.iter().enumerate()
1254 {
1255 let i = i.zx();
1256 let pi = row_perm_inv[i].zx();
1257 if (pi < s_end)
1258 || (row_global_to_local[i]
1259 == I(NONE))
1260 {
1261 continue;
1262 }
1263 d_active_rows[d_active_row_count] =
1264 I(d_i);
1265 d_active_row_count += 1;
1266 }
1267 }
1268 let j = j.zx();
1269 let mut taken_rows = 0usize;
1270 let s_j = col_global_to_local[j];
1271 if s_j == I(NONE) {
1272 continue;
1273 }
1274 let s_j = s_j.zx();
1275 let mut dst = s_LU.rb_mut().col_mut(s_j);
1276 let mut src = d_LU.rb_mut().col_mut(d_j);
1277 assert!(dst.row_stride() == 1);
1278 assert!(src.row_stride() == 1);
1279 for d_i in
1280 &d_active_rows[..d_active_row_count]
1281 {
1282 let d_i = d_i.zx();
1283 let i = d_row_idx[d_i].zx();
1284 let d_active_mat = &mut d_active_mat
1285 [(d_i, d_j + d_col_start)];
1286 if *d_active_mat == 0 {
1287 continue;
1288 }
1289 let s_i = row_global_to_local[i].zx()
1290 - s_size;
1291 dst[s_i] += &src[d_i];
1292 src[d_i] = zero::<T>();
1293 taken_rows += 1;
1294 *d_active_mat = 0;
1295 }
1296 d_active[d_j] -= I(taken_rows);
1297 if d_active[d_j] == I(0) {
1298 *d_active_count -= 1;
1299 }
1300 }
1301 }
1302 if *d_active_count == 0 {
1303 left_contrib.0.clear();
1304 left_contrib.1 = alloc::vec::Vec::new();
1305 left_contrib.2 = 0;
1306 left_contrib.3 = MatU8::new();
1307 }
1308 }
1309 }
1310 })
1311 }
1312 for i in s_row_idx.iter() {
1313 row_global_to_local[i.zx()] = I(NONE);
1314 }
1315 for j in s_col_idx.iter() {
1316 col_global_to_local[j.zx()] = I(NONE);
1317 }
1318 }
1319 assert!(A_leftover == 0);
1320 for idx in
1321 &mut lu.l_row_idx[..lu.l_col_ptr_for_row_idx[n_supernodes].zx()]
1322 {
1323 *idx = row_perm_inv[idx.zx()];
1324 }
1325 lu.nrows = m;
1326 lu.ncols = n;
1327 lu.nsupernodes = n_supernodes;
1328 lu.supernode_ptr.clone_from(supernode_ptr);
1329 Ok(())
1330 }
1331}
1332
1333pub mod simplicial {
1339 use super::*;
1340 use crate::assert;
1341
1342 pub fn solve_in_place_scratch<I: Index, T: ComplexField>(
1343 dim: usize,
1344 rhs_ncols: usize,
1345 par: ParShape,
1346 ) -> StackReq {
1347 let _ = par;
1348 crate::perm::permute_rows_in_place_scratch::<I, T>(dim, rhs_ncols)
1349 }
1350
1351 pub fn solve_transpose_in_place_scratch<I: Index, T: ComplexField>(
1352 dim: usize,
1353 rhs_ncols: usize,
1354 par: ParShape,
1355 ) -> StackReq {
1356 solve_in_place_scratch::<I, T>(dim, rhs_ncols, par)
1357 }
1358
1359 #[derive(Debug, Clone)]
1362 pub struct SimplicialLu<I, T> {
1363 nrows: usize,
1364 ncols: usize,
1365 l_col_ptr: alloc::vec::Vec<I>,
1366 l_row_idx: alloc::vec::Vec<I>,
1367 l_val: alloc::vec::Vec<T>,
1368 u_col_ptr: alloc::vec::Vec<I>,
1369 u_row_idx: alloc::vec::Vec<I>,
1370 u_val: alloc::vec::Vec<T>,
1371 }
1372 impl<I: Index, T> Default for SimplicialLu<I, T> {
1373 fn default() -> Self {
1374 Self::new()
1375 }
1376 }
1377 impl<I: Index, T> SimplicialLu<I, T> {
1378 #[inline]
1380 pub fn new() -> Self {
1381 Self {
1382 nrows: 0,
1383 ncols: 0,
1384 l_col_ptr: alloc::vec::Vec::new(),
1385 u_col_ptr: alloc::vec::Vec::new(),
1386 l_row_idx: alloc::vec::Vec::new(),
1387 u_row_idx: alloc::vec::Vec::new(),
1388 l_val: alloc::vec::Vec::new(),
1389 u_val: alloc::vec::Vec::new(),
1390 }
1391 }
1392
1393 #[inline]
1395 pub fn nrows(&self) -> usize {
1396 self.nrows
1397 }
1398
1399 #[inline]
1401 pub fn ncols(&self) -> usize {
1402 self.ncols
1403 }
1404
1405 #[inline]
1408 pub fn l_factor_unsorted(&self) -> SparseColMatRef<'_, I, T> {
1409 SparseColMatRef::<'_, I, T>::new(
1410 unsafe {
1411 SymbolicSparseColMatRef::new_unchecked(
1412 self.nrows(),
1413 self.ncols(),
1414 &self.l_col_ptr,
1415 None,
1416 &self.l_row_idx,
1417 )
1418 },
1419 &self.l_val,
1420 )
1421 }
1422
1423 #[inline]
1426 pub fn u_factor_unsorted(&self) -> SparseColMatRef<'_, I, T> {
1427 SparseColMatRef::<'_, I, T>::new(
1428 unsafe {
1429 SymbolicSparseColMatRef::new_unchecked(
1430 self.ncols(),
1431 self.ncols(),
1432 &self.u_col_ptr,
1433 None,
1434 &self.u_row_idx,
1435 )
1436 },
1437 &self.u_val,
1438 )
1439 }
1440
1441 #[track_caller]
1448 pub fn solve_in_place_with_conj(
1449 &self,
1450 row_perm: PermRef<'_, I>,
1451 col_perm: PermRef<'_, I>,
1452 conj_lhs: Conj,
1453 rhs: MatMut<'_, T>,
1454 mut par: Par,
1455 stack: &mut MemStack,
1456 ) where
1457 T: ComplexField,
1458 {
1459 assert!(self.nrows() == self.ncols());
1460 assert!(self.nrows() == rhs.nrows());
1461 let mut X = rhs;
1462 let l = self.l_factor_unsorted();
1463 let u = self.u_factor_unsorted();
1464 crate::perm::permute_rows_in_place(X.rb_mut(), row_perm, stack);
1465 linalg_sp::triangular_solve::solve_unit_lower_triangular_in_place(
1466 l,
1467 conj_lhs,
1468 X.rb_mut(),
1469 par.reborrow(),
1470 );
1471 linalg_sp::triangular_solve::solve_upper_triangular_in_place(
1472 u,
1473 conj_lhs,
1474 X.rb_mut(),
1475 par,
1476 );
1477 crate::perm::permute_rows_in_place(
1478 X.rb_mut(),
1479 col_perm.inverse(),
1480 stack,
1481 );
1482 }
1483
1484 #[track_caller]
1491 pub fn solve_transpose_in_place_with_conj(
1492 &self,
1493 row_perm: PermRef<'_, I>,
1494 col_perm: PermRef<'_, I>,
1495 conj_lhs: Conj,
1496 rhs: MatMut<'_, T>,
1497 mut par: Par,
1498 stack: &mut MemStack,
1499 ) where
1500 T: ComplexField,
1501 {
1502 assert!(all(
1503 self.nrows() == self.ncols(),
1504 self.nrows() == rhs.nrows()
1505 ));
1506 let mut X = rhs;
1507 let l = self.l_factor_unsorted();
1508 let u = self.u_factor_unsorted();
1509 crate::perm::permute_rows_in_place(X.rb_mut(), col_perm, stack);
1510 linalg_sp::triangular_solve::solve_upper_triangular_transpose_in_place(u, conj_lhs, X.rb_mut(), par.reborrow());
1511 linalg_sp::triangular_solve::solve_unit_lower_triangular_transpose_in_place(l, conj_lhs, X.rb_mut(), par);
1512 crate::perm::permute_rows_in_place(
1513 X.rb_mut(),
1514 row_perm.inverse(),
1515 stack,
1516 );
1517 }
1518 }
1519 fn depth_first_search<I: Index>(
1520 marked: &mut [I],
1521 mark: I,
1522 xi: &mut [I],
1523 l: SymbolicSparseColMatRef<'_, I>,
1524 row_perm_inv: &[I],
1525 b: usize,
1526 stack: &mut [I],
1527 ) -> usize {
1528 let I = I::truncate;
1529 let mut tail_start = xi.len();
1530 let mut head_len = 1usize;
1531 xi[0] = I(b);
1532 let li = l.row_idx();
1533 'dfs_loop: while head_len > 0 {
1534 let b = xi[head_len - 1].zx().zx();
1535 let pb = row_perm_inv[b].zx();
1536 let range = if pb < l.ncols() {
1537 l.col_range(pb)
1538 } else {
1539 0..0
1540 };
1541 if marked[b] < mark {
1542 marked[b] = mark;
1543 stack[head_len - 1] = I(range.start);
1544 }
1545 let start = stack[head_len - 1].zx();
1546 let end = range.end;
1547 for ptr in start..end {
1548 let i = li[ptr].zx();
1549 if marked[i] == mark {
1550 continue;
1551 }
1552 stack[head_len - 1] = I(ptr);
1553 xi[head_len] = I(i);
1554 head_len += 1;
1555 continue 'dfs_loop;
1556 }
1557 head_len -= 1;
1558 tail_start -= 1;
1559 xi[tail_start] = I(b);
1560 }
1561 tail_start
1562 }
1563 fn reach<I: Index>(
1564 marked: &mut [I],
1565 mark: I,
1566 xi: &mut [I],
1567 l: SymbolicSparseColMatRef<'_, I>,
1568 row_perm_inv: &[I],
1569 bi: &[I],
1570 stack: &mut [I],
1571 ) -> usize {
1572 let n = l.nrows();
1573 let mut tail_start = n;
1574 for b in bi {
1575 let b = b.zx();
1576 if marked[b] < mark {
1577 tail_start = depth_first_search(
1578 marked,
1579 mark,
1580 &mut xi[..tail_start],
1581 l,
1582 row_perm_inv,
1583 b,
1584 stack,
1585 );
1586 }
1587 }
1588 tail_start
1589 }
1590 fn l_incomplete_solve_sparse<I: Index, T: ComplexField>(
1591 marked: &mut [I],
1592 mark: I,
1593 xi: &mut [I],
1594 x: &mut [T],
1595 l: SparseColMatRef<'_, I, T>,
1596 row_perm_inv: &[I],
1597 bi: &[I],
1598 bx: &[T],
1599 stack: &mut [I],
1600 ) -> usize {
1601 let tail_start =
1602 reach(marked, mark, xi, l.symbolic(), row_perm_inv, bi, stack);
1603 let xi = &xi[tail_start..];
1604 for (i, b) in iter::zip(bi, bx) {
1605 let i = i.zx();
1606 x[i] += b;
1607 }
1608 for i in xi {
1609 let i = i.zx();
1610 let pi = row_perm_inv[i].zx();
1611 if pi >= l.ncols() {
1612 continue;
1613 }
1614 let li = l.row_idx_of_col_raw(pi);
1615 let lx = l.val_of_col(pi);
1616 let len = li.len();
1617 let ref xi = x[i].copy();
1618 for (li, lx) in iter::zip(&li[1..], &lx[1..len]) {
1619 let li = li.zx();
1620 x[li] -= lx * xi;
1621 }
1622 }
1623 tail_start
1624 }
1625 pub fn factorize_simplicial_numeric_lu_scratch<
1628 I: Index,
1629 T: ComplexField,
1630 >(
1631 nrows: usize,
1632 ncols: usize,
1633 ) -> StackReq {
1634 let idx = StackReq::new::<I>(nrows);
1635 let val = temp_mat_scratch::<T>(nrows, 1);
1636 let _ = ncols;
1637 StackReq::all_of(&[val, idx, idx, idx])
1638 }
1639 pub fn factorize_simplicial_numeric_lu<I: Index, T: ComplexField>(
1643 row_perm: &mut [I],
1644 row_perm_inv: &mut [I],
1645 lu: &mut SimplicialLu<I, T>,
1646 A: SparseColMatRef<'_, I, T>,
1647 col_perm: PermRef<'_, I>,
1648 stack: &mut MemStack,
1649 ) -> Result<(), LuError> {
1650 let I = I::truncate;
1651 assert!(all(
1652 A.nrows() == row_perm.len(),
1653 A.nrows() == row_perm_inv.len(),
1654 A.ncols() == col_perm.len(),
1655 A.nrows() == A.ncols()
1656 ));
1657 lu.nrows = 0;
1658 lu.ncols = 0;
1659 let m = A.nrows();
1660 let n = A.ncols();
1661 resize_vec(&mut lu.l_col_ptr, n + 1, true, false, I(0))?;
1662 resize_vec(&mut lu.u_col_ptr, n + 1, true, false, I(0))?;
1663 let (mut x, stack) = temp_mat_zeroed::<T, _, _>(m, 1, stack);
1664 let x = x
1665 .as_mat_mut()
1666 .col_mut(0)
1667 .try_as_col_major_mut()
1668 .unwrap()
1669 .as_slice_mut();
1670 let (marked, stack) = unsafe { stack.make_raw::<I>(m) };
1671 let (xj, stack) = unsafe { stack.make_raw::<I>(m) };
1672 let (stack, _) = unsafe { stack.make_raw::<I>(m) };
1673 marked.fill(I(0));
1674 row_perm_inv.fill(I(n));
1675 let mut l_pos = 0usize;
1676 let mut u_pos = 0usize;
1677 lu.l_col_ptr[0] = I(0);
1678 lu.u_col_ptr[0] = I(0);
1679 for j in 0..n {
1680 let l = SparseColMatRef::<'_, I, T>::new(
1681 unsafe {
1682 SymbolicSparseColMatRef::new_unchecked(
1683 m,
1684 j,
1685 &lu.l_col_ptr[..j + 1],
1686 None,
1687 &lu.l_row_idx,
1688 )
1689 },
1690 &lu.l_val,
1691 );
1692 let pj = col_perm.arrays().0[j].zx();
1693 let tail_start = l_incomplete_solve_sparse(
1694 marked,
1695 I(j + 1),
1696 xj,
1697 x,
1698 l,
1699 row_perm_inv,
1700 A.row_idx_of_col_raw(pj),
1701 A.val_of_col(pj),
1702 stack,
1703 );
1704 let xj = &xj[tail_start..];
1705 resize_vec::<T>(
1706 &mut lu.l_val,
1707 l_pos + xj.len() + 1,
1708 false,
1709 false,
1710 zero::<T>(),
1711 )?;
1712 resize_vec(
1713 &mut lu.l_row_idx,
1714 l_pos + xj.len() + 1,
1715 false,
1716 false,
1717 I(0),
1718 )?;
1719 resize_vec::<T>(
1720 &mut lu.u_val,
1721 u_pos + xj.len() + 1,
1722 false,
1723 false,
1724 zero::<T>(),
1725 )?;
1726 resize_vec(
1727 &mut lu.u_row_idx,
1728 u_pos + xj.len() + 1,
1729 false,
1730 false,
1731 I(0),
1732 )?;
1733 let l_val = &mut *lu.l_val;
1734 let u_val = &mut *lu.u_val;
1735 let mut pivot_idx = n;
1736 let mut pivot_val = -one::<T::Real>();
1737 for i in xj {
1738 let i = i.zx();
1739 let xi = x[i].copy();
1740 if row_perm_inv[i] == I(n) {
1741 let val = xi.abs();
1742 if matches!(
1743 val.partial_cmp(&pivot_val),
1744 None | Some(core::cmp::Ordering::Greater)
1745 ) {
1746 pivot_idx = i;
1747 pivot_val = val;
1748 }
1749 } else {
1750 lu.u_row_idx[u_pos] = row_perm_inv[i];
1751 u_val[u_pos] = xi;
1752 u_pos += 1;
1753 }
1754 }
1755 if pivot_idx == n {
1756 return Err(LuError::SymbolicSingular { index: j });
1757 }
1758 let x_piv = x[pivot_idx].copy();
1759 if x_piv.abs1() == zero::<T::Real>() {
1762 return Err(LuError::NumericSingular { index: j });
1763 }
1764 let ref x_piv_inv = x_piv.recip();
1765 row_perm_inv[pivot_idx] = I(j);
1766 lu.u_row_idx[u_pos] = I(j);
1767 u_val[u_pos] = x_piv;
1768 u_pos += 1;
1769 lu.u_col_ptr[j + 1] = I(u_pos);
1770 lu.l_row_idx[l_pos] = I(pivot_idx);
1771 l_val[l_pos] = one::<T>();
1772 l_pos += 1;
1773 for i in xj {
1774 let i = i.zx();
1775 let xi = x[i].copy();
1776 if row_perm_inv[i] == I(n) {
1777 lu.l_row_idx[l_pos] = I(i);
1778 l_val[l_pos] = xi * x_piv_inv;
1779 l_pos += 1;
1780 }
1781 x[i] = zero::<T>();
1782 }
1783 lu.l_col_ptr[j + 1] = I(l_pos);
1784 }
1785 for i in &mut lu.l_row_idx[..l_pos] {
1786 *i = row_perm_inv[(*i).zx()];
1787 }
1788 for (idx, p) in row_perm_inv.iter().enumerate() {
1789 row_perm[p.zx()] = I(idx);
1790 }
1791 lu.nrows = m;
1792 lu.ncols = n;
1793 Ok(())
1794 }
1795}
1796#[derive(Copy, Clone, Debug, Default)]
1798pub struct LuSymbolicParams<'a> {
1799 pub colamd_params: colamd::Control,
1801 pub supernodal_flop_ratio_threshold: SupernodalThreshold,
1803 pub supernodal_params: SymbolicSupernodalParams<'a>,
1805}
1806#[derive(Debug, Clone)]
1809pub enum SymbolicLuRaw<I: Index> {
1810 Simplicial {
1812 nrows: usize,
1814 ncols: usize,
1816 },
1817 Supernodal(supernodal::SymbolicSupernodalLu<I>),
1819}
1820#[derive(Debug, Clone)]
1822pub struct SymbolicLu<I: Index> {
1823 raw: SymbolicLuRaw<I>,
1824 col_perm_fwd: alloc::vec::Vec<I>,
1825 col_perm_inv: alloc::vec::Vec<I>,
1826 A_nnz: usize,
1827}
1828#[derive(Debug, Clone)]
1829enum NumericLuRaw<I: Index, T> {
1830 None,
1831 Supernodal(supernodal::SupernodalLu<I, T>),
1832 Simplicial(simplicial::SimplicialLu<I, T>),
1833}
1834#[derive(Debug, Clone)]
1837pub struct NumericLu<I: Index, T> {
1838 raw: NumericLuRaw<I, T>,
1839 row_perm_fwd: alloc::vec::Vec<I>,
1840 row_perm_inv: alloc::vec::Vec<I>,
1841}
1842impl<I: Index, T> Default for NumericLu<I, T> {
1843 fn default() -> Self {
1844 Self::new()
1845 }
1846}
1847impl<I: Index, T> NumericLu<I, T> {
1848 #[inline]
1850 pub fn new() -> Self {
1851 Self {
1852 raw: NumericLuRaw::None,
1853 row_perm_fwd: alloc::vec::Vec::new(),
1854 row_perm_inv: alloc::vec::Vec::new(),
1855 }
1856 }
1857}
1858#[derive(Debug)]
1860pub struct LuRef<'a, I: Index, T> {
1861 symbolic: &'a SymbolicLu<I>,
1862 numeric: &'a NumericLu<I, T>,
1863}
1864impl<I: Index, T> Copy for LuRef<'_, I, T> {}
1865impl<I: Index, T> Clone for LuRef<'_, I, T> {
1866 fn clone(&self) -> Self {
1867 *self
1868 }
1869}
1870impl<'a, I: Index, T> LuRef<'a, I, T> {
1871 #[inline]
1878 pub fn new_unchecked(
1879 symbolic: &'a SymbolicLu<I>,
1880 numeric: &'a NumericLu<I, T>,
1881 ) -> Self {
1882 match (&symbolic.raw, &numeric.raw) {
1883 (SymbolicLuRaw::Simplicial { .. }, NumericLuRaw::Simplicial(_)) => {
1884 },
1885 (SymbolicLuRaw::Supernodal { .. }, NumericLuRaw::Supernodal(_)) => {
1886 },
1887 _ => panic!("incompatible symbolic and numeric variants"),
1888 }
1889 Self { symbolic, numeric }
1890 }
1891
1892 #[inline]
1894 pub fn symbolic(self) -> &'a SymbolicLu<I> {
1895 self.symbolic
1896 }
1897
1898 #[inline]
1900 pub fn row_perm(self) -> PermRef<'a, I> {
1901 unsafe {
1902 PermRef::new_unchecked(
1903 &self.numeric.row_perm_fwd,
1904 &self.numeric.row_perm_inv,
1905 self.symbolic.nrows(),
1906 )
1907 }
1908 }
1909
1910 #[inline]
1912 pub fn col_perm(self) -> PermRef<'a, I> {
1913 self.symbolic.col_perm()
1914 }
1915
1916 #[track_caller]
1923 pub fn solve_in_place_with_conj(
1924 self,
1925 conj: Conj,
1926 rhs: MatMut<'_, T>,
1927 par: Par,
1928 stack: &mut MemStack,
1929 ) where
1930 T: ComplexField,
1931 {
1932 match (&self.symbolic.raw, &self.numeric.raw) {
1933 (
1934 SymbolicLuRaw::Simplicial { .. },
1935 NumericLuRaw::Simplicial(numeric),
1936 ) => numeric.solve_in_place_with_conj(
1937 self.row_perm(),
1938 self.col_perm(),
1939 conj,
1940 rhs,
1941 par,
1942 stack,
1943 ),
1944 (
1945 SymbolicLuRaw::Supernodal(_),
1946 NumericLuRaw::Supernodal(numeric),
1947 ) => numeric.solve_in_place_with_conj(
1948 self.row_perm(),
1949 self.col_perm(),
1950 conj,
1951 rhs,
1952 par,
1953 stack,
1954 ),
1955 _ => unreachable!(),
1956 }
1957 }
1958
1959 #[track_caller]
1966 pub fn solve_transpose_in_place_with_conj(
1967 self,
1968 conj: Conj,
1969 rhs: MatMut<'_, T>,
1970 par: Par,
1971 stack: &mut MemStack,
1972 ) where
1973 T: ComplexField,
1974 {
1975 match (&self.symbolic.raw, &self.numeric.raw) {
1976 (
1977 SymbolicLuRaw::Simplicial { .. },
1978 NumericLuRaw::Simplicial(numeric),
1979 ) => numeric.solve_transpose_in_place_with_conj(
1980 self.row_perm(),
1981 self.col_perm(),
1982 conj,
1983 rhs,
1984 par,
1985 stack,
1986 ),
1987 (
1988 SymbolicLuRaw::Supernodal(_),
1989 NumericLuRaw::Supernodal(numeric),
1990 ) => numeric.solve_transpose_in_place_with_conj(
1991 self.row_perm(),
1992 self.col_perm(),
1993 conj,
1994 rhs,
1995 par,
1996 stack,
1997 ),
1998 _ => unreachable!(),
1999 }
2000 }
2001}
2002impl<I: Index> SymbolicLu<I> {
2003 #[inline]
2005 pub fn nrows(&self) -> usize {
2006 match &self.raw {
2007 SymbolicLuRaw::Simplicial { nrows, .. } => *nrows,
2008 SymbolicLuRaw::Supernodal(this) => this.nrows,
2009 }
2010 }
2011
2012 #[inline]
2014 pub fn ncols(&self) -> usize {
2015 match &self.raw {
2016 SymbolicLuRaw::Simplicial { ncols, .. } => *ncols,
2017 SymbolicLuRaw::Supernodal(this) => this.ncols,
2018 }
2019 }
2020
2021 #[inline]
2024 pub fn col_perm(&self) -> PermRef<'_, I> {
2025 unsafe {
2026 PermRef::new_unchecked(
2027 &self.col_perm_fwd,
2028 &self.col_perm_inv,
2029 self.ncols(),
2030 )
2031 }
2032 }
2033
2034 pub fn factorize_numeric_lu_scratch<T>(
2037 &self,
2038 par: ParShape,
2039 params: Spec<PartialPivLuParams, T>,
2040 ) -> StackReq
2041 where
2042 T: ComplexField,
2043 {
2044 match &self.raw {
2045 SymbolicLuRaw::Simplicial { nrows, ncols } => {
2046 simplicial::factorize_simplicial_numeric_lu_scratch::<I, T>(
2047 *nrows, *ncols,
2048 )
2049 },
2050 SymbolicLuRaw::Supernodal(symbolic) => {
2051 let _ = par;
2052 let m = symbolic.nrows;
2053 let A_nnz = self.A_nnz;
2054 let AT_scratch = StackReq::all_of(&[
2055 temp_mat_scratch::<T>(A_nnz, 1),
2056 StackReq::new::<I>(m + 1),
2057 StackReq::new::<I>(A_nnz),
2058 ]);
2059 StackReq::and(
2060 AT_scratch,
2061 supernodal::factorize_supernodal_numeric_lu_scratch::<I, T>(
2062 symbolic, params,
2063 ),
2064 )
2065 },
2066 }
2067 }
2068
2069 pub fn solve_in_place_scratch<T>(
2072 &self,
2073 rhs_ncols: usize,
2074 par: ParShape,
2075 ) -> StackReq
2076 where
2077 T: ComplexField,
2078 {
2079 match &self.raw {
2080 SymbolicLuRaw::Simplicial { nrows, .. } => {
2081 simplicial::solve_in_place_scratch::<I, T>(
2082 *nrows, rhs_ncols, par,
2083 )
2084 },
2085 SymbolicLuRaw::Supernodal(this) => {
2086 supernodal::solve_in_place_scratch::<I, T>(
2087 this.nrows, rhs_ncols, par,
2088 )
2089 },
2090 }
2091 }
2092
2093 pub fn solve_transpose_in_place_scratch<T>(
2096 &self,
2097 rhs_ncols: usize,
2098 par: ParShape,
2099 ) -> StackReq
2100 where
2101 T: ComplexField,
2102 {
2103 self.solve_in_place_scratch::<T>(rhs_ncols, par)
2104 }
2105
2106 #[track_caller]
2108 pub fn factorize_numeric_lu<'out, T: ComplexField>(
2109 &'out self,
2110 numeric: &'out mut NumericLu<I, T>,
2111 A: SparseColMatRef<'_, I, T>,
2112 par: Par,
2113 stack: &mut MemStack,
2114 params: Spec<PartialPivLuParams, T>,
2115 ) -> Result<LuRef<'out, I, T>, LuError> {
2116 if matches!(self.raw, SymbolicLuRaw::Simplicial { .. })
2117 && !matches!(numeric.raw, NumericLuRaw::Simplicial(_))
2118 {
2119 numeric.raw =
2120 NumericLuRaw::Simplicial(simplicial::SimplicialLu::new());
2121 }
2122 if matches!(self.raw, SymbolicLuRaw::Supernodal(_))
2123 && !matches!(numeric.raw, NumericLuRaw::Supernodal(_))
2124 {
2125 numeric.raw =
2126 NumericLuRaw::Supernodal(supernodal::SupernodalLu::new());
2127 }
2128 let nrows = self.nrows();
2129 numeric
2130 .row_perm_fwd
2131 .try_reserve_exact(nrows.saturating_sub(numeric.row_perm_fwd.len()))
2132 .ok()
2133 .ok_or(FaerError::OutOfMemory)?;
2134 numeric
2135 .row_perm_inv
2136 .try_reserve_exact(nrows.saturating_sub(numeric.row_perm_inv.len()))
2137 .ok()
2138 .ok_or(FaerError::OutOfMemory)?;
2139 numeric.row_perm_fwd.resize(nrows, I::truncate(0));
2140 numeric.row_perm_inv.resize(nrows, I::truncate(0));
2141 match (&self.raw, &mut numeric.raw) {
2142 (
2143 SymbolicLuRaw::Simplicial { nrows, ncols },
2144 NumericLuRaw::Simplicial(lu),
2145 ) => {
2146 assert!(all(A.nrows() == *nrows, A.ncols() == *ncols));
2147 simplicial::factorize_simplicial_numeric_lu(
2148 &mut numeric.row_perm_fwd,
2149 &mut numeric.row_perm_inv,
2150 lu,
2151 A,
2152 self.col_perm(),
2153 stack,
2154 )?;
2155 },
2156 (
2157 SymbolicLuRaw::Supernodal(symbolic),
2158 NumericLuRaw::Supernodal(lu),
2159 ) => {
2160 let m = symbolic.nrows;
2161 let (new_col_ptr, stack) =
2162 unsafe { stack.make_raw::<I>(m + 1) };
2163 let (new_row_idx, stack) =
2164 unsafe { stack.make_raw::<I>(self.A_nnz) };
2165 let (mut new_values, stack) =
2166 unsafe { temp_mat_uninit::<T, _, _>(self.A_nnz, 1, stack) };
2167 let new_values = new_values
2168 .as_mat_mut()
2169 .col_mut(0)
2170 .try_as_col_major_mut()
2171 .unwrap()
2172 .as_slice_mut();
2173 let AT = utils::transpose(
2174 new_values,
2175 new_col_ptr,
2176 new_row_idx,
2177 A,
2178 stack,
2179 )
2180 .into_const();
2181 supernodal::factorize_supernodal_numeric_lu(
2182 &mut numeric.row_perm_fwd,
2183 &mut numeric.row_perm_inv,
2184 lu,
2185 A,
2186 AT,
2187 self.col_perm(),
2188 symbolic,
2189 par,
2190 stack,
2191 params,
2192 )?;
2193 },
2194 _ => unreachable!(),
2195 }
2196 Ok(LuRef::new_unchecked(self, numeric))
2197 }
2198}
2199#[track_caller]
2202pub fn factorize_symbolic_lu<I: Index>(
2203 A: SymbolicSparseColMatRef<'_, I>,
2204 params: LuSymbolicParams<'_>,
2205) -> Result<SymbolicLu<I>, FaerError> {
2206 assert!(A.nrows() == A.ncols());
2207 let m = A.nrows();
2208 let n = A.ncols();
2209 let A_nnz = A.compute_nnz();
2210 with_dim!(M, m);
2211 with_dim!(N, n);
2212 let A = A.as_shape(M, N);
2213 let req = {
2214 let n_scratch = StackReq::new::<I>(n);
2215 let m_scratch = StackReq::new::<I>(m);
2216 let AT_scratch =
2217 StackReq::and(StackReq::new::<I>(m + 1), StackReq::new::<I>(A_nnz));
2218 StackReq::or(
2219 linalg_sp::colamd::order_scratch::<I>(m, n, A_nnz),
2220 StackReq::all_of(&[
2221 n_scratch,
2222 n_scratch,
2223 n_scratch,
2224 n_scratch,
2225 AT_scratch,
2226 StackReq::any_of(&[
2227 StackReq::and(n_scratch, m_scratch),
2228 StackReq::all_of(&[n_scratch; 3]),
2229 StackReq::all_of(&[
2230 n_scratch, n_scratch, n_scratch, n_scratch, n_scratch,
2231 m_scratch,
2232 ]),
2233 supernodal::factorize_supernodal_symbolic_lu_scratch::<I>(
2234 m, n,
2235 ),
2236 ]),
2237 ]),
2238 )
2239 };
2240 let mut mem = dyn_stack::MemBuffer::try_new(req)
2241 .ok()
2242 .ok_or(FaerError::OutOfMemory)?;
2243 let stack = MemStack::new(&mut mem);
2244 let mut col_perm_fwd = try_zeroed::<I>(n)?;
2245 let mut col_perm_inv = try_zeroed::<I>(n)?;
2246 let mut min_row = try_zeroed::<I>(m)?;
2247 linalg_sp::colamd::order(
2248 &mut col_perm_fwd,
2249 &mut col_perm_inv,
2250 A.as_dyn(),
2251 params.colamd_params,
2252 stack,
2253 )?;
2254 let col_perm =
2255 PermRef::new_checked(&col_perm_fwd, &col_perm_inv, n).as_shape(N);
2256 let (new_col_ptr, stack) = unsafe { stack.make_raw::<I>(m + 1) };
2257 let (new_row_idx, stack) = unsafe { stack.make_raw::<I>(A_nnz) };
2258 let AT = utils::adjoint(
2259 Symbolic::materialize(new_row_idx.len()),
2260 new_col_ptr,
2261 new_row_idx,
2262 SparseColMatRef::new(A, Symbolic::materialize(A.row_idx().len())),
2263 stack,
2264 )
2265 .symbolic();
2266 let (etree, stack) = unsafe { stack.make_raw::<I::Signed>(n) };
2267 let (post, stack) = unsafe { stack.make_raw::<I>(n) };
2268 let (col_counts, stack) = unsafe { stack.make_raw::<I>(n) };
2269 let (h_col_counts, stack) = unsafe { stack.make_raw::<I>(n) };
2270 linalg_sp::qr::ghost_col_etree(
2271 A,
2272 Some(col_perm),
2273 Array::from_mut(etree, N),
2274 stack,
2275 );
2276 let etree_ =
2277 Array::from_ref(MaybeIdx::<'_, I>::from_slice_ref_checked(etree, N), N);
2278 linalg_sp::cholesky::ghost_postorder(
2279 Array::from_mut(post, N),
2280 etree_,
2281 stack,
2282 );
2283 linalg_sp::qr::ghost_column_counts_aat(
2284 Array::from_mut(col_counts, N),
2285 Array::from_mut(bytemuck::cast_slice_mut(&mut min_row), M),
2286 AT,
2287 Some(col_perm),
2288 etree_,
2289 Array::from_ref(Idx::from_slice_ref_checked(post, N), N),
2290 stack,
2291 );
2292 let min_col = min_row;
2293 let mut threshold = params.supernodal_flop_ratio_threshold;
2294 if threshold != SupernodalThreshold::FORCE_SIMPLICIAL
2295 && threshold != SupernodalThreshold::FORCE_SUPERNODAL
2296 {
2297 h_col_counts.fill(I::truncate(0));
2298 for i in 0..m {
2299 let min_col = min_col[i];
2300 if min_col.to_signed() < I::Signed::truncate(0) {
2301 continue;
2302 }
2303 h_col_counts[min_col.zx()] += I::truncate(1);
2304 }
2305 for j in 0..n {
2306 let parent = etree[j];
2307 if parent < I::Signed::truncate(0) {
2308 continue;
2309 }
2310 h_col_counts[parent.zx()] +=
2311 I::truncate(h_col_counts[j].zx().saturating_sub(1));
2312 }
2313 let mut nnz = 0.0f64;
2314 let mut flops = 0.0f64;
2315 for j in 0..n {
2316 let hj = h_col_counts[j].zx() as f64;
2317 let rj = col_counts[j].zx() as f64;
2318 flops += hj + hj * rj;
2319 nnz += hj + rj;
2320 }
2321 if flops / nnz > threshold.0 * linalg_sp::LU_SUPERNODAL_RATIO_FACTOR {
2322 threshold = SupernodalThreshold::FORCE_SUPERNODAL;
2323 } else {
2324 threshold = SupernodalThreshold::FORCE_SIMPLICIAL;
2325 }
2326 }
2327 if threshold == SupernodalThreshold::FORCE_SUPERNODAL {
2328 let symbolic = supernodal::factorize_supernodal_symbolic_lu::<I>(
2329 A.as_dyn(),
2330 Some(col_perm.as_shape(n)),
2331 &min_col,
2332 EliminationTreeRef::<'_, I> { inner: etree },
2333 col_counts,
2334 stack,
2335 params.supernodal_params,
2336 )?;
2337 Ok(SymbolicLu {
2338 raw: SymbolicLuRaw::Supernodal(symbolic),
2339 col_perm_fwd,
2340 col_perm_inv,
2341 A_nnz,
2342 })
2343 } else {
2344 Ok(SymbolicLu {
2345 raw: SymbolicLuRaw::Simplicial { nrows: m, ncols: n },
2346 col_perm_fwd,
2347 col_perm_inv,
2348 A_nnz,
2349 })
2350 }
2351}
2352#[cfg(test)]
2353mod tests {
2354 use super::*;
2355 use crate::assert;
2356 use crate::stats::prelude::*;
2357 use dyn_stack::MemBuffer;
2358 use linalg_sp::cholesky::tests::load_mtx;
2359 use matrix_market_rs::MtxData;
2360 use std::path::PathBuf;
2361
2362 #[test]
2363 fn test_numeric_lu_multifrontal() {
2364 type T = c64;
2365 let (m, n, col_ptr, row_idx, val) = load_mtx::<usize>(
2366 MtxData::from_file(
2367 PathBuf::from(env!("CARGO_MANIFEST_DIR"))
2368 .join("test_data/sparse_lu/YAO.mtx"),
2369 )
2370 .unwrap(),
2371 );
2372 let mut rng = StdRng::seed_from_u64(0);
2373 let mut gen = || T::new(rng.random::<f64>(), rng.random::<f64>());
2374 let val = val.iter().map(|_| gen()).collect::<alloc::vec::Vec<_>>();
2375 let A = SparseColMatRef::<'_, usize, T>::new(
2376 SymbolicSparseColMatRef::new_checked(
2377 m, n, &col_ptr, None, &row_idx,
2378 ),
2379 &val,
2380 );
2381 let mut row_perm = vec![0usize; n];
2382 let mut row_perm_inv = vec![0usize; n];
2383 let mut col_perm = vec![0usize; n];
2384 let mut col_perm_inv = vec![0usize; n];
2385 for i in 0..n {
2386 col_perm[i] = i;
2387 col_perm_inv[i] = i;
2388 }
2389 let col_perm =
2390 PermRef::<'_, usize>::new_checked(&col_perm, &col_perm_inv, n);
2391 let mut etree = vec![0usize; n];
2392 let mut min_col = vec![0usize; m];
2393 let mut col_counts = vec![0usize; n];
2394 let nnz = A.compute_nnz();
2395 let mut new_col_ptr = vec![0usize; m + 1];
2396 let mut new_row_idx = vec![0usize; nnz];
2397 let mut new_values = vec![zero::<T>(); nnz];
2398 let AT = utils::transpose(
2399 &mut *new_values,
2400 &mut new_col_ptr,
2401 &mut new_row_idx,
2402 A,
2403 MemStack::new(&mut MemBuffer::new(StackReq::new::<usize>(m))),
2404 )
2405 .into_const();
2406 let etree = {
2407 let mut post = vec![0usize; n];
2408 let etree = linalg_sp::qr::col_etree(
2409 A.symbolic(),
2410 Some(col_perm),
2411 &mut etree,
2412 MemStack::new(&mut MemBuffer::new(StackReq::new::<usize>(
2413 m + n,
2414 ))),
2415 );
2416 linalg_sp::qr::postorder(
2417 &mut post,
2418 etree,
2419 MemStack::new(&mut MemBuffer::new(StackReq::new::<usize>(
2420 3 * n,
2421 ))),
2422 );
2423 linalg_sp::qr::column_counts_ata(
2424 &mut col_counts,
2425 &mut min_col,
2426 AT.symbolic(),
2427 Some(col_perm),
2428 etree,
2429 &post,
2430 MemStack::new(&mut MemBuffer::new(StackReq::new::<usize>(
2431 5 * n + m,
2432 ))),
2433 );
2434 etree
2435 };
2436 let symbolic = linalg_sp::lu::supernodal::factorize_supernodal_symbolic_lu::<usize>(
2437 A.symbolic(),
2438 Some(col_perm),
2439 &min_col,
2440 etree,
2441 &col_counts,
2442 MemStack::new(&mut MemBuffer::new(super::supernodal::factorize_supernodal_symbolic_lu_scratch::<usize>(
2443 m, n,
2444 ))),
2445 linalg_sp::SymbolicSupernodalParams {
2446 relax: Some(&[(4, 1.0), (16, 0.8), (48, 0.1), (usize::MAX, 0.05)]),
2447 },
2448 )
2449 .unwrap();
2450 let mut lu = supernodal::SupernodalLu::<usize, T>::new();
2451 supernodal::factorize_supernodal_numeric_lu(
2452 &mut row_perm,
2453 &mut row_perm_inv,
2454 &mut lu,
2455 A,
2456 AT,
2457 col_perm,
2458 &symbolic,
2459 Par::Seq,
2460 MemStack::new(&mut MemBuffer::new(
2461 supernodal::factorize_supernodal_numeric_lu_scratch::<usize, T>(
2462 &symbolic,
2463 Default::default(),
2464 ),
2465 )),
2466 Default::default(),
2467 )
2468 .unwrap();
2469 let k = 2;
2470 let rhs = Mat::from_fn(n, k, |_, _| gen());
2471 let A_dense = A.to_dense();
2472 let row_perm =
2473 PermRef::<'_, _>::new_checked(&row_perm, &row_perm_inv, m);
2474 {
2475 let mut x = rhs.clone();
2476 lu.solve_in_place_with_conj(
2477 row_perm,
2478 col_perm,
2479 Conj::No,
2480 x.as_mut(),
2481 Par::Seq,
2482 MemStack::new(&mut MemBuffer::new(
2483 supernodal::solve_in_place_scratch::<usize, T>(
2484 n,
2485 k,
2486 ParShape::Seq,
2487 ),
2488 )),
2489 );
2490 assert!((&A_dense * &x - &rhs).norm_max() < 1e-10);
2491 }
2492 {
2493 let mut x = rhs.clone();
2494 lu.solve_in_place_with_conj(
2495 row_perm,
2496 col_perm,
2497 Conj::Yes,
2498 x.as_mut(),
2499 Par::Seq,
2500 MemStack::new(&mut MemBuffer::new(
2501 supernodal::solve_in_place_scratch::<usize, T>(
2502 n,
2503 k,
2504 ParShape::Seq,
2505 ),
2506 )),
2507 );
2508 assert!((A_dense.conjugate() * &x - &rhs).norm_max() < 1e-10);
2509 }
2510 {
2511 let mut x = rhs.clone();
2512 lu.solve_transpose_in_place_with_conj(
2513 row_perm,
2514 col_perm,
2515 Conj::No,
2516 x.as_mut(),
2517 Par::Seq,
2518 MemStack::new(&mut MemBuffer::new(
2519 supernodal::solve_transpose_in_place_scratch::<usize, T>(
2520 n,
2521 k,
2522 ParShape::Seq,
2523 ),
2524 )),
2525 );
2526 assert!((A_dense.transpose() * &x - &rhs).norm_max() < 1e-10);
2527 }
2528 {
2529 let mut x = rhs.clone();
2530 lu.solve_transpose_in_place_with_conj(
2531 row_perm,
2532 col_perm,
2533 Conj::Yes,
2534 x.as_mut(),
2535 Par::Seq,
2536 MemStack::new(&mut MemBuffer::new(
2537 supernodal::solve_transpose_in_place_scratch::<usize, T>(
2538 n,
2539 k,
2540 ParShape::Seq,
2541 ),
2542 )),
2543 );
2544 assert!((A_dense.adjoint() * &x - &rhs).norm_max() < 1e-10);
2545 }
2546 }
2547
2548 #[test]
2549 fn test_numeric_lu_simplicial() {
2550 type T = c64;
2551 let (m, n, col_ptr, row_idx, val) = load_mtx::<usize>(
2552 MtxData::from_file(
2553 PathBuf::from(env!("CARGO_MANIFEST_DIR"))
2554 .join("test_data/sparse_lu/YAO.mtx"),
2555 )
2556 .unwrap(),
2557 );
2558 let mut rng = StdRng::seed_from_u64(0);
2559 let mut gen = || T::new(rng.random::<f64>(), rng.random::<f64>());
2560 let val = val.iter().map(|_| gen()).collect::<alloc::vec::Vec<_>>();
2561 let A = SparseColMatRef::<'_, usize, T>::new(
2562 SymbolicSparseColMatRef::new_checked(
2563 m, n, &col_ptr, None, &row_idx,
2564 ),
2565 &val,
2566 );
2567 let mut row_perm = vec![0usize; n];
2568 let mut row_perm_inv = vec![0usize; n];
2569 let mut col_perm = vec![0usize; n];
2570 let mut col_perm_inv = vec![0usize; n];
2571 for i in 0..n {
2572 col_perm[i] = i;
2573 col_perm_inv[i] = i;
2574 }
2575 let col_perm =
2576 PermRef::<'_, usize>::new_checked(&col_perm, &col_perm_inv, n);
2577 let mut lu = simplicial::SimplicialLu::<usize, T>::new();
2578 simplicial::factorize_simplicial_numeric_lu(
2579 &mut row_perm,
2580 &mut row_perm_inv,
2581 &mut lu,
2582 A,
2583 col_perm,
2584 MemStack::new(&mut MemBuffer::new(
2585 simplicial::factorize_simplicial_numeric_lu_scratch::<usize, T>(
2586 m, n,
2587 ),
2588 )),
2589 )
2590 .unwrap();
2591 let k = 1;
2592 let rhs = Mat::from_fn(n, k, |_, _| gen());
2593 let A_dense = A.to_dense();
2594 let row_perm =
2595 PermRef::<'_, _>::new_checked(&row_perm, &row_perm_inv, m);
2596 {
2597 let mut x = rhs.clone();
2598 lu.solve_in_place_with_conj(
2599 row_perm,
2600 col_perm,
2601 Conj::No,
2602 x.as_mut(),
2603 Par::Seq,
2604 MemStack::new(&mut MemBuffer::new(
2605 simplicial::solve_in_place_scratch::<usize, T>(
2606 n,
2607 k,
2608 ParShape::Seq,
2609 ),
2610 )),
2611 );
2612 assert!((&A_dense * &x - &rhs).norm_max() < 1e-10);
2613 }
2614 {
2615 let mut x = rhs.clone();
2616 lu.solve_in_place_with_conj(
2617 row_perm,
2618 col_perm,
2619 Conj::Yes,
2620 x.as_mut(),
2621 Par::Seq,
2622 MemStack::new(&mut MemBuffer::new(
2623 simplicial::solve_in_place_scratch::<usize, T>(
2624 n,
2625 k,
2626 ParShape::Seq,
2627 ),
2628 )),
2629 );
2630 assert!((A_dense.conjugate() * &x - &rhs).norm_max() < 1e-10);
2631 }
2632 {
2633 let mut x = rhs.clone();
2634 lu.solve_transpose_in_place_with_conj(
2635 row_perm,
2636 col_perm,
2637 Conj::No,
2638 x.as_mut(),
2639 Par::Seq,
2640 MemStack::new(&mut MemBuffer::new(
2641 simplicial::solve_transpose_in_place_scratch::<usize, T>(
2642 n,
2643 k,
2644 ParShape::Seq,
2645 ),
2646 )),
2647 );
2648 assert!((A_dense.transpose() * &x - &rhs).norm_max() < 1e-10);
2649 }
2650 {
2651 let mut x = rhs.clone();
2652 lu.solve_transpose_in_place_with_conj(
2653 row_perm,
2654 col_perm,
2655 Conj::Yes,
2656 x.as_mut(),
2657 Par::Seq,
2658 MemStack::new(&mut MemBuffer::new(
2659 simplicial::solve_transpose_in_place_scratch::<usize, T>(
2660 n,
2661 k,
2662 ParShape::Seq,
2663 ),
2664 )),
2665 );
2666 assert!((A_dense.adjoint() * &x - &rhs).norm_max() < 1e-10);
2667 }
2668 }
2669
2670 #[test]
2671 fn test_solver_lu_simplicial() {
2672 type T = c64;
2673 let (m, n, col_ptr, row_idx, val) = load_mtx::<usize>(
2674 MtxData::from_file(
2675 PathBuf::from(env!("CARGO_MANIFEST_DIR"))
2676 .join("test_data/sparse_lu/YAO.mtx"),
2677 )
2678 .unwrap(),
2679 );
2680 let mut rng = StdRng::seed_from_u64(0);
2681 let mut gen = || T::new(rng.random::<f64>(), rng.random::<f64>());
2682 let val = val.iter().map(|_| gen()).collect::<alloc::vec::Vec<_>>();
2683 let A = SparseColMatRef::<'_, usize, T>::new(
2684 SymbolicSparseColMatRef::new_checked(
2685 m, n, &col_ptr, None, &row_idx,
2686 ),
2687 &val,
2688 );
2689 let rhs = Mat::<T>::from_fn(m, 6, |_, _| gen());
2690 for supernodal_flop_ratio_threshold in [
2691 SupernodalThreshold::AUTO,
2692 SupernodalThreshold::FORCE_SUPERNODAL,
2693 SupernodalThreshold::FORCE_SIMPLICIAL,
2694 ] {
2695 let symbolic = factorize_symbolic_lu(
2696 A.symbolic(),
2697 LuSymbolicParams {
2698 supernodal_flop_ratio_threshold,
2699 ..Default::default()
2700 },
2701 )
2702 .unwrap();
2703 let mut numeric = NumericLu::<usize, T>::new();
2704 let lu = symbolic
2705 .factorize_numeric_lu(
2706 &mut numeric,
2707 A,
2708 Par::Seq,
2709 MemStack::new(&mut MemBuffer::new(
2710 symbolic.factorize_numeric_lu_scratch::<T>(
2711 ParShape::Seq,
2712 Default::default(),
2713 ),
2714 )),
2715 Default::default(),
2716 )
2717 .unwrap();
2718 {
2719 let mut x = rhs.clone();
2720 lu.solve_in_place_with_conj(
2721 crate::Conj::No,
2722 x.as_mut(),
2723 Par::Seq,
2724 MemStack::new(&mut MemBuffer::new(
2725 symbolic
2726 .solve_in_place_scratch::<T>(rhs.ncols(), ParShape::Seq),
2727 )),
2728 );
2729 let linsolve_diff = A * &x - &rhs;
2730 assert!(linsolve_diff.norm_max() <= 1e-10);
2731 }
2732 {
2733 let mut x = rhs.clone();
2734 lu.solve_in_place_with_conj(
2735 crate::Conj::Yes,
2736 x.as_mut(),
2737 Par::Seq,
2738 MemStack::new(&mut MemBuffer::new(
2739 symbolic
2740 .solve_in_place_scratch::<T>(rhs.ncols(), ParShape::Seq),
2741 )),
2742 );
2743 let linsolve_diff = A.conjugate() * &x - &rhs;
2744 assert!(linsolve_diff.norm_max() <= 1e-10);
2745 }
2746 {
2747 let mut x = rhs.clone();
2748 lu.solve_transpose_in_place_with_conj(
2749 crate::Conj::No,
2750 x.as_mut(),
2751 Par::Seq,
2752 MemStack::new(&mut MemBuffer::new(
2753 symbolic.solve_transpose_in_place_scratch::<T>(
2754 rhs.ncols(),
2755 ParShape::Seq,
2756 ),
2757 )),
2758 );
2759 let linsolve_diff = A.transpose() * &x - &rhs;
2760 assert!(linsolve_diff.norm_max() <= 1e-10);
2761 }
2762 {
2763 let mut x = rhs.clone();
2764 lu.solve_transpose_in_place_with_conj(
2765 crate::Conj::Yes,
2766 x.as_mut(),
2767 Par::Seq,
2768 MemStack::new(&mut MemBuffer::new(
2769 symbolic.solve_transpose_in_place_scratch::<T>(
2770 rhs.ncols(),
2771 ParShape::Seq,
2772 ),
2773 )),
2774 );
2775 let linsolve_diff = A.adjoint() * &x - &rhs;
2776 assert!(linsolve_diff.norm_max() <= 1e-10);
2777 }
2778 }
2779 }
2780 fn factorize_lu_error<T: ComplexField>(
2781 A: SparseColMatRef<'_, usize, T>,
2782 supernodal_flop_ratio_threshold: SupernodalThreshold,
2783 ) -> Result<(), LuError> {
2784 let symbolic = factorize_symbolic_lu(
2785 A.symbolic(),
2786 LuSymbolicParams {
2787 supernodal_flop_ratio_threshold,
2788 ..Default::default()
2789 },
2790 )
2791 .unwrap();
2792 let mut numeric = NumericLu::<usize, T>::new();
2793 symbolic
2794 .factorize_numeric_lu(
2795 &mut numeric,
2796 A,
2797 Par::Seq,
2798 MemStack::new(&mut MemBuffer::new(
2799 symbolic.factorize_numeric_lu_scratch::<T>(
2800 ParShape::Seq,
2801 Default::default(),
2802 ),
2803 )),
2804 Default::default(),
2805 )
2806 .map(|_| ())
2807 }
2808 fn numeric_singular_index<T: ComplexField>(
2809 n: usize,
2810 entries: &[(usize, usize, f64)],
2811 supernodal_flop_ratio_threshold: SupernodalThreshold,
2812 ) -> Option<usize> {
2813 let triplets = entries
2814 .iter()
2815 .map(|&(i, j, v)| Triplet::new(i, j, from_f64::<T>(v)))
2816 .collect::<alloc::vec::Vec<_>>();
2817 let A =
2818 SparseColMat::<usize, T>::try_new_from_triplets(n, n, &triplets)
2819 .unwrap();
2820 match factorize_lu_error(A.as_ref(), supernodal_flop_ratio_threshold) {
2821 Err(LuError::NumericSingular { index }) => Some(index),
2822 Ok(()) => None,
2823 Err(e) => panic!("unexpected error {e:?}"),
2824 }
2825 }
2826 #[test]
2827 fn codeberg_339() {
2828 let A = [(0, 0, 1.0), (0, 1, 1.0), (1, 0, 1.0), (1, 1, 1.0)];
2832 for threshold in [
2833 SupernodalThreshold::FORCE_SIMPLICIAL,
2834 SupernodalThreshold::FORCE_SUPERNODAL,
2835 ] {
2836 assert!(numeric_singular_index::<f64>(2, &A, threshold) == Some(1));
2837 assert!(numeric_singular_index::<f32>(2, &A, threshold) == Some(1));
2838 assert!(numeric_singular_index::<c64>(2, &A, threshold) == Some(1));
2839 assert!(numeric_singular_index::<c32>(2, &A, threshold) == Some(1));
2840 }
2841 }
2842 #[test]
2843 fn numeric_singular_after_an_elimination_step() {
2844 let A = [
2848 (0, 0, 2.0),
2849 (1, 0, 4.0),
2850 (0, 1, 1.0),
2851 (1, 1, 2.0),
2852 (2, 2, 1.0),
2853 (0, 3, 1.0),
2854 (3, 3, 1.0),
2855 (2, 3, 1.0),
2856 ];
2857 let B = [
2859 (0, 0, 2.0),
2860 (1, 0, 4.0),
2861 (0, 1, 1.0),
2862 (1, 1, 3.0),
2863 (2, 2, 1.0),
2864 (0, 3, 1.0),
2865 (3, 3, 1.0),
2866 (2, 3, 1.0),
2867 ];
2868 for threshold in [
2869 SupernodalThreshold::FORCE_SIMPLICIAL,
2870 SupernodalThreshold::FORCE_SUPERNODAL,
2871 ] {
2872 assert!(matches!(
2873 numeric_singular_index::<f64>(4, &A, threshold),
2874 Some(index) if index < 4
2875 ));
2876 assert!(numeric_singular_index::<f64>(4, &B, threshold) == None);
2877 }
2878 }
2879 #[test]
2880 fn nonzero_subnormal_pivot_is_not_singular() {
2881 let tiny = f64::from_bits(1);
2884 let triplets = [Triplet::new(0, 0, c64::new(tiny, 0.0))];
2885 let A =
2886 SparseColMat::<usize, c64>::try_new_from_triplets(1, 1, &triplets)
2887 .unwrap();
2888 for threshold in [
2889 SupernodalThreshold::FORCE_SIMPLICIAL,
2890 SupernodalThreshold::FORCE_SUPERNODAL,
2891 ] {
2892 assert!(factorize_lu_error(A.as_ref(), threshold).is_ok());
2893 }
2894 }
2895}