two more places for similarity: points='shape' (WHICH triple the model uses, argmin |phi-xi| over declared candidates — select, never weight) and direction='dissimilar' (the far end read by |f| rather than by distance); defaults bit-identical, P1-P5 pinned (7 green), inertness counters make an inert mode a failed EXPERIMENT rather than a pass; each grid writes to its own file
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,87 @@
|
|||||||
|
# PREREG — похожесть в двух ещё незанятых местах: СОСТАВ ТОЧЕК и НАПРАВЛЕНИЕ
|
||||||
|
|
||||||
|
Составлено до реализации. Основание — `FINDINGS_slope_gate.md`: решает **валюта и место** чтения
|
||||||
|
похожести. Одна и та же идея «отказывайся до шага» дала 4449 вызовов на APS при отказе по прогрессу
|
||||||
|
скобки и 2848 при отказе по форме модели — разница 1601 вызов. Карта мест решения показала два
|
||||||
|
незанятых:
|
||||||
|
|
||||||
|
| место | чем решается сейчас |
|
||||||
|
|---|---|
|
||||||
|
| **состав точек для модели** | **давностью**: `x_prev2 ← x_prev ← x ← x_new`, всегда три последних |
|
||||||
|
| **направление** (какой конец «дальний») | **расстоянием**: `far = B if \|B−x\| ≥ \|A−x\| else A` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Режим A — состав точек по ФОРМЕ (`points="shape"`)
|
||||||
|
|
||||||
|
Оценка наклона строится на тройке точек. Сейчас тройка берётся по давности, даже если она
|
||||||
|
плохо обусловлена. Brent, TOMS 748 и Chandrupatla все **управляют составом точек** — это часть их
|
||||||
|
конструкции, а у нас этого места нет вовсе.
|
||||||
|
|
||||||
|
**Признак — та же валюта, что уже сработала: форма конфигурации, безразмерно.** Для тройки
|
||||||
|
`(x, u, v)` считаются два отношения в духе Chandrupatla:
|
||||||
|
|
||||||
|
ξ = (x − u)/(v − u) — отношение ПОЗИЦИЙ
|
||||||
|
φ = (f(x) − f(u))/(f(v) − f(u)) — отношение ЗНАЧЕНИЙ
|
||||||
|
|
||||||
|
Для линейной `f` выполняется `φ = ξ` точно, поэтому `|φ − ξ|` — безразмерная мера непохожести
|
||||||
|
конфигурации на линейную. Правило: **argmin |φ − ξ|** по объявленному списку кандидатов.
|
||||||
|
|
||||||
|
**Список кандидатов объявляется здесь и не расширяется по результату:**
|
||||||
|
|
||||||
|
T1 = (x, x_prev, x_prev2) — нынешнее правило давности
|
||||||
|
T2 = (x, x_prev, far) — свежая точка плюс дальний конец скобки
|
||||||
|
T3 = (x, A, B) — оба конца доказанной скобки
|
||||||
|
|
||||||
|
Вырожденные (совпадающие абсциссы или значения) выбрасываются. Порога нет: это **argmin по
|
||||||
|
имеющимся вариантам**, а не сравнение с числом. Правило корпуса «select, never weight» соблюдено —
|
||||||
|
тройка **выбирается**, оценки не взвешиваются.
|
||||||
|
|
||||||
|
## 2. Режим B — направление по НЕПОХОЖЕСТИ (`direction="dissimilar"`)
|
||||||
|
|
||||||
|
Сейчас «дальний» конец — геометрически более удалённый. Похожесть даёт другой ответ: дальним считать
|
||||||
|
конец, чьё `|f|` **больше**, то есть менее похожее на ноль. Ни константы, ни порога — сравнение двух
|
||||||
|
имеющихся величин.
|
||||||
|
|
||||||
|
**Режимы независимы и мерятся отдельно** (правило «менять одно за раз»); комбинация A+B мерится
|
||||||
|
третьим прогоном только если оба по отдельности не ухудшили APS.
|
||||||
|
|
||||||
|
## 3. Цели
|
||||||
|
|
||||||
|
Точка отсчёта — уже достигнутое гейтом на наклоне (`slope.ratio.derived3.project`):
|
||||||
|
APS **2848**, CHA **3074**, два набора **5922**, 193/199, `over2x = 0`.
|
||||||
|
|
||||||
|
| величина | цель |
|
||||||
|
|---|---|
|
||||||
|
| APS-154 | **≤ 2848** (не хуже текущего; идеально — вернуть 2806) |
|
||||||
|
| CHA-45 | **≤ 2857** (недобранная цель ITP; сейчас 2903 по-наборно) |
|
||||||
|
| два набора | **< 5922** |
|
||||||
|
| решено | ≥ 193/199 |
|
||||||
|
| гарантия `2⌈log₂(w/xtol)⌉+3` | `over2x = 0` |
|
||||||
|
|
||||||
|
## 4. Предсказания и фальсификаторы
|
||||||
|
|
||||||
|
| # | Предсказание | ФАЛЬСИФИКАТОР → вывод |
|
||||||
|
|---|---|---|
|
||||||
|
| **A1** | режим A не ухудшает APS (≤2848) и улучшает CHA (<2903) | ухудшает APS → состав точек не то место, режим отвергается |
|
||||||
|
| **A2** | выбор тройки **не инертен**: отличается от давностной в ≥5 % шагов | <1 % → режим ничего не меняет, замер НЕ является свидетельством ни за, ни против |
|
||||||
|
| **B1** | режим B не ухудшает APS (≤2848) | ухудшает → направление по непохожести отвергается |
|
||||||
|
| **B2** | выбор конца отличается от геометрического в ≥5 % шагов | <1 % → инертен, замер пуст |
|
||||||
|
| **C** | гарантия цела в обоих режимах | нарушена → немедленное закрытие режима |
|
||||||
|
|
||||||
|
**Правило остановки:** APS ухудшился или гарантия нарушена ⇒ режим не принимается. Инертность (A2/B2)
|
||||||
|
⇒ результат не толкуется как подтверждение: это отсутствие эксперимента, а не его исход.
|
||||||
|
|
||||||
|
## 5. Гейты реализации (тесты ДО рейтинга)
|
||||||
|
|
||||||
|
| # | Условие | Проверка |
|
||||||
|
|---|---|---|
|
||||||
|
| **P1** | `points="recent"` и `direction="distance"` дают побитово прежний результат | равенство кортежей на наборе задач и сторожей |
|
||||||
|
| **P2** | в обоих признаках нет констант | греп по коду (без комментариев): только `0.0`, `0.5`, `1.0` |
|
||||||
|
| **P3** | счётчики `stats['triple_changed']`, `stats['dir_changed']` считают неинертность | тест на обоих |
|
||||||
|
| **P4** | вырожденные тройки отбрасываются, а не дают деление на ноль | тест на совпадающих абсциссах и значениях |
|
||||||
|
| **P5** | ни один режим не добавляет вызовов `f` | число вызовов ≤ число шагов + 2 |
|
||||||
|
|
||||||
|
**Дисциплина:** аудит после правки и до запуска; смоук ×2 локально; свипы в свои файлы
|
||||||
|
(`sweep_points.json`, `sweep_direction.json`), `sweep_full.json` не трогать; числа только с Linux;
|
||||||
|
замороженные копии; числа показываются немедленно.
|
||||||
+86
-5
@@ -121,6 +121,47 @@ GUARDS = ("none", "bracket", "step", "both", "project", "safe", "credit")
|
|||||||
SPACES = ("x", "log")
|
SPACES = ("x", "log")
|
||||||
|
|
||||||
|
|
||||||
|
POINT_RULES = ("recent", "shape")
|
||||||
|
DIRECTIONS = ("distance", "dissimilar")
|
||||||
|
|
||||||
|
|
||||||
|
def _shape_defect(x, fx, u, fu, v, fv):
|
||||||
|
"""|phi - xi| for the triple, or None when it is degenerate.
|
||||||
|
|
||||||
|
xi = (x - u)/(v - u) is a ratio of POSITIONS, phi = (fx - fu)/(fv - fu) a ratio of
|
||||||
|
VALUES; both are invariant under affine rescaling of x and of f. For a linear f they
|
||||||
|
are equal, so their gap measures how unlike a linear configuration the triple is —
|
||||||
|
the same dimensionless currency Chandrupatla's acceptance band is written in."""
|
||||||
|
if v == u or fv == fu:
|
||||||
|
return None
|
||||||
|
xi = (x - u) / (v - u)
|
||||||
|
ph = (fx - fu) / (fv - fu)
|
||||||
|
if not (math.isfinite(xi) and math.isfinite(ph)):
|
||||||
|
return None
|
||||||
|
return abs(ph - xi)
|
||||||
|
|
||||||
|
|
||||||
|
def _pick_triple(x, fx, x_prev, f_prev, x_prev2, f_prev2, A, FA, B, FB, far):
|
||||||
|
"""SELECT the triple whose configuration is least unlike a linear one (PREREG
|
||||||
|
2026-09-21, mode A). Candidates are declared, not searched: the recency triple, the
|
||||||
|
recent point with the far bracket end, and the two proven bracket ends. Returns
|
||||||
|
((xu, fu), (xv, fv)) or None when nothing is admissible."""
|
||||||
|
far_f = FB if far == B else FA
|
||||||
|
cands = (((x_prev, f_prev), (x_prev2, f_prev2)),
|
||||||
|
((x_prev, f_prev), (far, far_f)),
|
||||||
|
((A, FA), (B, FB)))
|
||||||
|
best, best_d = None, None
|
||||||
|
for u, v in cands:
|
||||||
|
if u[0] is None or v[0] is None:
|
||||||
|
continue
|
||||||
|
d = _shape_defect(x, fx, u[0], u[1], v[0], v[1])
|
||||||
|
if d is None or x == u[0] or x == v[0]:
|
||||||
|
continue
|
||||||
|
if best_d is None or d < best_d:
|
||||||
|
best, best_d = (u, v), d
|
||||||
|
return best
|
||||||
|
|
||||||
|
|
||||||
def _log_map(t, c, s):
|
def _log_map(t, c, s):
|
||||||
"""u = sign(t - c) * log1p(|t - c| / s). An ADDITIVE step in u is a
|
"""u = sign(t - c) * log1p(|t - c| / s). An ADDITIVE step in u is a
|
||||||
MULTIPLICATIVE step in x, which is what the wide-bracket families need: the
|
MULTIPLICATIVE step in x, which is what the wide-bracket families need: the
|
||||||
@@ -413,7 +454,8 @@ def _local_order(x2, f2, x1, f1, x0, f0):
|
|||||||
|
|
||||||
def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||||
guard="step", stats=None, budget_factor=2.0,
|
guard="step", stats=None, budget_factor=2.0,
|
||||||
budget_extra=None, endowment=1.0, space="x", prestep="none"):
|
budget_extra=None, endowment=1.0, space="x", prestep="none",
|
||||||
|
points="recent", direction="distance"):
|
||||||
"""The pure similarity root finder.
|
"""The pure similarity root finder.
|
||||||
|
|
||||||
similarity : key into PURE_SIMS
|
similarity : key into PURE_SIMS
|
||||||
@@ -469,6 +511,14 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
raise ValueError(f"unknown space {space!r}")
|
raise ValueError(f"unknown space {space!r}")
|
||||||
if prestep not in ("none", "progress", "slope"):
|
if prestep not in ("none", "progress", "slope"):
|
||||||
raise ValueError(f"unknown prestep {prestep!r}")
|
raise ValueError(f"unknown prestep {prestep!r}")
|
||||||
|
if points not in POINT_RULES:
|
||||||
|
raise ValueError(f"unknown points rule {points!r}")
|
||||||
|
if direction not in DIRECTIONS:
|
||||||
|
raise ValueError(f"unknown direction {direction!r}")
|
||||||
|
if points == "shape" and scale not in ("derived3", "derived3_rho", "derived3_m"):
|
||||||
|
# the triple only exists for the three-point scale; selecting one under a
|
||||||
|
# two-point scale would change nothing and hide that
|
||||||
|
raise ValueError("points='shape' requires a three-point scale (derived3*)")
|
||||||
if prestep == "slope" and scale not in ("derived", "derived3"):
|
if prestep == "slope" and scale not in ("derived", "derived3"):
|
||||||
# there is no slope to judge under a chosen or bracket scale; judging something
|
# there is no slope to judge under a chosen or bracket scale; judging something
|
||||||
# else would hide that, so the call fails instead
|
# else would hide that, so the call fails instead
|
||||||
@@ -501,11 +551,15 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
sg.credit = endowment
|
sg.credit = endowment
|
||||||
forced = 0
|
forced = 0
|
||||||
pre_refused = 0
|
pre_refused = 0
|
||||||
|
triple_changed = 0
|
||||||
|
dir_changed = 0
|
||||||
slope = 0.0
|
slope = 0.0
|
||||||
slope_prev = None
|
slope_prev = None
|
||||||
if stats is not None:
|
if stats is not None:
|
||||||
stats['forced'] = 0
|
stats['forced'] = 0
|
||||||
stats['pre_refused'] = 0
|
stats['pre_refused'] = 0
|
||||||
|
stats['triple_changed'] = 0
|
||||||
|
stats['dir_changed'] = 0
|
||||||
|
|
||||||
while abs(B - A) > xtol:
|
while abs(B - A) > xtol:
|
||||||
if sg.bisect_before_step(abs(B - A)):
|
if sg.bisect_before_step(abs(B - A)):
|
||||||
@@ -516,10 +570,37 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
sg.note_step(x_new - x)
|
sg.note_step(x_new - x)
|
||||||
else:
|
else:
|
||||||
far = B if abs(B - x) >= abs(A - x) else A
|
far = B if abs(B - x) >= abs(A - x) else A
|
||||||
|
if direction == "dissimilar":
|
||||||
|
# PREREG 2026-09-21, mode B: the far end read by SIMILARITY instead of
|
||||||
|
# geometry — the end whose |f| is larger is the one LESS similar to zero,
|
||||||
|
# i.e. the one the root is further from. Two values already in hand are
|
||||||
|
# compared; no constant.
|
||||||
|
far_sim = B if abs(FB) >= abs(FA) else A
|
||||||
|
if far_sim != far:
|
||||||
|
dir_changed += 1
|
||||||
|
far = far_sim
|
||||||
if scale in ("derived", "derived_rho"):
|
if scale in ("derived", "derived_rho"):
|
||||||
slope = ((fx - f_prev) / (x - x_prev)) if x != x_prev else 0.0
|
slope = ((fx - f_prev) / (x - x_prev)) if x != x_prev else 0.0
|
||||||
sc = abs(slope) * abs(far - x)
|
sc = abs(slope) * abs(far - x)
|
||||||
elif scale in ("derived3", "derived3_rho", "derived3_m"):
|
elif scale in ("derived3", "derived3_rho", "derived3_m"):
|
||||||
|
if points == "shape":
|
||||||
|
# PREREG 2026-09-21, mode A: which THREE points the model is built
|
||||||
|
# on, chosen by the SHAPE of the configuration instead of by recency.
|
||||||
|
# For a linear f the position ratio and the value ratio coincide, so
|
||||||
|
# |phi - xi| is a dimensionless measure of how unlike a linear
|
||||||
|
# configuration a triple is; the triple minimising it is SELECTED
|
||||||
|
# (the corpus rule is select, never weight). argmin over the declared
|
||||||
|
# candidates needs no threshold.
|
||||||
|
pick = _pick_triple(x, fx, x_prev, f_prev, x_prev2, f_prev2,
|
||||||
|
A, FA, B, FB, far)
|
||||||
|
if pick is None:
|
||||||
|
slope = _slope3(x, fx, x_prev, f_prev, x_prev2, f_prev2)
|
||||||
|
else:
|
||||||
|
(xu, fu), (xv, fv) = pick
|
||||||
|
if xu != x_prev or xv != x_prev2:
|
||||||
|
triple_changed += 1
|
||||||
|
slope = _slope3(x, fx, xu, fu, xv, fv)
|
||||||
|
else:
|
||||||
slope = _slope3(x, fx, x_prev, f_prev, x_prev2, f_prev2)
|
slope = _slope3(x, fx, x_prev, f_prev, x_prev2, f_prev2)
|
||||||
sc = abs(slope) * abs(far - x)
|
sc = abs(slope) * abs(far - x)
|
||||||
if scale == "derived3_m":
|
if scale == "derived3_m":
|
||||||
@@ -665,7 +746,7 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
if x_new == x or x_new == x_prev:
|
if x_new == x or x_new == x_prev:
|
||||||
# the bracket can no longer be split in floating point
|
# the bracket can no longer be split in floating point
|
||||||
if stats is not None:
|
if stats is not None:
|
||||||
stats['forced'] = forced; stats['pre_refused'] = pre_refused
|
stats['forced'] = forced; stats['pre_refused'] = pre_refused; stats['triple_changed'] = triple_changed; stats['dir_changed'] = dir_changed
|
||||||
return x_new, g.n, abs(B - A) <= xtol
|
return x_new, g.n, abs(B - A) <= xtol
|
||||||
|
|
||||||
w_before = abs(B - A)
|
w_before = abs(B - A)
|
||||||
@@ -677,7 +758,7 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
x, fx = x_new, fnew
|
x, fx = x_new, fnew
|
||||||
if fnew == 0.0:
|
if fnew == 0.0:
|
||||||
if stats is not None:
|
if stats is not None:
|
||||||
stats['forced'] = forced; stats['pre_refused'] = pre_refused
|
stats['forced'] = forced; stats['pre_refused'] = pre_refused; stats['triple_changed'] = triple_changed; stats['dir_changed'] = dir_changed
|
||||||
return x_new, g.n, True
|
return x_new, g.n, True
|
||||||
if FA * fnew < 0.0:
|
if FA * fnew < 0.0:
|
||||||
B, FB = x_new, fnew
|
B, FB = x_new, fnew
|
||||||
@@ -686,9 +767,9 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
|||||||
if guard == "credit":
|
if guard == "credit":
|
||||||
sg.note_shrink(w_before, abs(B - A))
|
sg.note_shrink(w_before, abs(B - A))
|
||||||
if stats is not None:
|
if stats is not None:
|
||||||
stats['forced'] = forced; stats['pre_refused'] = pre_refused
|
stats['forced'] = forced; stats['pre_refused'] = pre_refused; stats['triple_changed'] = triple_changed; stats['dir_changed'] = dir_changed
|
||||||
return 0.5 * (A + B), g.n, True
|
return 0.5 * (A + B), g.n, True
|
||||||
except _Budget:
|
except _Budget:
|
||||||
if stats is not None:
|
if stats is not None:
|
||||||
stats['forced'] = forced; stats['pre_refused'] = pre_refused
|
stats['forced'] = forced; stats['pre_refused'] = pre_refused; stats['triple_changed'] = triple_changed; stats['dir_changed'] = dir_changed
|
||||||
return x, g.n, False
|
return x, g.n, False
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+38
-2
@@ -111,10 +111,13 @@ def make_method(spec):
|
|||||||
sim, scale, guard = spec[1], spec[2], spec[3]
|
sim, scale, guard = spec[1], spec[2], spec[3]
|
||||||
space = spec[4] if len(spec) > 4 else "x"
|
space = spec[4] if len(spec) > 4 else "x"
|
||||||
prestep = spec[5] if len(spec) > 5 else "none"
|
prestep = spec[5] if len(spec) > 5 else "none"
|
||||||
|
points = spec[6] if len(spec) > 6 else "recent"
|
||||||
|
direction = spec[7] if len(spec) > 7 else "distance"
|
||||||
|
|
||||||
def m(f, a, b, xtol, maxfev):
|
def m(f, a, b, xtol, maxfev):
|
||||||
return pure_root(f, a, b, xtol, maxfev, similarity=sim,
|
return pure_root(f, a, b, xtol, maxfev, similarity=sim,
|
||||||
scale=scale, guard=guard, space=space, prestep=prestep)
|
scale=scale, guard=guard, space=space, prestep=prestep,
|
||||||
|
points=points, direction=direction)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
@@ -124,6 +127,10 @@ def label(spec):
|
|||||||
tag = "pure"
|
tag = "pure"
|
||||||
if len(spec) > 4 and spec[4] != "x":
|
if len(spec) > 4 and spec[4] != "x":
|
||||||
tag = spec[4]
|
tag = spec[4]
|
||||||
|
if len(spec) > 6 and spec[6] != "recent":
|
||||||
|
tag = f"{tag}+shape"
|
||||||
|
if len(spec) > 7 and spec[7] != "distance":
|
||||||
|
tag = f"{tag}+dissim"
|
||||||
if len(spec) > 5 and spec[5] != "none":
|
if len(spec) > 5 and spec[5] != "none":
|
||||||
# name the gate by WHICH gate it is: "progress" is a measured negative and must
|
# name the gate by WHICH gate it is: "progress" is a measured negative and must
|
||||||
# never be confused with "slope" in a results file
|
# never be confused with "slope" in a results file
|
||||||
@@ -191,6 +198,23 @@ def build_grid_log(smoke):
|
|||||||
for sim in sims for sc in ("derived", "derived3") for gu in guards]
|
for sim in sims for sc in ("derived", "derived3") for gu in guards]
|
||||||
|
|
||||||
|
|
||||||
|
def build_grid_points(smoke):
|
||||||
|
"""Mode A (PREREG 2026-09-21): the triple chosen by shape, on top of the slope gate that
|
||||||
|
already reached the project target. Three-point scale only — a triple must exist."""
|
||||||
|
sims = ["ratio", "sqrt"] if smoke else sorted(PURE_SIMS)
|
||||||
|
guards = ("credit", "project") if smoke else GUARD_MODES
|
||||||
|
return [("pure", sim, "derived3", gu, "x", "slope", "shape", "distance")
|
||||||
|
for sim in sims for gu in guards]
|
||||||
|
|
||||||
|
|
||||||
|
def build_grid_direction(smoke):
|
||||||
|
"""Mode B (PREREG 2026-09-21): the far end read by dissimilarity, on top of the slope gate."""
|
||||||
|
sims = ["ratio", "sqrt"] if smoke else sorted(PURE_SIMS)
|
||||||
|
guards = ("credit", "project") if smoke else GUARD_MODES
|
||||||
|
return [("pure", sim, sc, gu, "x", "slope", "recent", "dissimilar")
|
||||||
|
for sim in sims for sc in ("derived", "derived3") for gu in guards]
|
||||||
|
|
||||||
|
|
||||||
def build_grid_slope(smoke):
|
def build_grid_slope(smoke):
|
||||||
"""The slope-stability gate grid (PREREG 2026-09-21). Only derived scales: there must be
|
"""The slope-stability gate grid (PREREG 2026-09-21). Only derived scales: there must be
|
||||||
a slope to judge. Baselines are not recomputed — they are in sweep_full.json."""
|
a slope to judge. Baselines are not recomputed — they are in sweep_full.json."""
|
||||||
@@ -231,7 +255,11 @@ def main():
|
|||||||
logmode = "--log" in sys.argv # the u-space grid, written to its OWN file
|
logmode = "--log" in sys.argv # the u-space grid, written to its OWN file
|
||||||
gatemode = "--gate" in sys.argv # the pre-step gate grid, likewise
|
gatemode = "--gate" in sys.argv # the pre-step gate grid, likewise
|
||||||
slopemode = "--slope" in sys.argv # the slope-stability gate grid
|
slopemode = "--slope" in sys.argv # the slope-stability gate grid
|
||||||
specs = (build_grid_slope(smoke) if slopemode else
|
pointsmode = "--points" in sys.argv # mode A: triple by shape
|
||||||
|
dirmode = "--direction" in sys.argv # mode B: far end by dissimilarity
|
||||||
|
specs = (build_grid_points(smoke) if pointsmode else
|
||||||
|
build_grid_direction(smoke) if dirmode else
|
||||||
|
build_grid_slope(smoke) if slopemode else
|
||||||
build_grid_gate(smoke) if gatemode else
|
build_grid_gate(smoke) if gatemode else
|
||||||
build_grid_log(smoke) if logmode else build_grid(smoke))
|
build_grid_log(smoke) if logmode else build_grid(smoke))
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
@@ -249,6 +277,14 @@ def main():
|
|||||||
|
|
||||||
# the published sweep_full.json is what verify_article.py checks the paper
|
# the published sweep_full.json is what verify_article.py checks the paper
|
||||||
# against, so the log grid never writes into it
|
# against, so the log grid never writes into it
|
||||||
|
if pointsmode or dirmode:
|
||||||
|
base = "sweep_points" if pointsmode else "sweep_direction"
|
||||||
|
out = f"{base}_smoke.json" if smoke else f"{base}.json"
|
||||||
|
with open(out, "w", encoding="utf8") as fh:
|
||||||
|
json.dump(results, fh)
|
||||||
|
print(f"wrote {out} ({time.time() - t0:.0f}s total)", flush=True)
|
||||||
|
report(results)
|
||||||
|
return
|
||||||
if slopemode:
|
if slopemode:
|
||||||
out = "sweep_slope_smoke.json" if smoke else "sweep_slope.json"
|
out = "sweep_slope_smoke.json" if smoke else "sweep_slope.json"
|
||||||
with open(out, "w", encoding="utf8") as fh:
|
with open(out, "w", encoding="utf8") as fh:
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,106 @@
|
|||||||
|
"""Gates P1-P5 for the two new places similarity is read (PREREG_points_and_direction_2026-09-21).
|
||||||
|
|
||||||
|
mode A points="shape" — WHICH three points the model is built on, chosen by the shape
|
||||||
|
of the configuration (argmin |phi - xi|) instead of by recency
|
||||||
|
mode B direction="dissimilar" — WHICH end is "far", read as the end whose |f| is larger
|
||||||
|
(less similar to zero) instead of the geometrically distant one
|
||||||
|
|
||||||
|
Inertness is a declared failure of the EXPERIMENT, not a pass: if a mode changes nothing, its
|
||||||
|
numbers are not evidence either way, so the counters are pinned here.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
import math
|
||||||
|
import re
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from simroot import pure as P
|
||||||
|
|
||||||
|
SMOOTH = [
|
||||||
|
(lambda t: t ** 3 - 2.0 * t - 5.0, 2.0, 3.0),
|
||||||
|
(lambda t: math.exp(t) - 5.0, 0.0, 3.0),
|
||||||
|
(lambda t: t ** 2 - 2.0, 0.5, 2.0),
|
||||||
|
(lambda t: math.cos(t) - t, 0.0, 1.0),
|
||||||
|
(lambda t: t ** 3 - t + 0.2, -2.0, 2.0),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_P1_defaults_are_bit_identical_to_the_published_path():
|
||||||
|
for f, a, b in SMOOTH:
|
||||||
|
for guard in ("credit", "project", "safe", "step"):
|
||||||
|
base = P.pure_root(f, a, b, 1e-12, 300, similarity="ratio",
|
||||||
|
scale="derived3", guard=guard)
|
||||||
|
same = P.pure_root(f, a, b, 1e-12, 300, similarity="ratio", scale="derived3",
|
||||||
|
guard=guard, points="recent", direction="distance")
|
||||||
|
assert base == same, (guard, base, same)
|
||||||
|
|
||||||
|
|
||||||
|
def test_P2_neither_rule_carries_a_constant():
|
||||||
|
src = inspect.getsource(P._pick_triple) + inspect.getsource(P._shape_defect)
|
||||||
|
code = "\n".join(line.split("#", 1)[0] for line in src.splitlines())
|
||||||
|
literals = set(re.findall(r"(?<![\w.])\d+\.\d+(?![\w.])", code))
|
||||||
|
assert literals == set(), f"constants in the triple rule: {literals}"
|
||||||
|
body = inspect.getsource(P.pure_root)
|
||||||
|
start = body.index('if direction == "dissimilar":')
|
||||||
|
end = body.index('if scale in ("derived", "derived_rho")', start)
|
||||||
|
dcode = "\n".join(line.split("#", 1)[0] for line in body[start:end].splitlines())
|
||||||
|
assert set(re.findall(r"(?<![\w.])\d+\.\d+(?![\w.])", dcode)) == set()
|
||||||
|
|
||||||
|
|
||||||
|
def test_P3_the_counters_report_whether_a_mode_is_inert():
|
||||||
|
changed = 0
|
||||||
|
for f, a, b in SMOOTH:
|
||||||
|
st = {}
|
||||||
|
_x, _n, ok = P.pure_root(f, a, b, 1e-12, 300, similarity="ratio", scale="derived3",
|
||||||
|
guard="credit", points="shape", stats=st)
|
||||||
|
assert ok and "triple_changed" in st
|
||||||
|
changed += st["triple_changed"]
|
||||||
|
assert changed >= 1, "mode A never changed the triple on any probe problem — inert"
|
||||||
|
|
||||||
|
dchanged = 0
|
||||||
|
for f, a, b in SMOOTH:
|
||||||
|
st = {}
|
||||||
|
_x, _n, ok = P.pure_root(f, a, b, 1e-12, 300, similarity="ratio", scale="derived3",
|
||||||
|
guard="credit", direction="dissimilar", stats=st)
|
||||||
|
assert ok and "dir_changed" in st
|
||||||
|
dchanged += st["dir_changed"]
|
||||||
|
assert dchanged >= 1, "mode B never changed the far end — inert"
|
||||||
|
|
||||||
|
|
||||||
|
def test_P4_degenerate_triples_are_dropped_not_divided_by():
|
||||||
|
assert P._shape_defect(1.0, 1.0, 2.0, 5.0, 2.0, 7.0) is None # equal abscissae
|
||||||
|
assert P._shape_defect(1.0, 1.0, 2.0, 5.0, 3.0, 5.0) is None # equal values
|
||||||
|
d = P._shape_defect(1.0, 1.0, 0.0, 0.0, 2.0, 2.0) # exactly linear
|
||||||
|
assert d is not None and d == pytest.approx(0.0)
|
||||||
|
assert P._pick_triple(1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_P5_no_mode_adds_an_evaluation():
|
||||||
|
for f, a, b in SMOOTH:
|
||||||
|
st = {}
|
||||||
|
_x, n, ok = P.pure_root(f, a, b, 1e-12, 300, similarity="ratio", scale="derived3",
|
||||||
|
guard="credit", points="shape", direction="dissimilar",
|
||||||
|
stats=st)
|
||||||
|
assert ok
|
||||||
|
assert st["triple_changed"] <= n - 2 and st["dir_changed"] <= n - 2, (st, n)
|
||||||
|
|
||||||
|
|
||||||
|
def test_modes_are_validated():
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
P.pure_root(lambda t: t, -1.0, 1.0, 1e-10, 100, points="whatever")
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
P.pure_root(lambda t: t, -1.0, 1.0, 1e-10, 100, direction="whatever")
|
||||||
|
with pytest.raises(ValueError): # a triple needs the three-point scale
|
||||||
|
P.pure_root(lambda t: t, -1.0, 1.0, 1e-10, 100, scale="derived", points="shape")
|
||||||
|
|
||||||
|
|
||||||
|
def test_both_modes_still_solve():
|
||||||
|
for f, a, b in SMOOTH:
|
||||||
|
for kw in ({"points": "shape"}, {"direction": "dissimilar"},
|
||||||
|
{"points": "shape", "direction": "dissimilar"}):
|
||||||
|
x, _n, ok = P.pure_root(f, a, b, 1e-12, 300, similarity="ratio",
|
||||||
|
scale="derived3", guard="credit", **kw)
|
||||||
|
assert ok and abs(f(x)) < 1e-6, (kw, a, b, x)
|
||||||
Reference in New Issue
Block a user