use alloc::vec::Vec;
use super::{RationalKrylov, RationalReport};
use crate::{
error::SemiflowError,
float::{from_f64, SemiflowFloat},
lanczos_sched::tridiag_fn_e1,
scratch::ScratchPool,
};
pub(super) struct Lanczos<F: SemiflowFloat> {
pub(super) basis: Vec<Vec<F>>,
alpha: Vec<f64>,
beta: Vec<f64>,
n: usize,
}
impl<F: SemiflowFloat> Lanczos<F> {
pub(super) fn new(n: usize) -> Self {
Self {
basis: Vec::new(),
alpha: Vec::new(),
beta: Vec::new(),
n,
}
}
pub(super) fn basis_push(&mut self, scratch: &mut ScratchPool<F>) -> &mut Vec<F> {
self.basis.push(scratch.take_vec(self.n));
let last = self.basis.len() - 1;
&mut self.basis[last]
}
pub(super) fn start(
&mut self,
rk: &RationalKrylov<F>,
v: &[F],
down: F,
scratch: &mut ScratchPool<F>,
) -> F {
let first = self.basis_push(scratch);
for (q, &x) in first.iter_mut().zip(v) {
*q = x * down;
}
let norm = rk.norm_m(first);
for q in first.iter_mut() {
*q /= norm;
}
norm
}
pub(super) fn combine(&self, coeffs: &[f64], norm_v: F, up: F, out: &mut [F]) {
out.fill(F::zero());
for (q, &c) in self.basis.iter().zip(coeffs) {
let c = norm_v * from_f64::<F>(c);
for (o, &x) in out.iter_mut().zip(q) {
*o += c * x;
}
}
for o in out.iter_mut() {
*o *= up;
}
}
pub(super) fn release(self, scratch: &mut ScratchPool<F>) {
for q in self.basis {
scratch.return_vec(q);
}
}
pub(super) fn run(
&mut self,
rk: &RationalKrylov<F>,
scratch: &mut ScratchPool<F>,
) -> Result<(Vec<f64>, RationalReport), SemiflowError> {
let target = rk.m_min.unwrap_or(usize::MAX);
let mut inner = 0;
let mut w = scratch.take_vec(self.n);
let mut rhs = scratch.take_vec(self.n);
let outcome = loop {
let j = self.basis.len() - 1;
match self.step(rk, j, &mut w, &mut rhs, scratch) {
Ok(it) => inner += it,
Err(e) => break Err(e),
}
let breakdown =
self.beta[j] <= f64::EPSILON * rk.gamma.to_f64().unwrap_or(1.0) || j + 1 == self.n;
if j + 1 >= target || breakdown || j + 1 >= rk.m_max {
let reached = j + 1 >= target || breakdown;
break self.finish(rk, j, inner, reached);
}
self.push_normalised(&w, j, scratch);
};
scratch.return_vec(w);
scratch.return_vec(rhs);
outcome
}
fn finish(
&self,
rk: &RationalKrylov<F>,
j: usize,
inner: usize,
reached: bool,
) -> Result<(Vec<f64>, RationalReport), SemiflowError> {
let f = |th| rk.f(th);
let failed = SemiflowError::ConvergenceFailed {
last_residual: f64::NAN,
max_iter: rk.m_max,
};
let Some(c) = tridiag_fn_e1(&self.alpha, &self.beta[..j], f) else {
return Err(failed);
};
let prev = if j == 0 {
Some(Vec::new())
} else {
tridiag_fn_e1(&self.alpha[..j], &self.beta[..j - 1], f)
};
let diff = prev.map_or(f64::NAN, |p| coeff_diff(&c, &p));
if !reached {
return Err(SemiflowError::ConvergenceFailed {
last_residual: diff,
max_iter: rk.m_max,
});
}
let report = RationalReport {
solves: j + 1,
krylov_dim: j + 1,
est_error: diff,
inner_iterations: inner,
};
Ok((c, report))
}
fn push_normalised(&mut self, w: &[F], j: usize, scratch: &mut ScratchPool<F>) {
let inv_beta = from_f64::<F>(1.0 / self.beta[j]);
let next = self.basis_push(scratch);
for (q, &x) in next.iter_mut().zip(w) {
*q = x * inv_beta;
}
}
fn step(
&mut self,
rk: &RationalKrylov<F>,
j: usize,
w: &mut [F],
rhs: &mut [F],
scratch: &mut ScratchPool<F>,
) -> Result<usize, SemiflowError> {
for (i, (r, &q)) in rhs.iter_mut().zip(&self.basis[j]).enumerate() {
*r = rk.mass_at(i) * q;
}
let iterations = rk.inner.solve(rhs, w, scratch)?;
let alpha = rk.dot_m(w, &self.basis[j]);
axpy(w, -alpha, &self.basis[j]);
if j > 0 {
axpy(w, -from_f64::<F>(self.beta[j - 1]), &self.basis[j - 1]);
}
for q in &self.basis {
let c = rk.dot_m(w, q);
axpy(w, -c, q);
}
self.alpha.push(alpha.to_f64().unwrap_or(f64::NAN));
self.beta.push(rk.norm_m(w).to_f64().unwrap_or(f64::NAN));
Ok(iterations)
}
}
fn axpy<F: SemiflowFloat>(y: &mut [F], a: F, x: &[F]) {
for (yi, &xi) in y.iter_mut().zip(x) {
*yi += a * xi;
}
}
fn coeff_diff(c: &[f64], prev: &[f64]) -> f64 {
libm::sqrt(
c.iter()
.enumerate()
.map(|(i, &x)| {
let d = x - prev.get(i).copied().unwrap_or(0.0);
d * d
})
.sum::<f64>(),
)
}