Skip to main content

faer/operator/
mod.rs

1//! matrix-free linear operator traits and algorithms
2use crate::internal_prelude_sp::*;
3/// biconjugate gradient stabilized method.
4pub mod bicgstab;
5/// conjugate gradient method.
6pub mod conjugate_gradient;
7/// krylov-schur eigensolvers.
8pub mod eigen;
9/// least squares minimal residual.
10pub mod lsmr;
11mod operator_impl;
12mod self_adjoint_eigen;
13mod svd;
14/// specifies whether the initial guess should be assumed to be zero or not
15#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
16pub enum InitialGuessStatus {
17	/// initial guess is already zeroed
18	Zero,
19	/// initial guess may contain non-zero values
20	#[default]
21	MaybeNonZero,
22}
23/// identity preconditioner, no-op for most operations
24#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
25pub struct IdentityPrecond {
26	/// dimension of the preconditioner, equal to the dimension of the solution
27	pub dim: usize,
28}
29/// linear operator from a finite-dimensional vector space
30pub trait LinOp<T: ComplexField>: Sync + core::fmt::Debug {
31	/// computes the workspace layout required to apply `self` or the conjugate
32	/// o `self` to a matrix with `rhs_ncols` columns
33	fn apply_scratch(&self, rhs_ncols: usize, par: Par) -> StackReq;
34	/// output dimension of the operator
35	fn nrows(&self) -> usize;
36	/// input dimension of the operator
37	fn ncols(&self) -> usize;
38	/// applies `self` to `rhs`, and stores the result in `out`
39	fn apply(
40		&self,
41		out: MatMut<'_, T>,
42		rhs: MatRef<'_, T>,
43		par: Par,
44		stack: &mut MemStack,
45	);
46	/// applies the conjugate of `self` to `rhs`, and stores the result in `out`
47	fn conj_apply(
48		&self,
49		out: MatMut<'_, T>,
50		rhs: MatRef<'_, T>,
51		par: Par,
52		stack: &mut MemStack,
53	);
54}
55impl<T: ComplexField> LinOp<T> for IdentityPrecond {
56	#[inline]
57	#[track_caller]
58	fn apply_scratch(&self, _rhs_ncols: usize, _par: Par) -> StackReq {
59		StackReq::EMPTY
60	}
61
62	#[inline]
63	fn nrows(&self) -> usize {
64		self.dim
65	}
66
67	#[inline]
68	fn ncols(&self) -> usize {
69		self.dim
70	}
71
72	#[inline]
73	#[track_caller]
74	fn apply(
75		&self,
76		out: MatMut<'_, T>,
77		rhs: MatRef<'_, T>,
78		_par: Par,
79		_stack: &mut MemStack,
80	) {
81		{ out }.copy_from(rhs);
82	}
83
84	#[inline]
85	#[track_caller]
86	fn conj_apply(
87		&self,
88		out: MatMut<'_, T>,
89		rhs: MatRef<'_, T>,
90		_par: Par,
91		_stack: &mut MemStack,
92	) {
93		{ out }.copy_from(rhs);
94	}
95}
96impl<T: ComplexField> BiLinOp<T> for IdentityPrecond {
97	#[inline]
98	fn transpose_apply_scratch(
99		&self,
100		_rhs_ncols: usize,
101		_par: Par,
102	) -> StackReq {
103		StackReq::EMPTY
104	}
105
106	#[inline]
107	#[track_caller]
108	fn transpose_apply(
109		&self,
110		out: MatMut<'_, T>,
111		rhs: MatRef<'_, T>,
112		_par: Par,
113		_stack: &mut MemStack,
114	) {
115		{ out }.copy_from(rhs);
116	}
117
118	#[inline]
119	#[track_caller]
120	fn adjoint_apply(
121		&self,
122		out: MatMut<'_, T>,
123		rhs: MatRef<'_, T>,
124		_par: Par,
125		_stack: &mut MemStack,
126	) {
127		{ out }.copy_from(rhs);
128	}
129}
130impl<T: ComplexField> Precond<T> for IdentityPrecond {
131	fn apply_in_place_scratch(&self, _rhs_ncols: usize, _par: Par) -> StackReq {
132		StackReq::EMPTY
133	}
134
135	fn apply_in_place(
136		&self,
137		_rhs: MatMut<'_, T>,
138		_par: Par,
139		_stack: &mut MemStack,
140	) {
141	}
142
143	fn conj_apply_in_place(
144		&self,
145		_rhs: MatMut<'_, T>,
146		_par: Par,
147		_stack: &mut MemStack,
148	) {
149	}
150}
151impl<T: ComplexField> BiPrecond<T> for IdentityPrecond {
152	fn transpose_apply_in_place_scratch(
153		&self,
154		_rhs_ncols: usize,
155		_par: Par,
156	) -> StackReq {
157		StackReq::EMPTY
158	}
159
160	fn transpose_apply_in_place(
161		&self,
162		_rhs: MatMut<'_, T>,
163		_par: Par,
164		_stack: &mut MemStack,
165	) {
166	}
167
168	fn adjoint_apply_in_place(
169		&self,
170		_rhs: MatMut<'_, T>,
171		_par: Par,
172		_stack: &mut MemStack,
173	) {
174	}
175}
176/// linear operator that can be applied from either the right or the left side
177pub trait BiLinOp<T: ComplexField>: LinOp<T> {
178	/// computes the workspace layout required to apply the transpose or adjoint
179	/// o `self` to a matrix with `rhs_ncols` columns
180	fn transpose_apply_scratch(&self, rhs_ncols: usize, par: Par) -> StackReq;
181	/// applies the transpose of `self` to `rhs`, and stores the result in `out`
182	fn transpose_apply(
183		&self,
184		out: MatMut<'_, T>,
185		rhs: MatRef<'_, T>,
186		par: Par,
187		stack: &mut MemStack,
188	);
189	/// applies the adjoint of `self` to `rhs`, and stores the result in `out`
190	fn adjoint_apply(
191		&self,
192		out: MatMut<'_, T>,
193		rhs: MatRef<'_, T>,
194		par: Par,
195		stack: &mut MemStack,
196	);
197}
198/// preconditioner for a linear system
199///
200/// same as [`LinOp`] except that it can be applied in place
201pub trait Precond<T: ComplexField>: LinOp<T> {
202	/// computes the workspace layout required to apply `self` or the conjugate
203	/// of `self` to a matrix with `rhs_ncols` columns in place
204	fn apply_in_place_scratch(&self, rhs_ncols: usize, par: Par) -> StackReq {
205		temp_mat_scratch::<T>(self.nrows(), rhs_ncols)
206			.and(self.apply_scratch(rhs_ncols, par))
207	}
208	/// applies `self` to `rhs`, and stores the result in `rhs`
209	#[track_caller]
210	fn apply_in_place(
211		&self,
212		rhs: MatMut<'_, T>,
213		par: Par,
214		stack: &mut MemStack,
215	) {
216		let (mut tmp, stack) = unsafe {
217			temp_mat_uninit::<T, _, _>(self.nrows(), rhs.ncols(), stack)
218		};
219		let mut tmp = tmp.as_mat_mut();
220		self.apply(tmp.rb_mut(), rhs.rb(), par, stack);
221		{ rhs }.copy_from(&tmp);
222	}
223	/// applies the conjugate of `self` to `rhs`, and stores the result in `rhs`
224	#[track_caller]
225	fn conj_apply_in_place(
226		&self,
227		rhs: MatMut<'_, T>,
228		par: Par,
229		stack: &mut MemStack,
230	) {
231		let (mut tmp, stack) = unsafe {
232			temp_mat_uninit::<T, _, _>(self.nrows(), rhs.ncols(), stack)
233		};
234		let mut tmp = tmp.as_mat_mut();
235		self.conj_apply(tmp.rb_mut(), rhs.rb(), par, stack);
236		{ rhs }.copy_from(&tmp);
237	}
238}
239/// preconditioner for a linear system that can bee applied from either the
240/// right or the left side
241///
242/// same as [`BiLinOp`] except that it can be applied in place.
243pub trait BiPrecond<T: ComplexField>: Precond<T> + BiLinOp<T> {
244	/// computes the workspace layout required to apply the transpose or adjoint
245	/// of `self` to a matrix with `rhs_ncols` columns in place
246	fn transpose_apply_in_place_scratch(
247		&self,
248		rhs_ncols: usize,
249		par: Par,
250	) -> StackReq {
251		temp_mat_scratch::<T>(self.nrows(), rhs_ncols)
252			.and(self.transpose_apply_scratch(rhs_ncols, par))
253	}
254	/// applies the transpose of `self` to `rhs`, and stores the result in `rhs`
255	#[track_caller]
256	fn transpose_apply_in_place(
257		&self,
258		rhs: MatMut<'_, T>,
259		par: Par,
260		stack: &mut MemStack,
261	) {
262		let (mut tmp, stack) = unsafe {
263			temp_mat_uninit::<T, _, _>(self.nrows(), rhs.ncols(), stack)
264		};
265		let mut tmp = tmp.as_mat_mut();
266		self.transpose_apply(tmp.rb_mut(), rhs.rb(), par, stack);
267		{ rhs }.copy_from(&tmp);
268	}
269	/// applies the adjoint of `self` to `rhs`, and stores the result in `rhs`
270	#[track_caller]
271	fn adjoint_apply_in_place(
272		&self,
273		rhs: MatMut<'_, T>,
274		par: Par,
275		stack: &mut MemStack,
276	) {
277		let (mut tmp, stack) = unsafe {
278			temp_mat_uninit::<T, _, _>(self.nrows(), rhs.ncols(), stack)
279		};
280		let mut tmp = tmp.as_mat_mut();
281		self.adjoint_apply(tmp.rb_mut(), rhs.rb(), par, stack);
282		{ rhs }.copy_from(&tmp);
283	}
284}
285impl<T: ComplexField, M: Sized + LinOp<T>> LinOp<T> for &M {
286	#[inline]
287	#[track_caller]
288	fn apply_scratch(&self, rhs_ncols: usize, par: Par) -> StackReq {
289		(**self).apply_scratch(rhs_ncols, par)
290	}
291
292	#[inline]
293	fn nrows(&self) -> usize {
294		(**self).nrows()
295	}
296
297	#[inline]
298	fn ncols(&self) -> usize {
299		(**self).ncols()
300	}
301
302	#[inline]
303	#[track_caller]
304	fn apply(
305		&self,
306		out: MatMut<'_, T>,
307		rhs: MatRef<'_, T>,
308		par: Par,
309		stack: &mut MemStack,
310	) {
311		(**self).apply(out, rhs, par, stack)
312	}
313
314	#[inline]
315	#[track_caller]
316	fn conj_apply(
317		&self,
318		out: MatMut<'_, T>,
319		rhs: MatRef<'_, T>,
320		par: Par,
321		stack: &mut MemStack,
322	) {
323		(**self).conj_apply(out, rhs, par, stack)
324	}
325}
326impl<T: ComplexField, M: Sized + BiLinOp<T>> BiLinOp<T> for &M {
327	#[inline]
328	#[track_caller]
329	fn transpose_apply_scratch(&self, rhs_ncols: usize, par: Par) -> StackReq {
330		(**self).transpose_apply_scratch(rhs_ncols, par)
331	}
332
333	#[inline]
334	#[track_caller]
335	fn transpose_apply(
336		&self,
337		out: MatMut<'_, T>,
338		rhs: MatRef<'_, T>,
339		par: Par,
340		stack: &mut MemStack,
341	) {
342		(**self).transpose_apply(out, rhs, par, stack)
343	}
344
345	#[inline]
346	#[track_caller]
347	fn adjoint_apply(
348		&self,
349		out: MatMut<'_, T>,
350		rhs: MatRef<'_, T>,
351		par: Par,
352		stack: &mut MemStack,
353	) {
354		(**self).adjoint_apply(out, rhs, par, stack)
355	}
356}
357impl<T: ComplexField, M: Sized + Precond<T>> Precond<T> for &M {
358	fn apply_in_place_scratch(&self, rhs_ncols: usize, par: Par) -> StackReq {
359		(**self).apply_in_place_scratch(rhs_ncols, par)
360	}
361
362	fn apply_in_place(
363		&self,
364		rhs: MatMut<'_, T>,
365		par: Par,
366		stack: &mut MemStack,
367	) {
368		(**self).apply_in_place(rhs, par, stack);
369	}
370
371	fn conj_apply_in_place(
372		&self,
373		rhs: MatMut<'_, T>,
374		par: Par,
375		stack: &mut MemStack,
376	) {
377		(**self).conj_apply_in_place(rhs, par, stack);
378	}
379}
380impl<T: ComplexField, M: Sized + BiPrecond<T>> BiPrecond<T> for &M {
381	fn transpose_apply_in_place_scratch(
382		&self,
383		rhs_ncols: usize,
384		par: Par,
385	) -> StackReq {
386		(**self).transpose_apply_in_place_scratch(rhs_ncols, par)
387	}
388
389	fn transpose_apply_in_place(
390		&self,
391		rhs: MatMut<'_, T>,
392		par: Par,
393		stack: &mut MemStack,
394	) {
395		(**self).transpose_apply_in_place(rhs, par, stack);
396	}
397
398	fn adjoint_apply_in_place(
399		&self,
400		rhs: MatMut<'_, T>,
401		par: Par,
402		stack: &mut MemStack,
403	) {
404		(**self).adjoint_apply_in_place(rhs, par, stack);
405	}
406}