RustedSciThe 0.4.15

Rust framework for symbolic and numerical computing:BVP ( Newton-Raphson frozen/damped/with collocations ), IVP( BDF, Radau, Backward Euler, LSODE, LSODA, RK45, DoPri), nonlinear equations ( Levenberg, Gavin) and more
Documentation
#![cfg(feature = "arrayfire")]
use crate::somelinalg::iterative_solvers_gpu::bicgstab::bicgstab_matrix_api::sparsecol_to_banded;
use crate::somelinalg::iterative_solvers_gpu::bicgstab::bicgstab_with_preconditioneer::PreconditionerType;
use crate::somelinalg::iterative_solvers_gpu::gmres::gmres_with_preconditioners::solve_banded_gmres_f32;
use af::{Array, Dim4};
use arrayfire as af;
use faer::sparse::SparseColMat;

#[allow(non_snake_case)]
pub fn gmres_solver(
    A: SparseColMat<usize, f64>,
    b: Vec<f64>,
    x0: Vec<f64>,
    tol: f64,
    max_iter: usize,
    restart: usize,
    preconditioner: PreconditionerType,
) -> Result<(Vec<f64>, usize, f32), Box<dyn std::error::Error>> {
    let (offsets, diags_host) = sparsecol_to_banded(&A);
    let b_f32: Vec<f32> = b.iter().map(|&x| x as f32).collect();
    let x0_f32: Vec<f32> = x0.iter().map(|&x| x as f32).collect();
    let b_array = Array::new(&b_f32, Dim4::new(&[b_f32.len() as u64, 1, 1, 1]));
    let x0_array = Array::new(&x0_f32, Dim4::new(&[x0_f32.len() as u64, 1, 1, 1]));

    let (x_result, iter, res) = solve_banded_gmres_f32(
        A.nrows(),
        &offsets,
        &diags_host,
        &b_array,
        Some(&x0_array),
        tol as f32,
        max_iter,
        restart,
        preconditioner,
    );

    let mut x_host = vec![0.0f32; x_result.elements()];
    x_result.host(&mut x_host);
    let x_f64: Vec<f64> = x_host.iter().map(|&x| x as f64).collect();

    Ok((x_f64, iter, res))
}

#[cfg(all(test, feature = "cuda"))]
mod integration_tests {
    use super::*;
    use faer::{
        col::Col,
        prelude::Solve,
        sparse::{SparseColMat, SymbolicSparseColMat},
    };

    #[test]
    fn test_gmres_solver_on_diagonal() {
        let col_ptr = vec![0, 1, 2, 3];
        let row_idx = vec![0, 1, 2];
        let values = vec![4.0, 4.0, 4.0];
        let symbolic = SymbolicSparseColMat::new_checked(3, 3, col_ptr, None, row_idx);
        let mat = SparseColMat::new(symbolic, values);

        let b = vec![8.0f64, 8.0, 8.0];
        let x0 = vec![0.0f64; 3];

        let (x, iter, res) = gmres_solver(mat, b, x0, 1e-6, 100, 30, PreconditionerType::Vanilla)
            .expect("solver ok");

        for xi in x {
            assert!((xi - 2.0).abs() < 1e-4);
        }
        assert!(iter <= 5);
        assert!(res < 1e-3);
    }

    #[test]
    fn test_gmres_solver_on_tridiagonal() {
        let b = vec![1.0, 0.0, 0.0, 1.0];
        let col_ptr = vec![0, 2, 5, 8, 10];
        let row_idx = vec![0, 1, 0, 1, 2, 1, 2, 3, 2, 3];
        let values = vec![2.0, -1.0, -1.0, 2.0, -1.0, -1.0, 2.0, -1.0, -1.0, 2.0];
        let symbolic = SymbolicSparseColMat::new_checked(4, 4, col_ptr, None, row_idx);
        let mat = SparseColMat::new(symbolic, values);

        let lu = mat.sp_lu().unwrap();
        let b_col: Col<f64> = Col::from_iter(b.clone().iter().map(|x| *x));
        let binding = b_col.clone();
        let b_mat = binding.as_mat();
        let lu_solution = lu.solve(b_mat);
        let x_lu: Vec<f64> = lu_solution.row_iter().map(|x| x[0]).collect();

        let x0 = vec![0.7f64; 4];

        let (x, iter, res) = gmres_solver(
            mat.clone(),
            b,
            x0,
            1e-6,
            1500,
            30,
            PreconditionerType::GS {
                sweeps: 3,
                symmetric: false,
            },
        )
        .expect("solver ok");

        println!("GMRES: x = {:?}", x);
        println!("LU solution {:?}", x_lu);
        println!("iter = {:?}, res = {}", iter, res);

        for (xi, x_lui) in x.iter().zip(x_lu.iter()) {
            assert!((xi - x_lui).abs() < 1e-3, "xi={} xt={}", xi, x_lui);
        }
    }

    #[test]
    fn test_gmres_solver_on_tridiagonal2() {
        let b = vec![1.0, 0.0, 0.0, 1.0];
        let col_ptr = vec![0, 2, 5, 8, 10];
        let row_idx = vec![0, 1, 0, 1, 2, 1, 2, 3, 2, 3];
        let values = vec![
            2.0, -10.0, -10.0, 20.0, -10.0, -10.0, 20.0, -10.0, -10.0, 20.0,
        ];
        let symbolic = SymbolicSparseColMat::new_checked(4, 4, col_ptr, None, row_idx);
        let mat = SparseColMat::new(symbolic, values);

        let lu = mat.sp_lu().unwrap();
        let b_col: Col<f64> = Col::from_iter(b.clone().iter().map(|x| *x));
        let binding = b_col.clone();
        let b_mat = binding.as_mat();
        let lu_solution = lu.solve(b_mat);
        let x_lu: Vec<f64> = lu_solution.row_iter().map(|x| x[0]).collect();

        let x0 = vec![0.7f64; 4];

        let (x, iter, res) = gmres_solver(
            mat.clone(),
            b,
            x0,
            1e-7,
            1500,
            30,
            PreconditionerType::GS {
                sweeps: 3,
                symmetric: false,
            },
        )
        .expect("solver ok");

        println!("GMRES: x = {:?}", x);
        println!("LU solution {:?}", x_lu);
        println!("iter = {:?}, res = {}", iter, res);

        for (xi, x_lui) in x.iter().zip(x_lu.iter()) {
            assert!((xi - x_lui).abs() < 1e-3, "xi={} xt={}", xi, x_lui);
        }
    }
}