PREREG_flat_stick outcome: NOT accepted. Sticky regime under the projection: CHA 3220 -> 3019 (W1 holds), flat fun3-6 1823 -> 1057 (W4, near pure bisection 1044), guarantee holds (W3), but APS 2806 -> 6781 (W2 fails): the unkept-promise flag enters on 134/154 APS problems and 20/25 non-flat CHA problems, and a false entry now sticks to the end of the run. W6 fails: the order read from the promise is m = inf in 163/174 entries (r >= 1/e; our step is not a pure tangent), so the derived split equals the midpoint on CHA (3019 both). Audit bug fixed and pinned: an infinite |f| at an end made the split nan. Server regression x2: 999 passed, 1 failed = the expected paper-number check.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,62 @@
|
|||||||
|
"""Plausibility check of PREREG_flat_stick before the full grid: which problems enter
|
||||||
|
the sticky regime, what order the failed promise reads at entry, and where the cost goes.
|
||||||
|
|
||||||
|
python -u diag_flat_stick.py
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import math
|
||||||
|
from collections import Counter as Tally
|
||||||
|
|
||||||
|
import simroot.pure as P
|
||||||
|
from sweep_full import ACC, MAXFEV, XTOL, load_problems
|
||||||
|
|
||||||
|
FLAT = ("fun3", "fun4", "fun5", "fun6")
|
||||||
|
_orig = P._order_from_promise
|
||||||
|
ENTRIES = []
|
||||||
|
|
||||||
|
|
||||||
|
def _spy(r):
|
||||||
|
m = _orig(r)
|
||||||
|
ENTRIES.append((r, m))
|
||||||
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
def run(gate, f, a, b):
|
||||||
|
st = {}
|
||||||
|
x, n, conv = P.pure_root(f, a, b, XTOL, MAXFEV, similarity="ratio", scale="derived3",
|
||||||
|
guard="project", prestep=gate, stats=st)
|
||||||
|
return x, n, conv, st["pre_refused"]
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
P._order_from_promise = _spy
|
||||||
|
probs = load_problems()
|
||||||
|
for s in ("APS", "CHA"):
|
||||||
|
groups = {"flat": [0, 0, 0, 0], "other": [0, 0, 0, 0]} # problems, entered, base, stick
|
||||||
|
ms = Tally()
|
||||||
|
for st_, pid, f, a, b, root in probs:
|
||||||
|
if st_ != s:
|
||||||
|
continue
|
||||||
|
g = "flat" if s == "CHA" and pid.split(".")[0] in FLAT else "other"
|
||||||
|
_x, n0, c0, _ = run("none", f, a, b)
|
||||||
|
ENTRIES.clear()
|
||||||
|
x1, n1, c1, k = run("flatstick", f, a, b)
|
||||||
|
ok0 = c0 and abs(_x - root) <= ACC
|
||||||
|
ok1 = c1 and math.isfinite(x1) and abs(x1 - root) <= ACC
|
||||||
|
groups[g][0] += 1
|
||||||
|
groups[g][1] += 1 if ENTRIES else 0
|
||||||
|
groups[g][2] += n0 if ok0 else MAXFEV
|
||||||
|
groups[g][3] += n1 if ok1 else MAXFEV
|
||||||
|
for r, m in ENTRIES:
|
||||||
|
ms["inf" if math.isinf(m) else ("2-3" if m < 3 else "3-10" if m < 10 else ">=10")] += 1
|
||||||
|
for g, (np_, ent, c0, c1) in groups.items():
|
||||||
|
if np_:
|
||||||
|
print(f"{s} {g:<5}: {np_:3d} problems, entered sticky {ent:3d}, "
|
||||||
|
f"cost none {c0:5d} -> flatstick {c1:5d}")
|
||||||
|
print(f"{s} order read at entry: {dict(ms)}")
|
||||||
|
print("diag done")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
===== flatstick
|
||||||
|
tent, 3-point, pairs (no gate -> flat gate) APS CHA both flat fun3-6
|
||||||
|
none 2806->6781 4104->3019 6910->9800 2707->1057 solved 193 over 0
|
||||||
|
none +G2 2782->6802 3863->3055 6645->9857 2474->1057 solved 193 over 0
|
||||||
|
step 3036->6544 3524->3019 6560->9563 2111->1057 solved 193 over 0
|
||||||
|
step +G2 3089->6590 3529->3163 6618->9753 2106->1165 solved 193 over 1
|
||||||
|
bracket 7260->7173 3604->3604 10864->10777 1730->1730 solved 193 over 0
|
||||||
|
bracket +G2 6837->6845 3650->3639 10487->10484 1717->1717 solved 193 over 0
|
||||||
|
both 7239->7183 3515->3515 10754->10698 1529->1529 solved 193 over 0
|
||||||
|
both +G2 7130->7151 3463->3452 10593->10603 1504->1504 solved 193 over 0
|
||||||
|
project 2806->6781 3220->3019 6026->9800 1823->1057 solved 193 over 0
|
||||||
|
project +G2 2782->6802 3169->3055 5951->9857 1780->1057 solved 193 over 0
|
||||||
|
safe 3036->6544 3390->3019 6426->9563 1977->1057 solved 193 over 0
|
||||||
|
safe +G2 3089->6590 3392->3129 6481->9719 1969->1131 solved 193 over 0
|
||||||
|
credit 3568->6740 3020->3002 6588->9742 1044->951 solved 193 over 0
|
||||||
|
credit +G2 3799->6838 3064->3002 6863->9840 1044->951 solved 193 over 0
|
||||||
|
|
||||||
|
VERDICTS
|
||||||
|
proposed rule APS 2806->6781 (Z2 ✗) CHA 3220->3019 (Z1 ✓) Z3 ✓ (0 viol, 0 cells <193) Z4 ✓ both 9800 vs Chandrupatla APS 9/4/140 p=2.3e-31 not accepted
|
||||||
|
proposed rule + G2 APS 2782->6802 (Z2 ✗) CHA 3169->3055 (Z1 ✓) Z3 ✓ (0 viol, 0 cells <193) Z4 ✓ both 9857 vs Chandrupatla APS 9/4/140 p=2.3e-31 not accepted
|
||||||
|
|
||||||
|
nine functions, projection, no gate -> flat gate, both sets:
|
||||||
|
cbrt 18003 -> 12787
|
||||||
|
cosine 19741 -> 10974
|
||||||
|
exp_abs 6652 -> 10606
|
||||||
|
exp_sq 19786 -> 10777
|
||||||
|
rat_sq 19793 -> 10752
|
||||||
|
ratio 6026 -> 9800
|
||||||
|
ratio_soft 9352 -> 10297
|
||||||
|
rational 6823 -> 10639
|
||||||
|
sqrt 13388 -> 11541
|
||||||
|
===== flatstickmid
|
||||||
|
tent, 3-point, pairs (no gate -> flat gate) APS CHA both flat fun3-6
|
||||||
|
none 2806->6897 4104->3019 6910->9916 2707->1057 solved 193 over 0
|
||||||
|
none +G2 2782->6918 3863->3055 6645->9973 2474->1057 solved 193 over 0
|
||||||
|
step 3036->6663 3524->3019 6560->9682 2111->1057 solved 193 over 0
|
||||||
|
step +G2 3089->6709 3529->3055 6618->9764 2106->1057 solved 193 over 0
|
||||||
|
bracket 7260->7214 3604->3604 10864->10818 1730->1730 solved 193 over 0
|
||||||
|
bracket +G2 6837->6886 3650->3650 10487->10536 1717->1717 solved 193 over 0
|
||||||
|
both 7239->7241 3515->3515 10754->10756 1529->1529 solved 193 over 0
|
||||||
|
both +G2 7130->7196 3463->3463 10593->10659 1504->1504 solved 193 over 0
|
||||||
|
project 2806->6897 3220->3019 6026->9916 1823->1057 solved 193 over 0
|
||||||
|
project +G2 2782->6918 3169->3055 5951->9973 1780->1057 solved 193 over 0
|
||||||
|
safe 3036->6663 3390->3019 6426->9682 1977->1057 solved 193 over 0
|
||||||
|
safe +G2 3089->6709 3392->3055 6481->9764 1969->1057 solved 193 over 0
|
||||||
|
credit 3568->6837 3020->3002 6588->9839 1044->951 solved 193 over 0
|
||||||
|
credit +G2 3799->6953 3064->3002 6863->9955 1044->951 solved 193 over 0
|
||||||
|
|
||||||
|
VERDICTS
|
||||||
|
proposed rule APS 2806->6897 (Z2 ✗) CHA 3220->3019 (Z1 ✓) Z3 ✓ (0 viol, 0 cells <193) Z4 ✓ both 9916 vs Chandrupatla APS 9/4/140 p=2.3e-31 not accepted
|
||||||
|
proposed rule + G2 APS 2782->6918 (Z2 ✗) CHA 3169->3055 (Z1 ✓) Z3 ✓ (0 viol, 0 cells <193) Z4 ✓ both 9973 vs Chandrupatla APS 9/4/140 p=2.3e-31 not accepted
|
||||||
|
|
||||||
|
nine functions, projection, no gate -> flat gate, both sets:
|
||||||
|
cbrt 18003 -> 12694
|
||||||
|
cosine 19741 -> 10825
|
||||||
|
exp_abs 6652 -> 10621
|
||||||
|
exp_sq 19786 -> 10810
|
||||||
|
rat_sq 19793 -> 10793
|
||||||
|
ratio 6026 -> 9916
|
||||||
|
ratio_soft 9352 -> 10447
|
||||||
|
rational 6823 -> 10634
|
||||||
|
sqrt 13388 -> 11332
|
||||||
+62
-1
@@ -549,6 +549,50 @@ def _slope3(x2, f2, x1, f1, x0, f0):
|
|||||||
return s if math.isfinite(s) else d12
|
return s if math.isfinite(s) else d12
|
||||||
|
|
||||||
|
|
||||||
|
def _order_from_promise(r):
|
||||||
|
"""The order m that a tangent step's unkept promise r = |f_new/f_old| implies.
|
||||||
|
|
||||||
|
PREREG_flat_stick 2026-09-21. On an order-m approach a tangent step leaves
|
||||||
|
r = (1 - 1/m)^m, which rises monotonically from 1/4 at m = 2 to 1/e as m -> inf.
|
||||||
|
Inverted on u = 1/m by halving until the float interval stops shrinking - a
|
||||||
|
precision, not a chosen number. r >= 1/e has no finite solution: m = inf.
|
||||||
|
Called only with r >= 1/4 (the flag's own condition); below it m = 2 is returned,
|
||||||
|
the end of the range the flag admits."""
|
||||||
|
if r >= math.exp(-1.0):
|
||||||
|
return math.inf
|
||||||
|
target = math.log(r)
|
||||||
|
lo, hi = 0.0, 0.5 # u = 1/m in (0, 1/2]; h(u) = ln(1-u)/u falls with u
|
||||||
|
while True:
|
||||||
|
mid = 0.5 * (lo + hi)
|
||||||
|
if mid == lo or mid == hi:
|
||||||
|
break
|
||||||
|
if math.log1p(-mid) / mid > target:
|
||||||
|
lo = mid
|
||||||
|
else:
|
||||||
|
hi = mid
|
||||||
|
return 1.0 / hi
|
||||||
|
|
||||||
|
|
||||||
|
def _order_split(A, FA, B, FB, m):
|
||||||
|
"""The point where |f| ~ |x - r|^m puts the root: the ends are at distances
|
||||||
|
dA : dB = |FA|^(1/m) : |FB|^(1/m) from it. m = inf is the midpoint. Worked in
|
||||||
|
logarithms so neither ratio can overflow; a point that lands on an end is caught
|
||||||
|
by the loop's own fallback to the midpoint."""
|
||||||
|
if math.isinf(m):
|
||||||
|
return 0.5 * (A + B)
|
||||||
|
lq = (math.log(abs(FA)) - math.log(abs(FB))) / m # ln (|FA|/|FB|)^(1/m)
|
||||||
|
if not math.isfinite(lq):
|
||||||
|
# an infinite |f| at an end: no power law to read, and a nan step would reach
|
||||||
|
# the safeguard's bookkeeping before the loop's own finiteness check
|
||||||
|
return 0.5 * (A + B)
|
||||||
|
if lq > 0.0:
|
||||||
|
t = 1.0 / (1.0 + math.exp(-lq))
|
||||||
|
else:
|
||||||
|
e = math.exp(lq)
|
||||||
|
t = e / (1.0 + e)
|
||||||
|
return A + t * (B - A)
|
||||||
|
|
||||||
|
|
||||||
def _local_order(x2, f2, x1, f1, x0, f0):
|
def _local_order(x2, f2, x1, f1, x0, f0):
|
||||||
"""The local order m of f, from three evaluations and nothing else.
|
"""The local order m of f, from three evaluations and nothing else.
|
||||||
|
|
||||||
@@ -666,7 +710,7 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
if space not in SPACES:
|
if space not in SPACES:
|
||||||
raise ValueError(f"unknown space {space!r}")
|
raise ValueError(f"unknown space {space!r}")
|
||||||
if prestep not in ("none", "progress", "slope", "band", "defect", "simpair", "flat",
|
if prestep not in ("none", "progress", "slope", "band", "defect", "simpair", "flat",
|
||||||
"flat2", "flatcreep"):
|
"flat2", "flatcreep", "flatstick", "flatstickmid"):
|
||||||
raise ValueError(f"unknown prestep {prestep!r}")
|
raise ValueError(f"unknown prestep {prestep!r}")
|
||||||
if prestep in ("band", "defect", "simpair") and scale not in (
|
if prestep in ("band", "defect", "simpair") and scale not in (
|
||||||
"derived3", "derived3_cap", "derived3_m", "bracket", "far"):
|
"derived3", "derived3_cap", "derived3_m", "bracket", "far"):
|
||||||
@@ -721,6 +765,7 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
slope_prev = None
|
slope_prev = None
|
||||||
last_model = False # was the step just evaluated the model's own? (flat gate)
|
last_model = False # was the step just evaluated the model's own? (flat gate)
|
||||||
unkept_run = 0 # consecutive model steps whose promise failed (flat2)
|
unkept_run = 0 # consecutive model steps whose promise failed (flat2)
|
||||||
|
sticky_m = None # order read at entry to the sticky flat regime (flatstick*)
|
||||||
if stats is not None:
|
if stats is not None:
|
||||||
stats['forced'] = 0
|
stats['forced'] = 0
|
||||||
stats['pre_refused'] = 0
|
stats['pre_refused'] = 0
|
||||||
@@ -914,6 +959,22 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
# late, before the crossing (flat2's failure).
|
# late, before the crossing (flat2's failure).
|
||||||
step = 0.5 * (A + B) - x
|
step = 0.5 * (A + B) - x
|
||||||
pre_refused += 1
|
pre_refused += 1
|
||||||
|
if step is None and prestep in ("flatstick", "flatstickmid"):
|
||||||
|
# PREREG_flat_stick 2026-09-21. The same entry as "flat", but the
|
||||||
|
# regime is a property of the root, not of one step: once the
|
||||||
|
# promise has failed, every later step splits the bracket. The
|
||||||
|
# single flag replaced one step and the model undershot again.
|
||||||
|
# flatstick splits where |f| ~ |x-r|^m puts the root, m read from
|
||||||
|
# the failed promise itself; flatstickmid halves (the ablation).
|
||||||
|
if (sticky_m is None and last_model and f_prev != 0.0
|
||||||
|
and fx / f_prev >= 0.25):
|
||||||
|
sticky_m = _order_from_promise(fx / f_prev)
|
||||||
|
if sticky_m is not None:
|
||||||
|
if prestep == "flatstick":
|
||||||
|
step = _order_split(A, FA, B, FB, sticky_m) - x
|
||||||
|
else:
|
||||||
|
step = 0.5 * (A + B) - x
|
||||||
|
pre_refused += 1
|
||||||
if step is None and prestep == "flat2" and unkept_run >= 2:
|
if step is None and prestep == "flat2" and unkept_run >= 2:
|
||||||
# PREREG_flat_twice 2026-09-21. The same unkept promise as
|
# PREREG_flat_twice 2026-09-21. The same unkept promise as
|
||||||
# "flat" (model step, no crossing, r >= 1/4), but only after it has
|
# "flat" (model step, no crossing, r >= 1/4), but only after it has
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+15
-8
@@ -221,22 +221,26 @@ def _preflight_extrap_mode():
|
|||||||
|
|
||||||
def build_grid_flat(smoke, gate="flat"):
|
def build_grid_flat(smoke, gate="flat"):
|
||||||
"""PREREG_flat_regime_2026-09-21 stage 2: the flat gate (prestep='flat') in pairs,
|
"""PREREG_flat_regime_2026-09-21 stage 2: the flat gate (prestep='flat') in pairs,
|
||||||
on the proposed rule and on the proposed rule with G2 (extrapolate='pattern')."""
|
on the proposed rule and on the proposed rule with G2 (extrapolate='pattern').
|
||||||
|
`gate` may be a tuple: every gate in it gets its own cell next to the same 'none'
|
||||||
|
(PREREG_flat_stick: flatstick and its ablation flatstickmid in one grid)."""
|
||||||
|
gates = gate if isinstance(gate, tuple) else (gate,)
|
||||||
|
for gt in gates:
|
||||||
x, _n, ok = pure_root(lambda t: t - 1.0, 0.0, 3.0, 1e-10, 100, similarity="ratio",
|
x, _n, ok = pure_root(lambda t: t - 1.0, 0.0, 3.0, 1e-10, 100, similarity="ratio",
|
||||||
scale="derived3", guard="project", prestep=gate)
|
scale="derived3", guard="project", prestep=gt)
|
||||||
if not (ok and abs(x - 1.0) < 1e-8):
|
if not (ok and abs(x - 1.0) < 1e-8):
|
||||||
raise SystemExit(f"prestep={gate} preflight FAILED: refusing to report zeros")
|
raise SystemExit(f"prestep={gt} preflight FAILED: refusing to report zeros")
|
||||||
guards = ("project", "credit") if smoke else GUARD_MODES
|
guards = ("project", "credit") if smoke else GUARD_MODES
|
||||||
specs = []
|
specs = []
|
||||||
for gu in guards:
|
for gu in guards:
|
||||||
for ext in ("none", "pattern"):
|
for ext in ("none", "pattern"):
|
||||||
for pre in ("none", gate):
|
for pre in ("none",) + gates:
|
||||||
specs.append(("pure", "ratio", "derived3", gu, "x", pre, "recent",
|
specs.append(("pure", "ratio", "derived3", gu, "x", pre, "recent",
|
||||||
"distance", "none", ext))
|
"distance", "none", ext))
|
||||||
sims = ["sqrt"] if smoke else [s for s in sorted(PURE_SIMS) if s != "ratio"]
|
sims = ["sqrt"] if smoke else [s for s in sorted(PURE_SIMS) if s != "ratio"]
|
||||||
for sim in sims:
|
for sim in sims:
|
||||||
for gu in guards:
|
for gu in guards:
|
||||||
for pre in ("none", gate):
|
for pre in ("none",) + gates:
|
||||||
specs.append(("pure", sim, "derived3", gu, "x", pre, "recent",
|
specs.append(("pure", sim, "derived3", gu, "x", pre, "recent",
|
||||||
"distance", "none", "none"))
|
"distance", "none", "none"))
|
||||||
return specs
|
return specs
|
||||||
@@ -414,10 +418,12 @@ def main():
|
|||||||
flatmode = "--flat" in sys.argv # the flat-regime gate
|
flatmode = "--flat" in sys.argv # the flat-regime gate
|
||||||
flat2mode = "--flat2" in sys.argv # the flat gate, promise failed twice
|
flat2mode = "--flat2" in sys.argv # the flat gate, promise failed twice
|
||||||
creepmode = "--flatcreep" in sys.argv # the flat gate AND a creep
|
creepmode = "--flatcreep" in sys.argv # the flat gate AND a creep
|
||||||
|
stickmode = "--flatstick" in sys.argv # the sticky flat regime + its midpoint ablation
|
||||||
combomode = "--combo" in sys.argv # modes A and B together
|
combomode = "--combo" in sys.argv # modes A and B together
|
||||||
pointsmode = "--points" in sys.argv # mode A: triple by shape
|
pointsmode = "--points" in sys.argv # mode A: triple by shape
|
||||||
dirmode = "--direction" in sys.argv # mode B: far end by dissimilarity
|
dirmode = "--direction" in sys.argv # mode B: far end by dissimilarity
|
||||||
specs = (build_grid_flat(smoke, "flatcreep") if creepmode else
|
specs = (build_grid_flat(smoke, ("flatstick", "flatstickmid")) if stickmode else
|
||||||
|
build_grid_flat(smoke, "flatcreep") if creepmode else
|
||||||
build_grid_flat(smoke, "flat2") if flat2mode else
|
build_grid_flat(smoke, "flat2") if flat2mode else
|
||||||
build_grid_flat(smoke) if flatmode else
|
build_grid_flat(smoke) if flatmode else
|
||||||
build_grid_extrap2(smoke) if ext2mode else
|
build_grid_extrap2(smoke) if ext2mode else
|
||||||
@@ -446,8 +452,9 @@ def main():
|
|||||||
|
|
||||||
# the published sweep_full.json is what verify_article.py checks the paper
|
# the published sweep_full.json is what verify_article.py checks the paper
|
||||||
# against, so the log grid never writes into it
|
# against, so the log grid never writes into it
|
||||||
if creepmode or flat2mode or flatmode or ext2mode or extmode or decmode or ordermode or capmode or combomode or pointsmode or dirmode:
|
if stickmode or creepmode or flat2mode or flatmode or ext2mode or extmode or decmode or ordermode or capmode or combomode or pointsmode or dirmode:
|
||||||
base = ("sweep_flatcreep" if creepmode else
|
base = ("sweep_flatstick" if stickmode else
|
||||||
|
"sweep_flatcreep" if creepmode else
|
||||||
"sweep_flat2" if flat2mode else
|
"sweep_flat2" if flat2mode else
|
||||||
"sweep_flat" if flatmode else
|
"sweep_flat" if flatmode else
|
||||||
"sweep_extrap2" if ext2mode else
|
"sweep_extrap2" if ext2mode else
|
||||||
|
|||||||
@@ -97,3 +97,64 @@ def test_the_gate_fires_on_a_pure_power():
|
|||||||
pure_root(lambda t: t ** 9, -1.0, 2.0, XTOL, MAXFEV, similarity="ratio",
|
pure_root(lambda t: t ** 9, -1.0, 2.0, XTOL, MAXFEV, similarity="ratio",
|
||||||
scale="derived3", guard="project", prestep="flat", stats=st)
|
scale="derived3", guard="project", prestep="flat", stats=st)
|
||||||
assert st["pre_refused"] > 0
|
assert st["pre_refused"] > 0
|
||||||
|
|
||||||
|
|
||||||
|
# ---- PREREG_flat_stick_2026-09-21: the sticky flat regime and its midpoint ablation ----
|
||||||
|
from simroot.pure import _order_from_promise, _order_split # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("m", (2.0, 3.0, 5.0, 9.0, 19.0, 100.0))
|
||||||
|
def test_the_order_is_read_back_from_its_own_promise(m):
|
||||||
|
r = (1.0 - 1.0 / m) ** m
|
||||||
|
assert _order_from_promise(r) == pytest.approx(m, rel=1e-9)
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_finite_order_at_or_above_one_over_e_gives_the_midpoint():
|
||||||
|
assert math.isinf(_order_from_promise(math.exp(-1.0)))
|
||||||
|
assert math.isinf(_order_from_promise(0.9))
|
||||||
|
assert _order_split(-1.0, -3.0, 2.0, 5.0, math.inf) == 0.5
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("m", (1, 3, 5, 9))
|
||||||
|
def test_the_split_lands_on_the_root_of_an_exact_power(m):
|
||||||
|
f = lambda t: (t - 0.3) ** m # noqa: E731
|
||||||
|
assert _order_split(-1.0, f(-1.0), 2.0, f(2.0), float(m)) == pytest.approx(0.3, abs=1e-12)
|
||||||
|
|
||||||
|
|
||||||
|
def test_the_split_does_not_overflow_on_extreme_ends():
|
||||||
|
x = _order_split(0.0, -1e-300, 1.0, 1e300, 2.0)
|
||||||
|
assert 0.0 <= x < 1e-100
|
||||||
|
x = _order_split(0.0, -1e300, 1.0, 1e-300, 2.0)
|
||||||
|
assert 1.0 - x < 1e-100 and x <= 1.0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("f,a,b", PROBS)
|
||||||
|
@pytest.mark.parametrize("guard", ("project", "credit"))
|
||||||
|
@pytest.mark.parametrize("ext", ("none", "pattern"))
|
||||||
|
@pytest.mark.parametrize("gate", ("flatstick", "flatstickmid"))
|
||||||
|
def test_the_guarantee_holds_with_the_sticky_regime(f, a, b, guard, ext, gate):
|
||||||
|
x, n, ok = pure_root(f, a, b, XTOL, MAXFEV, similarity="ratio", scale="derived3",
|
||||||
|
guard=guard, prestep=gate, extrapolate=ext)
|
||||||
|
assert ok and min(a, b) <= x <= max(a, b)
|
||||||
|
half = math.ceil(math.log2(abs(b - a) / XTOL))
|
||||||
|
assert n <= (2 * half + 3 if guard == "project" else half + 4), n
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("gate", ("flatstick", "flatstickmid"))
|
||||||
|
def test_the_sticky_regime_is_silent_on_a_line_and_holds_on_a_power(gate):
|
||||||
|
st = {}
|
||||||
|
pure_root(lambda t: 3.0 * t - 1.0, -2.0, 5.0, XTOL, MAXFEV, similarity="ratio",
|
||||||
|
scale="derived3", guard="project", prestep=gate, stats=st)
|
||||||
|
assert st["pre_refused"] == 0
|
||||||
|
st = {}
|
||||||
|
_x, n, _ok = pure_root(lambda t: t ** 9, -1.0, 2.0, XTOL, MAXFEV, similarity="ratio",
|
||||||
|
scale="derived3", guard="project", prestep=gate, stats=st)
|
||||||
|
# once entered, every later non-forced step is a split: many, not one
|
||||||
|
assert st["pre_refused"] >= 3, st
|
||||||
|
|
||||||
|
|
||||||
|
def test_an_infinite_end_gives_the_midpoint_not_nan():
|
||||||
|
"""Audit bug 2026-09-21: an infinite |f| made lq nan, and the nan step reached the
|
||||||
|
safeguard before the loop checked finiteness."""
|
||||||
|
assert _order_split(0.0, -math.inf, 2.0, 1.0, 3.0) == 1.0
|
||||||
|
assert _order_split(0.0, -math.inf, 2.0, math.inf, 3.0) == 1.0
|
||||||
|
|||||||
Reference in New Issue
Block a user