diffsol_la/linear_solver/
mod.rs1use crate::{error::LaError, LinearOp, Matrix};
2
3#[cfg(feature = "nalgebra")]
4pub mod nalgebra;
5
6#[cfg(feature = "faer")]
7pub mod faer;
8
9#[cfg(feature = "suitesparse")]
10pub mod suitesparse;
11
12#[cfg(feature = "cuda")]
13pub mod cuda;
14
15#[cfg(feature = "cuda-oxide")]
16pub mod cuda_oxide;
17
18pub use faer::lu::LU as FaerLU;
19pub use nalgebra::lu::LU as NalgebraNativeLU;
20pub use nalgebra::reusable_lu::ReusableLU as NalgebraLU;
21
22pub trait LinearSolver<M: Matrix>: Default {
24 fn set_linearisation<C: LinearOp<V = M::V, T = M::T, M = M, C = M::C>>(&mut self, op: &C);
29
30 fn set_sparsity<C: LinearOp<V = M::V, T = M::T, M = M, C = M::C>>(&mut self, op: &C);
35
36 fn solve(&self, b: &M::V) -> Result<M::V, LaError> {
40 let mut b = b.clone();
41 self.solve_in_place(&mut b)?;
42 Ok(b)
43 }
44
45 fn solve_in_place(&self, b: &mut M::V) -> Result<(), LaError>;
46}
47
48#[cfg(test)]
49pub(crate) mod tests {
50 use super::LinearSolver;
51 use crate::{Context, IndexType, LinearOp, Matrix, Vector};
52
53 pub struct DiagonalOp<M: Matrix> {
55 matrix: M,
56 }
57
58 pub fn diagonal_op<M: Matrix>(value: f64) -> DiagonalOp<M> {
60 use num_traits::FromPrimitive;
61 let v = M::T::from_f64(value).unwrap();
62 let diag = M::V::from_vec(vec![v, v], Default::default());
63 DiagonalOp {
64 matrix: M::from_diagonal(&diag),
65 }
66 }
67
68 #[cfg_attr(not(feature = "cuda-oxide"), allow(dead_code))]
70 pub fn diagonal_op_n<M: Matrix>(nstates: IndexType, value: f64, ctx: M::C) -> DiagonalOp<M> {
71 use num_traits::FromPrimitive;
72 let v = M::T::from_f64(value).unwrap();
73 let diag = M::V::from_element(nstates, v, ctx);
74 DiagonalOp {
75 matrix: M::from_diagonal(&diag),
76 }
77 }
78
79 pub fn batched_diagonal_op<M: Matrix>(values: &[f64], ctx: M::C) -> DiagonalOp<M> {
81 use num_traits::FromPrimitive;
82 assert_eq!(values.len(), ctx.nbatch());
83 let diag = M::V::from_vec(
84 values
85 .iter()
86 .flat_map(|&v| [M::T::from_f64(v).unwrap(); 2])
87 .collect(),
88 ctx,
89 );
90 DiagonalOp {
91 matrix: M::from_diagonal(&diag),
92 }
93 }
94
95 pub fn test_grouped_lu_solve<M: Matrix, LS: LinearSolver<M>>(ctx2: M::C) {
99 use num_traits::FromPrimitive;
100 let f = |x: f64| M::T::from_f64(x).unwrap();
101 let wide = ctx2.clone_with_nbatch(4).unwrap();
102 let op = batched_diagonal_op::<M>(&[2.0, 5.0], ctx2);
103 let mut s = LS::default();
104 s.set_sparsity(&op);
105 s.set_linearisation(&op);
106 let b = M::V::from_vec((1..=8).map(|i| f(i as f64)).collect(), wide.clone());
107 let x = s.solve(&b).unwrap();
108 let expected = M::V::from_vec(
109 vec![
110 f(0.5),
111 f(1.0),
112 f(1.5),
113 f(2.0),
114 f(1.0),
115 f(1.2),
116 f(1.4),
117 f(1.6),
118 ],
119 wide,
120 );
121 x.assert_eq_st(&expected, f(1e-10));
122 }
123
124 pub fn test_narrow_state_lu_solve<M: Matrix, LS: LinearSolver<M>>(ctx2: M::C) {
127 let wide = ctx2.clone_with_nbatch(4).unwrap();
128 let op = batched_diagonal_op::<M>(&[2.0, 3.0, 4.0, 5.0], wide);
129 let mut s = LS::default();
130 s.set_sparsity(&op);
131 s.set_linearisation(&op);
132 let mut b = M::V::zeros(2, ctx2);
133 s.solve_in_place(&mut b).unwrap();
134 }
135
136 impl<M: Matrix> LinearOp for DiagonalOp<M> {
137 type T = M::T;
138 type V = M::V;
139 type M = M;
140 type C = M::C;
141
142 fn nrows(&self) -> IndexType {
143 self.matrix.nrows()
144 }
145 fn ncols(&self) -> IndexType {
146 self.matrix.ncols()
147 }
148 fn context(&self) -> &Self::C {
149 self.matrix.context()
150 }
151 fn matrix_inplace(&self, y: &mut Self::M) {
152 y.copy_from(&self.matrix);
153 }
154 fn sparsity(&self) -> Option<<Self::M as Matrix>::Sparsity> {
155 self.matrix.sparsity().map(|s| {
156 use crate::matrix::sparsity::MatrixSparsityRef;
157 s.to_owned()
158 })
159 }
160 }
161}