"""PREREG_roots_jl_2026-09-21: variants A, B, the plain rule and the three library rivals on the Roots.jl / ModAB bracketing benchmark (corpus_roots_jl.py). python -u sweep_rootsjl.py run -> sweep_rootsjl.json, then the verdicts python -u sweep_rootsjl.py --report verdicts only """ from __future__ import annotations import json import math import sys from multiprocessing import Pool from corpus_roots_jl import build, solved from sweep_full import MAXFEV, XTOL, make_method from sweep_oos import CONFIGS as OOS_CONFIGS from sweep_oos import sign_p NAMES = ("base", "A", "B", "chandrupatla", "brent", "toms748") def evaluate(name): method = make_method(OOS_CONFIGS[name]) probs, _ = build() res = {} for pid, f, a, b, ins in probs: try: x, n, conv = method(f, a, b, XTOL, MAXFEV) x = float(x) except Exception: x, n, conv = float("nan"), MAXFEV, False ok = bool(conv and solved(f, x)) over = ok and int(n) > 2 * math.ceil(math.log2(abs(b - a) / XTOL)) + 4 res[pid] = [int(n), ok, bool(over), bool(ins)] return name, res def cost(v): return v[0] if v[1] else MAXFEV def report(R): keep_main = lambda pid, v: not v[3] keep_strict = lambda pid, v: not v[3] and int(pid[1:]) >= 24 keep_ins = lambda pid, v: v[3] ch = R["chandrupatla"] for title, keep in (("MAIN (78 out-of-sample)", keep_main), ("STRICT (f24-f91, out-of-sample)", keep_strict), ("IN-SAMPLE overlaps (13, for reference)", keep_ins)): print(f"\n== {title}") for k in NAMES: r = R[k] sel = {p: v for p, v in r.items() if keep(p, v)} print(f" {k:<14} total {sum(cost(v) for v in sel.values()):6d} " f"solved {sum(v[1] for v in sel.values()):3d}/{len(sel)} " f"violations {sum(v[2] for v in sel.values())}") for v_name in ("A", "B"): r = R[v_name] sel = [p for p, v in r.items() if keep(p, v)] tot = sum(cost(r[p]) for p in sel) tot_ch = sum(cost(ch[p]) for p in sel) w = sum(cost(r[p]) < cost(ch[p]) for p in sel) l = sum(cost(r[p]) > cost(ch[p]) for p in sel) t = len(sel) - w - l viol = sum(r[p][2] for p in sel) sv, sc = sum(r[p][1] for p in sel), sum(ch[p][1] for p in sel) r1, r2, r3 = tot < tot_ch, w > l and sign_p(w, l) < 0.05, viol == 0 and sv >= sc line = (f" {v_name}: R1 {'✓' if r1 else '✗'} ({tot} vs {tot_ch}) R2 {'✓' if r2 else '✗'} " f"({w}/{t}/{l} p={sign_p(w, l):.2g}) R3 {'✓' if r3 else '✗'} (viol {viol}, " f"solved {sv} vs {sc})") if keep is keep_main: line += " -> " + ("TRANSFERS" if r1 and r2 and r3 else "does NOT transfer") print(line) for rn in ("brent", "toms748"): o = R[rn] w2 = sum(cost(r[p]) < cost(o[p]) for p in sel) l2 = sum(cost(r[p]) > cost(o[p]) for p in sel) print(f" vs {rn:<8} total {tot} vs {sum(cost(o[p]) for p in sel)} " f"per problem {w2}/{len(sel) - w2 - l2}/{l2} p={sign_p(w2, l2):.2g}") print("\nPER PROBLEM, out-of-sample (A / B / Chandrupatla / Brent / TOMS748), * = unsolved:") for p, v in R["A"].items(): if v[3]: continue cells = [] for k in ("A", "B", "chandrupatla", "brent", "toms748"): c = R[k][p] cells.append(f"{c[0]}{'' if c[1] else '*'}") print(f" {p}: " + " / ".join(cells)) def main(): if "--report" not in sys.argv: with Pool(len(NAMES)) as pool: R = dict(pool.map(evaluate, NAMES)) with open("sweep_rootsjl.json", "w", encoding="utf8") as fh: json.dump(R, fh) with open("sweep_rootsjl.json", encoding="utf8") as fh: R = json.load(fh) report(R) print("rootsjl done") if __name__ == "__main__": main()