// 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 { 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"); } } } }