trigivs 0.1.3

Rust library for solving tridiagonal systems of linear equations using Givens rotations
Documentation
use approx::assert_abs_diff_eq;
use crate::prelude::*;

#[test]
fn test_determinant(){
    let sup = [4., 7., 7., 100.];
    let diag = [1., 3., 6., 4., 12.];
    let sub = [2., 5., 10., 90.];

    let result = compute_tridiag_determinant(&sup, &diag, &sub);
    let expected = 586080.;

    assert_abs_diff_eq!(result, expected, epsilon=1e-6);
}

#[test]
fn test_1diag_solver(){
    let diag = [2.];
    let rhs = [10.];
    
    let result = solve_givens(&[], &diag, &[], &rhs).unwrap();
    let expected = [5.];

    assert_abs_diff_eq!(result.as_ref() as &[f64], expected.as_ref(), epsilon=1e-6);
}

#[test]
fn test_2diag_solver(){
    let sup = [-4.];
    let diag = [3., 2.];
    let sub = [5.];
    let rhs = [-3., 21.];
    
    let result = solve_givens(&sup, &diag, &sub, &rhs).unwrap();
    let expected = [3., 3.];

    assert_abs_diff_eq!(result.as_ref() as &[f64], expected.as_ref(), epsilon=1e-6);
}

#[test]
fn test_solver(){
    let sup = [4., 7., 7., 100.];
    let diag = [1., 3., 6., 4., 12.];
    let sub = [2., 5., 10., 90.];
    let rhs = [-7., 17., -20., 514., -300.];
    
    let result = solve_givens(&sup, &diag, &sub, &rhs).unwrap();
    let expected = [1., -2., 3., -4., 5.];

    assert_abs_diff_eq!(result.as_ref() as &[f64], expected.as_ref(), epsilon=1e-6);
}

#[test]
fn test_zero_division(){
    let sup = [4., 7., 0., 100.];
    let diag = [1., 3., 0., 4., 12.];
    let sub = [2., 0., 10., 90.];
    let rhs = [-7., 17., -20., 514., -300.];
    
    let result = solve_givens(&sup, &diag, &sub, &rhs);
    
    assert!(matches!(result, Err(SolverErrors::DivisionByZero)));
}

#[test]
fn test_1diag_precomp(){
    let diag = [2.];

    let rhs1 = [10.];
    let rhs2 = [-8.];
    
    let precomp = precompute_givens(&[], &diag, &[]).unwrap();

    let result1 = precomp.solve_givens_rhs(&rhs1).unwrap();
    let result2 = precomp.solve_givens_rhs(&rhs2).unwrap();

    let expected1 = [5.];
    let expected2 = [-4.];

    assert_abs_diff_eq!(result1.as_ref() as &[f64], expected1.as_ref(), epsilon=1e-6);
    assert_abs_diff_eq!(result2.as_ref() as &[f64], expected2.as_ref(), epsilon=1e-6);
}

#[test]
fn test_2diag_precomp(){
    let sup = [-4.];
    let diag = [3., 2.];
    let sub = [5.];

    let rhs1 = [-3., 21.];
    let rhs2  = [-23., 5.];
    
    let precomp = precompute_givens(&sup, &diag, &sub).unwrap();

    let result1 = precomp.solve_givens_rhs(&rhs1).unwrap();
    let result2 = precomp.solve_givens_rhs(&rhs2).unwrap();

    let expected1 = [3., 3.];
    let expected2 = [-1., 5.];

    assert_abs_diff_eq!(result1.as_ref() as &[f64], expected1.as_ref(), epsilon=1e-6);
    assert_abs_diff_eq!(result2.as_ref() as &[f64], expected2.as_ref(), epsilon=1e-6);
}

#[test]
fn test_precomp(){
    let sup = [4., 7., 7., 100.];
    let diag = [1., 3., 6., 4., 12.];
    let sub = [2., 5., 10., 90.];
    
    let rhs1 = [-7., 17., -20., 514., -300.];
    let rhs2 = [43., 57., -72., 450., -1740.];
    
    let precomp = precompute_givens(&sup, &diag, &sub).unwrap();
    
    let result1 = precomp.solve_givens_rhs(&rhs1).unwrap();
    let result2 = precomp.solve_givens_rhs(&rhs2).unwrap();

    let expected1 = [1., -2., 3., -4., 5.];
    let expected2 = [3., 10., 3., -20., 5.];

    assert_abs_diff_eq!(result1.as_ref() as &[f64], expected1.as_ref(), epsilon=1e-6);
    assert_abs_diff_eq!(result2.as_ref() as &[f64], expected2.as_ref(), epsilon=1e-6);
}

