#include "test_common.h" #include "./utils/utils.h" #include "./numerics/inverse.h" #include "./numerics/matmul.h" TEST_CASE(Inverse_GJ_Basic_3x3) { using T = double; utils::Matrix A(3,3, T{0}); // Well-conditioned 3x3 A(0,0)=3; A(0,1)=0.2; A(0,2)=-0.1; A(1,0)=0.1; A(1,1)=2.5; A(1,2)=0.3; A(2,0)=-0.2;A(2,1)=0.4; A(2,2)=2.0; auto Ainv = numerics::inverse(A, "Gauss-Jordan"); utils::Matrix I; I.eye(3); auto prod = numerics::matmul(A, Ainv); CHECK(prod.nearly_equal(I, (T)1e-10), "inverse(GJ): A*A^{-1} ≈ I"); } TEST_CASE(Inverse_LU_Basic_3x3) { using T = double; utils::Matrix A(3,3, T{0}); A(0,0)=3; A(0,1)=0.2; A(0,2)=-0.1; A(1,0)=0.1; A(1,1)=2.5; A(1,2)=0.3; A(2,0)=-0.2;A(2,1)=0.4; A(2,2)=2.0; auto Ainv = numerics::inverse(A, "LU"); utils::Matrix I; I.eye(3); auto prod = numerics::matmul(A, Ainv); CHECK(prod.nearly_equal(I, (T)1e-10), "inverse(LU): A*A^{-1} ≈ I"); } TEST_CASE(Inverse_GJ_vs_LU_Consistency) { using T = double; utils::Matrix A(3,3, T{0}); A(0,0)=4; A(0,1)=1; A(0,2)=2; A(1,0)=0; A(1,1)=3; A(1,2)=-1; A(2,0)=0; A(2,1)=0; A(2,2)=2; auto GJ = numerics::inverse(A, "Gauss-Jordan"); auto LU = numerics::inverse(A, "LU"); CHECK(GJ.nearly_equal(LU, (T)1e-12), "inverse: GJ and LU produce nearly the same result"); } TEST_CASE(Inplace_Inverse_LU) { using T = double; utils::Matrix A(3,3, T{0}); A(0,0)=3; A(0,1)=0.2; A(0,2)=-0.1; A(1,0)=0.1; A(1,1)=2.5; A(1,2)=0.3; A(2,0)=-0.2;A(2,1)=0.4; A(2,2)=2.0; auto Ainv_ref = numerics::inverse(A, "LU"); // out-of-place auto A_copy = A; numerics::inplace_inverse(A_copy, "LU"); // in-place CHECK(A_copy.nearly_equal(Ainv_ref, (T)1e-12), "inplace_inverse(LU) equals out-of-place"); } TEST_CASE(Inplace_Inverse_GJ) { using T = double; utils::Matrix A(2,2, T{0}); A(0,0)=2; A(0,1)=1; A(1,0)=1; A(1,1)=3; auto Ainv_ref = numerics::inverse(A, "Gauss-Jordan"); auto A_copy = A; numerics::inplace_inverse(A_copy, "Gauss-Jordan"); CHECK(A_copy.nearly_equal(Ainv_ref, (T)1e-12), "inplace_inverse(GJ) equals out-of-place"); } TEST_CASE(Inverse_Identity) { using T = double; utils::Matrix I; I.eye(3); auto invI = numerics::inverse(I, "LU"); CHECK(invI.nearly_equal(I, (T)0), "inverse(I) == I"); } TEST_CASE(Inverse_NonSquare_Throws) { using T = double; utils::Matrix A(2,3, T{0}); // non-square bool threw1=false, threw2=false; try { auto X = numerics::inverse(A, "LU"); (void)X; } catch(...) { threw1=true; } try { numerics::inplace_inverse(A, "Gauss-Jordan"); } catch(...) { threw2=true; } CHECK(threw1 && threw2, "inverse throws on non-square for both methods"); } TEST_CASE(Inverse_Singular_Throws) { using T = double; utils::Matrix S(3,3, T{0}); S(0,0)=1; S(0,1)=2; S(0,2)=3; S(1,0)=1; S(1,1)=2; S(1,2)=3; // duplicate row -> singular S(2,0)=0; S(2,1)=1; S(2,2)=0; bool threw_gj=false, threw_lu=false; try { auto X = numerics::inverse(S, "Gauss-Jordan"); (void)X; } catch(...) { threw_gj=true; } try { auto X = numerics::inverse(S, "LU"); (void)X; } catch(...) { threw_lu=true; } CHECK(threw_gj && threw_lu, "inverse throws on singular for both methods"); } TEST_CASE(Inverse_Unknown_Method_Throws) { using T = double; utils::Matrix A(2,2, T{0}); A(0,0)=1; A(1,1)=1; bool threw=false; try { auto X = numerics::inverse(A, "Foobar"); (void)X; } catch(...) { threw=true; } CHECK(threw, "inverse unknown method throws"); }