3a53b6ebf7
done single-threded 1d interpolations; linear, rational, cubic spline, polynomial and barycentric. Still not done test functions yet. Still missing multi-core options.
115 lines
3.6 KiB
C++
115 lines
3.6 KiB
C++
#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<T> 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<T>(A, "Gauss-Jordan");
|
|
utils::Matrix<T> I;
|
|
I.eye(3);
|
|
auto prod = numerics::matmul<T>(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<T> 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<T>(A, "LU");
|
|
utils::Matrix<T> I;
|
|
I.eye(3);
|
|
auto prod = numerics::matmul<T>(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<T> 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<T>(A, "Gauss-Jordan");
|
|
auto LU = numerics::inverse<T>(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<T> 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<T>(A, "LU"); // out-of-place
|
|
auto A_copy = A;
|
|
numerics::inplace_inverse<T>(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<T> 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<T>(A, "Gauss-Jordan");
|
|
auto A_copy = A;
|
|
numerics::inplace_inverse<T>(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<T> I;
|
|
I.eye(3);
|
|
auto invI = numerics::inverse<T>(I, "LU");
|
|
CHECK(invI.nearly_equal(I, (T)0), "inverse(I) == I");
|
|
}
|
|
TEST_CASE(Inverse_NonSquare_Throws) {
|
|
using T = double;
|
|
utils::Matrix<T> A(2,3, T{0}); // non-square
|
|
bool threw1=false, threw2=false;
|
|
try { auto X = numerics::inverse<T>(A, "LU"); (void)X; } catch(...) { threw1=true; }
|
|
try { numerics::inplace_inverse<T>(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<T> 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<T>(S, "Gauss-Jordan"); (void)X; } catch(...) { threw_gj=true; }
|
|
try { auto X = numerics::inverse<T>(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<T> A(2,2, T{0});
|
|
A(0,0)=1; A(1,1)=1;
|
|
bool threw=false;
|
|
try { auto X = numerics::inverse<T>(A, "Foobar"); (void)X; } catch(...) { threw=true; }
|
|
CHECK(threw, "inverse unknown method throws");
|
|
} |