#[test]
fn test_precom_direct_eq_solver(){
    let sup = [4., 7., 7., 100.];
    let diag = [1., 3., 6., 4., 12.];
    let sub = [2., 5., 10., 90.];
    let rhs = [-7., 17., -20., 514., -300.];
    
    let precomp = precompute_givens(&sup, &diag, &sub).unwrap();

    let result1 = solve_givens(&sup, &diag, &sub, &rhs).unwrap();
    let result2 = precomp.solve_givens_rhs(&rhs).unwrap();

    assert_abs_diff_eq!(result1.as_ref() as &[f64], result2.as_ref(), epsilon=1e-6);
}

#[test]
fn test_zero_sub_solver(){
    let sup = [4., 7., 7., 100.];
    let diag = [1., 3., 6., 4., 12.];
    let sub = [0., 0., 0., 0.];
    
    let rhs = [-7., 15., -10., 484., 60.];

    let result = solve_givens(&sup, &diag, &sub, &rhs).unwrap();
    let expected = [1., -2., 3., -4., 5.];

    assert_abs_diff_eq!(result.as_ref() as &[f64], expected.as_ref(), epsilon=1e-6);
}

#[test]
fn test_almost_zero_diag_solver(){
    let sup = [4., 7., 7., 100.];
    let diag = [0., 0., 0.5, 0., 0.];
    let sub = [2., 5., 10., 90.];
    
    let rhs = [-8., 23., -36.5, 530., -360.];

    let result = solve_givens(&sup, &diag, &sub, &rhs).unwrap();
    let expected = [1., -2., 3., -4., 5.];

    assert_abs_diff_eq!(result.as_ref() as &[f64], expected.as_ref(), epsilon=1e-6);
}

#[test]
fn test_zero_sub_precomp(){
    let sup = [4., 7., 7., 100.];
    let diag = [1., 3., 6., 4., 12.];
    let sub = [0., 0., 0., 0.];
    
    let rhs = [-7., 15., -10., 484., 60.];

    let precomp = precompute_givens(&sup, &diag, &sub).unwrap();

    let result = precomp.solve_givens_rhs(&rhs).unwrap();
    let expected = [1., -2., 3., -4., 5.];

    assert_abs_diff_eq!(result.as_ref() as &[f64], expected.as_ref(), epsilon=1e-6);
}

#[test]
fn test_almost_zero_diag_precomp(){
    let sup = [4., 7., 7., 100.];
    let diag = [0., 0., 0.5, 0., 0.];
    let sub = [2., 5., 10., 90.];
    
    let rhs = [-8., 23., -36.5, 530., -360.];

    let precomp = precompute_givens(&sup, &diag, &sub).unwrap();

    let result = precomp.solve_givens_rhs(&rhs).unwrap();
    let expected = [1., -2., 3., -4., 5.];

    assert_abs_diff_eq!(result.as_ref() as &[f64], expected.as_ref(), epsilon=1e-6);
}

#[test]
fn test_almost_zero_diag_neg_precomp(){
    let sup = [4., 7., 7., 100.];
    let diag = [0., 0., 0.5, 0., 0.];
    let sub = [-2., 5., 10., 90.];
    
    let rhs = [-8., 19., -36.5, 530., -360.];

    let precomp = precompute_givens(&sup, &diag, &sub).unwrap();

    let result = precomp.solve_givens_rhs(&rhs).unwrap();
    let expected = [1., -2., 3., -4., 5.];
    
    assert_abs_diff_eq!(result.as_ref() as &[f64], expected.as_ref(), epsilon=1e-6);
}

#[cfg(feature = "alloc")]
#[test]
fn test_invalid_diag(){
    let sup = [4., 7., 7., 100.];
    let diag = [1., 3., 6., 4., 12.];
    let sub = [2., 5., 10.];
    let rhs = [-7., 17., -20., 514., -300.];

    let result = solve_givens(&sup, &diag, &sub, &rhs);

    assert!(matches!(result, Err(SolverErrors::InvalidDiagonals)));
}

