pre-step gate: prestep='none'|'progress' (default bit-identical), refuse the similarity step BEFORE spending an evaluation when it promises less progress than bisection — two lengths already in hand, no new constant; R1-R4 pinned by tests/test_prestep_gate.py (6 green); the gate grid writes to its OWN sweep_gate.json
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+25
-6
@@ -413,7 +413,7 @@ def _local_order(x2, f2, x1, f1, x0, f0):
|
||||
|
||||
def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||
guard="step", stats=None, budget_factor=2.0,
|
||||
budget_extra=None, endowment=1.0, space="x"):
|
||||
budget_extra=None, endowment=1.0, space="x", prestep="none"):
|
||||
"""The pure similarity root finder.
|
||||
|
||||
similarity : key into PURE_SIMS
|
||||
@@ -467,6 +467,8 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||
raise ValueError(f"unknown scale {scale!r}")
|
||||
if space not in SPACES:
|
||||
raise ValueError(f"unknown space {space!r}")
|
||||
if prestep not in ("none", "progress"):
|
||||
raise ValueError(f"unknown prestep {prestep!r}")
|
||||
if space == "log" and scale not in ("derived", "derived3"):
|
||||
# u-space needs a slope; with a chosen or bracket scale there is none to
|
||||
# transform, and computing something anyway would hide that (PREREG §3a).
|
||||
@@ -494,8 +496,10 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||
sg = _Safeguard(guard, abs(B - A), xtol, budget_factor, budget_extra)
|
||||
sg.credit = endowment
|
||||
forced = 0
|
||||
pre_refused = 0
|
||||
if stats is not None:
|
||||
stats['forced'] = 0
|
||||
stats['pre_refused'] = 0
|
||||
|
||||
while abs(B - A) > xtol:
|
||||
if sg.bisect_before_step(abs(B - A)):
|
||||
@@ -592,7 +596,22 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||
x_new = 0.5 * (A + B)
|
||||
else:
|
||||
step = None
|
||||
if space == "log":
|
||||
if prestep == "progress":
|
||||
# PRE-STEP GATE (PREREG 2026-09-21). Measured cause of our cost:
|
||||
# the safeguard REPAIRS 77.5% of evaluations after the fact, while
|
||||
# Chandrupatla REFUSES its model on 72% of steps for free and
|
||||
# halves instead - 2.310 halvings per evaluation against our 1.422.
|
||||
# Our step never leaves the bracket (0 <= S <= 1), so the question
|
||||
# is not overshoot but PROGRESS: if the similarity step promises
|
||||
# less than bisection would remove, spend the same single
|
||||
# evaluation on the midpoint instead. Two lengths already in hand
|
||||
# are compared; there is no threshold and no new constant.
|
||||
cand = (1.0 - S(abs(fx) / sc)) * (far - x)
|
||||
half = 0.5 * (A + B) - x
|
||||
if abs(cand) < abs(half):
|
||||
step = half
|
||||
pre_refused += 1
|
||||
if step is None and space == "log":
|
||||
# c = the bracket end with the smaller |f| (the side the
|
||||
# root is nearer); s = xtol, fixed by derivation in
|
||||
# PREREG amendment 1, never chosen. Falls back to the
|
||||
@@ -624,7 +643,7 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||
if x_new == x or x_new == x_prev:
|
||||
# the bracket can no longer be split in floating point
|
||||
if stats is not None:
|
||||
stats['forced'] = forced
|
||||
stats['forced'] = forced; stats['pre_refused'] = pre_refused
|
||||
return x_new, g.n, abs(B - A) <= xtol
|
||||
|
||||
w_before = abs(B - A)
|
||||
@@ -636,7 +655,7 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||
x, fx = x_new, fnew
|
||||
if fnew == 0.0:
|
||||
if stats is not None:
|
||||
stats['forced'] = forced
|
||||
stats['forced'] = forced; stats['pre_refused'] = pre_refused
|
||||
return x_new, g.n, True
|
||||
if FA * fnew < 0.0:
|
||||
B, FB = x_new, fnew
|
||||
@@ -645,9 +664,9 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||
if guard == "credit":
|
||||
sg.note_shrink(w_before, abs(B - A))
|
||||
if stats is not None:
|
||||
stats['forced'] = forced
|
||||
stats['forced'] = forced; stats['pre_refused'] = pre_refused
|
||||
return 0.5 * (A + B), g.n, True
|
||||
except _Budget:
|
||||
if stats is not None:
|
||||
stats['forced'] = forced
|
||||
stats['forced'] = forced; stats['pre_refused'] = pre_refused
|
||||
return x, g.n, False
|
||||
|
||||
Reference in New Issue
Block a user