#[cfg(all(feature = "legacy-pc-bridge", feature = "backend-faer"))]
use crate::algebra::prelude::*;
use crate::algebra::scalar::{KrystScalar, R, S};
use crate::error::KError;
pub use crate::matrix::format::OpFormat;
use crate::matrix::op::LinOp;
pub mod bridge;
pub mod pc_bridge;
#[cfg(all(feature = "legacy-pc-bridge", feature = "backend-faer"))]
use faer::Mat;
use std::str::FromStr;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum PcSide {
#[default]
Left,
Right,
Symmetric,
}
impl FromStr for PcSide {
type Err = KError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"left" => Ok(PcSide::Left),
"right" => Ok(PcSide::Right),
"symmetric" => Ok(PcSide::Symmetric),
_ => Err(KError::UnrecognizedPcSide(s.to_string())),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Op {
NoTrans,
Trans,
ConjTrans,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct PcCaps {
pub supports_transpose: bool,
pub supports_conj_trans: bool,
pub is_spd: bool,
pub side_restriction: Option<PcSide>,
}
#[derive(Clone, Copy, Debug)]
pub enum PcReusePolicy {
Never,
ReuseNumeric,
Auto,
}
impl PcReusePolicy {
pub fn allow_numeric(self) -> bool {
matches!(self, PcReusePolicy::ReuseNumeric | PcReusePolicy::Auto)
}
}
pub trait Preconditioner: Send + Sync {
fn dims(&self) -> (usize, usize) {
(0, 0)
}
fn setup(&mut self, a: &dyn LinOp<S = S>) -> Result<(), KError>;
fn apply(&self, side: PcSide, x: &[S], y: &mut [S]) -> Result<(), KError>;
fn apply_op(&self, op: Op, x: &[S], y: &mut [S]) -> Result<(), KError> {
match op {
Op::NoTrans => self.apply(PcSide::Left, x, y),
Op::Trans | Op::ConjTrans => {
Err(KError::Unsupported("transpose application not supported"))
}
}
}
fn apply_op_inplace(&self, op: Op, y: &mut [S]) -> Result<(), KError> {
let tmp = y.to_vec();
self.apply_op(op, &tmp, y)
}
fn capabilities(&self) -> PcCaps {
PcCaps::default()
}
fn apply_mut(&mut self, side: PcSide, x: &[S], y: &mut [S]) -> Result<(), KError> {
self.apply(side, x, y)
}
fn on_restart(&mut self, _outer_iter: usize, _residual_norm: R) -> Result<(), KError> {
Ok(())
}
fn direct_solve(
&mut self,
_op: &dyn LinOp<S = S>,
_b: &[S],
_x: &mut [S],
) -> Result<(), KError> {
Err(KError::SolveError(
"direct_solve not supported by this preconditioner".into(),
))
}
fn supports_numeric_update(&self) -> bool {
false
}
fn update_numeric(&mut self, _a: &dyn LinOp<S = S>) -> Result<(), KError> {
Err(KError::Unsupported("numeric update not supported"))
}
fn update_symbolic(&mut self, a: &dyn LinOp<S = S>) -> Result<(), KError> {
self.setup(a)
}
fn required_format(&self) -> OpFormat {
OpFormat::Any
}
fn preferred_drop_tol_for_format(&self) -> Option<R> {
None
}
}
pub trait LocalPreconditioner<T: KrystScalar = S>: Send + Sync {
fn dims(&self) -> (usize, usize) {
(0, 0)
}
fn apply_local(&self, x: &[T], y: &mut [T]) -> Result<(), crate::error::KError>;
fn apply_local_in_place(&self, x: &mut [T]) -> Result<(), crate::error::KError> {
let tmp = x.to_vec();
self.apply_local(&tmp, x)
}
}
pub trait FlexiblePreconditioner: Preconditioner {}
impl<T: Preconditioner + ?Sized> FlexiblePreconditioner for T {}
pub mod stats;
pub mod legacy {
use super::PcSide;
use crate::error::KError;
pub trait Preconditioner<M: ?Sized, V> {
fn setup(&mut self, a: &M) -> Result<(), KError>;
fn apply(&self, side: PcSide, r: &V, z: &mut V) -> Result<(), KError>;
}
pub trait FlexiblePreconditioner<M: ?Sized, V> {
fn setup(&mut self, a: &M) -> Result<(), KError>;
fn apply(&mut self, r: &V, z: &mut V) -> Result<(), KError>;
}
}
#[cfg(feature = "legacy-pc-bridge")]
use std::sync::Mutex;
#[cfg(feature = "legacy-pc-bridge")]
#[cfg_attr(docsrs, doc(cfg(feature = "legacy-pc-bridge")))]
pub struct LegacyOpPreconditioner {
inner: Box<dyn legacy::Preconditioner<Mat<f64>, Vec<R>> + Send + Sync>,
scratch: Mutex<Scratch>,
}
#[cfg(feature = "legacy-pc-bridge")]
#[derive(Default)]
struct Scratch {
x: Vec<R>,
y: Vec<R>,
}
#[cfg(feature = "legacy-pc-bridge")]
impl LegacyOpPreconditioner {
pub fn new(inner: Box<dyn legacy::Preconditioner<Mat<f64>, Vec<R>> + Send + Sync>) -> Self {
Self {
inner,
scratch: Mutex::new(Scratch::default()),
}
}
#[inline]
fn ensure_scratch(s: &mut Scratch, n: usize) {
if s.x.len() != n {
s.x.resize(n, R::default());
}
if s.y.len() != n {
s.y.resize(n, R::default());
}
}
}
#[cfg(feature = "legacy-pc-bridge")]
impl Preconditioner for LegacyOpPreconditioner {
fn setup(&mut self, a: &dyn LinOp<S = S>) -> Result<(), KError> {
use crate::error::KError;
let m = a
.as_any()
.downcast_ref::<Mat<f64>>()
.ok_or_else(|| KError::InvalidInput("expected faer::Mat<f64>".into()))?;
self.inner.setup(m)
}
fn apply(&self, side: PcSide, x: &[S], y: &mut [S]) -> Result<(), KError> {
use crate::error::KError;
if x.len() != y.len() {
return Err(KError::InvalidInput(format!(
"x.len()={} != y.len()={}",
x.len(),
y.len()
)));
}
let mut s = self.scratch.lock().unwrap();
Self::ensure_scratch(&mut s, x.len());
crate::algebra::scalar::copy_scalar_to_real_in(x, &mut s.x);
let Scratch { x: x_buf, y: y_buf } = &mut *s;
self.inner.apply(side, &*x_buf, y_buf)?;
crate::algebra::scalar::copy_real_to_scalar_in(&s.y, y);
Ok(())
}
fn apply_mut(&mut self, side: PcSide, x: &[S], y: &mut [S]) -> Result<(), KError> {
self.apply(side, x, y)
}
fn supports_numeric_update(&self) -> bool {
true
}
fn update_numeric(&mut self, a: &dyn LinOp<S = S>) -> Result<(), KError> {
self.setup(a)
}
fn required_format(&self) -> OpFormat {
OpFormat::Dense
}
}
#[cfg(all(feature = "legacy-pc-bridge", feature = "complex"))]
impl crate::ops::kpc::KPreconditioner for LegacyOpPreconditioner {
type Scalar = crate::S;
#[inline]
fn dims(&self) -> (usize, usize) {
let guard = self.scratch.lock().unwrap();
let n = guard.x.len();
if n == 0 { (0, 0) } else { (n, n) }
}
fn apply_s(
&self,
side: PcSide,
x: &[crate::S],
y: &mut [crate::S],
scratch: &mut crate::algebra::bridge::BridgeScratch,
) -> Result<(), KError> {
crate::preconditioner::bridge::apply_pc_s(self, side, x, y, scratch)
}
fn apply_mut_s(
&mut self,
side: PcSide,
x: &[crate::S],
y: &mut [crate::S],
scratch: &mut crate::algebra::bridge::BridgeScratch,
) -> Result<(), KError> {
crate::preconditioner::bridge::apply_pc_mut_s(self, side, x, y, scratch)
}
fn on_restart_s(
&mut self,
outer_iter: usize,
residual_norm: <Self::Scalar as crate::algebra::prelude::KrystScalar>::Real,
) -> Result<(), KError> {
self.on_restart(outer_iter, residual_norm)
}
}
#[cfg(all(not(feature = "legacy-pc-bridge"), feature = "backend-faer"))]
#[cfg_attr(docsrs, doc(cfg(feature = "legacy-pc-bridge")))]
pub struct LegacyOpPreconditioner {
_private: (),
}
#[cfg(all(not(feature = "legacy-pc-bridge"), feature = "backend-faer"))]
impl LegacyOpPreconditioner {
pub fn new(_: Box<dyn legacy::Preconditioner<faer::Mat<f64>, Vec<f64>> + Send + Sync>) -> Self {
panic!("legacy-pc-bridge feature is disabled")
}
}
#[cfg(all(not(feature = "legacy-pc-bridge"), feature = "backend-faer"))]
impl Preconditioner for LegacyOpPreconditioner {
fn setup(&mut self, _: &dyn LinOp<S = S>) -> Result<(), KError> {
Err(KError::Unsupported("legacy-pc-bridge feature is disabled"))
}
fn apply(&self, _: PcSide, _: &[S], _: &mut [S]) -> Result<(), KError> {
Err(KError::Unsupported("legacy-pc-bridge feature is disabled"))
}
}
#[cfg(not(feature = "backend-faer"))]
pub struct LegacyOpPreconditioner {
_private: (),
}
#[cfg(not(feature = "backend-faer"))]
impl LegacyOpPreconditioner {
pub fn new<T>(_: T) -> Self {
panic!("backend-faer feature is disabled")
}
}
#[cfg(not(feature = "backend-faer"))]
impl Preconditioner for LegacyOpPreconditioner {
fn setup(&mut self, _: &dyn LinOp<S = S>) -> Result<(), KError> {
Err(KError::Unsupported("backend-faer feature is disabled"))
}
fn apply(&self, _: PcSide, _: &[S], _: &mut [S]) -> Result<(), KError> {
Err(KError::Unsupported("backend-faer feature is disabled"))
}
}
#[cfg(feature = "backend-faer")]
pub mod amg;
#[cfg(feature = "backend-faer")]
pub mod approxinv;
#[cfg(feature = "backend-faer")]
pub mod approxinv_csr;
#[cfg(feature = "backend-faer")]
pub mod asm;
#[cfg(feature = "backend-faer")]
pub mod asm_amg;
#[cfg(feature = "backend-faer")]
pub mod block_jacobi;
#[cfg(feature = "backend-faer")]
pub mod builders;
pub mod builders_none;
#[cfg(feature = "backend-faer")]
pub mod builders_faer;
#[cfg(feature = "backend-nalgebra")]
pub mod builders_nalgebra;
pub mod chain;
#[cfg(feature = "backend-faer")]
pub mod chebyshev;
#[cfg(feature = "backend-faer")]
pub mod deflation;
#[cfg(feature = "backend-faer")]
pub mod direct;
#[cfg(feature = "backend-faer")]
pub mod dist;
#[cfg(feature = "backend-nalgebra")]
pub mod nalgebra_direct;
#[cfg(feature = "backend-faer")]
pub mod ilu;
#[cfg(feature = "backend-faer")]
pub mod ilu_csr;
pub mod ilu_options;
#[cfg(feature = "backend-faer")]
pub mod ilup;
#[cfg(feature = "backend-faer")]
pub mod ilut;
#[cfg(feature = "backend-faer")]
pub mod ilutp;
pub mod jacobi;
pub mod pivot;
#[cfg(feature = "backend-faer")]
pub mod sor;
pub mod tri_solve;
#[cfg(feature = "backend-faer")]
pub use self::sor::MatSorType;
#[cfg(feature = "backend-faer")]
pub use amg::AMG;
#[cfg(feature = "backend-faer")]
pub use approxinv::ApproxInv;
#[cfg(feature = "backend-faer")]
pub use approxinv_csr::{ApproxInvBuilder, ApproxInvKind, ApproxInvParams, FsaiCsr, SpaiCsr};
#[cfg(feature = "backend-faer")]
pub use asm::{AdditiveSchwarz, Asm, AsmBuilder, AsmCombine, AsmConfig, AsmLocalSolver};
#[cfg(feature = "backend-faer")]
pub use asm_amg::{AsmAmg, AsmAmgBuilder, TwoLevelConfig, TwoLevelMode};
pub use chain::PcChain;
#[cfg(feature = "backend-faer")]
pub use chebyshev::{Chebyshev, ChebyshevPre};
#[cfg(feature = "backend-faer")]
pub use deflation::{AmgCoarseSpace, DeflationOptions, DeflationPC, ZSource, with_amg_deflation};
#[cfg(feature = "backend-faer")]
pub use direct::SuperLuDistPc;
#[cfg(all(feature = "dense-direct", feature = "backend-faer"))]
pub use direct::{LuPc, QrPc};
#[cfg(feature = "backend-faer")]
pub use ilu::Ilu0;
#[cfg(feature = "backend-faer")]
pub use ilup::Ilup;
#[cfg(feature = "backend-faer")]
#[allow(deprecated)]
pub use ilut::{Ilut, RowFilterPreconditioner};
#[cfg(feature = "backend-faer")]
pub use ilutp::Ilutp;
pub use jacobi::Jacobi;
#[cfg(feature = "backend-faer")]
pub use sor::Sor;
pub use pivot::{PivotMode, PivotPolicy, PivotScale, PivotSignPolicy, PivotStats};
pub use tri_solve::TriangularSolve;
pub use crate::context::pc_context::SparsityPattern;
#[cfg(test)]
mod tests;