#[cfg(feature = "alloc")]
#[test]
fn test_invalid_rhs(){
    let sup = [4., 7., 7., 100.];
    let diag = [1., 3., 6., 4., 12.];
    let sub = [2., 5., 10., 90.];
    let rhs = [-7., 17., -20., 514.];
    
    let result = solve_givens(&sup, &diag, &sub, &rhs);

    assert!(matches!(result, Err(SolverErrors::InvalidRhsSizing)));
}

#[cfg(feature = "alloc")]
#[test]
fn test_invalid_diag_precomp(){
    let sup = [4., 7., 7., 100.];
    let diag = [1., 3., 6., 4., 12.];
    let sub = [2., 5., 10.];

    let result = precompute_givens(&sup, &diag, &sub);

    assert!(matches!(result, Err(SolverErrors::InvalidDiagonals)));
}

#[cfg(feature = "alloc")]
#[test]
fn test_invalid_rhs_precomp(){
    let sup = [4., 7., 7., 100.];
    let diag = [1., 3., 6., 4., 12.];
    let sub = [2., 5., 10., 90.];
    
    let rhs = [-7., 17., -20., 514.];
    
    let precomp = precompute_givens(&sup, &diag, &sub).unwrap();
    let result = precomp.solve_givens_rhs(&rhs);

    assert!(matches!(result, Err(SolverErrors::InvalidRhsSizing)));
}

#[test]
fn test_solution_norm(){
    let sup = [4., 7., 7., 100.];
    let diag = [1., 3., 6., 4., 12.];
    let sub = [2., 5., 10., 90.];
    
    let rhs = [-7., 17., -20., 514., -300.];
    let rhs_wrong = [-7., 17.1, -20., 514., -302.];
    
    let result = solve_givens(&sup, &diag, &sub, &rhs).unwrap();
    let norm = compute_solution_norm(&sup, &diag, &sub, &rhs_wrong, &result).unwrap();

    assert_abs_diff_eq!(norm, 2., epsilon=1e-6);
}

#[test]
fn test_1n_solution_norm(){
    let diag = [2.];

    let rhs = [10.];
    let rhs_wrong = [12.];
    
    let result = solve_givens(&[], &diag, &[], &rhs).unwrap();
    let norm = compute_solution_norm(&[], &diag, &[], &rhs_wrong, &result).unwrap();

    assert_abs_diff_eq!(norm, 2., epsilon=1e-6);
}

#[test]
fn test_2n_solution_norm(){
    let sup = [-4.];
    let diag = [3., 2.];
    let sub = [5.];
    let rhs = [-3., 21.];

    let rhs_wrong = [-1., 28.];
    
    let result = solve_givens(&sup, &diag, &sub, &rhs).unwrap();
    let norm = compute_solution_norm(&sup, &diag, &sub, &rhs_wrong, &result).unwrap();

    assert_abs_diff_eq!(norm, 7., epsilon=1e-6);
}

#[test]
fn test_iter_1n_sol(){
    let diag = [2.];

    let rhs = [100.];

    let x_init = [10_000.];
    let x = refine_tridiag_solution_iter_kaczmarz(&[], &diag, &[], &rhs, &x_init, 1000, 0.001).unwrap();

    assert_abs_diff_eq!(x[0], 50., epsilon=0.01);
}

#[test]
fn test_iter_2n_sol(){
    let sup = [-4.];
    let diag = [3., 2.];
    let sub = [5.];
    let rhs = [-3., 21.];

    let expected = [3., 3.];
    let x_init = [-10_000.; 2];

    let x = refine_tridiag_solution_iter_kaczmarz(&sub, &diag, &sup, &rhs, &x_init, 1000, 0.001).unwrap();

    assert_abs_diff_eq!(x.as_ref() as &[f64], &expected.as_ref(), epsilon=0.01);
}

#[test]
fn test_iter_n_sol(){
    let diag = [0.001;10];
    let sub = [-10000.;9];
    let sup = sub.clone();

    let rhs = [-10.; 10];

    let x_init = [-10.; 10];
    let x = refine_tridiag_solution_iter_kaczmarz(&sub, &diag, &sup, &rhs, &x_init, 1000, 0.001).unwrap();

    let norm = compute_solution_norm(&sup, &diag, &sub, &rhs, &x).unwrap();

    assert_abs_diff_eq!(norm, 0., epsilon=0.01);
}