diff --git a/PREREG_flat_regime_2026-09-21.md b/PREREG_flat_regime_2026-09-21.md new file mode 100644 index 0000000..f9fd49a --- /dev/null +++ b/PREREG_flat_regime_2026-09-21.md @@ -0,0 +1,56 @@ +# PREREG — распознавание плоского режима по несбывшемуся обещанию модели + +Составлено до кода и до прогона, на ветке `signfix-and-gates` (исправленная проверка знака). +Валерий: «давай распознаем плоский режим». + +## Что уже известно и опровергнуто (не повторять) + +- порядок `m ≥ 2` из параболы через три точки: 0.2% ложных на APS, но видит лишь 5.8% плоских; +- похожесть соседних сжатий |f|: сигнал перевёрнут (AUC 0.28–0.42); +- пустой шаг (похожесть |fₙ| и |fₙ₋₁|, порог ½): не различает; +- повтор рисунка сжатий: плоские против APS и fun1 различает, против прочих CHA — нет. + +Все четыре угадывали режим по форме траектории. Здесь признак выводится из причины проигрыша. + +## Вывод признака + +Шаг модели обещает f(x_new) = 0. Результат шага, который **не перешёл через корень**, +r = f(x_new)/f(x) > 0, показывает, какая доля |f| осталась. Для f ≈ K·eᵐ под касательным шагом +r = (1 − 1/m)ᵐ. Касательный шаг проигрывает делению пополам при m ≥ 2 (вывод раздела «Future work» +статьи: шаг ложится в e(1 − 1/m), что хуже половины при m ≥ 2). Точка безубыточности m = 2 даёт + + r* = (1 − 1/2)² = 1/4 + +— константа деления ½, пересчитанная через ту же формулу, а не выбранное число. + +**Флаг:** последний шаг был шагом модели (не делением от гейта и не изменён предохранителем), знак f +после него не сменился, и r ≥ ¼. Значит, модель на этом участке проигрывает делению. + +## Этап 1 — различимость (метод не меняется) + +На трассах предложенного метода (исправленный код) — доля состояний с флагом по группам: APS, fun1, +плоские fun3–6, fun7 (отдельно: она тоже плоская — f′(0) = 0), прочие CHA. + +| # | ПРОЙДЕНО, если | +|---|---| +| **F1** | доля флагов на плоских fun3–6 выше, чем на APS **и** на fun1 | +| **F2** | сообщается рядом с планками: `m ≥ 2` — 0.2% / 5.8% | + +**Фальсификатор этапа 1:** F1 ложна — признак не различает, этап 2 не проводится. + +## Этап 2 — в методе (только если этап 1 пройден) + +Режим `prestep="flat"`: если флаг поднят, следующий шаг — деление пополам; затем модель снова +получает шаг и снова проверяется своим результатом. Без констант, кроме r* = ¼. Сетка парами +«без / с флагом» — для предложенного метода и для предложенного метода с Г2 (`extrapolate="pattern"`), +все 7 предохранителей, tent, три точки; девять функций без чтений. + +| # | предсказание | ФАЛЬСИФИКАТОР | +|---|---|---| +| **Z1** | CHA ниже, чем без флага (3220; с Г2 — 3169) | нет — распознавание не окупается на широких скобках | +| **Z2** | APS не выше, чем без флага (2806; с Г2 — 2782) | выше — флаг ложно срабатывает там, где модель работает | +| **Z3** | гарантия: 0 нарушений в семействе, решено ≥ 193 | нарушение — стоп | +| **Z4** | плоские fun3–6 дешевле | нет — механизм не работает | +| **Z5** | по задачам против Чандрупатлы на APS; итог по обоим наборам против 5228 | сообщается | + +**Правило остановки:** режим принимается, только если Z1, Z2, Z3 выполнены одновременно. diff --git a/diag_flat_flag.py b/diag_flat_flag.py new file mode 100644 index 0000000..3cc94e6 --- /dev/null +++ b/diag_flat_flag.py @@ -0,0 +1,82 @@ +"""Stage 1 of PREREG_flat_regime_2026-09-21.md: is the 'unkept promise' flag selective? + +Flag after a step when: the step was the model's own (the evaluation that followed a traced +state is exactly x + raw, so no gate or safeguard replaced it), f did not change sign, and +r = f(x_new)/f(x) >= 1/4 - the break-even of a tangent step against a halving at m = 2. + + python -u diag_flat_flag.py +""" +from __future__ import annotations + +from scipy.optimize._tstutils import _APS_TESTS_DICTS, _CHANDRUPATLA_TESTS_DICTS + +from simroot.pure import pure_root + +XTOL, MAXFEV, ACC = 1e-12, 200, 1e-6 +R_STAR = 0.25 +CFG = dict(similarity="ratio", scale="derived3", guard="project") +FLAT = ("fun3", "fun4", "fun5", "fun6") + + +def flags(f, a, b, r_true): + """(model steps judged, flags raised) for one run of the proposed rule.""" + evals = [] + + def g(t): + v = f(t) + evals.append((t, v)) + return v + st = {"trace": []} + x, _n, conv = pure_root(g, a, b, XTOL, MAXFEV, stats=st, **CFG) + if not (conv and abs(float(x) - r_true) <= ACC): + return 0, 0 + order = [e[0] for e in evals] + judged = raised = 0 + for s in st["trace"]: + try: + i = order.index(s["x"]) + except ValueError: + continue + if i + 1 >= len(evals): + continue + x_next, f_next = evals[i + 1] + if x_next != s["x"] + s["raw"] or s["fx"] == 0.0: + continue # not the model's own step + judged += 1 + r = f_next / s["fx"] + if r > 0.0 and r >= R_STAR: + raised += 1 + return judged, raised + + +def main(): + groups = {"APS": [0, 0], "CHA fun1": [0, 0], "CHA flat fun3-6": [0, 0], + "CHA fun7": [0, 0], "CHA other": [0, 0]} + for d in _APS_TESTS_DICTS: + a, b = (float(v) for v in d["bracket"]) + f = (lambda ff, aa: lambda t: ff(t, *aa))(d["f"], d.get("args", ())) + j, r = flags(f, a, b, float(d["root"])) + groups["APS"][0] += j + groups["APS"][1] += r + for d in _CHANDRUPATLA_TESTS_DICTS: + a, b = (float(v) for v in d["bracket"]) + fam = str(d["ID"]).split(".")[0] + g = ("CHA flat fun3-6" if fam in FLAT else "CHA fun1" if fam == "fun1" + else "CHA fun7" if fam == "fun7" else "CHA other") + j, r = flags(d["f"], a, b, float(d["root"])) + groups[g][0] += j + groups[g][1] += r + print(f"{'group':<18} {'model steps':>12} {'flagged':>8} {'share':>7}") + out = {} + for g, (j, r) in groups.items(): + out[g] = (j, r, 100.0 * r / max(j, 1)) + print(f"{g:<18} {j:12d} {r:8d} {out[g][2]:6.1f}%") + flat, aps, f1 = (out["CHA flat fun3-6"][2], out["APS"][2], out["CHA fun1"][2]) + print(f"\nF1 flat > APS and flat > fun1: {flat:.1f}% vs {aps:.1f}% / {f1:.1f}% -> " + f"{'PASS' if flat > aps and flat > f1 else 'FAIL'}") + print("bar: m>=2 fires 0.2% canonical / 5.8% flat") + return out + + +if __name__ == "__main__": + main()