diffsol_la/linear_solver/faer/
sparse_lu.rs1use std::cell::RefCell;
2
3use crate::context::broadcast_batch;
4use crate::{
5 error::LaError, linear_solver::LinearSolver, linear_solver_error, scalar::IndexType, Context,
6 FaerContext, FaerScalar, FaerSparseMat, FaerVec, LinearOp, Matrix,
7};
8
9use faer::dyn_stack::{MemBuffer, MemStack};
10use faer::reborrow::{Reborrow, ReborrowMut};
11use faer::sparse::linalg::lu::{factorize_symbolic_lu, LuRef, NumericLu, SymbolicLu};
12use faer::Conj;
13
14struct SparseLu<T: FaerScalar> {
22 a: FaerSparseMat<T>,
23 symbolic: SymbolicLu<IndexType>,
24 numeric: Vec<NumericLu<IndexType, T>>,
25 factor_scratch: MemBuffer,
26 solve_scratch: RefCell<MemBuffer>,
27}
28
29impl<T: FaerScalar> SparseLu<T> {
30 fn new(a: FaerSparseMat<T>) -> Self {
31 let symbolic = factorize_symbolic_lu(a.data[0].symbolic(), Default::default())
32 .expect("Failed to create symbolic LU");
33 let par = a.context.par;
34 let factor_scratch =
37 MemBuffer::new(symbolic.factorize_numeric_lu_scratch::<T>(par, Default::default()));
38 let solve_scratch = RefCell::new(MemBuffer::new(
39 symbolic
40 .solve_in_place_scratch::<T>(1, par)
41 .or(symbolic.solve_transpose_in_place_scratch::<T>(1, par)),
42 ));
43 Self {
44 numeric: (0..a.data.len()).map(|_| NumericLu::new()).collect(),
45 a,
46 symbolic,
47 factor_scratch,
48 solve_scratch,
49 }
50 }
51
52 fn nbatch(&self) -> usize {
53 self.numeric.len()
54 }
55
56 fn factor(&mut self) {
57 let par = self.a.context.par;
58 for (numeric, block) in self.numeric.iter_mut().zip(self.a.data.iter()) {
59 self.symbolic
60 .factorize_numeric_lu(
61 numeric,
62 block.rb(),
63 par,
64 MemStack::new(&mut self.factor_scratch),
65 Default::default(),
66 )
67 .expect("Failed to factorise matrix");
68 }
69 }
70
71 fn solve_block(&self, batch: usize, rhs: faer::MatMut<'_, T>, par: faer::Par, transpose: bool) {
72 let lu = LuRef::new_unchecked(&self.symbolic, &self.numeric[batch]);
73 let mut scratch = self.solve_scratch.borrow_mut();
74 let stack = MemStack::new(&mut scratch);
75 if transpose {
76 lu.solve_transpose_in_place_with_conj(Conj::No, rhs, par, stack);
77 } else {
78 lu.solve_in_place_with_conj(Conj::No, rhs, par, stack);
79 }
80 }
81}
82
83pub struct FaerSparseLU<T>
90where
91 T: FaerScalar,
92{
93 lu: Option<SparseLu<T>>,
94}
95
96impl<T> Default for FaerSparseLU<T>
97where
98 T: FaerScalar,
99{
100 fn default() -> Self {
101 Self { lu: None }
102 }
103}
104
105impl<T: FaerScalar> LinearSolver<FaerSparseMat<T>> for FaerSparseLU<T> {
106 fn set_linearisation<
107 C: LinearOp<T = T, V = FaerVec<T>, M = FaerSparseMat<T>, C = FaerContext>,
108 >(
109 &mut self,
110 op: &C,
111 ) {
112 let lu = self.lu.as_mut().expect("Matrix not set");
113 op.matrix_inplace(&mut lu.a);
114 lu.factor();
115 }
116
117 fn solve_in_place(&self, x: &mut FaerVec<T>) -> Result<(), LaError> {
118 self.solve_in_place_impl(x, false)
119 }
120
121 fn solve_transpose_in_place(&self, x: &mut FaerVec<T>) -> Result<(), LaError> {
122 self.solve_in_place_impl(x, true)
123 }
124
125 fn set_sparsity<C: LinearOp<T = T, V = FaerVec<T>, M = FaerSparseMat<T>, C = FaerContext>>(
126 &mut self,
127 op: &C,
128 ) {
129 let ncols = op.ncols();
130 let nrows = op.nrows();
131 let a = C::M::new_from_sparsity(nrows, ncols, op.sparsity(), *op.context());
132 self.lu = Some(SparseLu::new(a));
133 }
134}
135
136impl<T: FaerScalar> FaerSparseLU<T> {
137 fn solve_in_place_impl(&self, x: &mut FaerVec<T>, transpose: bool) -> Result<(), LaError> {
138 let Some(lu) = self.lu.as_ref() else {
139 return Err(linear_solver_error!(LuNotInitialized));
140 };
141 x.context
142 .assert_broadcastable_into(lu.nbatch(), "sparse_lu_solve");
143 let par = x.context.par;
144 let nb = x.data.ncols();
145 for batch in 0..nb {
146 lu.solve_block(
147 broadcast_batch(batch, lu.nbatch(), nb),
148 x.data.rb_mut().col_mut(batch).as_mat_mut(),
149 par,
150 transpose,
151 );
152 }
153 Ok(())
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 crate::linear_solver::generate_lu_tests!(
162 f64,
163 FaerSparseMat<f64>,
164 FaerSparseLU<f64>,
165 FaerContext::with_nbatch(2)
166 );
167 crate::linear_solver::generate_lu_tests!(
168 f32,
169 FaerSparseMat<f32>,
170 FaerSparseLU<f32>,
171 FaerContext::with_nbatch(2)
172 );
173}