Handle boundary and line-search fixed-effect inference

This commit is contained in:
2026-08-14 16:54:57 +08:00
parent 48ab77f9ab
commit 9033201948
10 changed files with 220 additions and 41 deletions

View File

@@ -195,6 +195,30 @@ void test_ai_reml_fit_improves_likelihood() {
kr.hypothesis_tests.front().valid &&
kr.hypothesis_tests.front().numerator_df == 2,
"interior KR joint test is invalid");
auto line_search_flagged = fitted;
line_search_flagged.status =
spectra::reml::FitStatus::line_search_failed;
line_search_flagged.error = "synthetic retained line-search iterate";
const auto flagged_satterthwaite =
spectra::reml::infer_fixed_effects_spectral(
fixture.y, fixture.x, fixture.lambda, line_search_flagged,
spectra::reml::FixedEffectTestMethod::satterthwaite, {joint});
require(flagged_satterthwaite.status ==
spectra::reml::FixedEffectInferenceStatus::line_search_conditional,
"line-search Satterthwaite inference was not retained and flagged");
require(flagged_satterthwaite.hypothesis_tests.size() == 1 &&
flagged_satterthwaite.hypothesis_tests.front().valid,
"line-search Satterthwaite hypothesis test is invalid");
const auto flagged_kr = spectra::reml::infer_fixed_effects_spectral(
fixture.y, fixture.x, fixture.lambda, line_search_flagged,
spectra::reml::FixedEffectTestMethod::kenward_roger, {joint});
require(flagged_kr.status ==
spectra::reml::FixedEffectInferenceStatus::line_search_conditional,
"line-search KR inference was not retained and flagged");
require(flagged_kr.hypothesis_tests.size() == 1 &&
flagged_kr.hypothesis_tests.front().valid,
"line-search KR hypothesis test is invalid");
}
void test_probability_distributions_against_r() {
@@ -372,8 +396,11 @@ void test_residual_only_kkt_boundary() {
y, x, lambda, fitted,
spectra::reml::FixedEffectTestMethod::satterthwaite);
require(satterthwaite.status ==
spectra::reml::FixedEffectInferenceStatus::boundary_conditional,
"Satterthwaite boundary inference did not report conditional status");
spectra::reml::FixedEffectInferenceStatus::boundary_ols,
"Satterthwaite request at the boundary did not fall back to OLS");
require(satterthwaite.method ==
spectra::reml::FixedEffectTestMethod::ordinary_least_squares,
"boundary inference did not report ordinary least squares");
require(satterthwaite.coefficient_tests.size() == 1,
"Satterthwaite coefficient test is missing");
const auto& satt = satterthwaite.coefficient_tests.front();
@@ -389,8 +416,11 @@ void test_residual_only_kkt_boundary() {
y, x, lambda, fitted,
spectra::reml::FixedEffectTestMethod::kenward_roger);
require(kr.status ==
spectra::reml::FixedEffectInferenceStatus::boundary_conditional,
"KR boundary inference did not report conditional status");
spectra::reml::FixedEffectInferenceStatus::boundary_ols,
"KR request at the boundary did not fall back to OLS");
require(kr.method ==
spectra::reml::FixedEffectTestMethod::ordinary_least_squares,
"boundary KR request did not report ordinary least squares");
require(kr.coefficient_tests.size() == 1,
"KR coefficient test is missing");
const auto& kr_test = kr.coefficient_tests.front();
@@ -399,8 +429,8 @@ void test_residual_only_kkt_boundary() {
"KR did not recover OLS residual df");
require_near(kr_test.standard_error, expected_se, 2e-11,
"KR did not recover OLS standard error");
require_near(kr_test.statistic, expected_t * expected_t, 2e-10,
"KR did not recover OLS F statistic");
require_near(kr_test.statistic, expected_t, 2e-10,
"boundary fallback did not recover OLS t statistic");
}
} // namespace