Initial commit

This commit is contained in:
redclawsystems
2026-03-04 00:08:42 +00:00
commit 4d88dc0584
4449 changed files with 1556714 additions and 0 deletions
@@ -0,0 +1,406 @@
// Copyright (c) 2024 RustyTorch++ Team
// Licensed under the Apache License, Version 2.0
//! Comprehensive tests for GPU solvers using TDD methodology.
#[cfg(disabled)]
mod tests {
use super::super::*;
use crate::assembly::SparseMatrix;
use nalgebra::DVector;
/// Create a test symmetric positive definite matrix
fn create_spd_matrix(size: usize) -> SparseMatrix {
let mut matrix = SparseMatrix::new(size, size);
// Create a diagonally dominant SPD matrix
for i in 0..size {
matrix.add_entry(i, i, (size as f64) + 1.0).unwrap();
if i > 0 {
matrix.add_entry(i, i - 1, -1.0).unwrap();
matrix.add_entry(i - 1, i, -1.0).unwrap();
}
}
matrix.finalize().unwrap();
matrix
}
/// Create a test general matrix
fn create_general_matrix(size: usize) -> SparseMatrix {
let mut matrix = SparseMatrix::new(size, size);
// Create a non-symmetric matrix
for i in 0..size {
matrix.add_entry(i, i, (size as f64) + 1.0).unwrap();
if i > 0 {
matrix.add_entry(i, i - 1, -1.0).unwrap();
}
if i < size - 1 {
matrix.add_entry(i, i + 1, -2.0).unwrap();
}
}
matrix.finalize().unwrap();
matrix
}
/// Create a test right-hand side vector
fn create_rhs(size: usize) -> DVector<f64> {
DVector::from_fn(size, |i, _| (i as f64) + 1.0)
}
#[test]
fn test_gpu_cholesky_solver_capabilities() {
let solver = GpuCholeskyDirect::new();
let caps = solver.capabilities();
assert!(caps.symmetric);
assert!(caps.positive_definite);
assert!(caps.gpu_acceleration);
assert!(caps.multiple_rhs);
assert!(caps.iterative_refinement);
assert!(caps.memory_efficiency >= 3);
assert!(caps.computational_efficiency >= 4);
}
#[test]
fn test_gpu_lu_solver_capabilities() {
let solver = GpuLuDirect::new();
let caps = solver.capabilities();
assert!(!caps.symmetric);
assert!(!caps.positive_definite);
assert!(caps.gpu_acceleration);
assert!(caps.multiple_rhs);
assert!(caps.iterative_refinement);
assert!(caps.memory_efficiency >= 2);
assert!(caps.computational_efficiency >= 4);
}
#[test]
fn test_gpu_ldlt_solver_capabilities() {
let solver = GpuLdltDirect::new();
let caps = solver.capabilities();
assert!(caps.symmetric);
assert!(!caps.positive_definite);
assert!(caps.gpu_acceleration);
assert!(caps.multiple_rhs);
assert!(caps.iterative_refinement);
assert!(caps.memory_efficiency >= 3);
assert!(caps.computational_efficiency >= 4);
}
#[test]
fn test_gpu_cg_solver_capabilities() {
let solver = GpuConjugateGradient::new();
let caps = solver.capabilities();
assert!(caps.symmetric);
assert!(caps.positive_definite);
assert!(caps.gpu_acceleration);
assert!(!caps.multiple_rhs); // CG typically handles single RHS
assert!(!caps.iterative_refinement); // CG is already iterative
assert!(caps.memory_efficiency >= 4);
assert!(caps.computational_efficiency >= 4);
}
#[test]
fn test_gpu_cholesky_solve_small_spd_matrix() {
let matrix = create_spd_matrix(3);
let rhs = create_rhs(3);
let options = SolverOptions::default();
let mut solver = GpuCholeskyDirect::new();
let result = solver.solve(&matrix, &rhs, &options);
match result {
Ok((solution, info)) => {
assert_eq!(solution.len(), 3);
assert!(info.converged);
assert_eq!(info.iterations, 1); // Direct solver
assert!(info.solve_time.as_secs_f64() >= 0.0);
assert!(info.memory_usage > 0);
// Verify solution quality: Ax = b
let computed_rhs = matrix.multiply_vector(&solution).unwrap();
let residual = (&computed_rhs - &rhs).norm();
assert!(residual < 1e-10, "Residual too large: {}", residual);
}
Err(_) => {
// GPU might not be available in test environment
// This is acceptable for CI environments
println!("GPU solver not available, test skipped");
}
}
}
#[test]
fn test_gpu_lu_solve_general_matrix() {
let matrix = create_general_matrix(3);
let rhs = create_rhs(3);
let options = SolverOptions::default();
let mut solver = GpuLuDirect::new();
let result = solver.solve(&matrix, &rhs, &options);
match result {
Ok((solution, info)) => {
assert_eq!(solution.len(), 3);
assert!(info.converged);
assert_eq!(info.iterations, 1); // Direct solver
assert!(info.solve_time.as_secs_f64() >= 0.0);
assert!(info.memory_usage > 0);
// Verify solution quality: Ax = b
let computed_rhs = matrix.multiply_vector(&solution).unwrap();
let residual = (&computed_rhs - &rhs).norm();
assert!(residual < 1e-10, "Residual too large: {}", residual);
}
Err(_) => {
// GPU might not be available in test environment
println!("GPU solver not available, test skipped");
}
}
}
#[test]
fn test_gpu_ldlt_solve_symmetric_indefinite() {
// Create a symmetric but not positive definite matrix
let mut matrix = SparseMatrix::new(3, 3);
matrix.add_entry(0, 0, 1.0).unwrap();
matrix.add_entry(0, 1, 2.0).unwrap();
matrix.add_entry(1, 0, 2.0).unwrap();
matrix.add_entry(1, 1, -1.0).unwrap(); // Negative diagonal element
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 rhs = create_rhs(3);
let options = SolverOptions::default();
let mut solver = GpuLdltDirect::new();
let result = solver.solve(&matrix, &rhs, &options);
match result {
Ok((solution, info)) => {
assert_eq!(solution.len(), 3);
assert!(info.converged);
assert_eq!(info.iterations, 1); // Direct solver
assert!(info.solve_time.as_secs_f64() >= 0.0);
assert!(info.memory_usage > 0);
// Verify solution quality: Ax = b
let computed_rhs = matrix.multiply_vector(&solution).unwrap();
let residual = (&computed_rhs - &rhs).norm();
assert!(residual < 1e-8, "Residual too large: {}", residual);
}
Err(_) => {
// GPU might not be available in test environment
println!("GPU solver not available, test skipped");
}
}
}
#[test]
fn test_gpu_cg_solve_spd_matrix() {
let matrix = create_spd_matrix(5);
let rhs = create_rhs(5);
let mut options = SolverOptions::default();
options.max_iterations = 100;
options.tolerance = 1e-8;
let mut solver = GpuConjugateGradient::new();
let result = solver.solve(&matrix, &rhs, &options);
match result {
Ok((solution, info)) => {
assert_eq!(solution.len(), 5);
assert!(info.solve_time.as_secs_f64() >= 0.0);
assert!(info.memory_usage > 0);
assert!(info.iterations <= options.max_iterations);
if info.converged {
// Verify solution quality: Ax = b
let computed_rhs = matrix.multiply_vector(&solution).unwrap();
let residual = (&computed_rhs - &rhs).norm();
assert!(residual < 1e-6, "Residual too large: {}", residual);
}
}
Err(_) => {
// GPU might not be available in test environment
println!("GPU solver not available, test skipped");
}
}
}
#[test]
fn test_dimension_mismatch_error() {
let matrix = create_spd_matrix(3);
let rhs = create_rhs(2); // Wrong size
let options = SolverOptions::default();
let mut solver = GpuCholeskyDirect::new();
let result = solver.solve(&matrix, &rhs, &options);
// Should fail due to dimension mismatch, regardless of GPU availability
match result {
Err(crate::error::FeaError::Solver(crate::error::SolverError::DimensionMismatch {
..
})) => {
// Expected error
}
Err(_) => {
// Might fail for other reasons if GPU not available
println!("GPU solver not available or other error occurred");
}
Ok(_) => {
panic!("Should have failed due to dimension mismatch");
}
}
}
#[test]
fn test_non_symmetric_matrix_error() {
let matrix = create_general_matrix(3);
let rhs = create_rhs(3);
let options = SolverOptions::default();
// Test that Cholesky solver rejects non-symmetric matrices
let mut chol_solver = GpuCholeskyDirect::new();
let chol_result = chol_solver.solve(&matrix, &rhs, &options);
match chol_result {
Err(crate::error::FeaError::Solver(crate::error::SolverError::MatrixNotSymmetric)) => {
// Expected error
}
Err(_) => {
// Might fail for other reasons if GPU not available
println!("GPU solver not available or other error occurred");
}
Ok(_) => {
panic!("Cholesky solver should reject non-symmetric matrices");
}
}
// Test that CG solver rejects non-symmetric matrices
let mut cg_solver = GpuConjugateGradient::new();
let cg_result = cg_solver.solve(&matrix, &rhs, &options);
match cg_result {
Err(crate::error::FeaError::Solver(crate::error::SolverError::MatrixNotSymmetric)) => {
// Expected error
}
Err(_) => {
// Might fail for other reasons if GPU not available
println!("GPU solver not available or other error occurred");
}
Ok(_) => {
panic!("CG solver should reject non-symmetric matrices");
}
}
}
#[test]
fn test_solver_name_consistency() {
let chol_solver = GpuCholeskyDirect::new();
assert_eq!(chol_solver.name(), "GPU cuSOLVER Cholesky Direct");
let lu_solver = GpuLuDirect::new();
assert_eq!(lu_solver.name(), "GPU cuSOLVER LU Direct");
let ldlt_solver = GpuLdltDirect::new();
assert_eq!(ldlt_solver.name(), "GPU cuSOLVER LDLT Direct");
let cg_solver = GpuConjugateGradient::new();
assert_eq!(cg_solver.name(), "GPU cuSPARSE Conjugate Gradient");
}
#[test]
fn test_solver_supports_gpu() {
let chol_solver = GpuCholeskyDirect::new();
let lu_solver = GpuLuDirect::new();
let ldlt_solver = GpuLdltDirect::new();
let cg_solver = GpuConjugateGradient::new();
// These should return true if GPU is available, false otherwise
// The exact value depends on the test environment
let _ = chol_solver.supports_gpu();
let _ = lu_solver.supports_gpu();
let _ = ldlt_solver.supports_gpu();
let _ = cg_solver.supports_gpu();
}
#[test]
fn test_multiple_rhs_capability() {
let matrix = create_spd_matrix(3);
let mut rhs_matrix = nalgebra::DMatrix::zeros(3, 2);
rhs_matrix.set_column(0, &create_rhs(3));
rhs_matrix.set_column(1, &DVector::from_vec(vec![3.0, 2.0, 1.0]));
let options = SolverOptions::default();
let mut solver = GpuCholeskyDirect::new();
let result = solver.solve_multiple(&matrix, &rhs_matrix, &options);
match result {
Ok((solutions, info)) => {
assert_eq!(solutions.nrows(), 3);
assert_eq!(solutions.ncols(), 2);
assert!(info.converged);
assert!(info.solve_time.as_secs_f64() >= 0.0);
// Verify each solution
for col in 0..2 {
let solution = solutions.column(col);
let rhs = rhs_matrix.column(col);
let computed_rhs = matrix.multiply_vector(&solution.into_owned()).unwrap();
let residual = (&computed_rhs - &rhs.into_owned()).norm();
assert!(
residual < 1e-10,
"Residual too large for column {}: {}",
col,
residual
);
}
}
Err(_) => {
// GPU might not be available in test environment
println!("GPU solver not available, test skipped");
}
}
}
#[test]
fn test_iterative_refinement_option() {
let matrix = create_spd_matrix(3);
let rhs = create_rhs(3);
let mut options = SolverOptions::default();
options.iterative_refinement = true;
options.tolerance = 1e-12;
let mut solver = GpuCholeskyDirect::new();
let result = solver.solve(&matrix, &rhs, &options);
match result {
Ok((solution, info)) => {
assert_eq!(solution.len(), 3);
assert!(info.converged);
// With iterative refinement, the solution should be very accurate
let computed_rhs = matrix.multiply_vector(&solution).unwrap();
let residual = (&computed_rhs - &rhs).norm();
assert!(
residual < 1e-10,
"Residual too large with refinement: {}",
residual
);
}
Err(_) => {
// GPU might not be available in test environment
println!("GPU solver not available, test skipped");
}
}
}
}