xapphire 0.1.0

compute utility
Documentation
use ndarray::prelude::*;
use num::Float;

use crate::lu::solve_lu;
#[cfg(test)]
use crate::util::*;

pub fn solve_cg(
    a: &Array<f64, Ix2>,
    b: &Array<f64, Ix1>,
    x0: &Array<f64, Ix1>,
    eps: f64,
    iter_max: usize,
) -> Result<(Array<f64, Ix1>, usize, f64), &'static str> {
    if a.shape()[0] != a.shape()[1] {
        return Err("expect square matrix");
    }

    let n = a.shape()[0];

    if b.shape()[0] != n {
        return Err("dimension of b not matching a");
    }

    let mut r = b - &a.dot(x0);
    let r0 = r.clone();
    let mut d = r.clone();
    let mut alpha = 0.;
    let mut lambda: f64 = (&d * &r).sum() / (&d * &a.dot(&d)).sum();
    let mut x = x0 + &(lambda * &d);

    let mut it = 1;
    while it <= iter_max {
        let mut r_new = &r - &(a.dot(&d) * lambda);
        alpha = (&r_new * &r_new).sum() / (&r * &r).sum();
        d = &r_new + &(alpha * &d);
        lambda = (&d * &r_new).sum() / (&d * &a.dot(&d)).sum();
        x = x + lambda * &d;
        if (&r_new * &r_new).sum() / (&r0 * &r0).sum() < eps {
            break;
        }
        std::mem::swap(&mut r, &mut r_new);
        it += 1;
    }

    let d: Array<f64, Ix1> = b - &(a.dot(&x));
    let l2 = (&d * &d).sum();

    Ok((x, it, l2))
}

#[test]
fn test_cg_0() {
    let mut a = arr2(&[[4., 1.], [1., 3.]]);

    let orig = a.clone();

    let b = arr1(&[1., 2.]);

    let x0 = arr1(&[2., 1.]);

    let (x, it, res) = solve_cg(&a, &b, &x0, 1e-9, 100).expect("solve_cg");
    dbg!(&x, it, res);
    assert!(arr1_eq(&x, &arr1(&[1. / 11., 7. / 11.])));
}

#[test]
fn test_cg_1() {
    let mut a = arr2(&[[5., 3., 1.], [3., 4., 2.], [1., 2., 3.]]);

    let orig = a.clone();

    let b = arr1(&[41., 47., 32.]);

    let x0 = arr1(&[0., 0., 0.]);

    let (x, it, res) = solve_cg(&a, &b, &x0, 1e-9, 100).expect("solve_cg");
    dbg!(&x, it, res);
    assert!(arr1_eq(&x, &arr1(&[3., 7., 5.])));
}

pub fn solve_cg_precond(
    a: &Array<f64, Ix2>,
    b: &Array<f64, Ix1>,
    x0: &Array<f64, Ix1>,
    precond: &Array<f64, Ix2>,
    eps: f64,
    iter_max: usize,
) -> Result<(Array<f64, Ix1>, usize, f64), &'static str> {
    if a.shape()[0] != a.shape()[1] {
        return Err("expect square matrix");
    }

    let n = a.shape()[0];

    if b.shape()[0] != n {
        return Err("dimension of b not matching a");
    }

    let mut r = b - &a.dot(x0);
    let r0 = r.clone();
    let mut q = solve_lu(&mut precond.clone(), &r0)?;
    let q0 = q.clone();
    let mut d = q.clone();
    let mut alpha = 0.;
    let mut lambda: f64 = (&q * &r).sum() / (&d * &a.dot(&d)).sum();
    let mut x = x0 + &(lambda * &d);

    let mut it = 1;
    while it <= iter_max {
        let mut r_new = &r - &(a.dot(&d) * lambda);
        let mut q_new = solve_lu(&mut precond.clone(), &r_new)?;
        alpha = (&q_new * &r_new).sum() / (&q * &r).sum();
        d = &q_new + &(alpha * &d);
        lambda = (&q_new * &r_new).sum() / (&d * &a.dot(&d)).sum();
        x = x + lambda * &d;
        if (&q_new * &r_new).sum() / (&q0 * &r0).sum() < eps {
            break;
        }
        std::mem::swap(&mut r, &mut r_new);
        std::mem::swap(&mut q, &mut q_new);
        it += 1;
    }

    let d: Array<f64, Ix1> = b - &(a.dot(&x));
    let l2 = (&d * &d).sum();

    Ok((x, it, l2))
}

#[test]
fn test_cg_precond_1() {
    let mut a = arr2(&[[5., 3., 1.], [3., 4., 2.], [1., 2., 3.]]);

    let orig = a.clone();

    let b = arr1(&[41., 47., 32.]);

    let x0 = arr1(&[1., 1., 1.]);

    let mut precond = arr2(&[[5., 0., 0.], [0., 4., 0.], [0., 0., 5.]]);

    let (x, it, res) =
        solve_cg_precond(&a, &b, &x0, &precond, 1e-9, 100).expect("solve_cg_precond");
    dbg!(&x, it, res);
    assert!(arr1_eq_tol(&x, &arr1(&[3., 7., 5.]), 1e-6));
}