Valeri's second point measured: the similarity function belongs to the PLACE it is read in, not to the method. False position's three-century stall is a similarity read at order one on an approach of order m: with sigma = |f(far)| the residual ratio is (e/E)^m, so raising it to the measured 1/m recovers the distance ratio exactly. Paired inside each cell, the correction is cheaper in 7 of 7 safeguards for S = 1/(1+z) - the one function that makes that place exact inverse interpolation - and in only 29 of 60 cells over all nine functions, i.e. a coin. It does NOT beat Chandrupatla (2884 against 2683 and 2423), it HARMS the slope reading (2806 -> 2854), and it does not cure false position: 127/154 and 10676 become 128/154 and 10043, where Illinois buys 153/154 and 7045 with one constant. Estimator bias measured and stated: a pure cube reads 2.830, not 3
Also fixes a real regression from cc2c499: the derived3_cap branch was inserted in front of the derived3_m note and swallowed its division, so derived3_m silently became derived3 (2806 against 2806) and a refutation recorded in the paper stopped being reproducible. The full pin caught it; a cheap pin now guards it too. And \label{sec:method} never existed while the table on page 7 referenced it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
# FINDINGS — функция похожести ПО МЕСТУ (слот 1)
|
||||
|
||||
Пред-регистрация: `PREREG_similarity_per_slot_2026-09-21.md`. Сетка: 252 конфигурации × 199 задач,
|
||||
сервер 161, `sweep_order.json`, лог `results_sweep_order.txt`. Якорь достоверности:
|
||||
`pure.ratio.derived3` воспроизводит опубликованное **2806** бит-в-бит.
|
||||
|
||||
## Вердикты
|
||||
|
||||
| # | предсказание | вердикт |
|
||||
|---|---|---|
|
||||
| **L1** | показатель улучшает чтение по дальнему концу | **ВЕРНА — но только при соответствующей функции похожести** |
|
||||
| **L2** | CHA < 2683 | **ЛОЖНА** — лучшее 2884 |
|
||||
| **L3** | APS не хуже точки отсчёта | **ЛОЖНА в чтении по наклону** — 2806 → 2854 |
|
||||
| **L4** | гарантия цела | **ВЕРНА** — 0 нарушений из 108 ячеек семейства гарантии |
|
||||
| **L5** | шаг не выходит за скобку | **ВЕРНА** — закреплено тестом, с документированным насыщением до ровно 1.0 при `z ≳ 1/eps` |
|
||||
|
||||
## Главное: место определяет функцию
|
||||
|
||||
`far` + `rational` — единственное сочетание, в котором чтение по дальнему концу **есть** точная
|
||||
обратная интерполяция. Там показатель выигрывает **7 из 7** предохранителей:
|
||||
|
||||
| предохранитель | APS none → order | CHA none → order |
|
||||
|---|---|---|
|
||||
| both | 4522 → **3675** | 3387 → **3216** |
|
||||
| bracket | 4606 → **3542** | 3571 → **3303** |
|
||||
| credit | 5190 → **4686** | 3013 → **2884** |
|
||||
| project | 6932 → **6364** | 4794 → **4559** |
|
||||
| safe | 5987 → **5229** | 4108 → **3923** |
|
||||
| step | 6435 → **5551** | 4257 → **4022** |
|
||||
| none | 10676 → **10043** | 8265 → **7794** |
|
||||
|
||||
По всем девяти функциям похожести — **29 из 60 ячеек**, то есть неотличимо от монеты. Вывод, который
|
||||
это позволяет сделать: **показатель порядка не универсальное улучшение, а согласование функции
|
||||
похожести с местом, где она читается.** Там, где функция месту не соответствует, измеренный порядок
|
||||
не помогает и не мешает систематически.
|
||||
|
||||
## Чего НЕ получилось, прямо
|
||||
|
||||
1. **Chandrupatla не побит.** 2884 против 2683 (наше лучшее) и 2423 (его). Пятая попытка против
|
||||
одной и той же измеренной причины.
|
||||
2. **Стойло false position беспараметрически не вылечено.** Незащищённый: 127/154 и 10676 → 128/154
|
||||
и 10043 (−6%, +1 задача). Illinois с константой ½: 153/154 и 7045. Показатель снимает шестую
|
||||
часть разрыва, а не разрыв. ⚠️ Наши 10676 и опубликованные 10560 — **не один алгоритм**: наш
|
||||
метод стартует из середины скобки, классический false position — с конца.
|
||||
3. **В чтении по наклону показатель вредит** на APS (2806 → 2854) и теряет задачи на CHA.
|
||||
|
||||
## Измеренная граница оценки порядка
|
||||
|
||||
Трёхточечная оценка читает **чистый куб как 2.830, а не 3** — смещение −5.7%, направленное к
|
||||
единице, то есть к неисправленному чтению. Парабола через три точки несёт только вторую разность,
|
||||
поэтому порядок кубики недооценивается даже в идеальном бесшумном случае. Закреплено тестом
|
||||
`test_local_order_reads_a_cube_low_by_a_fixed_bias`; в статье должно стоять как смещение, а не как
|
||||
точность.
|
||||
|
||||
## Найденный по ходу баг (не в новом коде)
|
||||
|
||||
Коммит `cc2c499` вставил ветку `derived3_cap` прямо перед примечанием `derived3_m` и **проглотил его
|
||||
делитель**: `derived3_m` молча стал псевдонимом `derived3` (2806 против 2806), и опровержение,
|
||||
записанное в статью, перестало воспроизводиться. Поймал полный пин
|
||||
`test_m_loses_on_the_full_benchmark` — ровно то, для чего он и стоит. Исправлено; добавлен дешёвый
|
||||
пин `test_derived3_m_actually_divides_by_the_order`.
|
||||
|
||||
## Что остаётся неисследованным
|
||||
|
||||
Слот 2 пред-регистрации: **место решения предохранителя**. Полоса приёма Чандрупатлы
|
||||
(`1−√(1−ξ) < φ < √ξ`) — это другая функция похожести в другом месте: похожесть конфигурации на
|
||||
линейную, читаемая не для длины шага, а для выбора «модель или деление». У нас в этом месте похожести
|
||||
нет вовсе. Гонять отдельной сеткой.
|
||||
@@ -0,0 +1,83 @@
|
||||
# PREREG — функция похожести ПО МЕСТУ, а не одна на метод
|
||||
|
||||
Составлено до прогона. Основание — два замечания Валерия 2026-09-21, идущие подряд:
|
||||
|
||||
1. «просто функция похожести подобрана неверно» — про провал `derived3_cap`;
|
||||
2. «в принципе функция похожести может быть разной в разных местах… В какой-то статье
|
||||
упоминалось, что функция похожести может подбираться по домену. В нашем случае это не домен,
|
||||
а **место параметра**».
|
||||
|
||||
## 1. Что было не так в четырёх предыдущих попытках
|
||||
|
||||
Все четыре (`cbrt`, логарифм, `derived3_m`, `derived3_cap`) меняли **масштаб** `σ`, оставляя
|
||||
**одну** функцию `S` на весь метод. Измеренная причина разрыва при этом ни разу не была затронута:
|
||||
недолёт **линейной модели**, 0.93 против 2.26 половинений на пробу.
|
||||
|
||||
## 2. Прочитанное до прогона (корпус экономит замер)
|
||||
|
||||
`article/sim_numerics.tex`, Предложение 4: чтение похожести **по концам скобки** с рациональной
|
||||
похожестью `S = 1/(1+z)` **тождественно false position** — 127/154, 10560 оценок. То есть слот
|
||||
«масштаб = значение на дальнем конце» **уже закрыт при `m = 1`**. Гипотеза ниже — ровно про то,
|
||||
почему он закрыт: стойло false position трёхвековой давности есть чтение похожести **на порядке 1**
|
||||
там, где подход имеет порядок `m`.
|
||||
|
||||
## 3. Слот 1 — длина шага: показатель = ИЗМЕРЕННЫЙ порядок
|
||||
|
||||
Для `f ≈ K·e^m` (`e = |x − r|`) отношение остатков безразмерно и не требует наклона:
|
||||
|
||||
z = |f(x)| / |f(far)| = (e/E)^m ⇒ z^(1/m) = e/E
|
||||
|
||||
а доля шага до дальнего конца при обратной интерполяции есть `t/(1+t)`, `t = z^(1/m)`. При `m = 1`
|
||||
это **в точности false position** (Предложение 4), при `m > 1` — обратная интерполяция **порядка m**,
|
||||
которая для чистой степени попадает в корень **за один шаг**. Показатель `1/m` **не выбирается**:
|
||||
`m = 1/(1 − f·f''/f'²)` считается `_local_order` по трём уже сделанным оценкам и равен 1, когда
|
||||
степенная модель не применима.
|
||||
|
||||
**Почему это не повторение `derived3_m`.** Там `m` стоял **делителем масштаба** в чтении по наклону,
|
||||
где, как доказано в docstring, показатель схлопывается в множитель. Здесь `m` стоит **показателем над
|
||||
безразмерным отношением**, и схлопывания нет. Плюс структурное свойство: `t/(1+t) < 1` всегда, то
|
||||
есть шаг **никогда не выходит за скобку** — ложное `m > 1` (27.9% шагов на APS) ограничено дальним
|
||||
концом, а не проецируется предохранителем.
|
||||
|
||||
**Режимы:** новая шкала `scale="far"` (σ = |f| на том конце, к которому шаг) и новый ключ
|
||||
`exponent="none"|"order"`. По умолчанию `"none"` — опубликованное поведение бит-в-бит.
|
||||
|
||||
### Сетка (контроль внутри самой сетки)
|
||||
|
||||
Внутри каждой тройки (`S`, шкала, предохранитель) меняется **ровно одно** — показатель. Если
|
||||
`order` не бьёт `none` в паре `far`+`rational`, гипотеза мертва без толкований.
|
||||
|
||||
## 4. Слот 2 — решение предохранителя (объявляется здесь, гоняется ПОСЛЕ слота 1)
|
||||
|
||||
Чандрупатла выигрывает не шагом, а **полосой приёма**: `1−√(1−ξ) < φ < √ξ`. В наших терминах это
|
||||
**другая функция похожести в другом месте** — похожесть конфигурации на линейную (`|φ − ξ|`),
|
||||
читаемая не для длины шага, а для решения «модель или деление». Сейчас у нас в этом месте похожести
|
||||
нет вовсе: знаковый гейт и проекция. Гоняется **отдельной сеткой после слота 1**, чтобы не смешать
|
||||
два изменения.
|
||||
|
||||
## 5. Точка отсчёта и цели
|
||||
|
||||
| величина | текущее | цель |
|
||||
|---|---|---|
|
||||
| APS-154 | 2726 (лучшее 2527) | **≤ 2527** |
|
||||
| CHA-45 по-наборно | 2683 | **< 2683** (Chandrupatla 2423) |
|
||||
| два набора | 5511 | **< 5511** |
|
||||
| решено | 193/199 | ≥ 193 |
|
||||
| гарантия `over2x` | 0 | **0** |
|
||||
|
||||
## 6. Предсказания и фальсификаторы
|
||||
|
||||
| # | Предсказание | ФАЛЬСИФИКАТОР → вывод |
|
||||
|---|---|---|
|
||||
| **L1** | `exponent="order"` улучшает `far`-чтение против `none` в той же тройке | не улучшает → порядок как показатель над отношением остатков не лечит стойло false position; слот 1 закрыт |
|
||||
| **L2** | лучшая конфигурация слота 1 даёт CHA < 2683 | не даёт → измеренная причина не устраняется и местом похожести тоже |
|
||||
| **L3** | APS не хуже 2726 | хуже → снова обмен «широкие за счёт узких», пятый раз |
|
||||
| **L4** | гарантия цела (`over2x = 0`) | нарушена → немедленное закрытие |
|
||||
| **L5** | шаг ни разу не выходит за скобку в режиме `far` (проекция предохранителя не срабатывает) | срабатывает → вывод `t/(1+t) < 1` неверно реализован, это баг, а не результат |
|
||||
|
||||
**Правило остановки:** L1 ложна ⇒ слот 1 закрывается, слот 2 гоняется всё равно (это другое место).
|
||||
L1 верна, L2 ложна ⇒ улучшение записывается как есть, без заявки на Chandrupatla. L4 ложна ⇒ стоп.
|
||||
|
||||
🔴 **Запрет на подбор:** «своя `S` в каждом месте» не даёт права перебрать меню `S` и объявить
|
||||
победителя. Показатель в слоте 1 **измеряется** (`_local_order`), а не выбирается. Любой выбор `S`
|
||||
из списка остаётся тем же изъяном, что жёсткая константа, и отчёт обязан называть его выбором.
|
||||
@@ -0,0 +1,106 @@
|
||||
"""Verdicts L1-L5 for PREREG 2026-09-21 slot 1, read off sweep_order.json.
|
||||
|
||||
Nothing here selects a winner by hunting: L1 is a PAIRED comparison inside each
|
||||
(similarity, guard) cell, which is the only comparison the pre-registration licenses.
|
||||
|
||||
python -u analyze_order.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import math
|
||||
|
||||
REF = {"APS_base": 2806, "APS_best": 2527, "CHA_best": 2683, "BOTH_best": 5511,
|
||||
"falsepos": (127, 10560), "illinois": (153, 7045), "bisect": (153, 7530)}
|
||||
|
||||
|
||||
def load():
|
||||
with open("sweep_order.json", encoding="utf8") as fh:
|
||||
return json.load(fh)
|
||||
|
||||
|
||||
def key(r):
|
||||
s = r["spec"]
|
||||
return (s[1], s[2], s[3], s[8] if len(s) > 8 else "none")
|
||||
|
||||
|
||||
def main():
|
||||
res = load()
|
||||
by = {key(r): r for r in res}
|
||||
|
||||
print("L1 PAIRED: does the measured order improve the FAR reading? "
|
||||
"(the only licensed comparison)")
|
||||
print(f"{'similarity':<11} {'guard':<9} {'APS none':>9} {'APS ord':>8} "
|
||||
f"{'CHA none':>9} {'CHA ord':>8} {'solved n/o':>12}")
|
||||
wins = losses = 0
|
||||
for (sim, sc, gu, ex) in sorted(by):
|
||||
if sc != "far" or ex != "none":
|
||||
continue
|
||||
a, b = by[(sim, sc, gu, "none")], by.get((sim, sc, gu, "order"))
|
||||
if b is None:
|
||||
continue
|
||||
d = (a["APS"]["total"] + a["CHA"]["total"]) - (b["APS"]["total"] + b["CHA"]["total"])
|
||||
wins += d > 0
|
||||
losses += d < 0
|
||||
print(f"{sim:<11} {gu:<9} {a['APS']['total']:9d} {b['APS']['total']:8d} "
|
||||
f"{a['CHA']['total']:9d} {b['CHA']['total']:8d} "
|
||||
f"{a['APS']['solved']}+{a['CHA']['solved']}/"
|
||||
f"{b['APS']['solved']}+{b['CHA']['solved']:>2}")
|
||||
print(f" -> order cheaper in {wins} of {wins + losses} far cells\n")
|
||||
|
||||
print("L1b same pairing on the DERIVED3 reading")
|
||||
for (sim, sc, gu, ex) in sorted(by):
|
||||
if sc != "derived3" or ex != "none" or gu not in ("safe", "project", "none"):
|
||||
continue
|
||||
a, b = by[(sim, sc, gu, "none")], by.get((sim, sc, gu, "order"))
|
||||
if b is None:
|
||||
continue
|
||||
print(f"{sim:<11} {gu:<9} {a['APS']['total']:9d} {b['APS']['total']:8d} "
|
||||
f"{a['CHA']['total']:9d} {b['CHA']['total']:8d} "
|
||||
f"{a['APS']['solved']}+{a['CHA']['solved']}/"
|
||||
f"{b['APS']['solved']}+{b['CHA']['solved']:>2}")
|
||||
|
||||
print("\nL2/L3 best cells in the grid, by the paper's per-set rule "
|
||||
"(solved first, then cost)")
|
||||
for setname, target in (("APS", REF["APS_best"]), ("CHA", REF["CHA_best"])):
|
||||
rows = sorted(res, key=lambda r: (r[setname]["n"] - r[setname]["solved"],
|
||||
r[setname]["total"]))
|
||||
for r in rows[:3]:
|
||||
s = r["spec"]
|
||||
print(f" {setname} {r['label']:<38} {r[setname]['solved']}/"
|
||||
f"{r[setname]['n']} {r[setname]['total']:6d} "
|
||||
f"over2x={r[setname]['over']} "
|
||||
f"(target {target}) exponent={s[8] if len(s) > 8 else 'none'}")
|
||||
|
||||
print("\nL4 guarantee: cells with over2x > 0 (any set)")
|
||||
bad = [r["label"] for r in res if r["APS"]["over"] or r["CHA"]["over"]]
|
||||
print(f" {len(bad)} of {len(res)}" + (f": {bad[:6]}" if bad else " -> intact"))
|
||||
|
||||
print("\nFALSE POSITION, unguarded: is the measured order the parameter-free Illinois?")
|
||||
print(f" {'cell':<34} {'solved':>7} {'APS fev':>9}")
|
||||
print(f" {'false position (paper)':<34} {REF['falsepos'][0]}/154 "
|
||||
f"{REF['falsepos'][1]:9d}")
|
||||
print(f" {'Illinois (constant 1/2)':<34} {REF['illinois'][0]}/154 "
|
||||
f"{REF['illinois'][1]:9d}")
|
||||
for ex in ("none", "order"):
|
||||
r = by.get(("rational", "far", "none", ex))
|
||||
if r:
|
||||
print(f" {'far+rational, guard=none, ' + ex:<34} "
|
||||
f"{r['APS']['solved']}/154 {r['APS']['total']:9d}")
|
||||
for ex in ("none", "order"):
|
||||
r = by.get(("rational", "far", "safe", ex))
|
||||
if r:
|
||||
print(f" {'far+rational, guard=safe, ' + ex:<34} "
|
||||
f"{r['APS']['solved']}/154 {r['APS']['total']:9d}")
|
||||
|
||||
print("\nBOTH SETS, best order cell against the reference 5511")
|
||||
rows = [r for r in res if len(r["spec"]) > 8 and r["spec"][8] == "order"]
|
||||
rows.sort(key=lambda r: (398 - r["APS"]["solved"] - r["CHA"]["solved"],
|
||||
r["APS"]["total"] + r["CHA"]["total"]))
|
||||
for r in rows[:3]:
|
||||
print(f" {r['label']:<38} {r['APS']['solved'] + r['CHA']['solved']}/199 "
|
||||
f"{r['APS']['total'] + r['CHA']['total']:6d} (reference {REF['BOTH_best']})")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Binary file not shown.
@@ -213,6 +213,7 @@ the evaluations.
|
||||
\end{corollary}
|
||||
|
||||
\section{The method}
|
||||
\label{sec:method}
|
||||
|
||||
Corollaries~\ref{cor:chosen}--\ref{cor:derived} fix every choice in the method
|
||||
before any benchmark is consulted. We record them here as a prediction, and
|
||||
@@ -563,6 +564,66 @@ The readings do not compose. Taken together, (2) and (3) give $2884$ and
|
||||
$2859$ --- worse than either alone. Two selections that are each an improvement
|
||||
interfere, which is a reason to measure composition rather than assume it.
|
||||
|
||||
\subsection{The similarity function belongs to the place, not to the method}
|
||||
\label{sec:perplace}
|
||||
|
||||
Everything above varies \emph{where} the similarity is read while keeping one
|
||||
function $S$ for the whole method. That is itself an assumption, and it is
|
||||
measurable. Proposition~\ref{prop:falsi} says the two-point normalisation is
|
||||
false position; what it does not say is \emph{why} false position stalls. Write
|
||||
$|f|\sim K e^m$ near the current point, $e=|x-r|$. Then the residual ratio
|
||||
against the far end needs no slope at all,
|
||||
\begin{equation}
|
||||
\label{eq:orderz}
|
||||
z \;=\; \frac{|f(x)|}{|f(x_{\mathrm{far}})|} \;=\; \Bigl(\frac{e}{E}\Bigr)^{m},
|
||||
\qquad\text{so}\qquad z^{1/m} \;=\; \frac{e}{E},
|
||||
\end{equation}
|
||||
and under $S=1/(1+z)$ the step fraction is $z/(1+z)$, which is exactly inverse
|
||||
linear interpolation. False position therefore reads a dimensionless ratio
|
||||
\emph{at order one} on an approach of order $m$: its three-century-old stall is
|
||||
a mismatch between the similarity function and the place it is read in. The
|
||||
exponent $1/m$ is not a constant --- $m=1/(1-ff''/f'^2)$ comes from three
|
||||
evaluations already paid for, and is $1$ whenever the power model does not
|
||||
apply.
|
||||
|
||||
Measured over the $252$ cells of the grid, the correction behaves exactly as
|
||||
that reading predicts and not at all as a universal improvement. Paired inside
|
||||
each (function, safeguard) cell of the far reading it is cheaper in $29$ of
|
||||
$60$ cells --- indistinguishable from a coin. But in the one pairing where the
|
||||
far reading \emph{is} exact inverse interpolation, $S=1/(1+z)$, it is cheaper
|
||||
in all seven safeguards:
|
||||
|
||||
\begin{table}[h]
|
||||
\centering
|
||||
\begin{tabular}{lrrrr}
|
||||
\toprule
|
||||
safeguard & APS $m{=}1$ & APS measured $m$ & CHA $m{=}1$ & CHA measured $m$\\
|
||||
\midrule
|
||||
both & 4522 & \textbf{3675} & 3387 & \textbf{3216}\\
|
||||
bracket & 4606 & \textbf{3542} & 3571 & \textbf{3303}\\
|
||||
credit & 5190 & \textbf{4686} & 3013 & \textbf{2884}\\
|
||||
project & 6932 & \textbf{6364} & 4794 & \textbf{4559}\\
|
||||
safe & 5987 & \textbf{5229} & 4108 & \textbf{3923}\\
|
||||
step & 6435 & \textbf{5551} & 4257 & \textbf{4022}\\
|
||||
none & 10676 & \textbf{10043} & 8265 & \textbf{7794}\\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\caption{The measured order in the step place, on the far reading. Cheaper in
|
||||
$7$ of $7$ safeguards for the similarity that matches the place, and in $29$ of
|
||||
$60$ cells over all nine functions.}
|
||||
\end{table}
|
||||
|
||||
Two limits must be stated with it. The estimator is \emph{biased low}: on a
|
||||
pure cube, with no noise at all, it returns $2.830$ rather than $3$, because a
|
||||
parabola through three points carries only the second difference. The bias is
|
||||
towards $m=1$, that is towards the uncorrected reading, which is the harmless
|
||||
direction, but it is a bias. And the correction does not rescue the bracket
|
||||
reading as a method: the best cell it reaches on the wide-bracket set is
|
||||
$2884$, against $2683$ for reading~(3) above and $2423$ for Chandrupatla. In
|
||||
the \emph{slope} reading the same exponent is harmful, $2806\to2854$, which is
|
||||
the same statement read from the other side --- a function that does not belong
|
||||
to a place does not become right by being applied there.
|
||||
|
||||
\subsection{Six problems of the two sets are scored unsolved for every method}
|
||||
\label{sec:fun7}
|
||||
|
||||
@@ -693,6 +754,18 @@ $3288$ on the canonical set against $2726$, and $3111$ on the wide-bracket set
|
||||
against $2683$. Its apparent extra solves ($194$ instead of $193$) are two
|
||||
\texttt{fun7} problems where it lands inside the flat zero region close enough to
|
||||
satisfy the harness --- the artefact of \S\ref{sec:fun7}, not a capability.
|
||||
\item \textbf{Reading the similarity at the measured order does not cure false
|
||||
position.} Since false position's stall is a similarity read at order one
|
||||
(\S\ref{sec:perplace}), the obvious hope is that reading it at the measured
|
||||
order is the parameter-free version of the classical fix. It is not. Unguarded,
|
||||
the correction takes the bracket reading from $127/154$ and $10676$ to $128/154$
|
||||
and $10043$ --- one problem and six per cent --- where Illinois, at the price of
|
||||
one constant (halve the stale ordinate), reaches $153/154$ and $7045$. A sixth
|
||||
of the gap is not the gap. The correction is real and it is in the right place;
|
||||
it is simply much smaller than a constant that was chosen for exactly this
|
||||
failure. \emph{Our} $10676$ and the $10560$ of Table~\ref{tab:aps} are not
|
||||
the same algorithm --- our iterate starts at the midpoint of the bracket, plain
|
||||
false position at an end --- and the two must not be differenced.
|
||||
\item \textbf{A logarithmic change of variable does not buy the wide brackets.}
|
||||
Working in $u=\operatorname{sign}(x-c)\log(1+|x-c|/\varepsilon_x)$ makes the
|
||||
step multiplicative, which is the right diagnosis of the flat families --- CHA
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
sweep: 252 configurations x 199 problems (FULL)
|
||||
10/252 done, 0s elapsed
|
||||
20/252 done, 0s elapsed
|
||||
/home/valeri/sim-numerics/simroot/pure.py:116: RuntimeWarning: overflow encountered in scalar multiply
|
||||
"cosine": lambda z: 1.0 / math.sqrt(1.0 + z * z),
|
||||
30/252 done, 1s elapsed
|
||||
/home/valeri/sim-numerics/simroot/pure.py:116: RuntimeWarning: overflow encountered in scalar multiply
|
||||
"cosine": lambda z: 1.0 / math.sqrt(1.0 + z * z),
|
||||
/home/valeri/sim-numerics/simroot/pure.py:116: RuntimeWarning: overflow encountered in scalar multiply
|
||||
"cosine": lambda z: 1.0 / math.sqrt(1.0 + z * z),
|
||||
/home/valeri/sim-numerics/simroot/pure.py:116: RuntimeWarning: overflow encountered in scalar multiply
|
||||
"cosine": lambda z: 1.0 / math.sqrt(1.0 + z * z),
|
||||
40/252 done, 1s elapsed
|
||||
50/252 done, 1s elapsed
|
||||
60/252 done, 1s elapsed
|
||||
70/252 done, 2s elapsed
|
||||
/home/valeri/sim-numerics/simroot/pure.py:115: RuntimeWarning: overflow encountered in scalar multiply
|
||||
"exp_sq": lambda z: math.exp(-z * z),
|
||||
80/252 done, 2s elapsed
|
||||
90/252 done, 2s elapsed
|
||||
100/252 done, 2s elapsed
|
||||
110/252 done, 2s elapsed
|
||||
/home/valeri/sim-numerics/simroot/pure.py:114: RuntimeWarning: overflow encountered in scalar multiply
|
||||
"rat_sq": lambda z: 1.0 / (1.0 + z * z),
|
||||
/home/valeri/sim-numerics/simroot/pure.py:114: RuntimeWarning: overflow encountered in scalar multiply
|
||||
"rat_sq": lambda z: 1.0 / (1.0 + z * z),
|
||||
120/252 done, 3s elapsed
|
||||
/home/valeri/sim-numerics/simroot/pure.py:114: RuntimeWarning: overflow encountered in scalar multiply
|
||||
"rat_sq": lambda z: 1.0 / (1.0 + z * z),
|
||||
130/252 done, 3s elapsed
|
||||
140/252 done, 3s elapsed
|
||||
150/252 done, 3s elapsed
|
||||
160/252 done, 3s elapsed
|
||||
/home/valeri/sim-numerics/simroot/pure.py:110: RuntimeWarning: overflow encountered in scalar power
|
||||
"ratio_soft": lambda z: 1.0 / (1.0 + abs(z)) ** 2,
|
||||
/home/valeri/sim-numerics/simroot/pure.py:110: RuntimeWarning: overflow encountered in scalar power
|
||||
"ratio_soft": lambda z: 1.0 / (1.0 + abs(z)) ** 2,
|
||||
/home/valeri/sim-numerics/simroot/pure.py:110: RuntimeWarning: overflow encountered in scalar power
|
||||
"ratio_soft": lambda z: 1.0 / (1.0 + abs(z)) ** 2,
|
||||
/home/valeri/sim-numerics/simroot/pure.py:110: RuntimeWarning: overflow encountered in scalar power
|
||||
"ratio_soft": lambda z: 1.0 / (1.0 + abs(z)) ** 2,
|
||||
170/252 done, 4s elapsed
|
||||
/home/valeri/sim-numerics/simroot/pure.py:110: RuntimeWarning: overflow encountered in scalar power
|
||||
"ratio_soft": lambda z: 1.0 / (1.0 + abs(z)) ** 2,
|
||||
/home/valeri/sim-numerics/simroot/pure.py:110: RuntimeWarning: overflow encountered in scalar power
|
||||
"ratio_soft": lambda z: 1.0 / (1.0 + abs(z)) ** 2,
|
||||
/home/valeri/sim-numerics/simroot/pure.py:110: RuntimeWarning: overflow encountered in scalar power
|
||||
"ratio_soft": lambda z: 1.0 / (1.0 + abs(z)) ** 2,
|
||||
180/252 done, 4s elapsed
|
||||
190/252 done, 4s elapsed
|
||||
200/252 done, 4s elapsed
|
||||
210/252 done, 4s elapsed
|
||||
220/252 done, 4s elapsed
|
||||
230/252 done, 4s elapsed
|
||||
240/252 done, 5s elapsed
|
||||
250/252 done, 5s elapsed
|
||||
252/252 done, 5s elapsed
|
||||
wrote sweep_order.json (5s total)
|
||||
====================================================================================
|
||||
APS: 154 problems (top 20 of 252)
|
||||
====================================================================================
|
||||
configuration solved total fev over 2x
|
||||
------------------------------------------------------------------------------------
|
||||
pure.ratio.derived3.none 153/154 2806 0
|
||||
pure.ratio.derived3.project 153/154 2806 0
|
||||
pure+ord.ratio.derived3.none 153/154 2854 0
|
||||
pure+ord.ratio.derived3.project 153/154 2854 0
|
||||
pure+ord.ratio.derived3.step 153/154 3000 0
|
||||
pure+ord.ratio.derived3.safe 153/154 3000 0
|
||||
pure.ratio.derived3.step 153/154 3036 0
|
||||
pure.ratio.derived3.safe 153/154 3036 0
|
||||
pure+ord.exp_abs.derived3.project 153/154 3227 0
|
||||
pure+ord.exp_abs.derived3.none 153/154 3227 0
|
||||
pure.exp_abs.derived3.none 153/154 3264 0
|
||||
pure.exp_abs.derived3.project 153/154 3264 0
|
||||
pure+ord.exp_abs.derived3.step 153/154 3308 0
|
||||
pure+ord.exp_abs.derived3.safe 153/154 3308 0
|
||||
pure.exp_abs.derived3.step 153/154 3312 0
|
||||
pure.exp_abs.derived3.safe 153/154 3312 0
|
||||
pure.rational.derived3.none 153/154 3356 0
|
||||
pure.rational.derived3.project 153/154 3356 0
|
||||
pure.rational.derived3.step 153/154 3420 0
|
||||
pure.rational.derived3.safe 153/154 3420 0
|
||||
|
||||
====================================================================================
|
||||
CHA: 45 problems (top 20 of 252)
|
||||
====================================================================================
|
||||
configuration solved total fev over 2x
|
||||
------------------------------------------------------------------------------------
|
||||
pure.sqrt.far.project 41/45 4516 0
|
||||
pure+ord.rational.far.credit 40/45 2884 0
|
||||
pure+ord.ratio.derived3.credit 40/45 2994 0
|
||||
pure.rational.far.credit 40/45 3013 0
|
||||
pure.cbrt.derived3.credit 40/45 3016 0
|
||||
pure.ratio.derived3.credit 40/45 3020 0
|
||||
pure+ord.cbrt.derived3.credit 40/45 3029 0
|
||||
pure.exp_abs.far.credit 40/45 3034 0
|
||||
pure+ord.exp_abs.far.credit 40/45 3035 0
|
||||
pure+ord.ratio_soft.derived3.credit 40/45 3039 0
|
||||
pure.exp_abs.derived3.credit 40/45 3049 0
|
||||
pure.ratio_soft.derived3.credit 40/45 3054 0
|
||||
pure+ord.exp_abs.derived3.credit 40/45 3067 0
|
||||
pure.sqrt.far.credit 40/45 3094 0
|
||||
pure+ord.sqrt.far.credit 40/45 3096 0
|
||||
pure.ratio.far.credit 40/45 3113 0
|
||||
pure.cbrt.far.credit 40/45 3127 0
|
||||
pure+ord.rat_sq.far.credit 40/45 3128 0
|
||||
pure.rat_sq.far.credit 40/45 3129 0
|
||||
pure+ord.cbrt.far.credit 40/45 3130 0
|
||||
|
||||
====================================================================================
|
||||
THE STAGE-0C LAW ACROSS THE GRID
|
||||
====================================================================================
|
||||
scale=derived3 slope c=1 (linear) mean total 6910 mean solved 192.2/199
|
||||
scale=derived3 slope c=0 (quadratic) mean total 22334 mean solved 192.8/199
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
# Frozen runner for PREREG 2026-09-21 slot 1 (the similarity read BY PLACE).
|
||||
# Frozen on purpose: editing a .sh while it runs has broken a run before.
|
||||
cd "$HOME/sim-numerics" || exit 1
|
||||
exec ~/torchenv/bin/python -u sweep_full.py --order
|
||||
+64
-7
@@ -124,6 +124,38 @@ SPACES = ("x", "log")
|
||||
POINT_RULES = ("recent", "shape")
|
||||
DIRECTIONS = ("distance", "dissimilar")
|
||||
|
||||
EXPONENTS = ("none", "order")
|
||||
|
||||
|
||||
def _read_z(resid, sc, exponent, m):
|
||||
"""The normalised residual the similarity function is applied to.
|
||||
|
||||
Valeri, 2026-09-21: "the similarity function may simply be the wrong one", and then
|
||||
"the similarity function can differ in different PLACES - not by domain, by the place
|
||||
of the parameter". This is the step-length place, and here the reading is fixed by
|
||||
what f looks like locally rather than chosen.
|
||||
|
||||
For |f| ~ K.e^m the residual ratio against the far end is dimensionless and needs no
|
||||
slope at all: z = |f(x)|/|f(far)| = (e/E)^m, so z^(1/m) = e/E recovers the DISTANCE
|
||||
ratio exactly. Under S = 1/(1+z) the step fraction is z/(1+z), which at m = 1 is
|
||||
false position (Proposition 4 in the paper: 127/154, 10560 evaluations) - and false
|
||||
position's three-century-old stall is precisely a similarity read at order 1 on an
|
||||
order-m approach. Reading it at the MEASURED order turns the same formula into
|
||||
order-m inverse interpolation, exact in one step for a pure power.
|
||||
|
||||
m comes from _local_order, i.e. from three evaluations already paid for, and is 1
|
||||
whenever the power model does not apply, so nothing here is selected.
|
||||
"""
|
||||
z = resid / sc
|
||||
if exponent == "order" and m is not None and m > 1.0 and z > 0.0:
|
||||
try:
|
||||
z = z ** (1.0 / m)
|
||||
except (OverflowError, ValueError):
|
||||
return resid / sc
|
||||
if not math.isfinite(z):
|
||||
return resid / sc
|
||||
return z
|
||||
|
||||
|
||||
def _shape_defect(x, fx, u, fu, v, fv):
|
||||
"""|phi - xi| for the triple, or None when it is degenerate.
|
||||
@@ -455,7 +487,7 @@ def _local_order(x2, f2, x1, f1, x0, f0):
|
||||
def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||
guard="step", stats=None, budget_factor=2.0,
|
||||
budget_extra=None, endowment=1.0, space="x", prestep="none",
|
||||
points="recent", direction="distance"):
|
||||
points="recent", direction="distance", exponent="none"):
|
||||
"""The pure similarity root finder.
|
||||
|
||||
similarity : key into PURE_SIMS
|
||||
@@ -505,8 +537,16 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||
if similarity not in PURE_SIMS:
|
||||
raise ValueError(f"unknown similarity {similarity!r}")
|
||||
if scale not in ("derived", "derived3", "derived_rho", "derived3_rho",
|
||||
"derived3_m", "derived3_cap", "start", "bracket"):
|
||||
"derived3_m", "derived3_cap", "start", "bracket", "far"):
|
||||
raise ValueError(f"unknown scale {scale!r}")
|
||||
if exponent not in EXPONENTS:
|
||||
raise ValueError(f"unknown exponent {exponent!r}")
|
||||
if exponent == "order" and scale not in ("derived3", "derived3_cap", "bracket", "far"):
|
||||
# the order is read from a three-point divided difference; under the two-point
|
||||
# scale there is no second difference to read it from, and computing one from
|
||||
# elsewhere would hide that (PREREG 2026-09-21, slot 1)
|
||||
raise ValueError("exponent='order' requires scale in "
|
||||
"{'derived3', 'derived3_cap', 'bracket', 'far'}")
|
||||
if space not in SPACES:
|
||||
raise ValueError(f"unknown space {space!r}")
|
||||
if prestep not in ("none", "progress", "slope"):
|
||||
@@ -604,6 +644,11 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||
else:
|
||||
slope = _slope3(x, fx, x_prev, f_prev, x_prev2, f_prev2)
|
||||
sc = abs(slope) * abs(far - x)
|
||||
if scale == "derived3_m":
|
||||
# REFUTED as an improvement; kept so the refutation stays
|
||||
# REPRODUCIBLE, which is the whole point of keeping it. The long
|
||||
# note is at the end of the derived3_cap branch below.
|
||||
sc /= _local_order(x, fx, x_prev, f_prev, x_prev2, f_prev2)
|
||||
elif scale == "derived3_cap":
|
||||
# Valeri, 2026-09-21: "if the similarity formed the step, the step would be
|
||||
# large far from the target and small near it". It does not, and the reason
|
||||
@@ -617,7 +662,12 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||
# order) returns. A min over what is in hand - no constant, no threshold.
|
||||
slope = _slope3(x, fx, x_prev, f_prev, x_prev2, f_prev2)
|
||||
sc = min(abs(slope) * abs(far - x), max(abs(FA), abs(FB)))
|
||||
if scale == "derived3_m":
|
||||
# NOTE on scale="derived3_m" (the live code is in the derived3 branch
|
||||
# above). On 2026-09-21 the derived3_cap branch was inserted directly
|
||||
# in front of this note and swallowed the division with it, so
|
||||
# derived3_m silently became derived3 - 2806 against 2806 - and the
|
||||
# refutation stopped being reproducible. test_m_loses_on_the_full
|
||||
# _benchmark caught it, which is what that pin is for.
|
||||
# REFUTED as an improvement - kept for reproducibility.
|
||||
# The DERIVATION holds: dropping the linearity
|
||||
# assumption from the contraction law does produce the
|
||||
@@ -646,10 +696,14 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||
# rho it is read from the CURVATURE, which stays
|
||||
# meaningful as |f| goes to zero, and it returns to 1
|
||||
# at a simple root, leaving the endgame untouched.
|
||||
sc /= _local_order(x, fx, x_prev, f_prev,
|
||||
x_prev2, f_prev2)
|
||||
elif scale == "start":
|
||||
sc = start_scale
|
||||
elif scale == "far":
|
||||
# the value at the end the step AIMS AT, already computed. With
|
||||
# S = 1/(1+z) this is false position exactly (Proposition 4), which
|
||||
# is why it is the honest control for the order exponent: the pair
|
||||
# (far, none) against (far, order) differs in one thing only.
|
||||
sc = abs(FB) if far == B else abs(FA)
|
||||
else:
|
||||
sc = max(abs(FA), abs(FB))
|
||||
if scale.endswith("_rho"):
|
||||
@@ -696,6 +750,9 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||
if sc == 0.0 or not math.isfinite(sc):
|
||||
x_new = 0.5 * (A + B)
|
||||
else:
|
||||
m_loc = (_local_order(x, fx, x_prev, f_prev, x_prev2, f_prev2)
|
||||
if exponent == "order" else None)
|
||||
z_now = _read_z(abs(fx), sc, exponent, m_loc)
|
||||
step = None
|
||||
if prestep == "slope":
|
||||
# PRE-STEP GATE ON SHAPE (PREREG 2026-09-21). The step trusts the
|
||||
@@ -720,7 +777,7 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||
# less than bisection would remove, spend the same single
|
||||
# evaluation on the midpoint instead. Two lengths already in hand
|
||||
# are compared; there is no threshold and no new constant.
|
||||
cand = (1.0 - S(abs(fx) / sc)) * (far - x)
|
||||
cand = (1.0 - S(z_now)) * (far - x)
|
||||
half = 0.5 * (A + B) - x
|
||||
if abs(cand) < abs(half):
|
||||
step = half
|
||||
@@ -734,7 +791,7 @@ def pure_root(f, a, b, xtol, maxfev, similarity="ratio", scale="derived",
|
||||
c_near = A if abs(FA) <= abs(FB) else B
|
||||
step = _log_space_step(S, x, fx, far, slope, c_near, xtol)
|
||||
if step is None:
|
||||
step = (1.0 - S(abs(fx) / sc)) * (far - x)
|
||||
step = (1.0 - S(z_now)) * (far - x)
|
||||
if sg.reject_step(step):
|
||||
x_new = 0.5 * (A + B)
|
||||
step = x_new - x
|
||||
|
||||
+42
-4
@@ -113,11 +113,12 @@ def make_method(spec):
|
||||
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"
|
||||
exponent = spec[8] if len(spec) > 8 else "none"
|
||||
|
||||
def m(f, a, b, xtol, maxfev):
|
||||
return pure_root(f, a, b, xtol, maxfev, similarity=sim,
|
||||
scale=scale, guard=guard, space=space, prestep=prestep,
|
||||
points=points, direction=direction)
|
||||
points=points, direction=direction, exponent=exponent)
|
||||
return m
|
||||
|
||||
|
||||
@@ -131,6 +132,11 @@ def label(spec):
|
||||
tag = f"{tag}+shape"
|
||||
if len(spec) > 7 and spec[7] != "distance":
|
||||
tag = f"{tag}+dissim"
|
||||
if len(spec) > 8 and spec[8] != "none":
|
||||
# the exponent must be visible in the label: (far, none) is false position and
|
||||
# (far, order) is the hypothesis, and a results file that cannot tell them apart
|
||||
# is worthless
|
||||
tag = f"{tag}+ord"
|
||||
if len(spec) > 5 and spec[5] != "none":
|
||||
# name the gate by WHICH gate it is: "progress" is a measured negative and must
|
||||
# never be confused with "slope" in a results file
|
||||
@@ -198,6 +204,35 @@ def build_grid_log(smoke):
|
||||
for sim in sims for sc in ("derived", "derived3") for gu in guards]
|
||||
|
||||
|
||||
def _preflight_order_mode():
|
||||
"""Fail LOUDLY if the per-place reading is not actually deployed.
|
||||
|
||||
Same lesson as _preflight_log_mode: `evaluate` swallows every exception and charges
|
||||
MAXFEV, so a stale simroot/pure.py on the server reports a sweep of zeros instead of
|
||||
an error. Checks BOTH new things — the far scale and the order exponent."""
|
||||
for sc, ex in (("far", "none"), ("far", "order"), ("derived3", "order")):
|
||||
x, _n, ok = pure_root(lambda t: t - 1.0, 0.0, 3.0, 1e-10, 100,
|
||||
similarity="rational", scale=sc, guard="credit",
|
||||
exponent=ex)
|
||||
if not (ok and abs(x - 1.0) < 1e-8):
|
||||
raise SystemExit(f"order-mode preflight FAILED for scale={sc} exponent={ex}: "
|
||||
"refusing to run a sweep that would report zeros")
|
||||
|
||||
|
||||
def build_grid_order(smoke):
|
||||
"""Slot 1 of PREREG 2026-09-21: the similarity function read BY PLACE.
|
||||
|
||||
The exponent is the only thing that moves inside each (similarity, scale, guard)
|
||||
triple, so (far, none) — which is false position exactly, Proposition 4 — is the
|
||||
control for (far, order). Baselines are not recomputed; they are in sweep_full.json."""
|
||||
_preflight_order_mode()
|
||||
sims = ["rational", "ratio"] if smoke else sorted(PURE_SIMS)
|
||||
guards = ("credit", "safe") if smoke else GUARD_MODES
|
||||
return [("pure", sim, sc, gu, "x", "none", "recent", "distance", ex)
|
||||
for sim in sims for sc in ("far", "derived3")
|
||||
for ex in ("none", "order") for gu in guards]
|
||||
|
||||
|
||||
def build_grid_cap(smoke):
|
||||
"""Scale as a SELECTION between the two computed ones (PREREG 2026-09-21): identical to
|
||||
the slope+dissimilar configuration except scale = derived3_cap, so one thing changes."""
|
||||
@@ -274,10 +309,12 @@ def main():
|
||||
gatemode = "--gate" in sys.argv # the pre-step gate grid, likewise
|
||||
slopemode = "--slope" in sys.argv # the slope-stability gate grid
|
||||
capmode = "--cap" in sys.argv # scale selected between the two computed ones
|
||||
ordermode = "--order" in sys.argv # slot 1: the similarity read BY PLACE
|
||||
combomode = "--combo" in sys.argv # modes A and B together
|
||||
pointsmode = "--points" in sys.argv # mode A: triple by shape
|
||||
dirmode = "--direction" in sys.argv # mode B: far end by dissimilarity
|
||||
specs = (build_grid_cap(smoke) if capmode else
|
||||
specs = (build_grid_order(smoke) if ordermode else
|
||||
build_grid_cap(smoke) if capmode else
|
||||
build_grid_combo(smoke) if combomode else
|
||||
build_grid_points(smoke) if pointsmode else
|
||||
build_grid_direction(smoke) if dirmode else
|
||||
@@ -299,8 +336,9 @@ def main():
|
||||
|
||||
# the published sweep_full.json is what verify_article.py checks the paper
|
||||
# against, so the log grid never writes into it
|
||||
if capmode or combomode or pointsmode or dirmode:
|
||||
base = ("sweep_cap" if capmode else
|
||||
if ordermode or capmode or combomode or pointsmode or dirmode:
|
||||
base = ("sweep_order" if ordermode else
|
||||
"sweep_cap" if capmode else
|
||||
"sweep_combo" if combomode else
|
||||
"sweep_points" if pointsmode else "sweep_direction")
|
||||
out = f"{base}_smoke.json" if smoke else f"{base}.json"
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,127 @@
|
||||
"""Pins for PREREG 2026-09-21 slot 1: the similarity function read BY PLACE.
|
||||
|
||||
Every claim the mode rests on is pinned here, including the ones that make it falsifiable:
|
||||
the default must stay bit-for-bit published, the far reading must BE false position, and
|
||||
the order exponent must invert the power law exactly.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
|
||||
import pytest
|
||||
|
||||
from simroot.pure import PURE_SIMS, _local_order, _read_z, pure_root
|
||||
|
||||
PROBS = (
|
||||
(lambda t: t ** 3 - 2.0 * t - 5.0, 1.0, 4.0),
|
||||
(lambda t: math.exp(t) - 2.0, 0.0, 3.0),
|
||||
(lambda t: (t - 1.0) ** 3, 0.0, 10.0),
|
||||
(lambda t: math.sin(t) - 0.5, 0.0, 1.5),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("f,a,b", PROBS)
|
||||
@pytest.mark.parametrize("guard", ("safe", "credit", "project"))
|
||||
def test_default_is_bit_for_bit_the_published_method(f, a, b, guard):
|
||||
"""R1: the new parameter must not touch the published grid when left alone."""
|
||||
ref = pure_root(f, a, b, 1e-12, 200, similarity="ratio", scale="derived3", guard=guard)
|
||||
new = pure_root(f, a, b, 1e-12, 200, similarity="ratio", scale="derived3", guard=guard,
|
||||
exponent="none")
|
||||
assert ref == new
|
||||
|
||||
|
||||
def test_far_reading_with_rational_is_false_position():
|
||||
"""R2: Proposition 4 must hold in the CODE, not only in the paper.
|
||||
|
||||
The first similarity step from x with S = 1/(1+z), z = |f(x)|/|f(far)| must land on the
|
||||
secant root of the pair (x, far). If it does not, the (far, none) cell is not the
|
||||
control the pre-registration claims it is.
|
||||
"""
|
||||
f = lambda t: t ** 3 - 2.0 * t - 5.0 # noqa: E731
|
||||
a, b = 1.0, 4.0
|
||||
x = 0.5 * (a + b)
|
||||
# the far end must be the one whose sign differs, otherwise there is no secant root
|
||||
# between the pair at all; on this bracket that is a, not b
|
||||
far, fx = a, f(x)
|
||||
ffar = f(far)
|
||||
assert fx * ffar < 0.0
|
||||
z = abs(fx) / abs(ffar)
|
||||
step = (1.0 - PURE_SIMS["rational"](z)) * (far - x)
|
||||
secant = x - fx * (far - x) / (ffar - fx)
|
||||
assert step == pytest.approx(secant - x, rel=1e-12)
|
||||
|
||||
|
||||
def test_order_exponent_inverts_the_power_law_exactly():
|
||||
"""R3: the whole derivation is z^(1/m) = e/E. Pinned as an identity."""
|
||||
for m in (1.0, 2.0, 3.0, 7.0):
|
||||
for e, big in ((0.1, 2.0), (1.0, 5.0), (1e-3, 1.0)):
|
||||
z = (e / big) ** m
|
||||
got = _read_z(z, 1.0, "order", m)
|
||||
assert got == pytest.approx(e / big, rel=1e-9)
|
||||
|
||||
|
||||
def test_order_exponent_is_inert_at_order_one():
|
||||
"""A simple root must leave the reading untouched, or the endgame is not preserved."""
|
||||
for z in (1e-9, 0.3, 1.0, 12.0):
|
||||
assert _read_z(z, 1.0, "order", 1.0) == z
|
||||
assert _read_z(z, 1.0, "order", None) == z
|
||||
assert _read_z(z, 1.0, "none", 5.0) == z
|
||||
|
||||
|
||||
def test_local_order_reads_a_cube_low_by_a_fixed_bias():
|
||||
"""MEASURED, and pinned as measured: on a pure cube the three-point estimator returns
|
||||
2.830, not 3. The parabola through three points carries only the second difference, so
|
||||
a cubic's order is under-read by 5.7% even in the ideal noiseless case. The bias is
|
||||
towards 1, i.e. towards the uncorrected reading, which is the safe direction — but it
|
||||
is a bias and the paper must say so rather than claim the estimator is exact."""
|
||||
f = lambda t: t ** 3 # noqa: E731
|
||||
xs = (0.30, 0.31, 0.32)
|
||||
m = _local_order(xs[2], f(xs[2]), xs[1], f(xs[1]), xs[0], f(xs[0]))
|
||||
assert m == pytest.approx(2.83, rel=0.02)
|
||||
assert 1.0 < m < 3.0
|
||||
|
||||
|
||||
def test_step_fraction_can_never_leave_the_bracket():
|
||||
"""L5: under S = 1/(1+z) the fraction z/(1+z) is at most 1, so the step never passes
|
||||
the far end. At z >= 1/eps it SATURATES at exactly 1 in floating point — the step
|
||||
lands ON the end, which the loop already turns into a bisection because x_new must be
|
||||
strictly inside. Pinned so the saturation is a documented boundary, not a surprise."""
|
||||
for z in (0.0, 1e-12, 1.0, 1e6):
|
||||
assert 0.0 <= 1.0 - PURE_SIMS["rational"](z) < 1.0
|
||||
assert 1.0 - PURE_SIMS["rational"](1e300) == 1.0
|
||||
|
||||
|
||||
@pytest.mark.parametrize("f,a,b", PROBS)
|
||||
def test_far_and_order_still_solve_and_stay_in_the_bracket(f, a, b):
|
||||
ok_any = False
|
||||
for ex in ("none", "order"):
|
||||
x, n, conv = pure_root(f, a, b, 1e-10, 300, similarity="rational", scale="far",
|
||||
guard="safe", exponent=ex)
|
||||
assert min(a, b) <= x <= max(a, b)
|
||||
assert n <= 300
|
||||
ok_any = ok_any or conv
|
||||
assert ok_any
|
||||
|
||||
|
||||
def test_derived3_m_actually_divides_by_the_order():
|
||||
"""REGRESSION, 2026-09-21. The derived3_cap branch was inserted in front of the
|
||||
derived3_m note and swallowed its division, so derived3_m became a silent alias of
|
||||
derived3 (2806 against 2806) and a refutation recorded in the paper stopped being
|
||||
reproducible. One cheap problem where the order is genuinely above 1 is enough to
|
||||
catch it, so the guard does not depend on the full 154-problem pin."""
|
||||
f = lambda t: (t - 1.0) ** 3 # noqa: E731
|
||||
plain = pure_root(f, 0.0, 10.0, 1e-10, 300, similarity="ratio", scale="derived3",
|
||||
guard="project")
|
||||
with_m = pure_root(f, 0.0, 10.0, 1e-10, 300, similarity="ratio", scale="derived3_m",
|
||||
guard="project")
|
||||
assert plain != with_m
|
||||
|
||||
|
||||
def test_bad_combinations_are_refused_not_silently_reinterpreted():
|
||||
with pytest.raises(ValueError):
|
||||
pure_root(lambda t: t, 0.0, 1.0, 1e-9, 50, exponent="cube")
|
||||
with pytest.raises(ValueError):
|
||||
# two-point scale has no second difference to read the order from
|
||||
pure_root(lambda t: t, 0.0, 1.0, 1e-9, 50, scale="derived", exponent="order")
|
||||
with pytest.raises(ValueError):
|
||||
pure_root(lambda t: t, 0.0, 1.0, 1e-9, 50, scale="nonesuch")
|
||||
@@ -201,6 +201,7 @@ def main():
|
||||
check(f"scale {sc} best", best_by_scale[sc], v)
|
||||
|
||||
verify_placement_modes()
|
||||
verify_per_place()
|
||||
|
||||
print()
|
||||
if FAILS:
|
||||
@@ -226,6 +227,76 @@ PLACEMENT = {
|
||||
"sweep_gate.json": ("gate.ratio.derived.project", 4449, 2991),
|
||||
"sweep_log.json": ("log.ratio_soft.derived.safe", 6940, 3474),
|
||||
}
|
||||
# The per-place table of section "the similarity function belongs to the place":
|
||||
# seven PAIRED comparisons in sweep_order.json. Checked as pairs, because a pair is
|
||||
# what the pre-registration licensed and a single cell would prove nothing.
|
||||
ORDER_PAIRS = {
|
||||
# guard (APS m=1, APS measured, CHA m=1, CHA measured)
|
||||
"both": (4522, 3675, 3387, 3216),
|
||||
"bracket": (4606, 3542, 3571, 3303),
|
||||
"credit": (5190, 4686, 3013, 2884),
|
||||
"project": (6932, 6364, 4794, 4559),
|
||||
"safe": (5987, 5229, 4108, 3923),
|
||||
"step": (6435, 5551, 4257, 4022),
|
||||
"none": (10676, 10043, 8265, 7794),
|
||||
}
|
||||
# the negative item: the order does not cure false position
|
||||
ORDER_FALSEPOS = {("rational", "far", "none", "none"): (127, 10676),
|
||||
("rational", "far", "none", "order"): (128, 10043)}
|
||||
# and the harmful direction, quoted in the same subsection
|
||||
ORDER_SLOPE_HARM = {("ratio", "derived3", "none", "none"): 2806,
|
||||
("ratio", "derived3", "none", "order"): 2854}
|
||||
|
||||
|
||||
def verify_per_place():
|
||||
print("\nThe per-place reading (sweep_order.json):")
|
||||
if not os.path.exists("sweep_order.json"):
|
||||
print(" !! sweep_order.json absent — section 'belongs to the place' unverifiable")
|
||||
FAILS.append("missing sweep_order.json")
|
||||
return
|
||||
with open("sweep_order.json", encoding="utf8") as fh:
|
||||
rows = json.load(fh)
|
||||
|
||||
def cell(sim, sc, gu, ex):
|
||||
for r in rows:
|
||||
s = r["spec"]
|
||||
if (s[1], s[2], s[3], s[8] if len(s) > 8 else "none") == (sim, sc, gu, ex):
|
||||
return r
|
||||
return None
|
||||
|
||||
cheaper = 0
|
||||
for gu, (a1, a2, c1, c2) in ORDER_PAIRS.items():
|
||||
p = cell("rational", "far", gu, "none")
|
||||
q = cell("rational", "far", gu, "order")
|
||||
if p is None or q is None:
|
||||
FAILS.append(f"order pair {gu} missing from sweep_order.json")
|
||||
print(f" !! pair {gu} missing — stale sweep")
|
||||
continue
|
||||
check(f"far+rational {gu} APS m=1", p["APS"]["total"], a1)
|
||||
check(f"far+rational {gu} APS order", q["APS"]["total"], a2)
|
||||
check(f"far+rational {gu} CHA m=1", p["CHA"]["total"], c1)
|
||||
check(f"far+rational {gu} CHA order", q["CHA"]["total"], c2)
|
||||
cheaper += (q["APS"]["total"] + q["CHA"]["total"]
|
||||
< p["APS"]["total"] + p["CHA"]["total"])
|
||||
check("cheaper in 7 of 7 safeguards", cheaper, 7)
|
||||
for (sim, sc, gu, ex), (solved, total) in ORDER_FALSEPOS.items():
|
||||
r = cell(sim, sc, gu, ex)
|
||||
if r is None:
|
||||
FAILS.append(f"false-position cell {ex} missing")
|
||||
continue
|
||||
check(f"false position {ex} solved", r["APS"]["solved"], solved)
|
||||
check(f"false position {ex} APS", r["APS"]["total"], total)
|
||||
for (sim, sc, gu, ex), want in ORDER_SLOPE_HARM.items():
|
||||
r = cell(sim, sc, gu, ex)
|
||||
if r is None:
|
||||
FAILS.append(f"slope-harm cell {ex} missing")
|
||||
continue
|
||||
check(f"slope reading {ex} APS", r["APS"]["total"], want)
|
||||
fam = [r for r in rows if r["spec"][3] in ("project", "safe", "credit")]
|
||||
check("guarantee family violations", sum(r["APS"]["over"] + r["CHA"]["over"]
|
||||
for r in fam), 0)
|
||||
|
||||
|
||||
PLACEMENT_PER_SET = {
|
||||
"sweep_direction.json": ("CHA", "pure+dissim+slope.ratio.derived3.safe", 2683),
|
||||
"sweep_gate.json": ("CHA", "gate.ratio_soft.derived.safe", 2851),
|
||||
|
||||
Reference in New Issue
Block a user