Why the wide-bracket gap remains, taken apart on the SAME evaluations: 'order of the model' explains only a third. Of the 262 evaluations lost on 33 problems, 178 sit on fun3-fun6 - pure powers of order 3, 5, 9, 19 - where no interpolating model works (our step 0.41-0.81 bracket halvings per evaluation, IQI from our own points 0.01-0.17) and Chandrupatla wins by BISECTING 93-100% of his steps; our step is slower than bisection there and our 2x-bisection guarantee licenses it. His band inside our step confirms it per family: fun4/5/6 573/456/456 -> 296/239/235 against his 293/228/228, while +164/+111/+170/+144 on fun1/2/8/9 where it refuses good steps - slot 2's total was two opposite effects summed. Only fun1 (58) is model order: IQI from our three points 10.37 halvings against our 0.62; a convex two-point step cannot carry IQI's negative weight (428/428 non-degenerate states; five underflowed to -0.0).

The flat regime is not detectable by the three-point order estimate: m >= 2 (the exact condition for a tangent step to lose to a halving) fires on 0.2% of APS states and on only 5.8% of states of pure powers of order 3-19. Paper corrected where it said the whole gap is model order (widelimit, decision, abstract, future work - the refuted log transform removed). WARNING recorded in FINDINGS_model_order.md: article/sim_numerics.tex has carried a STALE header since the first commit (current text = sim_numerics.md, unchanged since 08-24); every paper edit of 09-20/21 went into the .tex, the .md has none of them. Which file is canonical is Valeri's call. The new text numbers trace to results_diag_*.txt but are not yet machine-checked by verify_article.py. Trace hook in pure_root is behaviour-neutral and pinned; 762 pass on the server twice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
vvs
2026-09-21 07:38:16 +01:00
co-authored by Claude Opus 5
parent 46c713cdd3
commit f5d036c9d8
13 changed files with 693 additions and 28 deletions
+83
View File
@@ -0,0 +1,83 @@
"""Two checks on diag_model_order.py before its result may be stated.
C1 WHAT Chandrupatla does on the problems where we lose, by family: the share of his
steps that are bisections, from the traced mirror whose evaluation count is asserted
equal to the original's. If the flat families are won by bisecting, the gap there
is not a better model on his side.
C2 the 5 states where "exactly one negative Lagrange weight" failed: print them. The
sign argument is exact in real arithmetic, so each exception must be a
floating-point degeneracy (a weight that underflowed to 0, or f values so close
that a difference cancelled) - or the argument is wrong and must be withdrawn.
python -u diag_model_order_checks.py
"""
from __future__ import annotations
import math
from collections import defaultdict
from scipy.optimize._tstutils import _CHANDRUPATLA_TESTS_DICTS
from diag_model_order import ACC, MAXFEV, OURS, XTOL, ch_cost, iqi
from explain_chandrupatla import chandrupatla_traced
from simroot.baselines import chandrupatla
from simroot.pure import pure_root
def main():
fam = defaultdict(lambda: [0, 0, 0, 0, 0]) # bisect, iqi, ours n, ch n, problems
exceptions = []
for d in _CHANDRUPATLA_TESTS_DICTS:
pid = str(d["ID"])
f = d["f"]
a, b = (float(v) for v in d["bracket"])
r = float(d["root"])
st = {"trace": []}
x, n, conv = pure_root(f, a, b, XTOL, MAXFEV, stats=st, **OURS)
if not (conv and abs(float(x) - r) <= ACC):
continue
ch = ch_cost(f, a, b, r)
if ch is None or n <= ch:
continue
_xc, nc, okc, tr = chandrupatla_traced(f, a, b, XTOL, MAXFEV)
_xo, no, _oko = chandrupatla(f, a, b, XTOL, MAXFEV)
assert nc == no, f"mirror drifted on {pid}: {nc} vs {no}"
k = pid.split(".")[0]
cell = fam[k]
cell[0] += tr["bisect"]
cell[1] += tr["iqi"]
cell[2] += n
cell[3] += ch
cell[4] += 1
for s in st["trace"]:
if s["xp2"] is None:
continue
pts = ((s["x"], s["fx"]), (s["xp"], s["fp"]), (s["xp2"], s["fp2"]))
fs = [p[1] for p in pts]
if not (min(fs) < 0.0 < max(fs)):
continue
_xq, w = iqi(*pts)
if w is not None and sum(v < 0.0 for v in w) != 1:
exceptions.append((pid, fs, w))
print("C1 Chandrupatla on the problems where we lose, by family")
print(f" {'family':<6} {'probs':>5} {'ours':>6} {'CH':>6} {'gap':>5} "
f"{'CH bisect share':>16}")
tot_gap = 0
for k in sorted(fam):
bi, iq, on, cn, p = fam[k]
tot_gap += on - cn
share = 100.0 * bi / max(bi + iq, 1)
print(f" {k:<6} {p:5d} {on:6d} {cn:6d} {on - cn:5d} {share:15.0f}%")
print(f" total gap on the losing problems: {tot_gap}")
print(f"\nC2 the {len(exceptions)} states breaking 'exactly one negative weight'")
for pid, fs, w in exceptions:
tiny = min(abs(v) for v in fs)
print(f" {pid:<8} f = [{', '.join(f'{v:.3e}' for v in fs)}] "
f"w = [{', '.join(f'{v:.3e}' for v in w)}] "
f"zero weights: {sum(v == 0.0 for v in w)} min|f| {tiny:.1e}")
if __name__ == "__main__":
main()