From be10fc0b220d02e3f2694b79f343a36b5a1754ec Mon Sep 17 00:00:00 2001 From: Valeri Sitnikov Date: Sun, 20 Sep 2026 23:05:22 +0100 Subject: [PATCH] =?UTF-8?q?explained,=20with=20numbers:=20Chandrupatla=20w?= =?UTF-8?q?ins=20the=20probe=20by=20REFUSING=20its=20model=20cheaply,=20no?= =?UTF-8?q?t=20by=20trusting=20it=20=E2=80=94=20it=20accepts=20the=20inter?= =?UTF-8?q?polation=20on=20only=2028%=20of=20steps,=20its=20gate=20costs?= =?UTF-8?q?=20zero=20evaluations=20(read=20from=20data=20already=20in=20ha?= =?UTF-8?q?nd)=20and=20its=20refusal=20branch=20still=20halves=20the=20bra?= =?UTF-8?q?cket;=20ours=20repairs=2077.5%=20of=20evaluations=20after=20the?= =?UTF-8?q?=20fact=20and=20drops=20to=200.92=20halvings/eval=20on=20fun8/f?= =?UTF-8?q?un9,=20worse=20than=20bisection.=20In=20similarity=20terms:=20t?= =?UTF-8?q?heir=20similarity=20is=20a=20PRE-step=20GATE=20on=20dimensionle?= =?UTF-8?q?ss=20ratios=20(a=20neighbourhood=20of=20the=20linear=20configur?= =?UTF-8?q?ation),=20ours=20is=20an=20IN-step=20SCALE=20on=20the=20residua?= =?UTF-8?q?l=20magnitude=20=E2=80=94=20the=20PLACEMENT=20of=20the=20readin?= =?UTF-8?q?g=20matters=20more=20than=20its=20form?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 --- FINDINGS_logarithmic.md | 48 ++++++++++ explain_chandrupatla.py | 149 +++++++++++++++++++++++++++++++ results_explain_chandrupatla.txt | 54 +++++++++++ 3 files changed, 251 insertions(+) create mode 100644 explain_chandrupatla.py create mode 100644 results_explain_chandrupatla.txt diff --git a/FINDINGS_logarithmic.md b/FINDINGS_logarithmic.md index 86d1d6f..5916651 100644 --- a/FINDINGS_logarithmic.md +++ b/FINDINGS_logarithmic.md @@ -88,3 +88,51 @@ Chandrupatla-1997 (2423 против наших 4516) при **нуле** настроечных констант, то есть существующий алгоритм уже делает то, чего мы добиваемся, и объяснение его преимущества — отдельная задача (качество пробы, `stage4_why.py`), а не новая шкала шага. + +--- + +## 3. 🟢 ЧЕМ Chandrupatla выигрывает пробу — измерено, и это переводится на язык похожести + +Инструмент: `explain_chandrupatla.py` (лог `results_explain_chandrupatla.txt`). Трассируемая копия +опубликованной процедуры; её верность **проверена** — совпадение с оригиналом по числу вызовов и +исходу на всех 45 задачах. Метрика пробы беспараметрическая: **половинок скобки на вызов** +`= log₂(w₀/xtol)/nfev`, деление пополам = 1.0. + +| величина (45 задач CHA) | Chandrupatla | наш `ratio.derived3.credit` | +|---|---|---| +| вызовов всего (провал = 200) | **1488** | 2073 | +| **половинок на вызов** | **2.310** | 1.422 | +| модель принята | **28.0 % шагов** | — | +| сторож ремонтировал | — | **77.5 % вызовов** | + +Семейства `fun8`/`fun9`: у них 8–15 вызовов при 3–5 половинках, у нас 44–50 вызовов при **0.92** — +то есть **хуже деления пополам**, при 93 % ремонтов. + +### Ответ + +**Он выигрывает не тем, что больше доверяет модели, а тем, что дешевле от неё отказывается.** +Интерполяция принимается лишь в 28 % шагов; в остальных 72 % он делит пополам. Гейт считается из уже +имеющихся `x1,x2,x3,f1,f2,f3` — **ноль новых вызовов**, а ветвь отказа сама снимает половину скобки. +Наш отказ **платный**: шаг сделан, лёг плохо, сторож ремонтирует, ремонт — это вызов. + +### Перевод + +| | где читается похожесть | на чём | цена отказа | +|---|---|---|---| +| Chandrupatla | **до** шага, как **ГЕЙТ** | на форме конфигурации: `ξ = (x1−x2)/(x3−x2)`, `φ = (f1−f2)/(f3−f2)` — два отношения, инвариантные к масштабу по `x` и по `f`; полоса `1−√(1−ξ) < φ < √ξ` есть окрестность **линейной** конфигурации (для линейной `f` точно `φ = ξ`, и она внутри полосы) | **0 вызовов** | +| наш метод | **внутри** шага, как **МАСШТАБ** | на величине остатка `\|f\|/σ` | **1 вызов** | + +**Следствие, которое дороже самого объяснения:** решает не форма функции похожести, а **её место в +алгоритме**. Похожесть, прочитанная ДО шага на отношениях, стоит ноль и отсекает недостоверную +модель; похожесть, прочитанная ВНУТРИ шага на величине, требует сторожа, и сторож платит. Это +дополняет закон корпуса «рассуждение платит тем больше, чем хуже похожесть»: здесь **расположение +чтения важнее его формы**. + +### Что из этого следует как проверяемый шаг (пред-регистрировать до реализации) + +Добавить в наш метод **предшаговый гейт на отношениях** — читать похожесть конфигурации к +хорошо обусловленной ДО того, как шаг сделан, и при отказе сразу делить пополам, вместо +масштабированного шага с последующим ремонтом. Новой константы это не вводит: границы полосы у +Чандрупатлы выведены из требования «проба модели не должна выйти за скобку» — того же требования, +которое наш проекционный сторож выполняет, но **после** факта. Цель замера: довести долю ремонтов с +77.5 % к уровню их 72 % отказов **без** роста цены на APS-154. diff --git a/explain_chandrupatla.py b/explain_chandrupatla.py new file mode 100644 index 0000000..690f3c6 --- /dev/null +++ b/explain_chandrupatla.py @@ -0,0 +1,149 @@ +"""Why Chandrupatla wins the probe — measured, and stated in similarity terms. + +ANALYSIS ONLY. The traced copy below mirrors `simroot.baselines.chandrupatla` so its +decisions can be counted; it is NEVER used for a ranking number, and its fidelity is +asserted against the original (identical evaluation count on all 45 problems). If that +assert fails, the mirror has drifted and nothing here may be believed. + +The reading being tested: + + Chandrupatla = similarity(configuration, well-conditioned configuration) used as a + GATE on a model step, with bisection as the refusal branch. + ours = similarity(residual, 0) used as a SCALE for the step, with the + safeguard bolted on afterwards. + + * the decision variable is t in (0,1): the probe is a FRACTION of the bracket, so the + method is scale-free by construction (what the logarithmic attempt tried to buy); + * the gate is written in two RATIOS, xi = (x1-x2)/(x3-x2) and phi = (f1-f2)/(f3-f2), + both invariant under affine rescaling of x and of f — relations, not magnitudes; + * for a LINEAR f, phi = xi exactly, and that point lies strictly inside the band + 1 - sqrt(1-xi) < phi < sqrt(xi), so the band is a neighbourhood of the linear + configuration: the model is trusted exactly where the data still look linear enough. + +Metric for "probe quality", parameter-free and comparable across methods: + halvings per evaluation = log2(w0 / xtol) / nfev +Bisection is 1.0 by definition; more is better. + + python -u explain_chandrupatla.py +""" + +from __future__ import annotations + +import math + +from simroot.baselines import Counter, chandrupatla +from simroot.pure import pure_root +from sweep_full import ACC, MAXFEV, XTOL, load_problems + + +def chandrupatla_traced(f, a, b, xtol, maxfev): + """Mirror of the published routine plus counters. Returns (x, nfev, ok, trace).""" + g = Counter(f, maxfev) + trace = {"iqi": 0, "bisect": 0, "gate_rejected": 0, "no_third_point": 0} + x1, x2 = b, a + f1 = f2 = 0.0 + try: + f1, f2 = g(x1), g(x2) + if f1 == 0.0: + return x1, g.n, True, trace + if f2 == 0.0: + return x2, g.n, True, trace + x3, f3 = x2, f2 + t = 0.5 + while True: + x = x1 + t * (x2 - x1) + fx = g(x) + if (fx > 0.0) == (f1 > 0.0): + x3, f3 = x1, f1 + x1, f1 = x, fx + else: + x3, f3 = x2, f2 + x2, f2 = x1, f1 + x1, f1 = x, fx + xm, fm = (x1, f1) if abs(f1) < abs(f2) else (x2, f2) + if fm == 0.0: + return xm, g.n, True, trace + tol = 2.0 * 2.22e-16 * abs(xm) + xtol + span = abs(x2 - x1) + if span == 0.0: + return xm, g.n, True, trace + tl = tol / span + if tl > 0.5: + return xm, g.n, True, trace + t = 0.5 + took_iqi = False + if x3 != x2 and f3 != f2: + xi = (x1 - x2) / (x3 - x2) + ph = (f1 - f2) / (f3 - f2) + if 0.0 < xi < 1.0 and 1.0 - math.sqrt(1.0 - xi) < ph < math.sqrt(xi): + d1, d2, d3 = f1 - f2, f1 - f3, f2 - f3 + if d1 != 0.0 and d2 != 0.0 and d3 != 0.0: + t = (f1 / d1) * (f3 / (-d3)) + \ + ((x3 - x1) / (x2 - x1)) * (f1 / d2) * (f2 / d3) + took_iqi = True + else: + trace["gate_rejected"] += 1 + else: + trace["no_third_point"] += 1 + if not math.isfinite(t): + t = 0.5 + took_iqi = False + if t < tl: + t = tl + elif t > 1.0 - tl: + t = 1.0 - tl + trace["iqi" if took_iqi else "bisect"] += 1 + except Exception: + return float("nan"), g.n, False, trace + + +def main(): + probs = [p for p in load_problems() if p[0] == "CHA"] + print(f"CHA problems: {len(probs)} metric: halvings per evaluation " + f"= log2(w0/xtol)/nfev (bisection = 1.0)\n") + print(f"{'problem':<16} {'CH nfev':>8} {'CH h/ev':>8} {'gate ok%':>9} " + f"{'ours nfev':>10} {'ours h/ev':>10} {'forced%':>8}") + + agg = {"ch_n": 0, "our_n": 0, "ch_h": [], "our_h": [], "gate": [], "forced": []} + drift = 0 + for _s, pid, f, a, b, root in probs: + w0 = abs(b - a) + decades = math.log2(w0 / XTOL) + + x_ref, n_ref, ok_ref = chandrupatla(f, a, b, XTOL, MAXFEV) + x_t, n_t, ok_t, tr = chandrupatla_traced(f, a, b, XTOL, MAXFEV) + if (n_ref, ok_ref) != (n_t, ok_t): + drift += 1 + steps = tr["iqi"] + tr["bisect"] + gate_ok = 100.0 * tr["iqi"] / steps if steps else float("nan") + + stats = {} + x_o, n_o, ok_o = pure_root(f, a, b, XTOL, MAXFEV, similarity="ratio", + scale="derived3", guard="credit", stats=stats) + forced = 100.0 * stats.get("forced", 0) / max(n_o, 1) + + ch_h = decades / n_ref if n_ref else float("nan") + our_h = decades / n_o if n_o else float("nan") + agg["ch_n"] += n_ref if ok_ref else MAXFEV + agg["our_n"] += n_o if ok_o else MAXFEV + if ok_ref: + agg["ch_h"].append(ch_h) + agg["gate"].append(gate_ok) + if ok_o: + agg["our_h"].append(our_h) + agg["forced"].append(forced) + print(f"{pid:<16} {n_ref:8d} {ch_h:8.3f} {gate_ok:8.1f}% " + f"{n_o:10d} {our_h:10.3f} {forced:7.1f}%") + + mean = lambda v: sum(v) / len(v) if v else float("nan") # noqa: E731 + print(f"\nmirror fidelity: {'OK' if drift == 0 else f'DRIFTED on {drift} problems'}") + print(f"totals (failures charged {MAXFEV}): Chandrupatla {agg['ch_n']}, ours {agg['our_n']}") + print(f"halvings per evaluation: Chandrupatla {mean(agg['ch_h']):.3f}, " + f"ours {mean(agg['our_h']):.3f}") + print(f"Chandrupatla accepted the model on {mean(agg['gate']):.1f}% of steps " + f"(the rest it REFUSED and halved)") + print(f"our safeguard had to repair {mean(agg['forced']):.1f}% of evaluations") + + +if __name__ == "__main__": + main() diff --git a/results_explain_chandrupatla.txt b/results_explain_chandrupatla.txt new file mode 100644 index 0000000..b00b01b --- /dev/null +++ b/results_explain_chandrupatla.txt @@ -0,0 +1,54 @@ +CHA problems: 45 metric: halvings per evaluation = log2(w0/xtol)/nfev (bisection = 1.0) + +problem CH nfev CH h/ev gate ok% ours nfev ours h/ev forced% +fun1.1 8 4.983 100.0% 25 1.595 20.0% +fun1.2 12 3.586 55.6% 47 0.916 93.6% +fun1.3 15 3.099 41.7% 50 0.930 94.0% +fun1.4 24 2.256 33.3% 58 0.934 93.1% +fun1.5 44 1.684 17.1% 78 0.950 84.6% +fun2.1 9 4.431 66.7% 12 3.323 25.0% +fun2.2 23 2.311 25.0% 57 0.932 94.7% +fun2.3 29 2.062 19.2% 63 0.949 95.2% +fun2.4 42 1.740 10.3% 77 0.949 77.9% +fun2.5 49 1.627 10.9% 83 0.961 65.1% +fun3.1 44 0.959 0.0% 46 0.917 93.5% +fun3.2 46 0.961 0.0% 48 0.921 93.8% +fun3.3 61 0.888 8.6% 58 0.934 65.5% +fun3.4 69 0.881 13.6% 64 0.950 65.6% +fun3.5 82 0.903 7.6% 58 1.277 1.7% +fun4.1 44 0.959 0.0% 46 0.917 93.5% +fun4.2 46 0.961 0.0% 48 0.921 93.8% +fun4.3 56 0.967 5.7% 58 0.934 75.9% +fun4.4 67 0.907 7.8% 64 0.950 51.6% +fun4.5 80 0.926 5.2% 78 0.950 51.3% +fun5.1 44 0.959 0.0% 46 0.917 93.5% +fun5.2 44 0.970 0.0% 46 0.928 93.5% +fun5.3 45 0.963 0.0% 47 0.922 93.6% +fun5.4 47 0.971 0.0% 49 0.932 93.9% +fun5.5 48 0.972 0.0% 50 0.933 94.0% +fun6.1 44 0.959 0.0% 46 0.917 93.5% +fun6.2 44 0.970 0.0% 46 0.928 93.5% +fun6.3 45 0.963 0.0% 47 0.922 91.5% +fun6.4 47 0.971 0.0% 49 0.932 91.8% +fun6.5 48 0.972 0.0% 50 0.933 92.0% +fun7.1 8 5.273 40.0% 9 4.687 66.7% +fun7.2 8 5.334 40.0% 8 5.334 62.5% +fun7.3 11 3.938 37.5% 10 4.332 70.0% +fun7.4 18 2.536 40.0% 13 3.511 53.8% +fun7.5 19 2.455 43.8% 13 3.588 38.5% +fun8.1 10 4.086 71.4% 44 0.929 93.2% +fun8.2 11 3.768 62.5% 45 0.921 93.3% +fun8.3 13 3.310 60.0% 47 0.916 93.6% +fun8.4 14 3.187 45.5% 48 0.930 93.8% +fun8.5 15 3.080 41.7% 36 1.283 63.9% +fun9.1 8 4.983 100.0% 16 2.491 25.0% +fun9.2 9 4.605 83.3% 45 0.921 93.3% +fun9.3 11 3.912 62.5% 47 0.916 93.6% +fun9.4 13 3.432 60.0% 48 0.930 93.8% +fun9.5 14 3.300 45.5% 50 0.924 94.0% + +mirror fidelity: OK +totals (failures charged 200): Chandrupatla 1488, ours 2073 +halvings per evaluation: Chandrupatla 2.310, ours 1.422 +Chandrupatla accepted the model on 28.0% of steps (the rest it REFUSED and halved) +our safeguard had to repair 77.5% of evaluations