faer/operator/
mod.rs

1//! matrix-free linear operator traits and algorithms
2
3use crate::internal_prelude_sp::*;
4
5/// biconjugate gradient stabilized method.
6pub mod bicgstab;
7/// conjugate gradient method.
8pub mod conjugate_gradient;
9/// least squares minimal residual.
10pub mod lsmr;
11
12/// krylov-schur eigensolvers.
13pub mod eigen;
14
15mod operator_impl;
16
17/// specifies whether the initial guess should be assumed to be zero or not
18#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
19pub enum InitialGuessStatus {
20	/// initial guess is already zeroed
21	Zero,
22	/// initial guess may contain non-zero values
23	#[default]
24	MaybeNonZero,
25}
26
27/// identity preconditioner, no-op for most operations
28#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
29pub struct IdentityPrecond {
30	/// dimension of the preconditioner, equal to the dimension of the solution
31	pub dim: usize,
32}
33
34/// linear operator from a finite-dimensional vector space
35pub trait LinOp<T: ComplexField>: Sync + core::fmt::Debug {
36	/// computes the workspace size and alignment required to apply `self` or the conjugate o
37	/// `self` to a matrix with `rhs_ncols` columns
38	fn apply_scratch(&self, rhs_ncols: usize, par: Par) -> StackReq;
39
40	/// output dimension of the operator
41	fn nrows(&self) -> usize;
42	/// input dimension of the operator
43	fn ncols(&self) -> usize;
44
45	/// applies `self` to `rhs`, and stores the result in `out`
46	fn apply(&self, out: MatMut<'_, T>, rhs: MatRef<'_, T>, par: Par, stack: &mut MemStack);
47
48	/// applies the conjugate of `self` to `rhs`, and stores the result in `out`
49	fn conj_apply(&self, out: MatMut<'_, T>, rhs: MatRef<'_, T>, par: Par, stack: &mut MemStack);
50}
51
52impl<T: ComplexField> LinOp<T> for IdentityPrecond {
53	#[inline]
54	#[track_caller]
55	fn apply_scratch(&self, _rhs_ncols: usize, _par: Par) -> StackReq {
56		StackReq::EMPTY
57	}
58
59	#[inline]
60	fn nrows(&self) -> usize {
61		self.dim
62	}
63
64	#[inline]
65	fn ncols(&self) -> usize {
66		self.dim
67	}
68
69	#[inline]
70	#[track_caller]
71	fn apply(&self, out: MatMut<'_, T>, rhs: MatRef<'_, T>, _par: Par, _stack: &mut MemStack) {
72		{ out }.copy_from(rhs);
73	}
74
75	#[inline]
76	#[track_caller]
77	fn conj_apply(&self, out: MatMut<'_, T>, rhs: MatRef<'_, T>, _par: Par, _stack: &mut MemStack) {
78		{ out }.copy_from(rhs);
79	}
80}
81impl<T: ComplexField> BiLinOp<T> for IdentityPrecond {
82	#[inline]
83	fn transpose_apply_scratch(&self, _rhs_ncols: usize, _par: Par) -> StackReq {
84		StackReq::EMPTY
85	}
86
87	#[inline]
88	#[track_caller]
89	fn transpose_apply(&self, out: MatMut<'_, T>, rhs: MatRef<'_, T>, _par: Par, _stack: &mut MemStack) {
90		{ out }.copy_from(rhs);
91	}
92
93	#[inline]
94	#[track_caller]
95	fn adjoint_apply(&self, out: MatMut<'_, T>, rhs: MatRef<'_, T>, _par: Par, _stack: &mut MemStack) {
96		{ out }.copy_from(rhs);
97	}
98}
99impl<T: ComplexField> Precond<T> for IdentityPrecond {
100	fn apply_in_place_scratch(&self, _rhs_ncols: usize, _par: Par) -> StackReq {
101		StackReq::EMPTY
102	}
103
104	fn apply_in_place(&self, _rhs: MatMut<'_, T>, _par: Par, _stack: &mut MemStack) {}
105
106	fn conj_apply_in_place(&self, _rhs: MatMut<'_, T>, _par: Par, _stack: &mut MemStack) {}
107}
108impl<T: ComplexField> BiPrecond<T> for IdentityPrecond {
109	fn transpose_apply_in_place_scratch(&self, _rhs_ncols: usize, _par: Par) -> StackReq {
110		StackReq::EMPTY
111	}
112
113	fn transpose_apply_in_place(&self, _rhs: MatMut<'_, T>, _par: Par, _stack: &mut MemStack) {}
114
115	fn adjoint_apply_in_place(&self, _rhs: MatMut<'_, T>, _par: Par, _stack: &mut MemStack) {}
116}
117
118/// linear operator that can be applied from either the right or the left side
119pub trait BiLinOp<T: ComplexField>: LinOp<T> {
120	/// computes the workspace size and alignment required to apply the transpose or adjoint o
121	/// `self` to a matrix with `rhs_ncols` columns
122	fn transpose_apply_scratch(&self, rhs_ncols: usize, par: Par) -> StackReq;
123
124	/// applies the transpose of `self` to `rhs`, and stores the result in `out`
125	fn transpose_apply(&self, out: MatMut<'_, T>, rhs: MatRef<'_, T>, par: Par, stack: &mut MemStack);
126
127	/// applies the adjoint of `self` to `rhs`, and stores the result in `out`
128	fn adjoint_apply(&self, out: MatMut<'_, T>, rhs: MatRef<'_, T>, par: Par, stack: &mut MemStack);
129}
130
131/// preconditioner for a linear system
132///
133/// same as [`LinOp`] except that it can be applied in place
134pub trait Precond<T: ComplexField>: LinOp<T> {
135	/// computes the workspace size and alignment required to apply `self` or the conjugate of
136	/// `self` to a matrix with `rhs_ncols` columns in place
137	fn apply_in_place_scratch(&self, rhs_ncols: usize, par: Par) -> StackReq {
138		temp_mat_scratch::<T>(self.nrows(), rhs_ncols).and(self.apply_scratch(rhs_ncols, par))
139	}
140
141	/// applies `self` to `rhs`, and stores the result in `rhs`
142	#[track_caller]
143	fn apply_in_place(&self, rhs: MatMut<'_, T>, par: Par, stack: &mut MemStack) {
144		let (mut tmp, stack) = unsafe { temp_mat_uninit::<T, _, _>(self.nrows(), rhs.ncols(), stack) };
145		let mut tmp = tmp.as_mat_mut();
146		self.apply(tmp.rb_mut(), rhs.rb(), par, stack);
147		{ rhs }.copy_from(&tmp);
148	}
149
150	/// applies the conjugate of `self` to `rhs`, and stores the result in `rhs`
151	#[track_caller]
152	fn conj_apply_in_place(&self, rhs: MatMut<'_, T>, par: Par, stack: &mut MemStack) {
153		let (mut tmp, stack) = unsafe { temp_mat_uninit::<T, _, _>(self.nrows(), rhs.ncols(), stack) };
154		let mut tmp = tmp.as_mat_mut();
155
156		self.conj_apply(tmp.rb_mut(), rhs.rb(), par, stack);
157		{ rhs }.copy_from(&tmp);
158	}
159}
160
161/// preconditioner for a linear system that can bee applied from either the right or the left side
162///
163/// same as [`BiLinOp`] except that it can be applied in place.
164pub trait BiPrecond<T: ComplexField>: Precond<T> + BiLinOp<T> {
165	/// computes the workspace size and alignment required to apply the transpose or adjoint of
166	/// `self` to a matrix with `rhs_ncols` columns in place
167	fn transpose_apply_in_place_scratch(&self, rhs_ncols: usize, par: Par) -> StackReq {
168		temp_mat_scratch::<T>(self.nrows(), rhs_ncols).and(self.transpose_apply_scratch(rhs_ncols, par))
169	}
170
171	/// applies the transpose of `self` to `rhs`, and stores the result in `rhs`
172	#[track_caller]
173	fn transpose_apply_in_place(&self, rhs: MatMut<'_, T>, par: Par, stack: &mut MemStack) {
174		let (mut tmp, stack) = unsafe { temp_mat_uninit::<T, _, _>(self.nrows(), rhs.ncols(), stack) };
175		let mut tmp = tmp.as_mat_mut();
176		self.transpose_apply(tmp.rb_mut(), rhs.rb(), par, stack);
177		{ rhs }.copy_from(&tmp);
178	}
179
180	/// applies the adjoint of `self` to `rhs`, and stores the result in `rhs`
181	#[track_caller]
182	fn adjoint_apply_in_place(&self, rhs: MatMut<'_, T>, par: Par, stack: &mut MemStack) {
183		let (mut tmp, stack) = unsafe { temp_mat_uninit::<T, _, _>(self.nrows(), rhs.ncols(), stack) };
184		let mut tmp = tmp.as_mat_mut();
185
186		self.adjoint_apply(tmp.rb_mut(), rhs.rb(), par, stack);
187		{ rhs }.copy_from(&tmp);
188	}
189}
190
191impl<T: ComplexField, M: Sized + LinOp<T>> LinOp<T> for &M {
192	#[inline]
193	#[track_caller]
194	fn apply_scratch(&self, rhs_ncols: usize, par: Par) -> StackReq {
195		(**self).apply_scratch(rhs_ncols, par)
196	}
197
198	#[inline]
199	fn nrows(&self) -> usize {
200		(**self).nrows()
201	}
202
203	#[inline]
204	fn ncols(&self) -> usize {
205		(**self).ncols()
206	}
207
208	#[inline]
209	#[track_caller]
210	fn apply(&self, out: MatMut<'_, T>, rhs: MatRef<'_, T>, par: Par, stack: &mut MemStack) {
211		(**self).apply(out, rhs, par, stack)
212	}
213
214	#[inline]
215	#[track_caller]
216	fn conj_apply(&self, out: MatMut<'_, T>, rhs: MatRef<'_, T>, par: Par, stack: &mut MemStack) {
217		(**self).conj_apply(out, rhs, par, stack)
218	}
219}
220
221impl<T: ComplexField, M: Sized + BiLinOp<T>> BiLinOp<T> for &M {
222	#[inline]
223	#[track_caller]
224	fn transpose_apply_scratch(&self, rhs_ncols: usize, par: Par) -> StackReq {
225		(**self).transpose_apply_scratch(rhs_ncols, par)
226	}
227
228	#[inline]
229	#[track_caller]
230	fn transpose_apply(&self, out: MatMut<'_, T>, rhs: MatRef<'_, T>, par: Par, stack: &mut MemStack) {
231		(**self).transpose_apply(out, rhs, par, stack)
232	}
233
234	#[inline]
235	#[track_caller]
236	fn adjoint_apply(&self, out: MatMut<'_, T>, rhs: MatRef<'_, T>, par: Par, stack: &mut MemStack) {
237		(**self).adjoint_apply(out, rhs, par, stack)
238	}
239}
240
241impl<T: ComplexField, M: Sized + Precond<T>> Precond<T> for &M {
242	fn apply_in_place_scratch(&self, rhs_ncols: usize, par: Par) -> StackReq {
243		(**self).apply_in_place_scratch(rhs_ncols, par)
244	}
245
246	fn apply_in_place(&self, rhs: MatMut<'_, T>, par: Par, stack: &mut MemStack) {
247		(**self).apply_in_place(rhs, par, stack);
248	}
249
250	fn conj_apply_in_place(&self, rhs: MatMut<'_, T>, par: Par, stack: &mut MemStack) {
251		(**self).conj_apply_in_place(rhs, par, stack);
252	}
253}
254
255impl<T: ComplexField, M: Sized + BiPrecond<T>> BiPrecond<T> for &M {
256	fn transpose_apply_in_place_scratch(&self, rhs_ncols: usize, par: Par) -> StackReq {
257		(**self).transpose_apply_in_place_scratch(rhs_ncols, par)
258	}
259
260	fn transpose_apply_in_place(&self, rhs: MatMut<'_, T>, par: Par, stack: &mut MemStack) {
261		(**self).transpose_apply_in_place(rhs, par, stack);
262	}
263
264	fn adjoint_apply_in_place(&self, rhs: MatMut<'_, T>, par: Par, stack: &mut MemStack) {
265		(**self).adjoint_apply_in_place(rhs, par, stack);
266	}
267}