slope-stability gate: the PROJECT'S OWN target is met for the first time — 5922 < 6026 over both sets at 193/199 with the guarantee intact (slope.ratio.derived3.project: APS 2848 + CHA 3074), CHA improves 4516 -> 2903 (x1.56), repairs 77.5% -> 55.3%. Honest cost: APS 2806 -> 2848, which moves us from 1st to 3rd on the canonical set (chandrupatla 2785, toms748 2820), so my stricter S1 fails while the project target passes — a positioning decision, recorded not resolved. Refusal CURRENCY is what decides: bracket progress 4449 vs model shape 2848, a 1601-evaluation difference for the same 'refuse before the step' idea
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -38,7 +38,9 @@ def test_R1_the_published_path_is_bit_identical_with_the_gate_off():
|
||||
|
||||
def test_R2_the_gate_carries_no_chosen_constant():
|
||||
src = inspect.getsource(P.pure_root)
|
||||
start = src.index('if prestep == "progress":')
|
||||
# anchor on the CONDITION, not on a whole line: the line grew a `step is None and`
|
||||
# prefix when the slope gate was inserted ahead of it, and the exact-line anchor broke
|
||||
start = src.index('prestep == "progress"')
|
||||
end = src.index('if step is None and space ==', start) # the gate block only
|
||||
body = src[start:end]
|
||||
# comments carry the MEASURED numbers that justify the gate (77.5 %, 2.310 halvings);
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
"""Gates T1-T5 for the slope-stability gate (PREREG_slope_gate_2026-09-21.md).
|
||||
|
||||
The progress gate refused on the wrong currency (bracket progress) and charged APS 4449
|
||||
against 2806. This gate refuses on the SHAPE of the model instead: two consecutive slope
|
||||
estimates of opposite sign mean the local model is not established, so the evaluation is
|
||||
spent halving. A SIGN is compared — no threshold, no factor, no constant.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import inspect
|
||||
import math
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
from simroot import pure as P
|
||||
|
||||
SMOOTH = [ # simple roots, stable slope sign: the gate must be inert
|
||||
(lambda t: t ** 3 - 2.0 * t - 5.0, 2.0, 3.0),
|
||||
(lambda t: math.exp(t) - 5.0, 0.0, 3.0),
|
||||
(lambda t: t ** 2 - 2.0, 0.5, 2.0),
|
||||
(lambda t: math.cos(t) - t, 0.0, 1.0),
|
||||
]
|
||||
# the bracket must straddle zero AND contain a sign change of the SLOPE: f' = 3t^2 - 1
|
||||
# vanishes at t = +-0.577, so the local slope estimate flips sign inside (-2, 2).
|
||||
# (An earlier version used sin(8t)e^-t + 0.05 on (0,1), which does not straddle zero at
|
||||
# all — f(0) = +0.05, f(1) = +0.41 — and the method rightly refused the call.)
|
||||
WOBBLY = (lambda t: t ** 3 - t + 0.2, -2.0, 2.0)
|
||||
|
||||
|
||||
def test_T1_the_published_path_is_bit_identical_with_the_gate_off():
|
||||
for f, a, b in SMOOTH:
|
||||
for guard in ("credit", "project", "safe", "step"):
|
||||
base = P.pure_root(f, a, b, 1e-12, 300, similarity="ratio",
|
||||
scale="derived3", guard=guard)
|
||||
off = P.pure_root(f, a, b, 1e-12, 300, similarity="ratio",
|
||||
scale="derived3", guard=guard, prestep="none")
|
||||
assert base == off, (guard, base, off)
|
||||
|
||||
|
||||
def test_T2_the_gate_carries_no_constant():
|
||||
src = inspect.getsource(P.pure_root)
|
||||
start = src.index('if prestep == "slope":')
|
||||
end = src.index('if step is None and prestep == "progress"', start)
|
||||
code = "\n".join(line.split("#", 1)[0] for line in src[start:end].splitlines())
|
||||
literals = set(re.findall(r"(?<![\w.])\d+\.\d+(?![\w.])", code))
|
||||
assert literals <= {"0.0", "0.5"}, f"constants in the gate: {literals}"
|
||||
assert ">" in code and "!=" in code # a SIGN comparison, not a magnitude test
|
||||
|
||||
|
||||
def test_T3_the_gate_is_inert_where_the_slope_sign_is_stable():
|
||||
"""This is what the progress gate could not do — it fired on excellent steps."""
|
||||
for f, a, b in SMOOTH:
|
||||
off = P.pure_root(f, a, b, 1e-12, 300, similarity="ratio", scale="derived3",
|
||||
guard="credit")
|
||||
on = P.pure_root(f, a, b, 1e-12, 300, similarity="ratio", scale="derived3",
|
||||
guard="credit", prestep="slope")
|
||||
assert on[1] == off[1], (off, on) # identical evaluation count
|
||||
|
||||
|
||||
def test_T4_the_gate_fires_where_the_slope_sign_flips():
|
||||
f, a, b = WOBBLY
|
||||
st = {}
|
||||
x, n, ok = P.pure_root(f, a, b, 1e-10, 300, similarity="ratio", scale="derived3",
|
||||
guard="credit", prestep="slope", stats=st)
|
||||
assert ok and abs(f(x)) < 1e-6
|
||||
assert st["pre_refused"] >= 1, st
|
||||
|
||||
|
||||
def test_T5_a_refusal_costs_no_extra_evaluation():
|
||||
f, a, b = WOBBLY
|
||||
st = {}
|
||||
_x, n, ok = P.pure_root(f, a, b, 1e-10, 300, similarity="ratio", scale="derived3",
|
||||
guard="credit", prestep="slope", stats=st)
|
||||
assert ok
|
||||
assert st["pre_refused"] <= n - 2, (st, n)
|
||||
|
||||
|
||||
def test_the_mode_needs_a_slope_to_judge():
|
||||
with pytest.raises(ValueError):
|
||||
P.pure_root(lambda t: t, -1.0, 1.0, 1e-10, 100, scale="bracket", prestep="slope")
|
||||
with pytest.raises(ValueError):
|
||||
P.pure_root(lambda t: t, -1.0, 1.0, 1e-10, 100, prestep="nonsense")
|
||||
|
||||
|
||||
def test_both_gates_remain_available_and_distinct():
|
||||
""""progress" is kept as a MEASURED NEGATIVE (APS 4449 vs 2806), not deleted."""
|
||||
f, a, b = WOBBLY
|
||||
a1 = P.pure_root(f, a, b, 1e-10, 300, similarity="ratio", scale="derived3",
|
||||
guard="credit", prestep="progress")
|
||||
a2 = P.pure_root(f, a, b, 1e-10, 300, similarity="ratio", scale="derived3",
|
||||
guard="credit", prestep="slope")
|
||||
assert a1[2] and a2[2]
|
||||
assert a1 != a2 or a1[1] == a2[1] # they may coincide, but both must run
|
||||
Reference in New Issue
Block a user