use crate::array::Array;
use crate::error::{NumRs2Error, Result};
use num_traits::{Float, Zero};
use super::core::{compute_norm, compute_norm_vec, dot_vec, matvec, SolverResult};
use super::preconditioners::{JacobiPreconditioner, Preconditioner};
pub fn gmres<T>(
a: &Array<T>,
b: &Array<T>,
x0: Option<&Array<T>>,
tol: Option<T>,
max_iter: Option<usize>,
restart: Option<usize>,
) -> Result<SolverResult<T>>
where
T: Float + Clone + Zero,
{
let shape = a.shape();
if shape.len() != 2 || shape[0] != shape[1] {
return Err(NumRs2Error::DimensionMismatch(
"Matrix must be square".to_string(),
));
}
let n = shape[0];
if b.size() != n {
return Err(NumRs2Error::ShapeMismatch {
expected: vec![n],
actual: b.shape(),
});
}
let tol = tol.unwrap_or_else(|| T::from(1e-6).unwrap_or(T::epsilon()));
let max_iter = max_iter.unwrap_or(n);
let restart = restart.unwrap_or(30.min(n));
let x_init = match x0 {
Some(x) => x.clone(),
None => Array::zeros(&[n]),
};
let b_norm = compute_norm(b)?;
if b_norm.is_zero() {
return Ok(SolverResult {
solution: x_init,
iterations: 0,
residual_norm: T::zero(),
converged: true,
});
}
let mut total_iter = 0;
let mut x_vec = x_init.to_vec();
let b_vec = b.to_vec();
for _ in 0..(max_iter / restart + 1) {
let x_arr = Array::from_vec(x_vec.clone());
let ax = matvec(a, &x_arr)?;
let ax_vec = ax.to_vec();
let r_vec: Vec<T> = b_vec
.iter()
.zip(ax_vec.iter())
.map(|(&bi, &axi)| bi - axi)
.collect();
let r_norm = compute_norm_vec(&r_vec);
if r_norm / b_norm < tol {
return Ok(SolverResult {
solution: Array::from_vec(x_vec),
iterations: total_iter,
residual_norm: r_norm,
converged: true,
});
}
let mut v_vecs: Vec<Vec<T>> = vec![vec![T::zero(); n]; restart + 1];
let inv_r_norm = T::one() / r_norm;
for i in 0..n {
v_vecs[0][i] = r_vec[i] * inv_r_norm;
}
let mut h = vec![vec![T::zero(); restart]; restart + 1];
let mut g = vec![T::zero(); restart + 1]; g[0] = r_norm;
let mut cs_vec = vec![T::zero(); restart];
let mut sn_vec = vec![T::zero(); restart];
let mut k = 0;
for j in 0..restart {
if total_iter >= max_iter {
break;
}
total_iter += 1;
let v_arr = Array::from_vec(v_vecs[j].clone());
let w = matvec(a, &v_arr)?;
let mut w_vec = w.to_vec();
for i in 0..=j {
h[i][j] = dot_vec(&v_vecs[i], &w_vec);
let h_val = h[i][j];
for l in 0..n {
w_vec[l] = w_vec[l] - h_val * v_vecs[i][l];
}
}
h[j + 1][j] = compute_norm_vec(&w_vec);
if h[j + 1][j].abs() < T::from(1e-14).unwrap_or(T::epsilon()) {
k = j + 1;
break;
}
let inv_h = T::one() / h[j + 1][j];
for l in 0..n {
v_vecs[j + 1][l] = w_vec[l] * inv_h;
}
for i in 0..j {
let temp = h[i][j];
h[i][j] = cs_vec[i] * temp + sn_vec[i] * h[i + 1][j];
h[i + 1][j] = -sn_vec[i] * temp + cs_vec[i] * h[i + 1][j];
}
let r_val = (h[j][j].powi(2) + h[j + 1][j].powi(2)).sqrt();
if r_val < T::from(1e-14).unwrap_or(T::epsilon()) {
k = j + 1;
break;
}
let cs = h[j][j] / r_val;
let sn = h[j + 1][j] / r_val;
cs_vec[j] = cs;
sn_vec[j] = sn;
h[j][j] = r_val;
h[j + 1][j] = T::zero();
let temp_g = g[j];
g[j] = cs * temp_g;
g[j + 1] = -sn * temp_g;
k = j + 1;
if g[j + 1].abs() / b_norm < tol {
break;
}
}
let mut y = vec![T::zero(); k];
for i in (0..k).rev() {
let mut sum = g[i];
for j in (i + 1)..k {
sum = sum - h[i][j] * y[j];
}
y[i] = sum / h[i][i];
}
for j in 0..k {
let y_j = y[j];
for i in 0..n {
x_vec[i] = x_vec[i] + y_j * v_vecs[j][i];
}
}
let x_arr = Array::from_vec(x_vec.clone());
let ax = matvec(a, &x_arr)?;
let ax_vec = ax.to_vec();
let r_final_vec: Vec<T> = b_vec
.iter()
.zip(ax_vec.iter())
.map(|(&bi, &axi)| bi - axi)
.collect();
let final_r_norm = compute_norm_vec(&r_final_vec);
if final_r_norm / b_norm < tol || total_iter >= max_iter {
return Ok(SolverResult {
solution: Array::from_vec(x_vec),
iterations: total_iter,
residual_norm: final_r_norm,
converged: final_r_norm / b_norm < tol,
});
}
}
let x_arr = Array::from_vec(x_vec.clone());
let ax = matvec(a, &x_arr)?;
let ax_vec = ax.to_vec();
let r_final_vec: Vec<T> = b_vec
.iter()
.zip(ax_vec.iter())
.map(|(&bi, &axi)| bi - axi)
.collect();
let r_norm = compute_norm_vec(&r_final_vec);
Ok(SolverResult {
solution: Array::from_vec(x_vec),
iterations: total_iter,
residual_norm: r_norm,
converged: false,
})
}
pub fn gmres_precond<T, P>(
a: &Array<T>,
b: &Array<T>,
preconditioner: &P,
x0: Option<&Array<T>>,
tol: Option<T>,
max_iter: Option<usize>,
restart: Option<usize>,
) -> Result<SolverResult<T>>
where
T: Float + Clone + Zero,
P: Preconditioner<T>,
{
let shape = a.shape();
if shape.len() != 2 || shape[0] != shape[1] {
return Err(NumRs2Error::DimensionMismatch(
"Matrix must be square".to_string(),
));
}
let n = shape[0];
if b.size() != n {
return Err(NumRs2Error::ShapeMismatch {
expected: vec![n],
actual: b.shape(),
});
}
let tol = tol.unwrap_or_else(|| T::from(1e-6).unwrap_or(T::epsilon()));
let max_iter = max_iter.unwrap_or(n);
let restart = restart.unwrap_or(30.min(n));
let x_init = match x0 {
Some(x) => x.clone(),
None => Array::zeros(&[n]),
};
let b_norm = compute_norm(b)?;
if b_norm.is_zero() {
return Ok(SolverResult {
solution: x_init,
iterations: 0,
residual_norm: T::zero(),
converged: true,
});
}
let mut total_iter = 0;
let mut x_vec = x_init.to_vec();
let b_vec = b.to_vec();
let mut v_arrays: Vec<Array<T>> = vec![Array::zeros(&[n]); restart + 1];
for _ in 0..(max_iter / restart + 1) {
let x_arr = Array::from_vec(x_vec.clone());
let ax = matvec(a, &x_arr)?;
let ax_vec = ax.to_vec();
let r_vec: Vec<T> = b_vec
.iter()
.zip(ax_vec.iter())
.map(|(&bi, &axi)| bi - axi)
.collect();
let r_norm = compute_norm_vec(&r_vec);
if r_norm / b_norm < tol {
return Ok(SolverResult {
solution: Array::from_vec(x_vec),
iterations: total_iter,
residual_norm: r_norm,
converged: true,
});
}
let mut v_vecs: Vec<Vec<T>> = vec![vec![T::zero(); n]; restart + 1];
let inv_r_norm = T::one() / r_norm;
for i in 0..n {
v_vecs[0][i] = r_vec[i] * inv_r_norm;
}
v_arrays[0] = Array::from_vec(v_vecs[0].clone());
let mut h = vec![vec![T::zero(); restart]; restart + 1];
let mut g = vec![T::zero(); restart + 1]; g[0] = r_norm;
let mut cs_vec = vec![T::zero(); restart];
let mut sn_vec = vec![T::zero(); restart];
let mut k = 0;
for j in 0..restart {
if total_iter >= max_iter {
break;
}
total_iter += 1;
let z = preconditioner.apply(&v_arrays[j])?;
let w = matvec(a, &z)?;
let mut w_vec = w.to_vec();
for i in 0..=j {
h[i][j] = dot_vec(&v_vecs[i], &w_vec);
let h_val = h[i][j];
for l in 0..n {
w_vec[l] = w_vec[l] - h_val * v_vecs[i][l];
}
}
h[j + 1][j] = compute_norm_vec(&w_vec);
if h[j + 1][j].abs() < T::from(1e-14).unwrap_or(T::epsilon()) {
k = j + 1;
break;
}
let inv_h = T::one() / h[j + 1][j];
for l in 0..n {
v_vecs[j + 1][l] = w_vec[l] * inv_h;
}
v_arrays[j + 1] = Array::from_vec(v_vecs[j + 1].clone());
for i in 0..j {
let temp = h[i][j];
h[i][j] = cs_vec[i] * temp + sn_vec[i] * h[i + 1][j];
h[i + 1][j] = -sn_vec[i] * temp + cs_vec[i] * h[i + 1][j];
}
let r_val = (h[j][j].powi(2) + h[j + 1][j].powi(2)).sqrt();
if r_val < T::from(1e-14).unwrap_or(T::epsilon()) {
k = j + 1;
break;
}
let cs = h[j][j] / r_val;
let sn = h[j + 1][j] / r_val;
cs_vec[j] = cs;
sn_vec[j] = sn;
h[j][j] = r_val;
h[j + 1][j] = T::zero();
let temp_g = g[j];
g[j] = cs * temp_g;
g[j + 1] = -sn * temp_g;
k = j + 1;
if g[j + 1].abs() / b_norm < tol {
break;
}
}
let mut y = vec![T::zero(); k];
for i in (0..k).rev() {
let mut sum = g[i];
for jj in (i + 1)..k {
sum = sum - h[i][jj] * y[jj];
}
y[i] = sum / h[i][i];
}
for j in 0..k {
let z_j = preconditioner.apply(&v_arrays[j])?;
let z_j_vec = z_j.to_vec();
let y_j = y[j];
for i in 0..n {
x_vec[i] = x_vec[i] + y_j * z_j_vec[i];
}
}
let x_arr = Array::from_vec(x_vec.clone());
let ax = matvec(a, &x_arr)?;
let ax_vec = ax.to_vec();
let r_final_vec: Vec<T> = b_vec
.iter()
.zip(ax_vec.iter())
.map(|(&bi, &axi)| bi - axi)
.collect();
let final_r_norm = compute_norm_vec(&r_final_vec);
if final_r_norm / b_norm < tol || total_iter >= max_iter {
return Ok(SolverResult {
solution: Array::from_vec(x_vec),
iterations: total_iter,
residual_norm: final_r_norm,
converged: final_r_norm / b_norm < tol,
});
}
}
let x_arr = Array::from_vec(x_vec.clone());
let ax = matvec(a, &x_arr)?;
let ax_vec = ax.to_vec();
let r_final_vec: Vec<T> = b_vec
.iter()
.zip(ax_vec.iter())
.map(|(&bi, &axi)| bi - axi)
.collect();
let r_norm = compute_norm_vec(&r_final_vec);
Ok(SolverResult {
solution: Array::from_vec(x_vec),
iterations: total_iter,
residual_norm: r_norm,
converged: false,
})
}
pub fn gmres_jacobi<T>(
a: &Array<T>,
b: &Array<T>,
x0: Option<&Array<T>>,
tol: Option<T>,
max_iter: Option<usize>,
restart: Option<usize>,
) -> Result<SolverResult<T>>
where
T: Float + Clone + Zero,
{
let precond = JacobiPreconditioner::new(a)?;
gmres_precond(a, b, &precond, x0, tol, max_iter, restart)
}
pub fn fgmres<T, F>(
a: &Array<T>,
b: &Array<T>,
preconditioner: F,
x0: Option<&Array<T>>,
tol: Option<T>,
max_iter: Option<usize>,
restart: Option<usize>,
) -> Result<SolverResult<T>>
where
T: Float + Clone + Zero,
F: Fn(&Array<T>) -> Result<Array<T>>,
{
let shape = a.shape();
if shape.len() != 2 || shape[0] != shape[1] {
return Err(NumRs2Error::DimensionMismatch(
"Matrix must be square".to_string(),
));
}
let n = shape[0];
if b.size() != n {
return Err(NumRs2Error::ShapeMismatch {
expected: vec![n],
actual: b.shape(),
});
}
let tol = tol.unwrap_or_else(|| T::from(1e-6).unwrap_or(T::epsilon()));
let max_iter = max_iter.unwrap_or(n);
let restart = restart.unwrap_or(30.min(n));
let x_init = match x0 {
Some(x) => x.clone(),
None => Array::zeros(&[n]),
};
let b_norm = compute_norm(b)?;
if b_norm.is_zero() {
return Ok(SolverResult {
solution: x_init,
iterations: 0,
residual_norm: T::zero(),
converged: true,
});
}
let mut total_iter = 0;
let mut x_vec = x_init.to_vec();
let b_vec = b.to_vec();
for _ in 0..(max_iter / restart + 1) {
let x_arr = Array::from_vec(x_vec.clone());
let ax = matvec(a, &x_arr)?;
let ax_vec = ax.to_vec();
let r_vec: Vec<T> = b_vec
.iter()
.zip(ax_vec.iter())
.map(|(&bi, &axi)| bi - axi)
.collect();
let r_norm = compute_norm_vec(&r_vec);
if r_norm / b_norm < tol {
return Ok(SolverResult {
solution: Array::from_vec(x_vec),
iterations: total_iter,
residual_norm: r_norm,
converged: true,
});
}
let mut v_vecs: Vec<Vec<T>> = vec![vec![T::zero(); n]; restart + 1];
let mut z_vecs: Vec<Vec<T>> = vec![vec![]; restart];
let inv_r_norm = T::one() / r_norm;
for i in 0..n {
v_vecs[0][i] = r_vec[i] * inv_r_norm;
}
let mut h = vec![vec![T::zero(); restart]; restart + 1];
let mut g = vec![T::zero(); restart + 1]; g[0] = r_norm;
let mut cs_vec = vec![T::zero(); restart];
let mut sn_vec = vec![T::zero(); restart];
let mut k = 0;
for j in 0..restart {
if total_iter >= max_iter {
break;
}
total_iter += 1;
let v_arr = Array::from_vec(v_vecs[j].clone());
let z_arr = preconditioner(&v_arr)?;
z_vecs[j] = z_arr.to_vec();
let z_arr = Array::from_vec(z_vecs[j].clone());
let w = matvec(a, &z_arr)?;
let mut w_vec = w.to_vec();
for i in 0..=j {
h[i][j] = dot_vec(&v_vecs[i], &w_vec);
let h_val = h[i][j];
for l in 0..n {
w_vec[l] = w_vec[l] - h_val * v_vecs[i][l];
}
}
h[j + 1][j] = compute_norm_vec(&w_vec);
if h[j + 1][j].abs() < T::from(1e-14).unwrap_or(T::epsilon()) {
k = j + 1;
break;
}
let inv_h = T::one() / h[j + 1][j];
for l in 0..n {
v_vecs[j + 1][l] = w_vec[l] * inv_h;
}
for i in 0..j {
let temp = h[i][j];
h[i][j] = cs_vec[i] * temp + sn_vec[i] * h[i + 1][j];
h[i + 1][j] = -sn_vec[i] * temp + cs_vec[i] * h[i + 1][j];
}
let r_val = (h[j][j].powi(2) + h[j + 1][j].powi(2)).sqrt();
if r_val < T::from(1e-14).unwrap_or(T::epsilon()) {
k = j + 1;
break;
}
let cs = h[j][j] / r_val;
let sn = h[j + 1][j] / r_val;
cs_vec[j] = cs;
sn_vec[j] = sn;
h[j][j] = r_val;
h[j + 1][j] = T::zero();
let temp_g = g[j];
g[j] = cs * temp_g;
g[j + 1] = -sn * temp_g;
k = j + 1;
if g[j + 1].abs() / b_norm < tol {
break;
}
}
let mut y = vec![T::zero(); k];
for i in (0..k).rev() {
let mut sum = g[i];
for jj in (i + 1)..k {
sum = sum - h[i][jj] * y[jj];
}
y[i] = sum / h[i][i];
}
for j in 0..k {
let y_j = y[j];
for i in 0..n {
x_vec[i] = x_vec[i] + y_j * z_vecs[j][i];
}
}
let x_arr = Array::from_vec(x_vec.clone());
let ax = matvec(a, &x_arr)?;
let ax_vec = ax.to_vec();
let r_final_vec: Vec<T> = b_vec
.iter()
.zip(ax_vec.iter())
.map(|(&bi, &axi)| bi - axi)
.collect();
let final_r_norm = compute_norm_vec(&r_final_vec);
if final_r_norm / b_norm < tol || total_iter >= max_iter {
return Ok(SolverResult {
solution: Array::from_vec(x_vec),
iterations: total_iter,
residual_norm: final_r_norm,
converged: final_r_norm / b_norm < tol,
});
}
}
let x_arr = Array::from_vec(x_vec.clone());
let ax = matvec(a, &x_arr)?;
let ax_vec = ax.to_vec();
let r_final_vec: Vec<T> = b_vec
.iter()
.zip(ax_vec.iter())
.map(|(&bi, &axi)| bi - axi)
.collect();
let r_norm = compute_norm_vec(&r_final_vec);
Ok(SolverResult {
solution: Array::from_vec(x_vec),
iterations: total_iter,
residual_norm: r_norm,
converged: false,
})
}
pub fn fgmres_jacobi<T>(
a: &Array<T>,
b: &Array<T>,
x0: Option<&Array<T>>,
tol: Option<T>,
max_iter: Option<usize>,
restart: Option<usize>,
) -> Result<SolverResult<T>>
where
T: Float + Clone + Zero,
{
let precond = JacobiPreconditioner::new(a)?;
fgmres(a, b, |v| precond.apply(v), x0, tol, max_iter, restart)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::linalg::iterative_solvers::preconditioners::IdentityPreconditioner;
use approx::assert_relative_eq;
#[test]
fn test_gmres_simple() {
let a = Array::from_vec(vec![4.0, 1.0, 0.0, 1.0, 3.0, 1.0, 0.0, 1.0, 4.0]).reshape(&[3, 3]);
let b = Array::from_vec(vec![5.0, 5.0, 5.0]);
let result = gmres(&a, &b, None, Some(1e-10), Some(100), Some(10)).expect("Should solve");
assert!(result.converged, "GMRES should converge for 3x3 system");
let ax = matvec(&a, &result.solution).expect("matvec should work");
let residual: f64 = ax
.to_vec()
.iter()
.zip(b.to_vec().iter())
.map(|(a, b)| (a - b).powi(2))
.sum::<f64>()
.sqrt();
assert!(residual < 1e-6, "Residual should be small");
}
#[test]
fn test_gmres_2x2() {
let a = Array::from_vec(vec![3.0, 1.0, 1.0, 2.0]).reshape(&[2, 2]);
let b = Array::from_vec(vec![1.0, 2.0]);
let result = gmres(&a, &b, None, Some(1e-8), Some(100), Some(10)).expect("Should solve");
assert!(result.converged, "GMRES should converge for 2x2 system");
let x = &result.solution;
assert_relative_eq!(x.get(&[0]).expect("valid"), 0.0, epsilon = 1e-6);
assert_relative_eq!(x.get(&[1]).expect("valid"), 1.0, epsilon = 1e-6);
}
#[test]
fn test_gmres_identity() {
let a = Array::from_vec(vec![1.0, 0.0, 0.0, 1.0]).reshape(&[2, 2]);
let b = Array::from_vec(vec![2.0, 3.0]);
let result = gmres(&a, &b, None, Some(1e-10), Some(100), Some(10)).expect("Should solve");
assert!(result.converged, "GMRES should converge for identity");
assert_relative_eq!(
result.solution.get(&[0]).expect("valid"),
2.0,
epsilon = 1e-10
);
assert_relative_eq!(
result.solution.get(&[1]).expect("valid"),
3.0,
epsilon = 1e-10
);
}
#[test]
fn test_gmres_diagonal() {
let a = Array::from_vec(vec![2.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 4.0]).reshape(&[3, 3]);
let b = Array::from_vec(vec![4.0, 9.0, 16.0]);
let result = gmres(&a, &b, None, Some(1e-10), Some(100), Some(10)).expect("Should solve");
assert!(result.converged, "GMRES should converge for diagonal");
assert_relative_eq!(
result.solution.get(&[0]).expect("valid"),
2.0,
epsilon = 1e-6
);
assert_relative_eq!(
result.solution.get(&[1]).expect("valid"),
3.0,
epsilon = 1e-6
);
assert_relative_eq!(
result.solution.get(&[2]).expect("valid"),
4.0,
epsilon = 1e-6
);
}
#[test]
fn test_gmres_precond_jacobi_simple() {
let a = Array::from_vec(vec![4.0, 1.0, 1.0, 3.0]).reshape(&[2, 2]);
let b = Array::from_vec(vec![5.0, 5.0]);
let precond = JacobiPreconditioner::new(&a).expect("Should create");
let result = gmres_precond(&a, &b, &precond, None, Some(1e-10), Some(100), Some(10))
.expect("Should solve");
assert!(result.converged, "Preconditioned GMRES should converge");
let ax = matvec(&a, &result.solution).expect("matvec should work");
for i in 0..2 {
assert_relative_eq!(
ax.get(&[i]).expect("valid"),
b.get(&[i]).expect("valid"),
epsilon = 1e-6
);
}
}
#[test]
fn test_gmres_jacobi_convenience() {
let a = Array::from_vec(vec![4.0, 1.0, 1.0, 3.0]).reshape(&[2, 2]);
let b = Array::from_vec(vec![5.0, 5.0]);
let result =
gmres_jacobi(&a, &b, None, Some(1e-10), Some(100), Some(10)).expect("Should solve");
assert!(result.converged, "GMRES with Jacobi should converge");
let ax = matvec(&a, &result.solution).expect("matvec should work");
for i in 0..2 {
assert_relative_eq!(
ax.get(&[i]).expect("valid"),
b.get(&[i]).expect("valid"),
epsilon = 1e-6
);
}
}
#[test]
fn test_gmres_precond_identity() {
let a = Array::from_vec(vec![3.0, 1.0, 1.0, 2.0]).reshape(&[2, 2]);
let b = Array::from_vec(vec![1.0, 2.0]);
let precond = IdentityPreconditioner;
let result = gmres_precond(&a, &b, &precond, None, Some(1e-8), Some(100), Some(10))
.expect("Should solve");
assert!(
result.converged,
"GMRES with identity preconditioner should converge"
);
assert_relative_eq!(
result.solution.get(&[0]).expect("valid"),
0.0,
epsilon = 1e-6
);
assert_relative_eq!(
result.solution.get(&[1]).expect("valid"),
1.0,
epsilon = 1e-6
);
}
#[test]
fn test_fgmres_simple() {
let a = Array::from_vec(vec![4.0, 1.0, 1.0, 3.0]).reshape(&[2, 2]);
let b = Array::from_vec(vec![5.0, 5.0]);
let precond = JacobiPreconditioner::new(&a).expect("Should create");
let result = fgmres(
&a,
&b,
|v| precond.apply(v),
None,
Some(1e-10),
Some(100),
Some(10),
)
.expect("Should solve");
assert!(result.converged, "FGMRES should converge");
let ax = matvec(&a, &result.solution).expect("matvec should work");
for i in 0..2 {
assert_relative_eq!(
ax.get(&[i]).expect("valid"),
b.get(&[i]).expect("valid"),
epsilon = 1e-6
);
}
}
#[test]
fn test_fgmres_jacobi_convenience() {
let a = Array::from_vec(vec![4.0, 1.0, 1.0, 3.0]).reshape(&[2, 2]);
let b = Array::from_vec(vec![5.0, 5.0]);
let result =
fgmres_jacobi(&a, &b, None, Some(1e-10), Some(100), Some(10)).expect("Should solve");
assert!(result.converged, "FGMRES with Jacobi should converge");
let ax = matvec(&a, &result.solution).expect("matvec should work");
for i in 0..2 {
assert_relative_eq!(
ax.get(&[i]).expect("valid"),
b.get(&[i]).expect("valid"),
epsilon = 1e-6
);
}
}
#[test]
fn test_fgmres_identity_precond() {
let a = Array::from_vec(vec![3.0, 1.0, 1.0, 2.0]).reshape(&[2, 2]);
let b = Array::from_vec(vec![1.0, 2.0]);
let result = fgmres(
&a,
&b,
|v| Ok(v.clone()),
None,
Some(1e-8),
Some(100),
Some(10),
)
.expect("Should solve");
assert!(result.converged, "FGMRES with identity should converge");
assert_relative_eq!(
result.solution.get(&[0]).expect("valid"),
0.0,
epsilon = 1e-6
);
assert_relative_eq!(
result.solution.get(&[1]).expect("valid"),
1.0,
epsilon = 1e-6
);
}
}