use super::functions::*;
pub struct ConjugateGradient {
pub max_iter: usize,
pub tol: f64,
}
impl ConjugateGradient {
pub fn new(max_iter: usize, tol: f64) -> Self {
Self { max_iter, tol }
}
pub fn solve(&self, a: &[f64], b: &[f64], n: usize) -> Vec<f64> {
assert_eq!(a.len(), n * n);
assert_eq!(b.len(), n);
let mut x = vec![0.0; n];
let mut r = b.to_vec();
let mut p = r.clone();
let mut rs_old = dot_slice(&r, &r);
if rs_old.sqrt() < self.tol {
return x;
}
for _ in 0..self.max_iter {
let ap = dense_matvec(a, &p, n);
let p_ap = dot_slice(&p, &ap);
if p_ap.abs() < 1e-60 {
break;
}
let alpha = rs_old / p_ap;
for i in 0..n {
x[i] += alpha * p[i];
r[i] -= alpha * ap[i];
}
let rs_new = dot_slice(&r, &r);
if rs_new.sqrt() < self.tol {
break;
}
let beta = rs_new / rs_old;
for i in 0..n {
p[i] = r[i] + beta * p[i];
}
rs_old = rs_new;
}
x
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum SolverError {
NotPositiveDefinite,
DimensionMismatch,
}
pub struct CholeskyDense {
pub n: usize,
}
impl CholeskyDense {
pub fn new(n: usize) -> Self {
Self { n }
}
pub fn factorize(&self, a: &[f64]) -> Result<Vec<f64>, SolverError> {
let n = self.n;
if a.len() != n * n {
return Err(SolverError::DimensionMismatch);
}
let mut l = vec![0.0; n * n];
for i in 0..n {
for j in 0..=i {
let mut s = a[i * n + j];
for k in 0..j {
s -= l[i * n + k] * l[j * n + k];
}
if i == j {
if s <= 0.0 {
return Err(SolverError::NotPositiveDefinite);
}
l[i * n + j] = s.sqrt();
} else {
l[i * n + j] = s / l[j * n + j];
}
}
}
Ok(l)
}
pub fn solve(&self, l: &[f64], b: &[f64]) -> Vec<f64> {
let n = self.n;
let mut y = vec![0.0; n];
for i in 0..n {
let mut s = b[i];
for k in 0..i {
s -= l[i * n + k] * y[k];
}
y[i] = s / l[i * n + i];
}
let mut x = vec![0.0; n];
for i in (0..n).rev() {
let mut s = y[i];
for k in (i + 1)..n {
s -= l[k * n + i] * x[k];
}
x[i] = s / l[i * n + i];
}
x
}
}
#[derive(Debug, Clone)]
pub struct ConvergenceMonitor {
pub history: Vec<f64>,
pub tol: f64,
pub max_iter: usize,
}
impl ConvergenceMonitor {
pub fn new(tol: f64, max_iter: usize) -> Self {
Self {
history: Vec::new(),
tol,
max_iter,
}
}
pub fn record(&mut self, residual: f64) {
self.history.push(residual);
}
pub fn converged(&self) -> bool {
self.history.last().is_some_and(|&r| r < self.tol)
}
pub fn stagnated(&self) -> bool {
if self.history.len() < 2 {
return false;
}
let n = self.history.len();
let r0 = self.history[n - 2];
let r1 = self.history[n - 1];
let scale = r0.abs().max(1e-60);
(r1 - r0).abs() / scale < 1e-14
}
pub fn convergence_rate(&self) -> Option<f64> {
let n = self.history.len();
if n < 2 || self.history[n - 2].abs() < 1e-60 {
None
} else {
Some(self.history[n - 1] / self.history[n - 2])
}
}
pub fn iterations(&self) -> usize {
self.history.len()
}
}
pub struct GaussSeidel {
pub max_iter: usize,
pub tol: f64,
pub omega: f64,
}
impl GaussSeidel {
pub fn new(max_iter: usize, tol: f64, omega: f64) -> Self {
Self {
max_iter,
tol,
omega,
}
}
pub fn solve(&self, a: &[f64], b: &[f64], n: usize) -> Vec<f64> {
let mut x = vec![0.0; n];
for _ in 0..self.max_iter {
let mut max_change = 0.0_f64;
for i in 0..n {
let mut sigma = 0.0;
for j in 0..n {
if j != i {
sigma += a[i * n + j] * x[j];
}
}
let diag = a[i * n + i];
if diag.abs() < 1e-60 {
continue;
}
let x_new = (1.0 - self.omega) * x[i] + self.omega * (b[i] - sigma) / diag;
max_change = max_change.max((x_new - x[i]).abs());
x[i] = x_new;
}
if max_change < self.tol {
break;
}
}
x
}
pub fn solve_with_stats(&self, a: &[f64], b: &[f64], n: usize) -> (Vec<f64>, SolverStats) {
let mut x = vec![0.0; n];
let mut iters = 0;
let mut converged = false;
for _it in 0..self.max_iter {
iters += 1;
let mut max_change = 0.0_f64;
for i in 0..n {
let mut sigma = 0.0;
for j in 0..n {
if j != i {
sigma += a[i * n + j] * x[j];
}
}
let diag = a[i * n + i];
if diag.abs() < 1e-60 {
continue;
}
let x_new = (1.0 - self.omega) * x[i] + self.omega * (b[i] - sigma) / diag;
max_change = max_change.max((x_new - x[i]).abs());
x[i] = x_new;
}
if max_change < self.tol {
converged = true;
break;
}
}
let ax = dense_matvec(a, &x, n);
let r: Vec<f64> = b.iter().zip(ax.iter()).map(|(bi, ai)| bi - ai).collect();
let residual = dot_slice(&r, &r).sqrt();
(
x,
SolverStats {
iterations: iters,
residual,
converged,
},
)
}
}
#[derive(Debug, Clone)]
pub struct SolverStats {
pub iterations: usize,
pub residual: f64,
pub converged: bool,
}