logarithmic space mode: pure_root gains space='x'|'log' (default reproduces the published grid bit-for-bit), u = sign(x-c)*log1p(|x-c|/xtol) with c = the end of smaller |f| and s = xtol fixed by derivation; gates G1/G2' pinned by tests/test_log_space.py (7 green); sweep writes the log grid to its OWN sweep_log.json so the verified sweep_full.json is untouched
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+62
-2
@@ -118,6 +118,50 @@ PURE_SIMS = {
|
||||
|
||||
GUARDS = ("none", "bracket", "step", "both", "project", "safe", "credit")
|
||||
|
||||
SPACES = ("x", "log")
|
||||
|
||||
|
||||
def _log_map(t, c, s):
|
||||
"""u = sign(t - c) * log1p(|t - c| / s). An ADDITIVE step in u is a
|
||||
MULTIPLICATIVE step in x, which is what the wide-bracket families need: the
|
||||
search there must cross up to twelve decades, and an absolute step sized by
|
||||
the local slope knows nothing about how many decades remain."""
|
||||
d = t - c
|
||||
return math.copysign(math.log1p(abs(d) / s), d)
|
||||
|
||||
|
||||
def _log_unmap(u, c, s):
|
||||
return c + math.copysign(s * (math.expm1(abs(u))), u)
|
||||
|
||||
|
||||
def _log_space_step(S, x, fx, far, slope, c, s):
|
||||
"""The same similarity rule, the same derived scale, read in u rather than x.
|
||||
|
||||
dx/du = s + |x - c|, so the slope in u is slope_x * (s + |x - c|); the scale
|
||||
is that slope times the distance to the far end IN u, exactly as in x-space.
|
||||
Returns the step in x (None when u-space cannot be read).
|
||||
|
||||
`s` is NOT a free unit: PREREG amendment 1 fixes s = xtol by derivation - the
|
||||
only length in the problem that says "below this nothing is distinguishable",
|
||||
supplied by the caller like a, b and f. With it u spans log(w/xtol) units,
|
||||
the very measure the method's own guarantee 2*ceil(log2(w/xtol)) + 3 is
|
||||
stated in, so the march is linear in the measure of the guarantee.
|
||||
"""
|
||||
u_x = _log_map(x, c, s)
|
||||
u_far = _log_map(far, c, s)
|
||||
du = u_far - u_x
|
||||
if du == 0.0 or not math.isfinite(du):
|
||||
return None
|
||||
slope_u = slope * (s + abs(x - c))
|
||||
sc_u = abs(slope_u) * abs(du)
|
||||
if sc_u == 0.0 or not math.isfinite(sc_u):
|
||||
return None
|
||||
u_new = u_x + (1.0 - S(abs(fx) / sc_u)) * du
|
||||
x_new = _log_unmap(u_new, c, s)
|
||||
if not math.isfinite(x_new):
|
||||
return None
|
||||
return x_new - x
|
||||
|
||||
|
||||
class _Safeguard:
|
||||
"""Parameter-free worst-case protection. See the module docstring."""
|
||||
@@ -369,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):
|
||||
budget_extra=None, endowment=1.0, space="x"):
|
||||
"""The pure similarity root finder.
|
||||
|
||||
similarity : key into PURE_SIMS
|
||||
@@ -421,6 +465,12 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||
if scale not in ("derived", "derived3", "derived_rho", "derived3_rho",
|
||||
"derived3_m", "start", "bracket"):
|
||||
raise ValueError(f"unknown scale {scale!r}")
|
||||
if space not in SPACES:
|
||||
raise ValueError(f"unknown space {space!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).
|
||||
raise ValueError("space='log' requires scale in {'derived', 'derived3'}")
|
||||
S = PURE_SIMS[similarity]
|
||||
g = Counter(f, maxfev)
|
||||
x = 0.5 * (a + b)
|
||||
@@ -541,7 +591,17 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||
if sc == 0.0 or not math.isfinite(sc):
|
||||
x_new = 0.5 * (A + B)
|
||||
else:
|
||||
step = (1.0 - S(abs(fx) / sc)) * (far - x)
|
||||
step = None
|
||||
if 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
|
||||
# x-space step when u cannot be read, so the mode can
|
||||
# only differ where it is defined.
|
||||
c_near = A if abs(FA) <= abs(FB) else B
|
||||
step = _log_space_step(S, x, fx, far, slope, c_near, xtol)
|
||||
if step is None:
|
||||
step = (1.0 - S(abs(fx) / sc)) * (far - x)
|
||||
if sg.reject_step(step):
|
||||
x_new = 0.5 * (A + B)
|
||||
step = x_new - x
|
||||
|
||||
Reference in New Issue
Block a user