#include "test_common.h" #include "./utils/matrix.h" #include "./numerics/initializers/eye.h" #include // Tiny helper template static bool is_identity(const utils::Matrix& M) { if (M.rows() != M.cols()) return false; for (std::uint64_t i = 0; i < M.rows(); ++i) for (std::uint64_t j = 0; j < M.cols(); ++j) if ((i == j && M(i,j) != T{1}) || (i != j && M(i,j) != T{0})) return false; return true; } // ============ Basic correctness ============ TEST_CASE(Generate_EYE_double) { utils::Matrix I = numerics::eye(5); CHECK_EQ(I.rows(), 5, "rows should be 5"); CHECK_EQ(I.cols(), 5, "cols should be 5"); CHECK(is_identity(I), "eye(5) is not identity"); } TEST_CASE(Generate_EYE_int) { utils::Matrix I = numerics::eye(3); CHECK_EQ(I.rows(), 3, "rows should be 3"); CHECK_EQ(I.cols(), 3, "cols should be 3"); CHECK(is_identity(I), "eye(3) is not identity"); } TEST_CASE(Inplace_EYE_resize_creates_identity) { utils::Md A; // empty numerics::inplace_eye(A, 7); // resize to 7x7 and set identity CHECK_EQ(A.rows(), 7, "rows should be 7"); CHECK_EQ(A.cols(), 7, "cols should be 7"); CHECK(is_identity(A), "inplace_eye resize did not create identity"); } TEST_CASE(Inplace_EYE_inplace_on_square) { utils::Md A(4,4, 42.0); // junk values numerics::inplace_eye(A); // N==0 → must be square, zero + diag CHECK(is_identity(A), "inplace_eye on square did not produce identity"); } TEST_CASE(Inplace_EYE_throws_on_non_square) { utils::Md A(2,3, 5.0); bool threw = false; try { numerics::inplace_eye(A); // N==0 and non-square → throws } catch (const std::runtime_error&) { threw = true; } CHECK(threw, "inplace_eye should throw on non-square when N==0"); } // ============ OpenMP variants (compiled only when -fopenmp is used) ============ #ifdef _OPENMP TEST_CASE(EYE_OMP_matches_serial) { utils::Md I1 = numerics::eye(64); utils::Md I2 = numerics::eye_omp(64); for (uint64_t i = 0; i < I1.rows(); ++i){ for (uint64_t j = 0; j < I1.cols(); ++j){ CHECK(I1(i,j) == I2(i,j), "eye_omp != eye"); } } utils::Md A(64,64, 3.14); numerics::inplace_eye_omp(A); // N==0 → must be square CHECK(is_identity(A), "inplace_eye_omp did not produce identity"); } TEST_CASE(EYE_OMP_speed) { uint64_t prev = omp_get_max_threads(); if (prev <= 1) return; omp_set_num_threads(16); uint64_t N = 16384; utils::Matrix I1(N,N,1), I2(N,N,1); auto t0 = std::chrono::high_resolution_clock::now(); numerics::inplace_eye(I1); double t1 = std::chrono::duration(std::chrono::high_resolution_clock::now() - t0).count(); t0 = std::chrono::high_resolution_clock::now(); numerics::inplace_eye_omp(I2); double t2 = std::chrono::duration(std::chrono::high_resolution_clock::now() - t0).count(); omp_set_num_threads(prev); CHECK(t2 <= t1, "collapse_omp: multi-thread slower than single-thread"); } TEST_CASE(EYE_OMP_nested_ok) { // Ensure nested regions are allowed for this test (won't error if not) uint64_t prev_levels = omp_get_max_active_levels(); uint64_t prev = omp_get_max_threads(); omp_set_max_active_levels(2); // Outer team size (small); inner will be large when nesting is allowed const int outer_threads = 1; // Inner team size to request during nested-enabled run const int inner_threads = 12; uint64_t N = 4096*8; utils::Matrix I1(N, N, 7); utils::Matrix I2(N, N, 7); // ---------- baseline: inner runs with a small team (no outer region) ---------- omp_set_max_active_levels(1); // disable nesting for baseline omp_set_num_threads(outer_threads); for (uint64_t i = 0; i < 2; ++i){ numerics::inplace_eye_omp(I1); } // ---------- nested: outer×inner (only one outer thread launches inner) ---------- omp_set_max_active_levels(2); // allow one nested level #pragma omp parallel num_threads(outer_threads) { #pragma omp single // avoid racing on I1 { omp_set_num_threads(inner_threads); for (uint64_t i = 0; i < 2; ++i){ numerics::inplace_eye_omp(I2); } } } omp_set_max_active_levels(prev_levels); omp_set_num_threads(prev); CHECK(is_identity(I1), "EYE_OMP_nested did not produce identity"); CHECK(is_identity(I2), "EYE_OMP_nested did not produce identity"); } TEST_CASE(EYE_OMP_auto_is_identity) { auto I = numerics::eye_omp_auto(32); CHECK(is_identity(I), "eye_omp_auto did not produce identity"); } #endif