From 4ae2f3f13494f95f3589d7ad21ef2c730f0a07f2 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Thu, 17 Sep 2026 09:45:56 -0400 Subject: [PATCH] Handle null reduced actions with an absolute derivative floor --- experiments/check_numerical_strategies.py | 6 ++++-- experiments/check_numerical_strategies_README.md | 5 +++-- experiments/test_check_numerical_strategies.py | 12 ++++++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/experiments/check_numerical_strategies.py b/experiments/check_numerical_strategies.py index 6cf8a91..4ac8151 100644 --- a/experiments/check_numerical_strategies.py +++ b/experiments/check_numerical_strategies.py @@ -246,7 +246,8 @@ class Assessment: continue alpha, error, relative, floor = (number(row, key) for key in ("alpha", "difference_quotient_error", "relative_error", "residual_subtraction_roundoff_scale")) valid.append((alpha, error, relative, floor)) - self.check(f"reduced_fd_{alpha:g}_finite", all(nonnegative(x) for x in (error, relative, floor))) + self.check(f"reduced_fd_{alpha:g}_finite", all(nonnegative(x) for x in (error, floor)) and + (nonnegative(relative) or abs(action) <= roundoff(action))) self.check(f"reduced_fd_{alpha:g}_completed_sides", all(nonnegative(number(row, key)) and number(row, key) <= tolerance for key in ("plus_gravity_norm", "minus_gravity_norm"))) if error > 20.0 * floor: reliable.append((alpha, error)) @@ -254,7 +255,8 @@ class Assessment: if not valid: self.incomplete("reduced_central_difference_available", reason="No pair completed on both sides") return - self.check("reduced_central_difference_accuracy", min(item[2] for item in valid) <= 1e-5, + self.check("reduced_central_difference_accuracy", any( + (nonnegative(item[2]) and item[2] <= 1e-5) or item[1] <= 20.0 * item[3] for item in valid), minimum_relative_error=min(item[2] for item in valid), tolerance=1e-5) reliable.sort(reverse=True) orders = [math.log(b[1] / a[1]) / math.log(b[0] / a[0]) for a, b in zip(reliable, reliable[1:]) if a[0] != b[0]] diff --git a/experiments/check_numerical_strategies_README.md b/experiments/check_numerical_strategies_README.md index 9d00108..d46fe5d 100644 --- a/experiments/check_numerical_strategies_README.md +++ b/experiments/check_numerical_strategies_README.md @@ -33,8 +33,9 @@ can vanish. Reduced probes must satisfy the freshly prepared gravity completion tolerance, the lifted gravity tangent gate, and agreement of the Schur action with the retained part of the full lifted action. Central differences must reach relative -error at most 1e-5 and show a resolved interval with order at least 1.5, or reach -the recorded residual-subtraction scale. A plateau above that scale is incomplete +error at most 1e-5 or an absolute error within the recorded residual-subtraction +scale. They must also show a resolved interval with order at least 1.5, or reach +that scale. Relative error is not meaningful for a null action. A plateau above that scale is incomplete evidence rather than an automatic claim of a bad Jacobian: the retained-residual amplification of the remaining gravity error has not been bounded. The recorded roundoff scales are heuristics, not rigorous forward-error bounds. diff --git a/experiments/test_check_numerical_strategies.py b/experiments/test_check_numerical_strategies.py index 5ada8bf..2cda822 100644 --- a/experiments/test_check_numerical_strategies.py +++ b/experiments/test_check_numerical_strategies.py @@ -140,6 +140,18 @@ class NumericalStrategyChecks(unittest.TestCase): report, code = assess(self.folder, required=("gravity",)) self.assertEqual(code, 2, report) + def test_null_reduced_action_uses_absolute_arithmetic_floor(self): + summary, _, _, _, differences = gravity_fixture(self.folder) + summary["reduced_action_norm"] = 0.0 + summary["lifted_retained_action_difference"] = 0.0 + for row in differences: + row["difference_quotient_error"] = row["residual_subtraction_roundoff_scale"] * .1 + row["relative_error"] = float("inf") + write_metrics(self.folder, "numerical_strategy_summary.csv", summary) + write_rows(self.folder, "numerical_strategy_reduced_derivative.csv", differences) + report, code = assess(self.folder) + self.assertEqual(code, 0, report) + def test_unfinished_restoration_record_is_incomplete(self): summary, _, _, _, _ = gravity_fixture(self.folder) del summary["accepted_state_change"]