163 lines
5.4 KiB
Rust
163 lines
5.4 KiB
Rust
//! TDD Tests for SparseMatrix additional methods
|
|
//! Following strict Red-Green-Refactor cycle
|
|
//! No mocks, stubs, or TODOs - only full implementations
|
|
|
|
#[cfg(test)]
|
|
mod sparse_matrix_methods_tests {
|
|
use nalgebra::{DMatrix, DVector};
|
|
use rtx_fea::assembly::sparse_matrix::SparseMatrix;
|
|
use rtx_fea::error::FeaResult;
|
|
|
|
#[test]
|
|
fn test_transpose_multiply_vector() {
|
|
// RED: Test transpose multiplication A^T * x
|
|
let mut matrix = SparseMatrix::new(3, 2);
|
|
|
|
// Build a 3x2 matrix
|
|
// [1, 2]
|
|
// [3, 4]
|
|
// [5, 6]
|
|
matrix.add_entry(0, 0, 1.0).unwrap();
|
|
matrix.add_entry(0, 1, 2.0).unwrap();
|
|
matrix.add_entry(1, 0, 3.0).unwrap();
|
|
matrix.add_entry(1, 1, 4.0).unwrap();
|
|
matrix.add_entry(2, 0, 5.0).unwrap();
|
|
matrix.add_entry(2, 1, 6.0).unwrap();
|
|
matrix.finalize().unwrap();
|
|
|
|
let x = DVector::from_vec(vec![1.0, 2.0, 3.0]);
|
|
|
|
// GREEN: A^T * x should produce a 2-element vector
|
|
// A^T = [1, 3, 5] * [1] = [1+6+15] = [22]
|
|
// [2, 4, 6] [2] [2+8+18] [28]
|
|
// [3]
|
|
let result = matrix.transpose_multiply_vector(&x).unwrap();
|
|
assert_eq!(result.len(), 2);
|
|
assert!((result[0] - 22.0).abs() < 1e-10);
|
|
assert!((result[1] - 28.0).abs() < 1e-10);
|
|
}
|
|
|
|
#[test]
|
|
fn test_diagonal_extraction() {
|
|
// RED: Test extracting diagonal from matrix
|
|
let mut matrix = SparseMatrix::new(3, 3);
|
|
|
|
// Build a 3x3 matrix
|
|
// [2, 1, 0]
|
|
// [1, 3, 1]
|
|
// [0, 1, 4]
|
|
matrix.add_entry(0, 0, 2.0).unwrap();
|
|
matrix.add_entry(0, 1, 1.0).unwrap();
|
|
matrix.add_entry(1, 0, 1.0).unwrap();
|
|
matrix.add_entry(1, 1, 3.0).unwrap();
|
|
matrix.add_entry(1, 2, 1.0).unwrap();
|
|
matrix.add_entry(2, 1, 1.0).unwrap();
|
|
matrix.add_entry(2, 2, 4.0).unwrap();
|
|
matrix.finalize().unwrap();
|
|
|
|
// GREEN: Diagonal should be [2, 3, 4]
|
|
let diagonal = matrix.diagonal().unwrap();
|
|
assert_eq!(diagonal.len(), 3);
|
|
assert!((diagonal[0] - 2.0).abs() < 1e-10);
|
|
assert!((diagonal[1] - 3.0).abs() < 1e-10);
|
|
assert!((diagonal[2] - 4.0).abs() < 1e-10);
|
|
}
|
|
|
|
#[test]
|
|
fn test_forward_solve() {
|
|
// RED: Test forward substitution L * x = b
|
|
let mut matrix = SparseMatrix::new(3, 3);
|
|
|
|
// Build lower triangular matrix L
|
|
// [2, 0, 0]
|
|
// [1, 3, 0]
|
|
// [2, 1, 4]
|
|
matrix.add_entry(0, 0, 2.0).unwrap();
|
|
matrix.add_entry(1, 0, 1.0).unwrap();
|
|
matrix.add_entry(1, 1, 3.0).unwrap();
|
|
matrix.add_entry(2, 0, 2.0).unwrap();
|
|
matrix.add_entry(2, 1, 1.0).unwrap();
|
|
matrix.add_entry(2, 2, 4.0).unwrap();
|
|
matrix.finalize().unwrap();
|
|
|
|
let b = DVector::from_vec(vec![4.0, 7.0, 16.0]);
|
|
|
|
// GREEN: Solve L * x = b
|
|
// x[0] = 4.0 / 2.0 = 2.0
|
|
// x[1] = (7.0 - 1.0*2.0) / 3.0 = 5.0/3.0
|
|
// x[2] = (16.0 - 2.0*2.0 - 1.0*5.0/3.0) / 4.0 = (16.0 - 4.0 - 5.0/3.0) / 4.0
|
|
let x = matrix.forward_solve(&b).unwrap();
|
|
assert_eq!(x.len(), 3);
|
|
assert!((x[0] - 2.0).abs() < 1e-10);
|
|
assert!((x[1] - 5.0 / 3.0).abs() < 1e-10);
|
|
// Verify full solution
|
|
let result = matrix.multiply_vector(&x).unwrap();
|
|
for i in 0..3 {
|
|
assert!((result[i] - b[i]).abs() < 1e-10);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_backward_solve() {
|
|
// RED: Test backward substitution U * x = b
|
|
let mut matrix = SparseMatrix::new(3, 3);
|
|
|
|
// Build upper triangular matrix U
|
|
// [2, 1, 2]
|
|
// [0, 3, 1]
|
|
// [0, 0, 4]
|
|
matrix.add_entry(0, 0, 2.0).unwrap();
|
|
matrix.add_entry(0, 1, 1.0).unwrap();
|
|
matrix.add_entry(0, 2, 2.0).unwrap();
|
|
matrix.add_entry(1, 1, 3.0).unwrap();
|
|
matrix.add_entry(1, 2, 1.0).unwrap();
|
|
matrix.add_entry(2, 2, 4.0).unwrap();
|
|
matrix.finalize().unwrap();
|
|
|
|
let b = DVector::from_vec(vec![10.0, 7.0, 8.0]);
|
|
|
|
// GREEN: Solve U * x = b using backward substitution
|
|
// x[2] = 8.0 / 4.0 = 2.0
|
|
// x[1] = (7.0 - 1.0*2.0) / 3.0 = 5.0/3.0
|
|
// x[0] = (10.0 - 1.0*5.0/3.0 - 2.0*2.0) / 2.0
|
|
let x = matrix.backward_solve(&b).unwrap();
|
|
assert_eq!(x.len(), 3);
|
|
// Verify full solution
|
|
let result = matrix.multiply_vector(&x).unwrap();
|
|
for i in 0..3 {
|
|
assert!((result[i] - b[i]).abs() < 1e-10);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_solve_vector() {
|
|
// RED: Test general solve A * x = b
|
|
let mut matrix = SparseMatrix::new(3, 3);
|
|
|
|
// Build a symmetric positive definite matrix
|
|
// [4, 1, 0]
|
|
// [1, 3, 1]
|
|
// [0, 1, 2]
|
|
matrix.add_entry(0, 0, 4.0).unwrap();
|
|
matrix.add_entry(0, 1, 1.0).unwrap();
|
|
matrix.add_entry(1, 0, 1.0).unwrap();
|
|
matrix.add_entry(1, 1, 3.0).unwrap();
|
|
matrix.add_entry(1, 2, 1.0).unwrap();
|
|
matrix.add_entry(2, 1, 1.0).unwrap();
|
|
matrix.add_entry(2, 2, 2.0).unwrap();
|
|
matrix.finalize().unwrap();
|
|
|
|
let b = DVector::from_vec(vec![5.0, 6.0, 4.0]);
|
|
|
|
// GREEN: Solve A * x = b
|
|
let x = matrix.solve_vector(&b).unwrap();
|
|
assert_eq!(x.len(), 3);
|
|
|
|
// Verify solution A * x = b
|
|
let result = matrix.multiply_vector(&x).unwrap();
|
|
for i in 0..3 {
|
|
assert!((result[i] - b[i]).abs() < 1e-9);
|
|
}
|
|
}
|
|
}
|