use rsparse::data::Sprs;
pub mod dense_math;
fn get_sprs_col<T: rsparse::data::Numeric<T>>(a: &Sprs<T>, col: usize) -> Sprs<T> {
let mut r = Sprs::new();
r.nzmax = (a.p[col + 1] - a.p[col]) as usize;
r.m = a.m;
r.n = 1;
r.p.push(0);
r.p.push(r.nzmax as isize);
for i in a.p[col]..a.p[col + 1] {
r.i.push(a.i[i as usize]);
r.x.push(a.x[i as usize]);
}
r
}
fn add_col_dense<T: rsparse::data::Numeric<T>>(a: &mut [Vec<T>], cm: &[Vec<T>]) {
assert_eq!(a.len(), cm.len());
for i in 0..a.len() {
a[i].push(cm[i][0]);
}
}
fn add_col_sparse<T: rsparse::data::Numeric<T>>(a: &mut Sprs<T>, cm: &Sprs<T>) {
assert_eq!(a.m, cm.m);
a.p.push(a.p[a.p.len() - 1] + cm.nzmax as isize);
for i in 0..cm.nzmax {
a.i.push(cm.i[i]);
a.x.push(cm.x[i]);
}
a.nzmax += cm.nzmax;
a.n += 1;
}
fn norm2<T: rsparse::data::Numeric<T>>(v: &Sprs<T>) -> T {
let mut r: T = T::zero();
for i in 0..v.x.len() {
r += v.x[i].powf(2.);
}
r.powf(0.5)
}
fn norm2_vec<T: rsparse::data::Numeric<T>>(v: &[T]) -> T {
let mut r: T = T::zero();
for i in v.iter() {
r += i.powf(2.);
}
r.powf(0.5)
}
fn arnoldi<T: rsparse::data::Numeric<T>>(a: &Sprs<T>, q: &Sprs<T>, k: usize) -> (Vec<T>, Sprs<T>) {
let mut qv = a * get_sprs_col(q, k); let mut h = Vec::with_capacity(k + 2);
for i in 0..=k {
let qci = get_sprs_col(q, i);
let ht = rsparse::transpose(&qv) * &qci;
if ht.i[0] == 0 {
h.push(ht.x[0]);
} else {
h.push(T::zero());
}
qv = qv - rsparse::scxmat(h[i], &qci);
}
h.push(norm2(&qv));
qv = qv / h[k + 1];
(h, qv)
}
fn givens_rotation<T: rsparse::data::Numeric<T>>(v1: T, v2: T) -> (T, T) {
let t = (v1.powf(2.) + v2.powf(2.)).powf(0.5);
let cs = v1 / t;
let sn = v2 / t;
(cs, sn)
}
fn apply_givens_rotation<T: rsparse::data::Numeric<T>>(h: &mut [T], cs: &mut [T], sn: &mut [T], k: usize) {
for i in 0..k {
let temp = cs[i] * h[i] + sn[i] * h[i + 1];
h[i + 1] = -sn[i] * h[i] + cs[i] * h[i + 1];
h[i] = temp;
}
(cs[k], sn[k]) = givens_rotation(h[k], h[k + 1]);
h[k] = cs[k] * h[k] + sn[k] * h[k + 1];
h[k + 1] = T::zero();
}
pub fn gmres<T: rsparse::data::Numeric<T> + dense_math::Num>(
a: &Sprs<T>,
b: &[T],
x: &mut Vec<T>,
max_iter: usize,
threshold: T,
) -> Result<(), String> {
let r = rsparse::gaxpy(&rsparse::scxmat(-T::one(), a), x, b);
let b_norm = norm2_vec(b);
let r_norm = norm2_vec(&r);
let mut error = r_norm / b_norm;
let mut sn = vec![<T as rsparse::data::Zero>::zero(); max_iter];
let mut cs = vec![<T as rsparse::data::Zero>::zero(); max_iter];
let mut e1 = vec![<T as rsparse::data::Zero>::zero(); max_iter + 1];
e1[0] = T::one();
let mut e = vec![error];
let mut q = Sprs::new_from_vec(&dense_math::transpose(&[dense_math::scxvec(
r_norm.powf(-1.),
&r,
)]));
let mut beta = dense_math::scxvec(r_norm, &e1);
let mut hs = Vec::with_capacity(max_iter);
let mut ks = 0;
for k in 0..max_iter {
ks = k;
let (mut h, qv) = arnoldi(a, &q, k);
add_col_sparse(&mut q, &qv);
apply_givens_rotation(&mut h, &mut cs, &mut sn, k);
hs.push(h.clone());
beta[k + 1] = -sn[k] * beta[k];
beta[k] *= cs[k];
error = T::abs(beta[k + 1]) / b_norm;
e.push(error);
if error <= threshold {
break;
}
}
let col_len = ks + 1;
let mut hm = vec![vec![]; col_len];
for hi in hs.iter().take(col_len) {
let mut th = hi.clone();
th.resize(col_len, <T as rsparse::data::Zero>::zero());
add_col_dense(&mut hm, &dense_math::transpose(&[th[..col_len].to_vec()]));
}
q.p = q.p[..col_len + 1].to_vec();
q.n = col_len;
let mut y = beta[..col_len].to_vec();
let hms = rsparse::data::Sprs::new_from_vec(&hm);
rsparse::usolve(&hms, &mut y);
*x = rsparse::gaxpy(&q, &y, x);
if error <= threshold {
Ok(())
} else {
Err(format!(
"GMRES did not converge. Error: {}. Threshold: {}",
error, threshold
))
}
}
pub mod test_utils;
#[test]
fn norm2_1() {
let a = Sprs::new_from_vec(&[
vec![0.888641],
vec![0.695741],
vec![0.149974],
vec![0.429292],
vec![0.454428],
]);
let n = norm2(&a);
assert_eq!(n, 1.29885603324079);
}
#[test]
fn norm2_vec_1() {
let a = vec![0.888641, 0.695741, 0.149974, 0.429292, 0.454428];
let n = norm2_vec(&a);
assert_eq!(n, 1.29885603324079);
}
#[test]
fn arnoldi_1() {
let a = Sprs::new_from_vec(&[
vec![0.888641, 0.477151, 0.764081, 0.244348, 0.662542],
vec![0.695741, 0.991383, 0.800932, 0.089616, 0.250400],
vec![0.149974, 0.584978, 0.937576, 0.870798, 0.990016],
vec![0.429292, 0.459984, 0.056629, 0.567589, 0.048561],
vec![0.454428, 0.253192, 0.173598, 0.321640, 0.632031],
]);
let q = Sprs::new_from_vec(&[
vec![-0.491347],
vec![-0.200666],
vec![-0.817626],
vec![-0.137704],
vec![-0.175601],
]);
let (h, qv) = arnoldi(&a, &q, 0);
test_utils::assert_eq_f_vec(&h, &vec![2.077054, 1.022011], 1e-5);
test_utils::assert_eq_f2d_vec(
&qv.to_dense(),
&vec![
vec![-0.280376],
vec![-0.817181],
vec![0.437209],
vec![-0.146969],
vec![-0.202122],
],
1e-5,
);
}
#[test]
fn arnoldi_2() {
let a = Sprs::new_from_vec(&[
vec![0.888641, 0.477151, 0.764081, 0.244348, 0.662542],
vec![0.695741, 0.991383, 0.800932, 0.089616, 0.250400],
vec![0.149974, 0.584978, 0.937576, 0.870798, 0.990016],
vec![0.429292, 0.459984, 0.056629, 0.567589, 0.048561],
vec![0.454428, 0.253192, 0.173598, 0.321640, 0.632031],
]);
let q = Sprs::new_from_vec(&[
vec![-0.491347, -0.280376, 0.396178, 0.585492],
vec![-0.200666, -0.817181, 0.078428, -0.284736],
vec![-0.817626, 0.437209, -0.041516, -0.246211],
vec![-0.137704, -0.146969, -0.848475, 0.474661],
vec![-0.175601, -0.202122, -0.339498, -0.538704],
]);
let (h, qv) = arnoldi(&a, &q, 3);
test_utils::assert_eq_f_vec(
&h,
&vec![0.364447, -0.084894, -0.297025, 0.312162, 0.107295],
1e-5,
);
test_utils::assert_eq_f2d_vec(
&qv.to_dense(),
&vec![
vec![0.424511],
vec![-0.452464],
vec![-0.279270],
vec![-0.119267],
vec![0.723084],
],
1e-5,
);
}