#[cfg(not(feature = "std"))]
use alloc::vec;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use super::{
dot, hypot, negligible, norm, relative, residual, start, BreakdownKind, KrylovConfig,
KrylovSolution, KrylovStats, LinearOperator, LinearSolverError, Preconditioner, Stagnation,
Start,
};
use crate::math::Fix128;
pub fn gmres<A: LinearOperator + ?Sized, M: Preconditioner + ?Sized>(
op: &A,
precond: &M,
b: &[Fix128],
config: &KrylovConfig,
) -> Result<KrylovSolution, LinearSolverError> {
let (rhs_norm, target) = match start(op, precond, b, config)? {
Start::Trivial(solution) => return Ok(solution),
Start::Run { rhs_norm, target } => (rhs_norm, target),
};
let n = op.dim();
let m = config.restart.min(n);
let window = config
.stagnation_window
.max(u32::try_from(m).unwrap_or(u32::MAX).saturating_add(1));
let mut x = vec![Fix128::ZERO; n];
let mut r = b.to_vec();
let mut beta = rhs_norm;
let mut history = vec![beta];
let mut stagnation = Stagnation::new(beta, window);
let mut iterations = 0u32;
let mut cycles = 0u32;
let mut basis: Vec<Vec<Fix128>> = vec![vec![Fix128::ZERO; n]; m + 1];
let mut hess: Vec<Vec<Fix128>> = vec![vec![Fix128::ZERO; m + 1]; m];
let mut cs = vec![Fix128::ZERO; m];
let mut sn = vec![Fix128::ZERO; m];
let mut g = vec![Fix128::ZERO; m + 1];
let mut z = vec![Fix128::ZERO; n];
let mut w = vec![Fix128::ZERO; n];
loop {
if beta <= target {
return Ok(KrylovSolution {
x,
stats: KrylovStats {
iterations,
restarts: cycles.saturating_sub(1),
residual_history: history,
residual_norm: beta,
rhs_norm,
},
});
}
if iterations >= config.max_iterations {
return Err(LinearSolverError::NotConverged {
iterations,
relative_residual: relative(beta, rhs_norm),
});
}
for (v, &ri) in basis[0].iter_mut().zip(&r) {
*v = ri / beta;
}
g.fill(Fix128::ZERO);
g[0] = beta;
let mut cols = 0usize;
let mut breakdown = false;
for j in 0..m {
precond.apply(&basis[j], &mut z);
op.apply(&z, &mut w);
let w_scale = norm(&w)?;
for i in 0..=j {
let h = dot(&w, &basis[i])?;
hess[j][i] = h;
for (wk, &vk) in w.iter_mut().zip(&basis[i]) {
*wk = *wk - h * vk;
}
}
let h_next = norm(&w)?;
hess[j][j + 1] = h_next;
for i in 0..j {
let a = hess[j][i];
let bb = hess[j][i + 1];
hess[j][i] = cs[i] * a + sn[i] * bb;
hess[j][i + 1] = -(sn[i] * a) + cs[i] * bb;
}
let a = hess[j][j];
let d = hypot(a, h_next)?;
if negligible(d, w_scale) {
breakdown = true;
break;
}
cs[j] = a / d;
sn[j] = h_next / d;
hess[j][j] = d;
hess[j][j + 1] = Fix128::ZERO;
g[j + 1] = -(sn[j] * g[j]);
g[j] = cs[j] * g[j];
cols = j + 1;
iterations += 1;
let estimate = g[j + 1].abs();
history.push(estimate);
let stalled = stagnation.record(estimate);
let invariant = negligible(h_next, w_scale);
if !invariant {
for (v, &wk) in basis[j + 1].iter_mut().zip(&w) {
*v = wk / h_next;
}
}
if invariant || estimate <= target || iterations >= config.max_iterations || stalled {
break;
}
}
if cols == 0 {
return Err(LinearSolverError::Breakdown {
iterations,
kind: BreakdownKind::SingularHessenberg,
relative_residual: relative(beta, rhs_norm),
});
}
let mut y = vec![Fix128::ZERO; cols];
for i in (0..cols).rev() {
let mut s = g[i];
for k in i + 1..cols {
s = s - hess[k][i] * y[k];
}
y[i] = s / hess[i][i];
}
w.fill(Fix128::ZERO);
for (k, &yk) in y.iter().enumerate() {
for (wk, &vk) in w.iter_mut().zip(&basis[k]) {
*wk = *wk + yk * vk;
}
}
precond.apply(&w, &mut z);
for (xi, &zi) in x.iter_mut().zip(&z) {
*xi = *xi + zi;
}
residual(op, &x, b, &mut r);
beta = norm(&r)?;
if let Some(last) = history.last_mut() {
*last = beta;
}
cycles += 1;
if breakdown && beta > target {
return Err(LinearSolverError::Breakdown {
iterations,
kind: BreakdownKind::SingularHessenberg,
relative_residual: relative(beta, rhs_norm),
});
}
if beta > target && stagnation.since >= stagnation.window {
return Err(LinearSolverError::Stagnated {
iterations,
relative_residual: relative(stagnation.best, rhs_norm),
without_improvement: stagnation.since,
});
}
}
}