test: preserve Dev verification contracts through Refactor APIs

This commit is contained in:
KOKO\Mimi
2026-10-18 02:25:59 +09:00
parent 26009169bf
commit 74a2ce87ee
19 changed files with 6602 additions and 3 deletions
@@ -3,6 +3,7 @@
#include <gtest/gtest.h>
#include <cmath>
#include <future>
#include <initializer_list>
#include <limits>
#include <string>
@@ -11,6 +12,7 @@
#include "fesa/fem/dof_manager.h"
#include "fesa/math/sparse_matrix.h"
#include "math/dense_blas_internal.h"
namespace {
@@ -123,6 +125,42 @@ TEST(MklPardisoSolver, ReusesOneFactorizationForRepeatedRhs) {
EXPECT_LE(NormalizedResidual(matrix, second, rhs_second), 1.0e-10);
}
TEST(MklPardisoSolver, SingleEquationPolicyPreservesLifecycleAndConcurrentTls) {
const auto worker = [] {
const int previous = mkl_set_num_threads_local(0);
struct Restore {
int previous;
~Restore() { static_cast<void>(mkl_set_num_threads_local(previous)); }
} restore{previous};
const int default_threads = mkl_get_max_threads();
fesa::MklPardisoSolver solver;
for (int repeat = 0; repeat < 10; ++repeat) {
if (!solver.Factorize(MakeDenseCsr(1U, {2.0})).IsOk()) return false;
auto solution = MakeVector({9.0});
if (!solver.Solve(MakeVector({4.0}), solution).IsOk() ||
solution[0U] != 2.0) return false;
if (!solver.Factorize(MakeDenseCsr(1U, {4.0})).IsOk()) return false;
if (!solver.Solve(MakeVector({4.0}), solution).IsOk() ||
solution[0U] != 1.0) return false;
auto invalid_rhs = MakeVector({std::numeric_limits<double>::quiet_NaN()});
if (solver.Solve(invalid_rhs, solution).IsOk() || solution[0U] != 1.0)
return false;
if (!solver.Solve(MakeVector({8.0}), solution).IsOk() ||
solution[0U] != 2.0) return false;
if (solver.Factorize(MakeDenseCsr(1U, {-1.0})).IsOk()) return false;
if (solver.Solve(MakeVector({8.0}), solution).IsOk() ||
solution[0U] != 2.0) return false;
if (mkl_get_max_threads() != default_threads ||
mkl_set_num_threads_local(0) != 0) return false;
}
return true;
};
auto first = std::async(std::launch::async, worker);
auto second = std::async(std::launch::async, worker);
EXPECT_TRUE(first.get());
EXPECT_TRUE(second.get());
}
TEST(MklPardisoSolver, StillRejectsNonsymmetricPublicCsr) {
const auto nonsymmetric = MakeDenseCsr(2U, {2.0, 1.0, 0.0, 3.0});
fesa::MklPardisoSolver solver;
@@ -155,6 +193,32 @@ TEST(MklPardisoSolver, RefactorizesWithoutLeakingState) {
1.0e-10);
}
TEST(MklPardisoSolver, SingleEquationScopeTransitionsBetweenRetainedSizes) {
const int previous = mkl_set_num_threads_local(0);
struct Restore {
int previous;
~Restore() { static_cast<void>(mkl_set_num_threads_local(previous)); }
} restore{previous};
const int default_threads = mkl_get_max_threads();
fesa::MklPardisoSolver solver;
ASSERT_TRUE(solver.Factorize(MakeDenseCsr(1U, {2.0})).IsOk());
auto single = MakeVector({0.0});
ASSERT_TRUE(solver.Solve(MakeVector({4.0}), single).IsOk());
EXPECT_DOUBLE_EQ(single[0U], 2.0);
ASSERT_TRUE(solver.Factorize(MakeDenseCsr(2U, {2, 0, 0, 3})).IsOk());
fesa::Vector pair{2U};
ASSERT_TRUE(solver.Solve(MakeVector({4, 6}), pair).IsOk());
EXPECT_NEAR(pair[0U], 2, 1e-14);
EXPECT_NEAR(pair[1U], 2, 1e-14);
EXPECT_EQ(mkl_get_max_threads(), default_threads);
EXPECT_EQ(mkl_set_num_threads_local(0), 0);
ASSERT_TRUE(solver.Factorize(MakeDenseCsr(1U, {4.0})).IsOk());
ASSERT_TRUE(solver.Solve(MakeVector({8.0}), single).IsOk());
EXPECT_DOUBLE_EQ(single[0U], 2.0);
EXPECT_EQ(mkl_get_max_threads(), default_threads);
EXPECT_EQ(mkl_set_num_threads_local(0), 0);
}
TEST(MklPardisoSolver, ClassifiesSingularIndefiniteAndNonfiniteFailures) {
fesa::MklPardisoSolver solver;