K3 outcome (PREREG_estimated_transform, Linux run): the 6.6 claim stands. Corpus 4: A' 3656 vs Chandrupatla on sign(f)|f|^(1/m_hat) with the analogy order 3806 (A' 126/38/76, p=5e-4) and with Lagouanelle 7526; six corpora 13032 vs 15733 and 30991. On multiple/fractional roots the analogy order inside Chandrupatla closes 59%, 58%, 31% of the A'->oracle gap (CHA 322 vs 654, corpus 1 721 vs 1229, corpus 4 1766 vs 2159) and pays on simple roots (APS 3453 vs 2595); Lagouanelle on Chandrupatla's side-alternating triples reads a fractional order on 115 of 154 simple APS roots.

simroot/transformed.py: Chandrupatla on G(f) with m_hat estimated in the run; bit-identical to chandrupatla with no estimate (test). Bug found and pinned by a test: |f|^(1/m_hat) overflowed with an OverflowError on the fractional branch; now inf and the step halves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
vvs
2026-09-22 08:50:37 +01:00
co-authored by Claude Opus 5
parent 2fe6d729a8
commit 34b2a30d6c
5 changed files with 311 additions and 0 deletions
+60
View File
@@ -0,0 +1,60 @@
"""PREREG_estimated_transform_2026-09-22: the control K3 must be Chandrupatla's method itself when
no order is estimated, and must stay a bracketing method when one is."""
from __future__ import annotations
import math
import pytest
from simroot.baselines import chandrupatla
from simroot.transformed import chandrupatla_transformed
from sweep_full import load_problems
PROBS = load_problems()
def test_without_an_estimate_it_is_chandrupatla_bit_for_bit():
bad = []
for s, pid, f, a, b, _r in PROBS:
r1 = chandrupatla(f, a, b, 1e-12, 200)
r2 = chandrupatla_transformed(f, a, b, 1e-12, 200, estimator="none")
if (float(r1[0]).hex(), r1[1], r1[2]) != (float(r2[0]).hex(), r2[1], r2[2]):
bad.append(pid)
assert not bad, bad[:5]
@pytest.mark.parametrize("estimator", ("lag", "analogy"))
def test_with_an_estimate_it_still_brackets(estimator):
for s, pid, f, a, b, _r in PROBS:
x, n, ok = chandrupatla_transformed(f, a, b, 1e-12, 200, estimator=estimator)
assert min(a, b) <= x <= max(a, b), pid
assert n <= 200
def test_the_estimate_is_read_and_used_on_a_pure_power():
"""The analogy estimator must reach m_hat on (t - 1)^9 and pay. Lagouanelle's is NOT asserted:
on Chandrupatla's iterates, which alternate sides of the root, its three-point parabola reads
m ~ 1 at every step - an outcome of the experiment, reported by the run, not a property
to pin here."""
f = lambda t: (t - 1.0) ** 9 # noqa: E731
plain = chandrupatla_transformed(f, -2.0, 5.0, 1e-12, 200, estimator="none")[1]
st = {}
x, n, ok = chandrupatla_transformed(f, -2.0, 5.0, 1e-12, 200, estimator="analogy", stats=st)
assert ok and abs(x - 1.0) < 1e-6
assert st.get("updates", 0) >= 1
assert n < plain, (n, plain)
x, n, ok = chandrupatla_transformed(f, -2.0, 5.0, 1e-12, 200, estimator="lag")
assert ok and abs(x - 1.0) < 1e-6
def test_an_overflowing_transform_halves_instead_of_raising():
"""Found 2026-09-22: on the fractional branch 1/m_hat > 1 and |f|^(1/m_hat) overflowed with
an OverflowError that escaped the solver. Now it is inf, the interpolation is refused, and
the step halves."""
from simroot.transformed import _G
assert _G(1e300, 3.0) == math.inf and _G(-1e300, 3.0) == -math.inf
def test_unknown_estimator_is_refused():
with pytest.raises(ValueError):
chandrupatla_transformed(math.sin, 3.0, 4.0, 1e-12, 200, estimator="guess")