diff --git a/apply_article_revision3.py b/apply_article_revision3.py new file mode 100644 index 0000000..1fd3741 --- /dev/null +++ b/apply_article_revision3.py @@ -0,0 +1,49 @@ +"""Revision 3 into article/sim_numerics.md: + 4 the exact-zero stop stated in section 4 (it holds for every method on every corpus) + 5 Table 12: the post hoc Lagouanelle variant on the branch m >= 2 (marked), the missing exit of + the controls in the caption, and the conclusion from Table 12 restated in 7.1, 6.6 and 8 +Exact anchors, each found exactly once, or nothing is written. + + python -u apply_article_revision3.py +""" +from __future__ import annotations + +PATH = "article/sim_numerics.md" +R = [] + + +def E(old, new): + R.append((old, new)) + + +E("$x$-tolerance $10^{-12}$; budget 200 evaluations.", + "$x$-tolerance $10^{-12}$; budget 200 evaluations. Every method, ours included, stops when its bracket is no wider than $\\varepsilon_x$ or when it evaluates an exact zero of the function it works on; none stops on a small $|f|$.") +E("**Table 12.** Chandrupatla on the transformed function: the multiplicity estimated during the run, against A′, the oracle of Table 11 and Chandrupatla on $f$. Totals, failures charged; the 148 multiple and fractional roots of Table 11, and the simple-root canonical set.", + "**Table 12.** Chandrupatla on the transformed function: the multiplicity estimated during the run, against A′, the oracle of Table 11 and Chandrupatla on $f$. Totals, failures charged; the 148 multiple and fractional roots of Table 11, and the simple-root canonical set. The estimated forms have no exit: an order once taken is held to the end of the run. A′ leaves its regime by the exit of §6.2.") +E("| Chandrupatla on $g$, $\\hat m$ by Lagouanelle | 7526 | 30991 | 6041 | 7215 |", + "| Chandrupatla on $g$, $\\hat m$ by Lagouanelle | 7526 | 30991 | 6041 | 7215 |\n| the same, branch $m\\ge2$ only (post hoc) | 5568 | 18372 | 6540 | 2960 |") +E("(A variant restricted to the branch $m\\ge2$ was not among the controls recorded.)", + "Restricted after the fact to the branch $m\\ge2$ (post hoc, `POSTHOC_lag_branch_2026-09-22.md`), it no longer breaks, but in this frame it hardly sees a multiple root either: 6540 on the 148 multiple and fractional roots against 7011 for Chandrupatla on $f$, and 5568 on corpus 4, with A′ ahead 178/34/28.") +E("So the decision layer of A′ earns its place against the classical method with an estimated order as well; and the split (7), not the order estimate, is where A′ leaves most to the transformation.", + "Two conclusions follow. First, the order read by analogy transfers to the classical frame and works better there than in A′'s own split: on the multiple and fractional roots Chandrupatla's interpolation with that order beats A′ itself (2809 against 4042). Second, A′'s decision layer as a whole — the detector together with its exit — earns its place: without an exit the transferred detector loses more on simple roots than it gains on multiple ones, and A′ stays ahead on corpus 4 and over the six corpora.") +E("§7.1 puts this claim against the detectors a reader would try first, and against the transformation with the multiplicity known in advance or estimated during the run.", + "§7.1 tests this against the detectors a reader would try first and against the classical transformation with the multiplicity known or estimated, and the outcome is sharper than the claim: the order read by analogy transfers to the classical frame and works better there than in A′'s own split, while A′'s exit is what keeps simple roots cheap.") +E("Its decision layer beats Lagouanelle's estimate, Chandrupatla's band and no detector put in its place, and Chandrupatla's method on the transformed function with the order estimated during the run;", + "Its detector, the order read by analogy, beats Lagouanelle's estimate, Chandrupatla's band and no detector put in its place, and it transfers: inside Chandrupatla's interpolation on the transformed function it does better on multiple roots than A′'s own split, while only A′'s exit keeps simple roots cheap;") + + +def main(): + src = open(PATH, encoding="utf8").read() + bad = [o[:80] for o, _ in R if src.count(o) != 1] + if bad: + for b in bad: + print("ANCHOR NOT FOUND EXACTLY ONCE:", repr(b)) + raise SystemExit(f"{len(bad)} anchors failed; nothing written") + for o, n in R: + src = src.replace(o, n) + open(PATH, "w", encoding="utf8").write(src) + print(f"applied {len(R)}") + + +if __name__ == "__main__": + main() diff --git a/apply_verify_revision3.py b/apply_verify_revision3.py new file mode 100644 index 0000000..b42d8f1 --- /dev/null +++ b/apply_verify_revision3.py @@ -0,0 +1,48 @@ +"""verify_article.py checks the round-3 additions: the post hoc row of Table 12 +(sweep_estimated_posthoc.json), A' against it on corpus 4, its fractional-free holds, and the +statement that the transferred detector loses more on simple roots than it gains on multiple ones. + + python -u apply_verify_revision3.py +""" +PATH = "verify_article.py" +FUNC = ''' + +def verify_revision3(): + """Revision 3 (point 5): the post hoc branch m >= 2 and the reading of Table 12.""" + print("\\nRevision 3:") + + def cost(v): + return v[0] if v[1] else 200 + + def tot(r, keep=None): + return sum(cost(v) for p, v in r.items() if keep is None or keep(p)) + sets = ("APS", "CHA", "NEW", "RJL", "APP", "D") + P = _load("sweep_estimated_posthoc.json") + K = _load("sweep_estimated.json")["k3_analogy"] + seen, four = _load("sweep_fourth_seen.json"), _load("sweep_fourth_fourth.json") + ap = dict(seen["Aprime"]) + ap.update(four["Aprime"]) + K1 = _load("sweep_controls.json")["K1"] + nonsimple = lambda m: m >= 2.0 or m <= 2.0 / 3.0 # noqa: E731 + + def group(r): + return sum(tot(r[s], lambda p, _m=K1[s]["mstar"]: nonsimple(_m[p])) for s in ("CHA", "NEW", "D")) + check("Table 12 post hoc row", (tot(P["D"]), sum(tot(P[s]) for s in sets), group(P), tot(P["APS"])), + (5568, 18372, 6540, 2960)) + ps = list(ap["D"]) + w = sum(cost(ap["D"][p]) < cost(P["D"][p]) for p in ps) + l = sum(cost(ap["D"][p]) > cost(P["D"][p]) for p in ps) + check("A' vs the post hoc variant on corpus 4", (w, len(ps) - w - l, l), (178, 34, 28)) + check("post hoc violations", sum(v[2] for s in sets for v in P[s].values()), 0) + gain = group(ap) - group(K) + loss = (sum(tot(K[s]) for s in sets) - group(K)) - (sum(tot(ap[s]) for s in sets) - group(ap)) + check("transferred detector: gain on multiple roots < loss elsewhere (vs A')", (gain, loss, gain < loss), + (1233, 3934, True)) +''' +src = open(PATH, encoding="utf8").read() +call_old = " verify_revision2()\n\n print()" +marker = '\n\nif __name__ == "__main__":\n main()\n' +assert src.count(call_old) == 1 and src.count(marker) == 1 +src = src.replace(call_old, " verify_revision2()\n verify_revision3()\n\n print()").replace(marker, FUNC + marker) +open(PATH, "w", encoding="utf8").write(src) +print("added verify_revision3") diff --git a/article/sim_numerics.md b/article/sim_numerics.md index 1b4f4ed..012ec9c 100644 --- a/article/sim_numerics.md +++ b/article/sim_numerics.md @@ -183,7 +183,7 @@ Three remarks apply to both forms. **Problem sets.** The 154 problems of Alefeld, Potra and Shi [1] and the 45 of Chandrupatla [3], taken programmatically from SciPy's `scipy.optimize._tstutils` so that no bracket or root is transcribed by hand. -**Cost model.** Evaluations of $f$. A run counts as solved when it reports convergence and $f$ changes sign within $10^{-6}$ of the returned point, or vanishes there — the bracketing notion of a root, the same criterion on every corpus of this paper; $x$-tolerance $10^{-12}$; budget 200 evaluations. Scoring against the published root instead, as the benchmarks' harness does, charges every method the full budget on six problems where $f$ vanishes identically around the root in floating point (§A.5). Head-to-head counts (wins/ties/losses) are taken over every problem of a set with the same charging as the totals. *Failures are charged the full budget*, so no method can buy a low total by giving up early, and totals are reported over all problems rather than over the subset a method happened to solve. Rankings are by failures first, then by total. +**Cost model.** Evaluations of $f$. A run counts as solved when it reports convergence and $f$ changes sign within $10^{-6}$ of the returned point, or vanishes there — the bracketing notion of a root, the same criterion on every corpus of this paper; $x$-tolerance $10^{-12}$; budget 200 evaluations. Every method, ours included, stops when its bracket is no wider than $\varepsilon_x$ or when it evaluates an exact zero of the function it works on; none stops on a small $|f|$. Scoring against the published root instead, as the benchmarks' harness does, charges every method the full budget on six problems where $f$ vanishes identically around the root in floating point (§A.5). Head-to-head counts (wins/ties/losses) are taken over every problem of a set with the same charging as the totals. *Failures are charged the full budget*, so no method can buy a low total by giving up early, and totals are reported over all problems rather than over the subset a method happened to solve. Rankings are by failures first, then by total. **Competitors.** Where SciPy ships the method, the competitor is the library — Chandrupatla is `scipy.optimize.elementwise.find_root`, Brent `brentq`, TOMS 748 `toms748` — run with the stopping rule of every method (a bracket no wider than $\varepsilon_x$, the relative term at machine precision, no stopping on $|f|$) and counted by the same wrapper around $f$. ITP, which has no library implementation, is ours, run with $\epsilon=\varepsilon_x/2$ because it terminates on $b-a\le2\epsilon$, checked against its own theorem ($n_{1/2}+n_0$ iterations at most, on all 154 problems for $n_0\in\{0,1,2,5\}$), and reported both at the authors' constants and as a *per-problem oracle* over a $(\kappa_1,n_0)$ grid — not an achievable method, included so that it cannot be said to have been handicapped. Bisection and the false-position family are ours. @@ -435,7 +435,7 @@ Several mechanisms of A′ coincide with classical ones, reached from similarity | the tolerance step | Dekker [9], Brent [2] — borrowed and named as such | | the exit judgment of §6.2 (the value at a split against the line) | ModAB's linearity check at the midpoint [10, 20], $|y_m-y_3|= 2 only (post hoc) +ESTIMATORS = ("none", "lag", "analogy", "lag2") def _G(v, inv): @@ -49,8 +50,10 @@ def chandrupatla_transformed(f, a, b, xtol, maxfev, estimator="none", stats=None if estimator == "none" or len(hist) < 3: return (xp2, fp2), (xp, fp), (xc, fc) = hist[-3], hist[-2], hist[-1] - if estimator == "lag": + if estimator in ("lag", "lag2"): m = _lagouanelle_raw(xc, fc, xp, fp, xp2, fp2) + if estimator == "lag2" and m is not None and m < 2.0: + m = None # the fractional branch is not used (post hoc variant) else: cur = _config3(xc, fc, xp, fp, xp2, fp2) m = _analogy_order(cur, configs, translation=True) diff --git a/sweep_estimated_posthoc.json b/sweep_estimated_posthoc.json new file mode 100644 index 0000000..46686c6 --- /dev/null +++ b/sweep_estimated_posthoc.json @@ -0,0 +1 @@ +{"APS": {"aps.01.00": [9, true, false], "aps.02.00": [11, true, false], "aps.02.01": [46, true, false], "aps.02.02": [11, true, false], "aps.02.03": [11, true, false], "aps.02.04": [12, true, false], "aps.02.05": [47, true, false], "aps.02.06": [47, true, false], "aps.02.07": [47, true, false], "aps.02.08": [48, true, false], "aps.02.09": [48, true, false], "aps.03.00": [14, true, false], "aps.03.01": [16, true, false], "aps.03.02": [16, true, false], "aps.04.00": [13, true, false], "aps.04.01": [13, true, false], "aps.04.02": [14, true, false], "aps.04.03": [13, true, false], "aps.04.04": [14, true, false], "aps.04.05": [12, true, false], "aps.04.06": [12, true, false], "aps.04.07": [13, true, false], "aps.04.08": [13, true, false], "aps.04.09": [13, true, false], "aps.04.10": [12, true, false], "aps.04.11": [12, true, false], "aps.04.12": [45, true, false], "aps.04.13": [45, true, false], "aps.05.00": [8, true, false], "aps.06.00": [8, true, false], "aps.06.01": [9, true, false], "aps.06.02": [9, true, false], "aps.06.03": [10, true, false], "aps.06.04": [10, true, false], "aps.06.05": [12, true, false], "aps.06.06": [13, true, false], "aps.06.07": [14, true, false], "aps.06.08": [14, true, false], "aps.06.09": [14, true, false], "aps.07.00": [10, true, false], "aps.07.01": [10, true, false], "aps.07.02": [10, true, false], "aps.08.00": [3, true, false], "aps.08.01": [9, true, false], "aps.08.02": [11, true, false], "aps.08.03": [11, true, false], "aps.08.04": [12, true, false], "aps.09.00": [9, true, false], "aps.09.01": [10, true, false], "aps.09.02": [9, true, false], "aps.09.03": [8, true, false], "aps.09.04": [8, true, false], "aps.09.05": [7, true, false], "aps.09.06": [7, true, false], "aps.10.00": [8, true, false], "aps.10.01": [11, true, false], "aps.10.02": [43, true, false], "aps.10.03": [43, true, false], "aps.10.04": [43, true, false], "aps.11.00": [9, true, false], "aps.11.01": [10, true, false], "aps.11.02": [13, true, false], "aps.11.03": [13, true, false], "aps.12.00": [5, true, false], "aps.12.01": [11, true, false], "aps.12.02": [11, true, false], "aps.12.03": [11, true, false], "aps.12.04": [12, true, false], "aps.12.05": [11, true, false], "aps.12.06": [11, true, false], "aps.12.07": [11, true, false], "aps.12.08": [11, true, false], "aps.12.09": [10, true, false], "aps.12.10": [10, true, false], "aps.12.11": [10, true, false], "aps.12.12": [10, true, false], "aps.12.13": [10, true, false], "aps.12.14": [10, true, false], "aps.12.15": [9, true, false], "aps.12.16": [9, true, false], "aps.12.17": [9, true, false], "aps.12.18": [9, true, false], "aps.13.00": [8, true, false], "aps.14.00": [17, true, false], "aps.14.01": [17, true, false], "aps.14.02": [17, true, false], "aps.14.03": [17, true, false], "aps.14.04": [17, true, false], "aps.14.05": [17, true, false], "aps.14.06": [17, true, false], "aps.14.07": [17, true, false], "aps.14.08": [17, true, false], "aps.14.09": [17, true, false], "aps.14.10": [17, true, false], "aps.14.11": [17, true, false], "aps.14.12": [17, true, false], "aps.14.13": [17, true, false], "aps.14.14": [17, true, false], "aps.14.15": [17, true, false], "aps.14.16": [17, true, false], "aps.14.17": [17, true, false], "aps.14.18": [17, true, false], "aps.14.19": [17, true, false], "aps.14.20": [17, true, false], "aps.14.21": [17, true, false], "aps.14.22": [17, true, false], "aps.14.23": [17, true, false], "aps.14.24": [17, true, false], "aps.14.25": [17, true, false], "aps.14.26": [17, true, false], "aps.14.27": [17, true, false], "aps.14.28": [17, true, false], "aps.14.29": [17, true, false], "aps.14.30": [17, true, false], "aps.14.31": [17, true, false], "aps.14.32": [17, true, false], "aps.14.33": [17, true, false], "aps.14.34": [17, true, false], "aps.14.35": [17, true, false], "aps.14.36": [17, true, false], "aps.14.37": [17, true, false], "aps.14.38": [17, true, false], "aps.14.39": [17, true, false], "aps.15.00": [31, true, false], "aps.15.01": [31, true, false], "aps.15.02": [31, true, false], "aps.15.03": [31, true, false], "aps.15.04": [31, true, false], "aps.15.05": [31, true, false], "aps.15.06": [31, true, false], "aps.15.07": [31, true, false], "aps.15.08": [31, true, false], "aps.15.09": [31, true, false], "aps.15.10": [30, true, false], "aps.15.11": [31, true, false], "aps.15.12": [31, true, false], "aps.15.13": [31, true, false], "aps.15.14": [32, true, false], "aps.15.15": [32, true, false], "aps.15.16": [32, true, false], "aps.15.17": [32, true, false], "aps.15.18": [32, true, false], "aps.15.19": [32, true, false], "aps.15.20": [32, true, false], "aps.15.21": [33, true, false], "aps.15.22": [34, true, false], "aps.15.23": [35, true, false], "aps.15.24": [33, true, false], "aps.15.25": [36, true, false], "aps.15.26": [36, true, false], "aps.15.27": [36, true, false], "aps.15.28": [36, true, false], "aps.15.29": [36, true, false], "aps.15.30": [35, true, false]}, "CHA": {"fun1.1": [8, true, false], "fun1.2": [12, true, false], "fun1.3": [49, true, false], "fun1.4": [58, true, false], "fun1.5": [93, true, false], "fun2.1": [9, true, false], "fun2.2": [23, true, false], "fun2.3": [29, true, false], "fun2.4": [42, true, false], "fun2.5": [49, true, false], "fun3.1": [45, true, false], "fun3.2": [47, true, false], "fun3.3": [58, true, false], "fun3.4": [45, true, false], "fun3.5": [151, true, false], "fun4.1": [45, true, false], "fun4.2": [47, true, false], "fun4.3": [63, true, false], "fun4.4": [66, true, false], "fun4.5": [95, true, false], "fun5.1": [45, true, false], "fun5.2": [45, true, false], "fun5.3": [46, true, false], "fun5.4": [48, true, false], "fun5.5": [49, true, false], "fun6.1": [45, true, false], "fun6.2": [45, true, false], "fun6.3": [46, true, false], "fun6.4": [48, true, false], "fun6.5": [49, true, false], "fun7.1": [8, true, false], "fun7.2": [8, true, false], "fun7.3": [11, true, false], "fun7.4": [18, true, false], "fun7.5": [19, true, false], "fun8.1": [10, true, false], "fun8.2": [11, true, false], "fun8.3": [13, true, false], "fun8.4": [14, true, false], "fun8.5": [15, true, false], "fun9.1": [8, true, false], "fun9.2": [9, true, false], "fun9.3": [46, true, false], "fun9.4": [47, true, false], "fun9.5": [14, true, false]}, "NEW": {"C0.00": [10, true, false], "C0.01": [8, true, false], "C0.02": [9, true, false], "C0.03": [43, true, false], "C0.04": [43, true, false], "C0.05": [44, true, false], "C1.00": [47, true, false], "C1.01": [9, true, false], "C1.02": [50, true, false], "C1.03": [51, true, false], "C1.04": [12, true, false], "C1.05": [52, true, false], "C1.06": [8, true, false], "C1.07": [13, true, false], "C1.08": [7, true, false], "C1.09": [14, true, false], "C1.10": [9, true, false], "C1.11": [15, true, false], "C1.12": [10, true, false], "C1.13": [7, true, false], "C1.14": [10, true, false], "C1.15": [9, true, false], "C1.16": [14, true, false], "C1.17": [47, true, false], "C1.18": [15, true, false], "C1.19": [13, true, false], "C1.20": [16, true, false], "C1.21": [7, true, false], "C1.22": [46, true, false], "C1.23": [7, true, false], "C1.24": [10, true, false], "C1.25": [41, true, false], "C1.26": [22, true, false], "C1.27": [46, true, false], "C1.28": [46, true, false], "C1.29": [45, true, false], "C1.30": [10, true, false], "C1.31": [8, true, false], "C1.32": [13, true, false], "C1.33": [45, true, false], "C1.34": [13, true, false], "C1.35": [47, true, false], "C1.36": [45, true, false], "C1.37": [7, true, false], "C1.38": [10, true, false], "C1.39": [49, true, false], "C2.00": [13, true, false], "C2.01": [8, true, false], "C2.02": [50, true, false], "C2.03": [9, true, false], "C2.04": [10, true, false], "C2.05": [7, true, false], "C2.06": [7, true, false], "C2.07": [12, true, false], "C2.08": [13, true, false], "C2.09": [8, true, false], "C2.10": [8, true, false], "C2.11": [8, true, false], "C2.12": [15, true, false], "C2.13": [10, true, false], "C2.14": [8, true, false], "C2.15": [7, true, false], "C2.16": [6, true, false], "C2.17": [11, true, false], "C2.18": [10, true, false], "C2.19": [49, true, false], "C2.20": [10, true, false], "C2.21": [47, true, false], "C2.22": [16, true, false], "C2.23": [41, true, false], "C2.24": [10, true, false], "C2.25": [8, true, false], "C2.26": [14, true, false], "C2.27": [18, true, false], "C2.28": [7, true, false], "C2.29": [9, true, false], "C2.30": [8, true, false], "C2.31": [15, true, false], "C2.32": [14, true, false], "C2.33": [16, true, false], "C2.34": [15, true, false], "C2.35": [9, true, false], "C2.36": [7, true, false], "C2.37": [9, true, false], "C2.38": [10, true, false], "C2.39": [18, true, false], "C3.00": [44, true, false], "C3.01": [49, true, false], "C3.02": [42, true, false], "C3.03": [18, true, false], "C3.04": [41, true, false], "C3.05": [63, true, false], "C3.06": [43, true, false], "C3.07": [49, true, false], "C3.08": [46, true, false], "C3.09": [44, true, false], "C3.10": [46, true, false], "C3.11": [46, true, false], "C3.12": [37, true, false], "C3.13": [42, true, false], "C3.14": [41, true, false], "C3.15": [44, true, false], "C3.16": [48, true, false], "C3.17": [43, true, false], "C3.18": [48, true, false], "C3.19": [44, true, false], "C3.20": [41, true, false], "C3.21": [47, true, false], "C3.22": [50, true, false], "C3.23": [20, true, false], "C3.24": [46, true, false], "C3.25": [42, true, false], "C3.26": [45, true, false], "C3.27": [41, true, false], "C3.28": [41, true, false], "C3.29": [44, true, false], "C3.30": [40, true, false], "C3.31": [22, true, false], "C3.32": [45, true, false], "C3.33": [44, true, false], "C3.34": [43, true, false], "C3.35": [46, true, false], "C3.36": [42, true, false], "C3.37": [47, true, false], "C3.38": [46, true, false], "C3.39": [41, true, false], "C4.00": [8, true, false], "C4.01": [12, true, false], "C4.02": [14, true, false], "C4.03": [48, true, false], "C4.04": [14, true, false], "C4.05": [14, true, false], "C4.06": [7, true, false], "C4.07": [10, true, false], "C4.08": [10, true, false], "C4.09": [10, true, false], "C4.10": [48, true, false], "C4.11": [15, true, false], "C4.12": [7, true, false], "C4.13": [8, true, false], "C4.14": [13, true, false], "C4.15": [14, true, false], "C4.16": [8, true, false], "C4.17": [10, true, false], "C4.18": [8, true, false], "C4.19": [11, true, false], "C4.20": [18, true, false], "C4.21": [51, true, false], "C4.22": [18, true, false], "C4.23": [12, true, false], "C4.24": [11, true, false], "C4.25": [9, true, false], "C4.26": [18, true, false], "C4.27": [9, true, false], "C4.28": [9, true, false], "C4.29": [48, true, false], "C4.30": [8, true, false], "C4.31": [14, true, false], "C4.32": [15, true, false], "C4.33": [41, true, false], "C4.34": [8, true, false], "C4.35": [11, true, false], "C4.36": [11, true, false], "C4.37": [10, true, false], "C4.38": [7, true, false], "C4.39": [9, true, false], "C5.00": [9, true, false], "C5.01": [10, true, false], "C5.02": [7, true, false], "C5.03": [8, true, false], "C5.04": [12, true, false], "C5.05": [12, true, false], "C5.06": [7, true, false], "C5.07": [45, true, false], "C5.08": [46, true, false], "C5.09": [8, true, false], "C5.10": [47, true, false], "C5.11": [11, true, false], "C5.12": [8, true, false], "C5.13": [7, true, false], "C5.14": [7, true, false], "C5.15": [13, true, false], "C5.16": [10, true, false], "C5.17": [45, true, false], "C5.18": [13, true, false], "C5.19": [47, true, false], "C5.20": [44, true, false], "C5.21": [8, true, false], "C5.22": [7, true, false], "C5.23": [10, true, false], "C5.24": [45, true, false], "C5.25": [8, true, false], "C5.26": [7, true, false], "C5.27": [11, true, false], "C5.28": [45, true, false], "C5.29": [9, true, false], "C5.30": [7, true, false], "C5.31": [45, true, false], "C5.32": [10, true, false], "C5.33": [8, true, false], "C5.34": [10, true, false], "C5.35": [7, true, false], "C5.36": [10, true, false], "C5.37": [7, true, false], "C5.38": [10, true, false], "C5.39": [9, true, false]}, "RJL": {"f01": [3, true, false], "f02": [10, true, false], "f03": [12, true, false], "f04": [9, true, false], "f07": [11, true, false], "f09": [10, true, false], "f10": [10, true, false], "f11": [10, true, false], "f16": [8, true, false], "f21": [9, true, false], "f22": [10, true, false], "f23": [10, true, false], "f24": [43, true, false], "f25": [43, true, false], "f26": [43, true, false], "f27": [14, true, false], "f28": [44, true, false], "f29": [12, true, false], "f30": [42, true, false], "f31": [13, true, false], "f32": [12, true, false], "f33": [12, true, false], "f34": [10, true, false], "f35": [16, true, false], "f36": [47, true, false], "f37": [17, true, false], "f38": [47, true, false], "f39": [47, true, false], "f40": [47, true, false], "f42": [11, true, false], "f43": [44, true, false], "f44": [44, true, false], "f45": [44, true, false], "f46": [37, true, false], "f47": [15, true, false], "f48": [17, true, false], "f49": [10, true, false], "f50": [10, true, false], "f51": [9, true, false], "f52": [41, true, false], "f53": [12, true, false], "f54": [7, true, false], "f55": [9, true, false], "f56": [9, true, false], "f57": [11, true, false], "f58": [10, true, false], "f59": [9, true, false], "f60": [8, true, false], "f61": [11, true, false], "f63": [10, true, false], "f64": [9, true, false], "f65": [9, true, false], "f66": [12, true, false], "f67": [12, true, false], "f68": [9, true, false], "f69": [10, true, false], "f70": [12, true, false], "f71": [11, true, false], "f72": [81, true, false], "f73": [39, true, false], "f74": [43, true, false], "f75": [27, true, false], "f76": [43, true, false], "f77": [43, true, false], "f78": [43, true, false], "f79": [40, true, false], "f80": [45, true, false], "f81": [46, true, false], "f82": [5, true, false], "f83": [38, true, false], "f84": [13, true, false], "f85": [15, true, false], "f86": [13, true, false], "f87": [11, true, false], "f88": [12, true, false], "f89": [14, true, false], "f90": [12, true, false], "f91": [10, true, false]}, "APP": {"G.sin[3,4]": [8, true, false], "G.sin[-4,-3]": [8, true, false], "G.sin[-1/3,1]": [9, true, false], "G.cos[0,3]": [9, true, false], "G.cos[-3,0]": [9, true, false], "G.x^20-1": [13, true, false], "G.sqrt|x|sgn": [4, true, false], "G.x^2-1e-8": [21, true, false], "G.x*exp(-x)": [10, true, false], "G.(x-1)^7": [32, true, false], "P.kepler.e0.1.M0.001": [6, true, false], "P.kepler.e0.1.M0.01": [7, true, false], "P.kepler.e0.1.M0.1": [7, true, false], "P.kepler.e0.1.M0.5": [7, true, false], "P.kepler.e0.1.M1.0": [7, true, false], "P.kepler.e0.1.M2.0": [7, true, false], "P.kepler.e0.1.M3.0": [8, true, false], "P.kepler.e0.5.M0.001": [7, true, false], "P.kepler.e0.5.M0.01": [8, true, false], "P.kepler.e0.5.M0.1": [8, true, false], "P.kepler.e0.5.M0.5": [9, true, false], "P.kepler.e0.5.M1.0": [8, true, false], "P.kepler.e0.5.M2.0": [9, true, false], "P.kepler.e0.5.M3.0": [8, true, false], "P.kepler.e0.9.M0.001": [43, true, false], "P.kepler.e0.9.M0.01": [44, true, false], "P.kepler.e0.9.M0.1": [11, true, false], "P.kepler.e0.9.M0.5": [11, true, false], "P.kepler.e0.9.M1.0": [10, true, false], "P.kepler.e0.9.M2.0": [9, true, false], "P.kepler.e0.9.M3.0": [9, true, false], "P.kepler.e0.99.M0.001": [44, true, false], "P.kepler.e0.99.M0.01": [44, true, false], "P.kepler.e0.99.M0.1": [11, true, false], "P.kepler.e0.99.M0.5": [11, true, false], "P.kepler.e0.99.M1.0": [9, true, false], "P.kepler.e0.99.M2.0": [9, true, false], "P.kepler.e0.99.M3.0": [9, true, false], "P.kepler.e0.999.M0.001": [44, true, false], "P.kepler.e0.999.M0.01": [40, true, false], "P.kepler.e0.999.M0.1": [11, true, false], "P.kepler.e0.999.M0.5": [10, true, false], "P.kepler.e0.999.M1.0": [9, true, false], "P.kepler.e0.999.M2.0": [9, true, false], "P.kepler.e0.999.M3.0": [9, true, false], "P.colebrook.Re4e+03.e0.0": [10, true, false], "P.colebrook.Re4e+03.e1e-06": [11, true, false], "P.colebrook.Re4e+03.e0.0001": [10, true, false], "P.colebrook.Re4e+03.e0.001": [10, true, false], "P.colebrook.Re4e+03.e0.01": [10, true, false], "P.colebrook.Re4e+03.e0.05": [10, true, false], "P.colebrook.Re1e+04.e0.0": [11, true, false], "P.colebrook.Re1e+04.e1e-06": [11, true, false], "P.colebrook.Re1e+04.e0.0001": [11, true, false], "P.colebrook.Re1e+04.e0.001": [11, true, false], "P.colebrook.Re1e+04.e0.01": [11, true, false], "P.colebrook.Re1e+04.e0.05": [10, true, false], "P.colebrook.Re1e+05.e0.0": [12, true, false], "P.colebrook.Re1e+05.e1e-06": [12, true, false], "P.colebrook.Re1e+05.e0.0001": [12, true, false], "P.colebrook.Re1e+05.e0.001": [12, true, false], "P.colebrook.Re1e+05.e0.01": [10, true, false], "P.colebrook.Re1e+05.e0.05": [10, true, false], "P.colebrook.Re1e+06.e0.0": [13, true, false], "P.colebrook.Re1e+06.e1e-06": [13, true, false], "P.colebrook.Re1e+06.e0.0001": [11, true, false], "P.colebrook.Re1e+06.e0.001": [11, true, false], "P.colebrook.Re1e+06.e0.01": [10, true, false], "P.colebrook.Re1e+06.e0.05": [10, true, false], "P.colebrook.Re1e+07.e0.0": [13, true, false], "P.colebrook.Re1e+07.e1e-06": [13, true, false], "P.colebrook.Re1e+07.e0.0001": [13, true, false], "P.colebrook.Re1e+07.e0.001": [10, true, false], "P.colebrook.Re1e+07.e0.01": [10, true, false], "P.colebrook.Re1e+07.e0.05": [10, true, false], "P.colebrook.Re1e+08.e0.0": [13, true, false], "P.colebrook.Re1e+08.e1e-06": [13, true, false], "P.colebrook.Re1e+08.e0.0001": [12, true, false], "P.colebrook.Re1e+08.e0.001": [11, true, false], "P.colebrook.Re1e+08.e0.01": [10, true, false], "P.colebrook.Re1e+08.e0.05": [10, true, false], "P.lambert.y1e-06": [6, true, false], "P.lambert.y0.001": [7, true, false], "P.lambert.y0.1": [9, true, false], "P.lambert.y1.0": [9, true, false], "P.lambert.y10.0": [10, true, false], "P.lambert.y1000.0": [12, true, false], "P.lambert.y1000000.0": [44, true, false], "P.planck.n2": [10, true, false], "P.planck.n3": [10, true, false], "P.planck.n4": [10, true, false], "P.planck.n5": [10, true, false], "P.planck.n6": [10, true, false], "P.planck.n8": [9, true, false], "P.planck.n10": [9, true, false], "P.vdw.T280.P1": [7, true, false], "P.vdw.T280.P10": [8, true, false], "P.vdw.T280.P40": [12, true, false], "P.vdw.T280.P73": [42, true, false], "P.vdw.T280.P100": [41, true, false], "P.vdw.T304.P1": [7, true, false], "P.vdw.T304.P10": [8, true, false], "P.vdw.T304.P40": [10, true, false], "P.vdw.T304.P73": [13, true, false], "P.vdw.T304.P100": [42, true, false], "P.vdw.T350.P1": [6, true, false], "P.vdw.T350.P10": [8, true, false], "P.vdw.T350.P40": [9, true, false], "P.vdw.T350.P73": [12, true, false], "P.vdw.T350.P100": [11, true, false], "P.vdw.T500.P1": [6, true, false], "P.vdw.T500.P10": [7, true, false], "P.vdw.T500.P40": [8, true, false], "P.vdw.T500.P73": [9, true, false], "P.vdw.T500.P100": [10, true, false], "P.irr.c5.n5": [14, true, false], "P.irr.c5.n10": [15, true, false], "P.irr.c5.n30": [15, true, false], "P.irr.c10.n5": [14, true, false], "P.irr.c10.n10": [14, true, false], "P.irr.c10.n30": [15, true, false], "P.irr.c20.n5": [12, true, false], "P.irr.c20.n10": [46, true, false], "P.irr.c20.n30": [15, true, false], "P.irr.c50.n5": [13, true, false], "P.irr.c50.n10": [13, true, false], "P.irr.c50.n30": [13, true, false], "P.well.z01": [8, true, false], "P.well.z03": [9, true, false], "P.well.z05": [9, true, false], "P.well.z010": [9, true, false], "P.well.z020": [9, true, false]}, "D": {"D1.00": [15, true, false], "D1.01": [14, true, false], "D1.02": [8, true, false], "D1.03": [48, true, false], "D1.04": [7, true, false], "D1.05": [10, true, false], "D1.06": [10, true, false], "D1.07": [12, true, false], "D1.08": [9, true, false], "D1.09": [10, true, false], "D1.10": [10, true, false], "D1.11": [14, true, false], "D1.12": [47, true, false], "D1.13": [12, true, false], "D1.14": [8, true, false], "D1.15": [7, true, false], "D1.16": [9, true, false], "D1.17": [14, true, false], "D1.18": [15, true, false], "D1.19": [10, true, false], "D1.20": [8, true, false], "D1.21": [7, true, false], "D1.22": [7, true, false], "D1.23": [9, true, false], "D1.24": [9, true, false], "D1.25": [9, true, false], "D1.26": [8, true, false], "D1.27": [12, true, false], "D1.28": [10, true, false], "D1.29": [8, true, false], "D1.30": [7, true, false], "D1.31": [9, true, false], "D1.32": [12, true, false], "D1.33": [11, true, false], "D1.34": [16, true, false], "D1.35": [11, true, false], "D1.36": [10, true, false], "D1.37": [9, true, false], "D1.38": [14, true, false], "D1.39": [8, true, false], "D2.00": [45, true, false], "D2.01": [42, true, false], "D2.02": [44, true, false], "D2.03": [44, true, false], "D2.04": [44, true, false], "D2.05": [47, true, false], "D2.06": [40, true, false], "D2.07": [44, true, false], "D2.08": [41, true, false], "D2.09": [51, true, false], "D2.10": [43, true, false], "D2.11": [42, true, false], "D2.12": [41, true, false], "D2.13": [41, true, false], "D2.14": [44, true, false], "D2.15": [45, true, false], "D2.16": [36, true, false], "D2.17": [47, true, false], "D2.18": [44, true, false], "D2.19": [42, true, false], "D2.20": [46, true, false], "D2.21": [45, true, false], "D2.22": [45, true, false], "D2.23": [46, true, false], "D2.24": [44, true, false], "D2.25": [44, true, false], "D2.26": [44, true, false], "D2.27": [40, true, false], "D2.28": [46, true, false], "D2.29": [45, true, false], "D2.30": [44, true, false], "D2.31": [47, true, false], "D2.32": [45, true, false], "D2.33": [42, true, false], "D2.34": [42, true, false], "D2.35": [45, true, false], "D2.36": [43, true, false], "D2.37": [47, true, false], "D2.38": [45, true, false], "D2.39": [44, true, false], "D3.00": [24, true, false], "D3.01": [45, true, false], "D3.02": [59, true, false], "D3.03": [41, true, false], "D3.04": [43, true, false], "D3.05": [38, true, false], "D3.06": [23, true, false], "D3.07": [62, true, false], "D3.08": [43, true, false], "D3.09": [48, true, false], "D3.10": [27, true, false], "D3.11": [39, true, false], "D3.12": [56, true, false], "D3.13": [54, true, false], "D3.14": [49, true, false], "D3.15": [45, true, false], "D3.16": [38, true, false], "D3.17": [40, true, false], "D3.18": [44, true, false], "D3.19": [47, true, false], "D3.20": [47, true, false], "D3.21": [45, true, false], "D3.22": [34, true, false], "D3.23": [47, true, false], "D3.24": [40, true, false], "D3.25": [45, true, false], "D3.26": [38, true, false], "D3.27": [47, true, false], "D3.28": [46, true, false], "D3.29": [42, true, false], "D3.30": [46, true, false], "D3.31": [47, true, false], "D3.32": [53, true, false], "D3.33": [63, true, false], "D3.34": [43, true, false], "D3.35": [48, true, false], "D3.36": [46, true, false], "D3.37": [54, true, false], "D3.38": [42, true, false], "D3.39": [44, true, false], "D4.00": [46, true, false], "D4.01": [7, true, false], "D4.02": [7, true, false], "D4.03": [8, true, false], "D4.04": [8, true, false], "D4.05": [8, true, false], "D4.06": [10, true, false], "D4.07": [7, true, false], "D4.08": [8, true, false], "D4.09": [10, true, false], "D4.10": [9, true, false], "D4.11": [8, true, false], "D4.12": [9, true, false], "D4.13": [7, true, false], "D4.14": [10, true, false], "D4.15": [9, true, false], "D4.16": [13, true, false], "D4.17": [7, true, false], "D4.18": [8, true, false], "D4.19": [8, true, false], "D4.20": [9, true, false], "D4.21": [6, true, false], "D4.22": [9, true, false], "D4.23": [9, true, false], "D4.24": [8, true, false], "D4.25": [7, true, false], "D4.26": [6, true, false], "D4.27": [8, true, false], "D4.28": [8, true, false], "D4.29": [9, true, false], "D4.30": [7, true, false], "D4.31": [7, true, false], "D4.32": [7, true, false], "D4.33": [8, true, false], "D4.34": [10, true, false], "D4.35": [7, true, false], "D4.36": [9, true, false], "D4.37": [6, true, false], "D4.38": [8, true, false], "D4.39": [9, true, false], "D5.00": [8, true, false], "D5.01": [8, true, false], "D5.02": [26, true, false], "D5.03": [26, true, false], "D5.04": [8, true, false], "D5.05": [17, true, false], "D5.06": [22, true, false], "D5.07": [22, true, false], "D5.08": [12, true, false], "D5.09": [25, true, false], "D5.10": [9, true, false], "D5.11": [24, true, false], "D5.12": [10, true, false], "D5.13": [9, true, false], "D5.14": [9, true, false], "D5.15": [10, true, false], "D5.16": [11, true, false], "D5.17": [8, true, false], "D5.18": [7, true, false], "D5.19": [13, true, false], "D5.20": [6, true, false], "D5.21": [7, true, false], "D5.22": [24, true, false], "D5.23": [11, true, false], "D5.24": [7, true, false], "D5.25": [50, true, false], "D5.26": [9, true, false], "D5.27": [23, true, false], "D5.28": [7, true, false], "D5.29": [11, true, false], "D5.30": [50, true, false], "D5.31": [10, true, false], "D5.32": [6, true, false], "D5.33": [10, true, false], "D5.34": [20, true, false], "D5.35": [12, true, false], "D5.36": [7, true, false], "D5.37": [35, true, false], "D5.38": [19, true, false], "D5.39": [10, true, false], "D6.00": [8, true, false], "D6.01": [7, true, false], "D6.02": [7, true, false], "D6.03": [47, true, false], "D6.04": [7, true, false], "D6.05": [9, true, false], "D6.06": [7, true, false], "D6.07": [8, true, false], "D6.08": [7, true, false], "D6.09": [9, true, false], "D6.10": [7, true, false], "D6.11": [7, true, false], "D6.12": [7, true, false], "D6.13": [6, true, false], "D6.14": [9, true, false], "D6.15": [44, true, false], "D6.16": [7, true, false], "D6.17": [7, true, false], "D6.18": [10, true, false], "D6.19": [11, true, false], "D6.20": [9, true, false], "D6.21": [9, true, false], "D6.22": [49, true, false], "D6.23": [7, true, false], "D6.24": [9, true, false], "D6.25": [7, true, false], "D6.26": [7, true, false], "D6.27": [11, true, false], "D6.28": [7, true, false], "D6.29": [8, true, false], "D6.30": [45, true, false], "D6.31": [11, true, false], "D6.32": [8, true, false], "D6.33": [7, true, false], "D6.34": [50, true, false], "D6.35": [12, true, false], "D6.36": [11, true, false], "D6.37": [11, true, false], "D6.38": [7, true, false], "D6.39": [49, true, false]}} \ No newline at end of file diff --git a/sweep_estimated_posthoc.py b/sweep_estimated_posthoc.py new file mode 100644 index 0000000..d7c1c07 --- /dev/null +++ b/sweep_estimated_posthoc.py @@ -0,0 +1,84 @@ +"""POSTHOC_lag_branch_2026-09-22: Chandrupatla on sign(f)|f|^(1/m_hat) with Lagouanelle's estimate +restricted to the branch m >= 2 (K3-L2), post hoc, with the harness of sweep_fourth.py. The +registered K3 run (sweep_estimated.py / .json) is not touched. + + python -u sweep_estimated_posthoc.py run -> sweep_estimated_posthoc.json, report + python -u sweep_estimated_posthoc.py --report +""" +from __future__ import annotations + +import functools +import json +import sys +from multiprocessing import Pool + +import sweep_fourth +import sweep_full +from simroot.transformed import chandrupatla_transformed +from sweep_controls import nonsimple +from sweep_oos import sign_p + +sweep_full.EXTRA_BASELINES["k3_lag2"] = functools.partial(chandrupatla_transformed, estimator="lag2") +sweep_fourth.CONFIGS["k3_lag2"] = ("base", "k3_lag2") +SETS = ("APS", "CHA", "NEW", "RJL", "APP", "D") + + +def job(which): + return sweep_fourth.evaluate(("k3_lag2", which))[1] + + +def cost(v): + return v[0] if v[1] else 200 + + +def tot(r, keep=None): + return sum(cost(v) for p, v in r.items() if keep is None or keep(p)) + + +def held_on_simple_roots(): + """How many APS runs end holding m_hat != 1 (fresh).""" + n = 0 + for s, pid, f, a, b, _r in sweep_full.load_problems(): + if s != "APS": + continue + st = {} + chandrupatla_transformed(f, a, b, 1e-12, 200, estimator="lag2", stats=st) + n += st.get("m_hat", 1.0) != 1.0 + return n + + +def report(R): + seen = json.load(open("sweep_fourth_seen.json", encoding="utf8")) + four = json.load(open("sweep_fourth_fourth.json", encoding="utf8")) + ap = dict(seen["Aprime"]) + ap.update(four["Aprime"]) + K1 = json.load(open("sweep_controls.json", encoding="utf8"))["K1"] + grp = sum(tot(R[s], lambda p, _m=K1[s]["mstar"]: nonsimple(_m[p])) for s in ("CHA", "NEW", "D")) + ps = list(ap["D"]) + w = sum(cost(ap["D"][p]) < cost(R["D"][p]) for p in ps) + l = sum(cost(ap["D"][p]) > cost(R["D"][p]) for p in ps) + print(f"K3-L2 (post hoc): corpus 4 {tot(R['D'])} six corpora {sum(tot(R[s]) for s in SETS)} " + f"multiple/fractional {grp} APS {tot(R['APS'])}") + print(f" A' on corpus 4 against it: {w}/{len(ps) - w - l}/{l} p={sign_p(w, l):.2g}") + print(f" per corpus: " + " ".join(f"{s} {tot(R[s])}" for s in SETS)) + print(f" violations {sum(v[2] for s in SETS for v in R[s].values())}") + print(f" APS runs ending with m_hat != 1: {held_on_simple_roots()} of 154") + + +def main(): + if "--report" not in sys.argv: + with Pool(2) as pool: + parts = pool.map(job, ("seen", "fourth")) + R = {} + for p in parts: + R.update(p) + with open("sweep_estimated_posthoc.json", "w", encoding="utf8") as fh: + json.dump(R, fh) + with open("sweep_estimated_posthoc.json", encoding="utf8") as fh: + R = json.load(fh) + report(R) + print("posthoc done") + + +if __name__ == "__main__": + main() diff --git a/tests/test_transformed.py b/tests/test_transformed.py index 157a3ea..b0fac5e 100644 --- a/tests/test_transformed.py +++ b/tests/test_transformed.py @@ -23,6 +23,15 @@ def test_without_an_estimate_it_is_chandrupatla_bit_for_bit(): assert not bad, bad[:5] +def test_the_post_hoc_branch_never_holds_a_fractional_order(): + """POSTHOC_lag_branch_2026-09-22: 'lag2' takes only readings m >= 2.""" + for s, pid, f, a, b, _r in PROBS: + st = {} + x, n, ok = chandrupatla_transformed(f, a, b, 1e-12, 200, estimator="lag2", stats=st) + assert min(a, b) <= x <= max(a, b), pid + assert st.get("m_hat", 1.0) == 1.0 or st["m_hat"] >= 2.0, (pid, st) + + @pytest.mark.parametrize("estimator", ("lag", "analogy")) def test_with_an_estimate_it_still_brackets(estimator): for s, pid, f, a, b, _r in PROBS: diff --git a/verify_article.py b/verify_article.py index 8ff5485..c8755ba 100644 --- a/verify_article.py +++ b/verify_article.py @@ -223,6 +223,7 @@ def main(): verify_revision1() verify_controls() verify_revision2() + verify_revision3() print() if FAILS: @@ -1180,5 +1181,38 @@ def verify_revision2(): check(f"Table 3 row {lb}", (c["total"], c["solved"], c["over"]), want_) +def verify_revision3(): + """Revision 3 (point 5): the post hoc branch m >= 2 and the reading of Table 12.""" + print("\nRevision 3:") + + def cost(v): + return v[0] if v[1] else 200 + + def tot(r, keep=None): + return sum(cost(v) for p, v in r.items() if keep is None or keep(p)) + sets = ("APS", "CHA", "NEW", "RJL", "APP", "D") + P = _load("sweep_estimated_posthoc.json") + K = _load("sweep_estimated.json")["k3_analogy"] + seen, four = _load("sweep_fourth_seen.json"), _load("sweep_fourth_fourth.json") + ap = dict(seen["Aprime"]) + ap.update(four["Aprime"]) + K1 = _load("sweep_controls.json")["K1"] + nonsimple = lambda m: m >= 2.0 or m <= 2.0 / 3.0 # noqa: E731 + + def group(r): + return sum(tot(r[s], lambda p, _m=K1[s]["mstar"]: nonsimple(_m[p])) for s in ("CHA", "NEW", "D")) + check("Table 12 post hoc row", (tot(P["D"]), sum(tot(P[s]) for s in sets), group(P), tot(P["APS"])), + (5568, 18372, 6540, 2960)) + ps = list(ap["D"]) + w = sum(cost(ap["D"][p]) < cost(P["D"][p]) for p in ps) + l = sum(cost(ap["D"][p]) > cost(P["D"][p]) for p in ps) + check("A' vs the post hoc variant on corpus 4", (w, len(ps) - w - l, l), (178, 34, 28)) + check("post hoc violations", sum(v[2] for s in sets for v in P[s].values()), 0) + gain = group(ap) - group(K) + loss = (sum(tot(K[s]) for s in sets) - group(K)) - (sum(tot(ap[s]) for s in sets) - group(ap)) + check("transferred detector: gain on multiple roots < loss elsewhere (vs A')", (gain, loss, gain < loss), + (1233, 3934, True)) + + if __name__ == "__main__": main()