#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use super::{DenseMatrix, LinearSolverError, Preconditioner, BREAKDOWN_RELATIVE};
use crate::math::Fix128;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct IdentityPreconditioner {
dim: usize,
}
impl IdentityPreconditioner {
#[must_use]
pub const fn new(dim: usize) -> Self {
Self { dim }
}
}
impl Preconditioner for IdentityPreconditioner {
fn dim(&self) -> usize {
self.dim
}
fn apply(&self, r: &[Fix128], z: &mut [Fix128]) {
z.copy_from_slice(r);
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct JacobiPreconditioner {
diagonal: Vec<Fix128>,
}
impl JacobiPreconditioner {
pub fn from_diagonal(diagonal: Vec<Fix128>) -> Result<Self, LinearSolverError> {
if let Some(index) = diagonal.iter().position(|d| d.is_zero()) {
return Err(LinearSolverError::SingularPreconditioner { index });
}
Ok(Self { diagonal })
}
pub fn from_dense(matrix: &DenseMatrix) -> Result<Self, LinearSolverError> {
Self::from_diagonal((0..matrix.dim()).map(|i| matrix.get(i, i)).collect())
}
}
impl Preconditioner for JacobiPreconditioner {
fn dim(&self) -> usize {
self.diagonal.len()
}
fn apply(&self, r: &[Fix128], z: &mut [Fix128]) {
for ((zi, &ri), &d) in z.iter_mut().zip(r).zip(&self.diagonal) {
*zi = ri / d;
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
struct LuBlock {
offset: usize,
size: usize,
lu: Vec<Fix128>,
perm: Vec<usize>,
}
impl LuBlock {
fn factor(offset: usize, size: usize, mut lu: Vec<Fix128>) -> Option<Self> {
let scale = lu
.iter()
.fold(Fix128::ZERO, |m, v| if v.abs() > m { v.abs() } else { m });
let mut perm: Vec<usize> = (0..size).collect();
for k in 0..size {
let mut pivot_row = k;
let mut pivot = lu[k * size + k].abs();
for i in k + 1..size {
let candidate = lu[i * size + k].abs();
if candidate > pivot {
pivot = candidate;
pivot_row = i;
}
}
if scale.is_zero() || pivot / scale <= BREAKDOWN_RELATIVE {
return None;
}
if pivot_row != k {
for c in 0..size {
lu.swap(k * size + c, pivot_row * size + c);
}
perm.swap(k, pivot_row);
}
let diag = lu[k * size + k];
for i in k + 1..size {
let factor = lu[i * size + k] / diag;
lu[i * size + k] = factor;
for c in k + 1..size {
lu[i * size + c] = lu[i * size + c] - factor * lu[k * size + c];
}
}
}
Some(Self {
offset,
size,
lu,
perm,
})
}
fn solve(&self, r: &[Fix128], z: &mut [Fix128]) {
let n = self.size;
let rb = &r[self.offset..self.offset + n];
let zb = &mut z[self.offset..self.offset + n];
for i in 0..n {
let mut s = rb[self.perm[i]];
for (&l, &zk) in self.lu[i * n..i * n + i].iter().zip(zb.iter()) {
s = s - l * zk;
}
zb[i] = s;
}
for i in (0..n).rev() {
let mut s = zb[i];
for (&u, &zk) in self.lu[i * n + i + 1..(i + 1) * n].iter().zip(&zb[i + 1..]) {
s = s - u * zk;
}
zb[i] = s / self.lu[i * n + i];
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BlockJacobiPreconditioner {
dim: usize,
blocks: Vec<LuBlock>,
}
impl BlockJacobiPreconditioner {
pub fn from_dense(
matrix: &DenseMatrix,
block_sizes: &[usize],
) -> Result<Self, LinearSolverError> {
let total: usize = block_sizes.iter().sum();
if total != matrix.dim() {
return Err(LinearSolverError::DimensionMismatch {
expected: matrix.dim(),
found: total,
});
}
let mut blocks = Vec::with_capacity(block_sizes.len());
let mut offset = 0;
for (index, &size) in block_sizes.iter().enumerate() {
if size == 0 {
return Err(LinearSolverError::InvalidBlockLayout);
}
let mut entries = Vec::with_capacity(size * size);
for i in 0..size {
for j in 0..size {
entries.push(matrix.get(offset + i, offset + j));
}
}
let block = LuBlock::factor(offset, size, entries)
.ok_or(LinearSolverError::SingularPreconditioner { index })?;
blocks.push(block);
offset += size;
}
Ok(Self {
dim: matrix.dim(),
blocks,
})
}
}
impl Preconditioner for BlockJacobiPreconditioner {
fn dim(&self) -> usize {
self.dim
}
fn apply(&self, r: &[Fix128], z: &mut [Fix128]) {
for block in &self.blocks {
block.solve(r, z);
}
}
}