1use crate::klu_native::KluNativeSystem;
15use crate::sparse::{LinearSolver, RealSparseSystem};
16#[cfg(feature = "klu")]
17use crate::sparse_klu::KluRealSystem;
18#[cfg(feature = "pardiso")]
19use crate::sparse_pardiso::PardisoRealSystem;
20
21#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
23pub enum LuBackend {
24 #[default]
26 Faer,
27 KluNative,
29 #[cfg(feature = "klu")]
31 Klu,
32 #[cfg(feature = "pardiso")]
34 Pardiso,
35}
36
37enum System {
38 Faer(RealSparseSystem),
39 KluNative(KluNativeSystem),
40 #[cfg(feature = "klu")]
41 Klu(KluRealSystem),
42 #[cfg(feature = "pardiso")]
43 Pardiso(PardisoRealSystem),
44}
45
46pub struct SparseLu {
54 backend: LuBackend,
55 system: Option<System>,
56 order: Option<Vec<usize>>,
60}
61
62impl SparseLu {
63 pub fn new(backend: LuBackend) -> Self {
64 Self {
65 backend,
66 system: None,
67 order: None,
68 }
69 }
70
71 pub fn backend(&self) -> LuBackend {
72 self.backend
73 }
74
75 pub fn is_analyzed(&self) -> bool {
77 self.system.is_some()
78 }
79
80 pub fn analyze(&mut self, n: usize, entries: &[(usize, usize, f64)]) -> bool {
84 self.system = match self.backend {
85 LuBackend::Faer => RealSparseSystem::new(n, entries).map(System::Faer),
86 LuBackend::KluNative => KluNativeSystem::new(n, entries).map(System::KluNative),
87 #[cfg(feature = "klu")]
88 LuBackend::Klu => KluRealSystem::new(n, entries).map(System::Klu),
89 #[cfg(feature = "pardiso")]
90 LuBackend::Pardiso => PardisoRealSystem::new(n, entries).map(System::Pardiso),
91 };
92 self.order = None;
93 self.system.is_some()
94 }
95
96 pub fn adopt_storage_order(&mut self) -> Option<Vec<usize>> {
109 let order = match self.system.as_mut()? {
110 System::KluNative(s) => s.adopt_storage_order(),
111 #[cfg(feature = "klu")]
112 System::Klu(s) => s.adopt_storage_order(),
113 _ => None,
114 }?;
115 self.order = Some(order.clone());
116 Some(order)
117 }
118
119 pub fn storage_order(&self) -> Option<&[usize]> {
122 self.order.as_deref()
123 }
124
125 pub fn reset(&mut self) {
128 self.system = None;
129 self.order = None;
130 }
131
132 pub fn factor_and_solve(
134 &mut self,
135 entries: &[(usize, usize, f64)],
136 rhs: &[f64],
137 ) -> Option<Vec<f64>> {
138 match self.system.as_mut()? {
139 System::Faer(s) => s.factor_and_solve(entries, rhs),
140 System::KluNative(s) => s.factor_and_solve(entries, rhs),
141 #[cfg(feature = "klu")]
142 System::Klu(s) => s.factor_and_solve(entries, rhs),
143 #[cfg(feature = "pardiso")]
144 System::Pardiso(s) => s.factor_and_solve(entries, rhs),
145 }
146 }
147
148 pub fn factor_and_solve_values(&mut self, values: &[f64], rhs: &[f64]) -> Option<Vec<f64>> {
151 match self.system.as_mut()? {
152 System::Faer(s) => s.factor_and_solve_values(values, rhs),
153 System::KluNative(s) => s.factor_and_solve_values(values, rhs),
154 #[cfg(feature = "klu")]
155 System::Klu(s) => s.factor_and_solve_values(values, rhs),
156 #[cfg(feature = "pardiso")]
157 System::Pardiso(s) => s.factor_and_solve_values(values, rhs),
158 }
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 const ENTRIES: [(usize, usize, f64); 7] = [
169 (0, 0, 4.0),
170 (0, 1, 1.0),
171 (1, 1, 5.0),
172 (2, 1, 2.0),
173 (1, 0, 1.0),
174 (2, 2, 3.0),
175 (1, 2, 1.0),
176 ];
177 const RHS: [f64; 3] = [1.0, 2.0, 3.0];
178
179 fn adopted_order_solves_the_same_system(backend: LuBackend) {
180 let values: Vec<f64> = ENTRIES.iter().map(|e| e.2).collect();
181 let mut lu = SparseLu::new(backend);
182 assert!(lu.analyze(3, &ENTRIES));
183 let before = lu.factor_and_solve_values(&values, &RHS).unwrap();
184
185 let order = lu.adopt_storage_order().expect("KLU adopts its order");
186 assert_ne!(
187 order,
188 (0..ENTRIES.len()).collect::<Vec<_>>(),
189 "not a real permutation"
190 );
191 assert_eq!(lu.storage_order(), Some(&order[..]));
192 let reordered: Vec<f64> = order.iter().map(|&i| values[i]).collect();
193 let after = lu.factor_and_solve_values(&reordered, &RHS).unwrap();
194 assert_eq!(before, after);
195
196 lu.reset();
197 assert_eq!(lu.storage_order(), None);
198 }
199
200 #[test]
201 fn klu_native_adopts_its_storage_order() {
202 adopted_order_solves_the_same_system(LuBackend::KluNative);
203 }
204
205 #[cfg(feature = "klu")]
206 #[test]
207 fn klu_adopts_its_storage_order() {
208 adopted_order_solves_the_same_system(LuBackend::Klu);
209 }
210
211 #[test]
212 fn faer_keeps_the_callers_order() {
213 let mut lu = SparseLu::new(LuBackend::Faer);
214 assert!(lu.analyze(3, &ENTRIES));
215 assert_eq!(lu.adopt_storage_order(), None);
216 }
217
218 #[test]
219 fn duplicate_positions_keep_the_callers_order() {
220 let mut entries = ENTRIES.to_vec();
221 entries.push((0, 0, 1.0));
222 let mut lu = SparseLu::new(LuBackend::KluNative);
223 assert!(lu.analyze(3, &entries));
224 assert_eq!(lu.adopt_storage_order(), None);
225 }
226}