1use std::ops::Deref;
55
56use num_traits::Num;
57
58use sprs::errors::{LinalgError, SingularMatrixInfo};
59use sprs::indexing::SpIndex;
60use sprs::linalg;
61use sprs::stack::DStack;
62use sprs::{is_symmetric, CsMatViewI, PermOwnedI, Permutation};
63use sprs::{DenseVector, DenseVectorMut};
64use sprs::{FillInReduction, PermutationCheck, SymmetryCheck};
65
66#[cfg(feature = "sprs_suitesparse_ldl")]
67use sprs_suitesparse_ldl::LdlNumeric as LdlNumericC;
68#[cfg(feature = "sprs_suitesparse_ldl")]
69use sprs_suitesparse_ldl::LdlSymbolic as LdlSymbolicC;
70#[cfg(feature = "sprs_suitesparse_ldl")]
71use sprs_suitesparse_ldl::{LdlLongNumeric, LdlLongSymbolic};
72
73#[derive(Copy, Clone, Eq, PartialEq, Debug)]
75pub struct Ldl {
76 check_symmetry: SymmetryCheck,
77 check_perm: PermutationCheck,
78 fill_red_method: FillInReduction,
79}
80
81impl Default for Ldl {
82 fn default() -> Self {
83 Self {
84 check_symmetry: SymmetryCheck::CheckSymmetry,
85 fill_red_method: FillInReduction::ReverseCuthillMcKee,
86 check_perm: PermutationCheck::CheckPerm,
87 }
88 }
89}
90
91#[derive(Debug, Clone)]
93pub struct LdlSymbolic<I> {
94 colptr: Vec<I>,
95 parents: linalg::etree::ParentsOwned,
96 nz: Vec<I>,
97 flag_workspace: Vec<I>,
98 perm: Permutation<I, Vec<I>>,
99}
100
101#[derive(Debug, Clone)]
103pub struct LdlNumeric<N, I> {
104 symbolic: LdlSymbolic<I>,
105 l_indices: Vec<I>,
106 l_data: Vec<N>,
107 diag: Vec<N>,
108 y_workspace: Vec<N>,
109 pattern_workspace: DStack<I>,
110}
111
112impl Ldl {
113 pub fn new() -> Self {
114 Self::default()
115 }
116
117 pub fn check_symmetry(self, check: SymmetryCheck) -> Self {
118 Self {
119 check_symmetry: check,
120 ..self
121 }
122 }
123
124 pub fn check_perm(self, check: PermutationCheck) -> Self {
125 Self {
126 check_perm: check,
127 ..self
128 }
129 }
130
131 pub fn fill_in_reduction(self, method: FillInReduction) -> Self {
132 Self {
133 fill_red_method: method,
134 ..self
135 }
136 }
137
138 pub fn perm<N, I>(&self, mat: CsMatViewI<N, I>) -> PermOwnedI<I>
139 where
140 I: SpIndex,
141 {
142 match self.fill_red_method {
143 FillInReduction::NoReduction => PermOwnedI::identity(mat.rows()),
144 FillInReduction::ReverseCuthillMcKee => {
145 sprs::linalg::reverse_cuthill_mckee(mat.structure_view()).perm
146 }
147 FillInReduction::CAMDSuiteSparse => {
148 #[cfg(not(feature = "sprs_suitesparse_camd"))]
149 panic!(
150 "Unavailable without the `sprs_suitesparse_camd` feature"
151 );
152 #[cfg(feature = "sprs_suitesparse_camd")]
153 sprs_suitesparse_camd::camd(mat.structure_view())
154 }
155 _ => {
156 unreachable!(
157 "Unhandled method, report a bug at https://github.com/vbarrielle/sprs/issues/199"
158 )
159 }
160 }
161 }
162
163 pub fn symbolic<N, I>(self, mat: CsMatViewI<N, I>) -> LdlSymbolic<I>
164 where
165 I: SpIndex,
166 N: Copy + PartialEq,
167 {
168 LdlSymbolic::new_perm(mat, self.perm(mat), self.check_symmetry)
169 }
170
171 #[cfg(feature = "sprs_suitesparse_ldl")]
172 pub fn symbolic_c<N, I>(self, mat: CsMatViewI<N, I>) -> LdlSymbolicC
173 where
174 I: SpIndex,
175 N: Copy + PartialEq + Into<f64>,
176 {
177 LdlSymbolicC::new_perm(mat, self.perm(mat), self.check_perm)
178 }
179
180 #[cfg(feature = "sprs_suitesparse_ldl")]
181 pub fn symbolic_c_long<N, I>(self, mat: CsMatViewI<N, I>) -> LdlLongSymbolic
182 where
183 I: SpIndex,
184 N: Copy + PartialEq + Into<f64>,
185 {
186 LdlLongSymbolic::new_perm(mat, self.perm(mat), self.check_perm)
187 }
188
189 pub fn numeric<N, I>(
190 self,
191 mat: CsMatViewI<N, I>,
192 ) -> Result<LdlNumeric<N, I>, LinalgError>
193 where
194 I: SpIndex,
195 N: Copy + Num + PartialOrd,
196 {
197 let symb = self.symbolic(mat);
199 symb.factor(mat)
200 }
201
202 #[cfg(feature = "sprs_suitesparse_ldl")]
203 pub fn numeric_c<N, I>(
204 self,
205 mat: CsMatViewI<N, I>,
206 ) -> Result<LdlNumericC, LinalgError>
207 where
208 I: SpIndex,
209 N: Copy + Num + PartialOrd + Into<f64>,
210 {
211 self.symbolic_c(mat).factor(mat)
212 }
213
214 #[cfg(feature = "sprs_suitesparse_ldl")]
215 pub fn numeric_c_long<N, I>(
216 self,
217 mat: CsMatViewI<N, I>,
218 ) -> Result<LdlLongNumeric, LinalgError>
219 where
220 I: SpIndex,
221 N: Copy + Num + PartialOrd + Into<f64>,
222 {
223 self.symbolic_c_long(mat).factor(mat)
224 }
225}
226
227impl<I: SpIndex> LdlSymbolic<I> {
228 pub fn new<N>(mat: CsMatViewI<N, I>) -> Self
234 where
235 N: Copy + PartialEq,
236 {
237 assert_eq!(mat.rows(), mat.cols());
238 let perm: Permutation<I, Vec<I>> = Permutation::identity(mat.rows());
239 Self::new_perm(mat, perm, SymmetryCheck::CheckSymmetry)
240 }
241
242 pub fn new_perm<N>(
252 mat: CsMatViewI<N, I>,
253 perm: PermOwnedI<I>,
254 check_symmetry: SymmetryCheck,
255 ) -> Self
256 where
257 N: Copy + PartialEq,
258 I: SpIndex,
259 {
260 let n = mat.cols();
261 assert!(mat.rows() == n, "matrix should be square");
262 let mut l_colptr = vec![I::zero(); n + 1];
263 let mut parents = linalg::etree::ParentsOwned::new(n);
264 let mut l_nz = vec![I::zero(); n];
265 let mut flag_workspace = vec![I::zero(); n];
266 ldl_symbolic(
267 mat,
268 &perm,
269 &mut l_colptr,
270 parents.view_mut(),
271 &mut l_nz,
272 &mut flag_workspace,
273 check_symmetry,
274 );
275
276 Self {
277 colptr: l_colptr,
278 parents,
279 nz: l_nz,
280 flag_workspace,
281 perm,
282 }
283 }
284
285 #[inline]
287 pub fn problem_size(&self) -> usize {
288 self.parents.nb_nodes()
289 }
290
291 #[inline]
293 pub fn nnz(&self) -> usize {
294 let n = self.problem_size();
295 self.colptr[n].index()
296 }
297
298 pub fn factor<N>(
300 self,
301 mat: CsMatViewI<N, I>,
302 ) -> Result<LdlNumeric<N, I>, LinalgError>
303 where
304 N: Copy + Num + PartialOrd,
305 {
306 let n = self.problem_size();
307 let nnz = self.nnz();
308 let l_indices = vec![I::zero(); nnz];
309 let l_data = vec![N::zero(); nnz];
310 let diag = vec![N::zero(); n];
311 let y_workspace = vec![N::zero(); n];
312 let pattern_workspace = DStack::with_capacity(n);
313 let mut ldl_numeric = LdlNumeric {
314 symbolic: self,
315 l_indices,
316 l_data,
317 diag,
318 y_workspace,
319 pattern_workspace,
320 };
321 ldl_numeric.update(mat).map(|_| ldl_numeric)
322 }
323}
324
325impl<N, I: SpIndex> LdlNumeric<N, I> {
326 pub fn new(mat: CsMatViewI<N, I>) -> Result<Self, LinalgError>
332 where
333 N: Copy + Num + PartialOrd,
334 {
335 let symbolic = LdlSymbolic::new(mat.view());
336 symbolic.factor(mat)
337 }
338
339 pub fn new_perm(
349 mat: CsMatViewI<N, I>,
350 perm: PermOwnedI<I>,
351 check_symmetry: SymmetryCheck,
352 ) -> Result<Self, LinalgError>
353 where
354 N: Copy + Num + PartialOrd,
355 {
356 let symbolic = LdlSymbolic::new_perm(mat.view(), perm, check_symmetry);
357 symbolic.factor(mat)
358 }
359
360 pub fn update(&mut self, mat: CsMatViewI<N, I>) -> Result<(), LinalgError>
364 where
365 N: Copy + Num + PartialOrd,
366 {
367 ldl_numeric(
368 mat.view(),
369 &self.symbolic.colptr,
370 self.symbolic.parents.view(),
371 &self.symbolic.perm,
372 &mut self.symbolic.nz,
373 &mut self.l_indices,
374 &mut self.l_data,
375 &mut self.diag,
376 &mut self.y_workspace,
377 &mut self.pattern_workspace,
378 &mut self.symbolic.flag_workspace,
379 )
380 }
381
382 pub fn solve<'a, V>(
388 &self,
389 rhs: V,
390 ) -> <<V as DenseVector>::Owned as DenseVector>::Owned
391 where
392 N: 'a + Copy + Num + std::ops::SubAssign + std::ops::DivAssign,
393 N: for<'r> std::ops::DivAssign<&'r N>,
394 V: DenseVector<Scalar = N>,
395 <V as DenseVector>::Owned: DenseVectorMut + DenseVector<Scalar = N>,
396 for<'b> &'b <V as DenseVector>::Owned: DenseVector<Scalar = N>,
397 for<'b> &'b mut <V as DenseVector>::Owned:
398 DenseVectorMut + DenseVector<Scalar = N>,
399 <<V as DenseVector>::Owned as DenseVector>::Owned:
400 DenseVectorMut + DenseVector<Scalar = N>,
401 {
402 let mut x = &self.symbolic.perm * rhs;
403 let l = self.l();
404 ldl_lsolve(&l, &mut x);
405 linalg::diag_solve(&self.diag, &mut x);
406 ldl_ltsolve(&l, &mut x);
407 let pinv = self.symbolic.perm.inv();
408 &pinv * x
409 }
410
411 pub fn d(&self) -> &[N] {
413 &self.diag[..]
414 }
415
416 pub fn l(&self) -> CsMatViewI<N, I> {
418 use std::slice::from_raw_parts;
419 let n = self.symbolic.problem_size();
420 unsafe {
422 let indptr = from_raw_parts(self.symbolic.colptr.as_ptr(), n + 1);
423 let nnz = indptr[n].index();
424 let indices = from_raw_parts(self.l_indices.as_ptr(), nnz);
425 let data = from_raw_parts(self.l_data.as_ptr(), nnz);
426 CsMatViewI::new_unchecked(sprs::CSC, (n, n), indptr, indices, data)
427 }
428 }
429
430 #[inline]
432 pub fn problem_size(&self) -> usize {
433 self.symbolic.problem_size()
434 }
435
436 #[inline]
438 pub fn nnz(&self) -> usize {
439 self.symbolic.nnz()
440 }
441}
442
443pub fn ldl_symbolic<N, I, PStorage>(
445 mat: CsMatViewI<N, I>,
446 perm: &Permutation<I, PStorage>,
447 l_colptr: &mut [I],
448 mut parents: linalg::etree::ParentsViewMut,
449 l_nz: &mut [I],
450 flag_workspace: &mut [I],
451 check_symmetry: SymmetryCheck,
452) where
453 N: Clone + Copy + PartialEq,
454 I: SpIndex,
455 PStorage: Deref<Target = [I]>,
456{
457 match check_symmetry {
458 SymmetryCheck::DontCheckSymmetry => (),
459 SymmetryCheck::CheckSymmetry => {
460 if !is_symmetric(&mat) {
461 panic!("Matrix is not symmetric")
462 }
463 }
464 }
465
466 let n = mat.rows();
467
468 let outer_it = mat.outer_iterator_papt(perm.view());
469 for (k, (_, vec)) in outer_it.enumerate() {
471 flag_workspace[k] = I::from_usize(k); parents.set_root(k);
473 l_nz[k] = I::zero();
474
475 for (inner_ind, _) in vec.iter_perm(perm.inv()) {
476 let mut i = inner_ind;
477
478 if i < k {
479 while flag_workspace[i].index() != k {
480 parents.uproot(i, k);
481 l_nz[i] += I::one();
482 flag_workspace[i] = I::from_usize(k);
483 i = parents.get_parent(i).expect("uprooted so not a root");
484 }
485 }
486 }
487 }
488
489 let mut prev = I::zero();
490 for (k, colptr) in (0..n).zip(l_colptr.iter_mut()) {
491 *colptr = prev;
492 prev += l_nz[k];
493 }
494 l_colptr[n] = prev;
495}
496
497#[allow(clippy::too_many_arguments)]
501pub fn ldl_numeric<N, I, PStorage>(
502 mat: CsMatViewI<N, I>,
503 l_colptr: &[I],
504 parents: linalg::etree::ParentsView,
505 perm: &Permutation<I, PStorage>,
506 l_nz: &mut [I],
507 l_indices: &mut [I],
508 l_data: &mut [N],
509 diag: &mut [N],
510 y_workspace: &mut [N],
511 pattern_workspace: &mut DStack<I>,
512 flag_workspace: &mut [I],
513) -> Result<(), LinalgError>
514where
515 N: Clone + Copy + PartialEq + Num + PartialOrd,
516 I: SpIndex,
517 PStorage: Deref<Target = [I]>,
518{
519 assert!(y_workspace.len() == mat.outer_dims());
520 assert!(diag.len() == mat.outer_dims());
521 let outer_it = mat.outer_iterator_papt(perm.view());
522 for (k, (_, vec)) in outer_it.enumerate() {
523 flag_workspace[k] = I::from_usize(k); y_workspace[k] = N::zero();
528 l_nz[k] = I::zero();
529 pattern_workspace.clear_right();
530
531 for (inner_ind, &val) in
532 vec.iter_perm(perm.inv()).filter(|&(i, _)| i <= k)
533 {
534 y_workspace[inner_ind] = y_workspace[inner_ind] + val;
535 let mut i = inner_ind;
536 pattern_workspace.clear_left();
537 while flag_workspace[i].index_unchecked() != k {
538 pattern_workspace.push_left(I::from_usize(i));
539 flag_workspace[i] = I::from_usize(k);
540 i = parents.get_parent(i).expect("enforced by ldl_symbolic");
541 }
542 pattern_workspace.push_left_on_right();
543 }
544
545 diag[k] = y_workspace[k];
548 y_workspace[k] = N::zero();
549 #[allow(unused_labels)]
550 'pattern: for &i in pattern_workspace.iter_right() {
551 let i = i.index_unchecked();
552 let yi = y_workspace[i];
553 y_workspace[i] = N::zero();
554 let p2 = (l_colptr[i] + l_nz[i]).index();
555 let r0 = l_colptr[i].index()..p2;
556 let r1 = l_colptr[i].index()..p2; for (y_index, lx) in l_indices[r0].iter().zip(l_data[r1].iter()) {
558 let y_index = y_index.index_unchecked();
566 unsafe {
570 let yw = y_workspace.get_unchecked_mut(y_index);
571 *yw = *yw - *lx * yi;
572 }
573 }
574 let di = *unsafe { diag.get_unchecked(i) };
577 let dk = unsafe { diag.get_unchecked_mut(k) };
578 let l_ki = yi / di;
579 *dk = *dk - l_ki * yi;
580 l_indices[p2] = I::from_usize(k);
581 l_data[p2] = l_ki;
582 l_nz[i] += I::one();
583 }
584 if diag[k] == N::zero() {
585 return Err(LinalgError::SingularMatrix(SingularMatrixInfo {
586 index: k,
587 reason: "diagonal element is a numeric 0",
588 }));
589 }
590 }
591 Ok(())
592}
593
594pub fn ldl_lsolve<N, I, V>(l: &CsMatViewI<N, I>, mut x: V)
597where
598 N: Clone + Copy + Num + std::ops::SubAssign,
599 I: SpIndex,
600 V: DenseVectorMut + DenseVector<Scalar = N>,
601{
602 for (col_ind, vec) in l.outer_iterator().enumerate() {
603 let x_col = *x.index(col_ind);
604 for (row_ind, &value) in vec.iter() {
605 *x.index_mut(row_ind) -= value * x_col;
606 }
607 }
608}
609
610pub fn ldl_ltsolve<N, I, V>(l: &CsMatViewI<N, I>, mut x: V)
613where
614 N: Clone + Copy + Num + std::ops::SubAssign,
615 I: SpIndex,
616 V: DenseVectorMut + DenseVector<Scalar = N>,
617{
618 for (outer_ind, vec) in l.outer_iterator().enumerate().rev() {
619 let mut x_outer = *x.index(outer_ind);
620 for (inner_ind, &value) in vec.iter() {
621 x_outer -= value * *x.index(inner_ind);
622 }
623 *x.index_mut(outer_ind) = x_outer;
624 }
625}
626
627#[cfg(test)]
628mod test {
629 use super::SymmetryCheck;
630 use sprs::stack::DStack;
631 use sprs::{self, linalg, CsMat, CsMatView, Permutation};
632
633 fn test_mat1() -> CsMat<f64> {
634 let indptr = vec![0, 2, 5, 6, 7, 13, 14, 17, 20, 24, 28];
635 let indices = vec![
636 0, 8, 1, 4, 9, 2, 3, 1, 4, 6, 7, 8, 9, 5, 4, 6, 9, 4, 7, 8, 0, 4,
637 7, 8, 1, 4, 6, 9,
638 ];
639 let data = vec![
640 1.7, 0.13, 1., 0.02, 0.01, 1.5, 1.1, 0.02, 2.6, 0.16, 0.09, 0.52,
641 0.53, 1.2, 0.16, 1.3, 0.56, 0.09, 1.6, 0.11, 0.13, 0.52, 0.11, 1.4,
642 0.01, 0.53, 0.56, 3.1,
643 ];
644 CsMat::new_csc((10, 10), indptr, indices, data)
645 }
646
647 fn test_vec1() -> Vec<f64> {
648 vec![
649 0.287, 0.22, 0.45, 0.44, 2.486, 0.72, 1.55, 1.424, 1.621, 3.759,
650 ]
651 }
652
653 fn expected_factors1() -> (Vec<usize>, Vec<usize>, Vec<f64>, Vec<f64>) {
654 let expected_lp = vec![0, 1, 3, 3, 3, 7, 7, 10, 12, 13, 13];
655 let expected_li = vec![8, 4, 9, 6, 7, 8, 9, 7, 8, 9, 8, 9, 9];
656 let expected_lx = vec![
657 0.076470588235294124,
658 0.02,
659 0.01,
660 0.061547930450838589,
661 0.034620710878596701,
662 0.20003077396522542,
663 0.20380058470533929,
664 -0.0042935346524025902,
665 -0.024807089102770519,
666 0.40878266366119237,
667 0.05752526570865537,
668 -0.010068305077340346,
669 -0.071852278207562709,
670 ];
671 let expected_d = vec![
672 1.7,
673 1.,
674 1.5,
675 1.1000000000000001,
676 2.5996000000000001,
677 1.2,
678 1.290152331127866,
679 1.5968603527854308,
680 1.2799646117414738,
681 2.7695677698030283,
682 ];
683 (expected_lp, expected_li, expected_lx, expected_d)
684 }
685
686 fn expected_lsolve_res1() -> Vec<f64> {
687 vec![
688 0.28699999999999998,
689 0.22,
690 0.45000000000000001,
691 0.44,
692 2.4816000000000003,
693 0.71999999999999997,
694 1.3972626557931991,
695 1.3440844395148306,
696 1.0599997771886431,
697 2.7695677698030279,
698 ]
699 }
700
701 fn expected_dsolve_res1() -> Vec<f64> {
702 vec![
703 0.16882352941176471,
704 0.22,
705 0.29999999999999999,
706 0.39999999999999997,
707 0.95460840129250657,
708 0.59999999999999998,
709 1.0830214557467768,
710 0.84170443406044937,
711 0.82814772179243734,
712 0.99999999999999989,
713 ]
714 }
715
716 fn expected_res1() -> Vec<f64> {
717 vec![
718 0.099999999999999992,
719 0.19999999999999998,
720 0.29999999999999999,
721 0.39999999999999997,
722 0.5,
723 0.59999999999999998,
724 0.70000000000000007,
725 0.79999999999999993,
726 0.90000000000000002,
727 0.99999999999999989,
728 ]
729 }
730
731 #[test]
732 fn test_factor1() {
733 let mut l_colptr = [0; 11];
734 let mut parents = linalg::etree::ParentsOwned::new(10);
735 let mut l_nz = [0; 10];
736 let mut flag_workspace = [0; 10];
737 let perm: Permutation<usize, &[usize]> = Permutation::identity(10);
738 let mat = test_mat1();
739 super::ldl_symbolic(
740 mat.view(),
741 &perm,
742 &mut l_colptr,
743 parents.view_mut(),
744 &mut l_nz,
745 &mut flag_workspace,
746 SymmetryCheck::CheckSymmetry,
747 );
748
749 let nnz = l_colptr[10];
750 let mut l_indices = vec![0; nnz];
751 let mut l_data = vec![0.; nnz];
752 let mut diag = [0.; 10];
753 let mut y_workspace = [0.; 10];
754 let mut pattern_workspace = DStack::with_capacity(10);
755 super::ldl_numeric(
756 mat.view(),
757 &l_colptr,
758 parents.view(),
759 &perm,
760 &mut l_nz,
761 &mut l_indices,
762 &mut l_data,
763 &mut diag,
764 &mut y_workspace,
765 &mut pattern_workspace,
766 &mut flag_workspace,
767 )
768 .unwrap();
769
770 let (expected_lp, expected_li, expected_lx, expected_d) =
771 expected_factors1();
772
773 assert_eq!(&l_colptr, &expected_lp[..]);
774 assert_eq!(&l_indices, &expected_li);
775 assert_eq!(&l_data, &expected_lx);
776 assert_eq!(&diag, &expected_d[..]);
777 }
778
779 #[test]
780 fn test_solve1() {
781 let (expected_lp, expected_li, expected_lx, expected_d) =
782 expected_factors1();
783 let b = test_vec1();
784 let mut x = b.clone();
785 let n = b.len();
786 let l = CsMatView::new_csc(
787 (n, n),
788 &expected_lp,
789 &expected_li,
790 &expected_lx,
791 );
792 super::ldl_lsolve(&l, &mut x);
793 assert_eq!(&x, &expected_lsolve_res1());
794 linalg::diag_solve(&expected_d, &mut x);
795 assert_eq!(&x, &expected_dsolve_res1());
796 super::ldl_ltsolve(&l, &mut x);
797
798 let x0 = expected_res1();
799 assert_eq!(x, x0);
800 }
801
802 #[test]
803 fn test_factor_solve1() {
804 let mat = test_mat1();
805 let b = test_vec1();
806 let ldlt = super::LdlNumeric::new(mat.view()).unwrap();
807 let x = ldlt.solve(&b);
808 let x0 = expected_res1();
809 assert_eq!(x, x0);
810 }
811
812 #[test]
813 fn permuted_ldl_solve() {
814 let mat = CsMat::new_csc(
826 (4, 4),
827 vec![0, 2, 4, 6, 8],
828 vec![0, 3, 1, 2, 1, 2, 0, 3],
829 vec![1, 2, 21, 6, 6, 2, 2, 8],
830 );
831
832 let perm = Permutation::new(vec![0, 2, 1, 3]);
833
834 let ldlt = super::LdlNumeric::new_perm(
835 mat.view(),
836 perm,
837 super::SymmetryCheck::CheckSymmetry,
838 )
839 .unwrap();
840 let b = vec![9, 60, 18, 34];
841 let x0 = vec![1, 2, 3, 4];
842 let x = ldlt.solve(&b);
843 assert_eq!(x, x0);
844 }
845
846 #[test]
847 fn cuthill_ldl_solve() {
848 let mat = CsMat::new_csc(
849 (4, 4),
850 vec![0, 2, 4, 6, 8],
851 vec![0, 3, 1, 2, 1, 2, 0, 3],
852 vec![1., 2., 21., 6., 6., 2., 2., 8.],
853 );
854
855 let b = ndarray::arr1(&[9., 60., 18., 34.]);
856 let x0 = ndarray::arr1(&[1., 2., 3., 4.]);
857
858 let ldlt = super::Ldl::new()
859 .check_symmetry(super::SymmetryCheck::DontCheckSymmetry)
860 .fill_in_reduction(super::FillInReduction::ReverseCuthillMcKee)
861 .numeric(mat.view())
862 .unwrap();
863 let x = ldlt.solve(b.view());
864 assert_eq!(x, x0);
865 }
866
867 #[cfg(feature = "sprs_suitesparse_ldl")]
868 #[test]
869 fn cuthill_ldl_solve_c() {
870 let mat = CsMat::new_csc(
871 (4, 4),
872 vec![0, 2, 4, 6, 8],
873 vec![0, 3, 1, 2, 1, 2, 0, 3],
874 vec![1., 2., 21., 6., 6., 2., 2., 8.],
875 );
876
877 let b = vec![9., 60., 18., 34.];
878 let x0 = vec![1., 2., 3., 4.];
879
880 let ldlt = super::Ldl::new()
881 .check_perm(super::PermutationCheck::CheckPerm)
882 .fill_in_reduction(super::FillInReduction::ReverseCuthillMcKee)
883 .numeric_c(mat.view())
884 .unwrap();
885 let x = ldlt.solve(&b);
886 assert_eq!(x, x0);
887 }
888
889 #[cfg(feature = "sprs_suitesparse_camd")]
890 #[test]
891 fn camd_ldl_solve() {
892 #[rustfmt::skip]
898 let triangles = ndarray::arr2(
899 &[[0, 7, 5],
900 [0, 5, 10],
901 [10, 5, 6],
902 [10, 6, 2],
903 [2, 6, 3],
904 [3, 6, 4],
905 [7, 8, 5],
906 [5, 8, 9],
907 [5, 9, 6],
908 [6, 9, 1],
909 [6, 1, 11],
910 [6, 11, 4]],
911 );
912 let lap_mat =
913 sprs::special_mats::tri_mesh_graph_laplacian(12, triangles.view());
914 let ldlt_camd = super::Ldl::new()
915 .check_symmetry(super::SymmetryCheck::DontCheckSymmetry)
916 .fill_in_reduction(super::FillInReduction::CAMDSuiteSparse)
917 .numeric(lap_mat.view())
918 .unwrap();
919 let ldlt_cuthill = super::Ldl::new()
920 .check_symmetry(super::SymmetryCheck::DontCheckSymmetry)
921 .fill_in_reduction(super::FillInReduction::ReverseCuthillMcKee)
922 .numeric(lap_mat.view())
923 .unwrap();
924 let ldlt_raw = super::Ldl::new()
925 .check_symmetry(super::SymmetryCheck::DontCheckSymmetry)
926 .fill_in_reduction(super::FillInReduction::NoReduction)
927 .numeric(lap_mat.view())
928 .unwrap();
929 assert!(ldlt_camd.nnz() < ldlt_raw.nnz());
930 assert!(ldlt_camd.nnz() < ldlt_cuthill.nnz());
931 }
932}