RustedSciThe 0.4.22

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
use super::bvp_sparse_lu::{ReusableSparseLu, ReusableSparseLuError};
use faer::sparse::{SparseColMat, Triplet};

fn matrix(scale: f64, include_corner: bool) -> SparseColMat<usize, f64> {
    let mut entries = vec![
        Triplet::new(0, 0, 4.0 * scale),
        Triplet::new(1, 0, 2.0 * scale),
        Triplet::new(0, 1, 1.0 * scale),
        Triplet::new(1, 1, 5.0 * scale),
        Triplet::new(2, 1, 1.0 * scale),
        Triplet::new(2, 2, 3.0 * scale),
    ];
    if include_corner {
        entries.push(Triplet::new(0, 2, 0.25 * scale));
    }
    SparseColMat::try_new_from_triplets(3, 3, &entries).expect("test CSC matrix should assemble")
}

#[test]
fn repeated_numeric_factorization_reuses_pattern_and_solves_in_place() {
    let first = matrix(1.0, false);
    let second = matrix(2.0, false);
    let mut lu = ReusableSparseLu::new(3);

    assert!(lu.prepare_symbolic(first.as_ref()).unwrap());
    assert!(!lu.prepare_symbolic(second.as_ref()).unwrap());
    lu.factor_numeric(first.as_ref()).unwrap();
    let mut rhs = [6.0, 12.0, -1.0];
    lu.solve_in_place(&mut rhs).unwrap();
    for (actual, expected) in rhs.into_iter().zip([1.0, 2.0, -1.0]) {
        assert!((actual - expected).abs() < 1e-13);
    }

    lu.factor_numeric(second.as_ref()).unwrap();
    let mut scaled_rhs = [12.0, 24.0, -2.0];
    lu.solve_in_place(&mut scaled_rhs).unwrap();
    for (actual, expected) in scaled_rhs.into_iter().zip([1.0, 2.0, -1.0]) {
        assert!((actual - expected).abs() < 1e-13);
    }
}

#[test]
fn changed_csc_pattern_requires_new_symbolic_analysis() {
    let first = matrix(1.0, false);
    let changed = matrix(1.0, true);
    let mut lu = ReusableSparseLu::new(3);

    assert!(lu.prepare_symbolic(first.as_ref()).unwrap());
    lu.factor_numeric(first.as_ref()).unwrap();
    assert!(lu.prepare_symbolic(changed.as_ref()).unwrap());
    assert!(matches!(
        lu.solve_in_place(&mut [1.0, 2.0, 3.0]),
        Err(ReusableSparseLuError::NotFactorized)
    ));
    lu.factor_numeric(changed.as_ref()).unwrap();
}

#[test]
fn numeric_factorization_rejects_unprepared_pattern() {
    let first = matrix(1.0, false);
    let changed = matrix(1.0, true);
    let mut lu = ReusableSparseLu::new(3);
    lu.prepare_symbolic(first.as_ref()).unwrap();

    assert!(matches!(
        lu.factor_numeric(changed.as_ref()),
        Err(ReusableSparseLuError::PatternMismatch)
    ));
}