aboutsummaryrefslogtreecommitdiff
path: root/tests/test_cholesky.cpp
diff options
context:
space:
mode:
authory-jan137 <yousefjan24000@gmail.com>2026-05-03 17:33:09 +0300
committery-jan137 <yousefjan24000@gmail.com>2026-05-03 17:33:09 +0300
commit050ccc72c3c763eeffcb866948cdac15f485aa15 (patch)
tree2199e2b41d4bbd78083fd1773cc4d6ecc4c59bc9 /tests/test_cholesky.cpp
parent4602b36e9d5ea08656e3222846a1f161bbb1cec1 (diff)
Add cholesky solve
Diffstat (limited to 'tests/test_cholesky.cpp')
-rw-r--r--tests/test_cholesky.cpp135
1 files changed, 135 insertions, 0 deletions
diff --git a/tests/test_cholesky.cpp b/tests/test_cholesky.cpp
new file mode 100644
index 0000000..a92563f
--- /dev/null
+++ b/tests/test_cholesky.cpp
@@ -0,0 +1,135 @@
+import linalgebra;
+
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <cmath>
+#include <cstddef>
+#include <random>
+
+using linalgebra::CholeskyResult;
+using linalgebra::DimensionMismatchError;
+using linalgebra::LinAlgError;
+using linalgebra::Matrix;
+using linalgebra::Vector;
+
+namespace {
+
+double reconstruction_error(const Matrix& A, const CholeskyResult& chol) {
+ const Matrix LLT = chol.L * linalgebra::transpose(chol.L);
+ const std::size_t n = A.rows();
+ double err = 0.0;
+ for (std::size_t i = 0; i < n; ++i) {
+ for (std::size_t j = 0; j < n; ++j) {
+ const double d = A(i, j) - LLT(i, j);
+ err += d * d;
+ }
+ }
+ return std::sqrt(err);
+}
+
+Matrix make_spd(std::size_t n, std::mt19937& rng) {
+ std::uniform_real_distribution<double> dist(-1.0, 1.0);
+ Matrix B(n, n);
+ for (std::size_t i = 0; i < n; ++i) {
+ for (std::size_t j = 0; j < n; ++j) {
+ B(i, j) = dist(rng);
+ }
+ }
+ Matrix A = linalgebra::transpose(B) * B;
+ for (std::size_t i = 0; i < n; ++i) {
+ A(i, i) += static_cast<double>(n);
+ }
+ return A;
+}
+
+} // namespace
+
+TEST_CASE("Cholesky factor 2x2", "[cholesky]") {
+ Matrix A{{4.0, 2.0}, {2.0, 3.0}};
+ auto chol = linalgebra::cholesky_factor(A);
+
+ REQUIRE(reconstruction_error(A, chol) < 1e-12);
+
+ REQUIRE(chol.L(0, 0) == Catch::Approx(2.0));
+ REQUIRE(chol.L(1, 0) == Catch::Approx(1.0));
+ REQUIRE(chol.L(1, 1) == Catch::Approx(std::sqrt(2.0)));
+ REQUIRE(chol.L(0, 1) == Catch::Approx(0.0).margin(1e-15));
+}
+
+TEST_CASE("Cholesky factor 3x3", "[cholesky]") {
+ Matrix A{{25.0, 15.0, -5.0},
+ {15.0, 18.0, 0.0},
+ {-5.0, 0.0, 11.0}};
+ auto chol = linalgebra::cholesky_factor(A);
+ REQUIRE(reconstruction_error(A, chol) < 1e-12);
+}
+
+TEST_CASE("Cholesky factor identity", "[cholesky]") {
+ auto I = Matrix::identity(5);
+ auto chol = linalgebra::cholesky_factor(I);
+ REQUIRE(reconstruction_error(I, chol) < 1e-14);
+ for (std::size_t i = 0; i < 5; ++i) {
+ REQUIRE(chol.L(i, i) == Catch::Approx(1.0));
+ }
+}
+
+TEST_CASE("Cholesky factor random SPD", "[cholesky]") {
+ std::mt19937 rng(42);
+ for (std::size_t n : {4, 8, 16, 32}) {
+ Matrix A = make_spd(n, rng);
+ auto chol = linalgebra::cholesky_factor(A);
+ REQUIRE(reconstruction_error(A, chol) < 1e-10);
+ }
+}
+
+TEST_CASE("Cholesky solve", "[cholesky]") {
+ Matrix A{{4.0, 2.0}, {2.0, 3.0}};
+ Vector b{8.0, 7.0};
+ auto chol = linalgebra::cholesky_factor(A);
+ Vector x = linalgebra::cholesky_solve(chol, b);
+
+ double residual = linalgebra::norm2(A * x - b);
+ REQUIRE(residual < 1e-12);
+}
+
+TEST_CASE("Cholesky solve random SPD", "[cholesky]") {
+ std::mt19937 rng(123);
+ std::uniform_real_distribution<double> dist(-5.0, 5.0);
+
+ for (std::size_t n : {5, 10, 20}) {
+ Matrix A = make_spd(n, rng);
+ Vector b(n);
+ for (std::size_t i = 0; i < n; ++i) {
+ b[i] = dist(rng);
+ }
+
+ auto chol = linalgebra::cholesky_factor(A);
+ Vector x = linalgebra::cholesky_solve(chol, b);
+
+ double residual = linalgebra::norm2(A * x - b);
+ REQUIRE(residual < 1e-10);
+ }
+}
+
+TEST_CASE("Cholesky rejects non-square matrix", "[cholesky]") {
+ Matrix A(3, 4);
+ REQUIRE_THROWS_AS(linalgebra::cholesky_factor(A), DimensionMismatchError);
+}
+
+TEST_CASE("Cholesky rejects non-symmetric matrix", "[cholesky]") {
+ Matrix A{{1.0, 2.0}, {3.0, 4.0}};
+ REQUIRE_THROWS_AS(linalgebra::cholesky_factor(A), LinAlgError);
+}
+
+TEST_CASE("Cholesky rejects non-positive-definite matrix", "[cholesky]") {
+ Matrix A{{1.0, 0.0}, {0.0, -1.0}};
+ REQUIRE_THROWS_AS(linalgebra::cholesky_factor(A), LinAlgError);
+}
+
+TEST_CASE("Cholesky solve dimension mismatch", "[cholesky]") {
+ Matrix A{{4.0, 2.0}, {2.0, 3.0}};
+ auto chol = linalgebra::cholesky_factor(A);
+ Vector b{1.0, 2.0, 3.0};
+ REQUIRE_THROWS_AS(linalgebra::cholesky_solve(chol, b), DimensionMismatchError);
+}