From 38c56d00d2702026bdb89b3b2f67a73e75373e9d Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 12 Aug 2026 12:10:34 +0000 Subject: [PATCH 1/8] probes/weather-p1: golden_vs_tempered_probe.py -- T1-T4 pre-registered BEFORE the run Independent, committed reproduction of the hand-derived T1-T4 numbers in golden-vs-tempered-stride-v1.md. If a number disagrees with the plan's table, the plan's table is corrected -- this script is the source of truth going forward. Zero fetch, deterministic, no RNG. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi --- probes/weather-p1/golden_vs_tempered_probe.py | 206 ++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 probes/weather-p1/golden_vs_tempered_probe.py diff --git a/probes/weather-p1/golden_vs_tempered_probe.py b/probes/weather-p1/golden_vs_tempered_probe.py new file mode 100644 index 00000000..a6934168 --- /dev/null +++ b/probes/weather-p1/golden_vs_tempered_probe.py @@ -0,0 +1,206 @@ +"""golden-vs-tempered-stride-v1 -- T1-T4, pre-registered in the plan file +(.claude/plans/golden-vs-tempered-stride-v1.md), run here to independently +validate the hand-derived numbers already committed there. + +WHY THIS SCRIPT EXISTS. The plan's T1-T4 tables were computed via scratch +Python during the design session and copied into the plan as pre-registered +expectations. This script is the COMMITTED, INDEPENDENT reproduction: same +method, freshly executed, checkpointed, with no hand-editing of the output. +If a number here disagrees with the plan's table, the plan's table is wrong +and gets corrected -- this script is the source of truth going forward. + +SCOPE: zero fetch, pure arithmetic. No network. Deterministic (no RNG at all +in T1/T3/T4; nothing here needs a seed). +""" +import json +import pathlib +import statistics +from math import gcd, log2 + +PHI = (1 + 5 ** 0.5) / 2 +GOLDEN_FRAC = 2 - PHI # = 1/phi^2, the golden-angle fraction in turns + + +def star_discrepancy(pts): + """Star discrepancy D*(P) of a finite point set P in [0,1) (1-D form): + max over i of |i/n - x_(i)| and |(i+1)/n - x_(i)| on the sorted sample. + This is the standard low-discrepancy-sequence quality metric (Niederreiter). + """ + p = sorted(pts) + n = len(p) + d = 0.0 + for i, x in enumerate(p): + d = max(d, abs((i + 1) / n - x), abs(i / n - x)) + return d + + +def golden_pts(m): + """First m points of the golden-angle 1-D equidistribution sequence.""" + return [(i * GOLDEN_FRAC) % 1.0 for i in range(m)] + + +def tempered_pts(m, s, q): + """First m points of the coprime stride-s walk mod q, as fractions of q.""" + return [((s * i) % q) / q for i in range(m)] + + +def best_coprime_stride(q): + """The coprime stride s in [1,q) minimizing the MEDIAN star discrepancy + over the 'useful' prefix range m in [ceil(q/2), q] -- excludes the + degenerate tiny-m cases (m=2 is trivially discrepant for any stride) that + dominate a naive worst-case-over-all-m metric into near-uselessness. + Returns (score, stride). + """ + lo = max(2, q // 2) + best = None + for s in range(1, q): + if gcd(s, q) != 1: + continue + sc = statistics.median( + star_discrepancy(tempered_pts(m, s, q)) for m in range(lo, q + 1) + ) + if best is None or sc < best[0]: + best = (sc, s) + return best + + +def t1_crossover(q_list): + """T1: for each q, find the best coprime stride (useful-range metric), + the matching golden score in the same range, and m* -- the first prefix + length beyond q where golden's discrepancy permanently drops below the + tempered stride's frozen m=q value. + """ + rows = [] + for q in q_list: + temp_score, s = best_coprime_stride(q) + lo = max(2, q // 2) + gold_score = statistics.median( + star_discrepancy(golden_pts(m)) for m in range(lo, q + 1) + ) + temp_frozen = star_discrepancy(tempered_pts(q, s, q)) + m_star = None + for m in range(q, 20 * q + 1): + if star_discrepancy(golden_pts(m)) < temp_frozen: + m_star = m + break + m_big = 200 * q + ratio_big = star_discrepancy(golden_pts(m_big)) and ( + temp_frozen / star_discrepancy(golden_pts(m_big)) + ) + rows.append({ + "q": q, "best_stride": s, + "temp_score_useful_range": temp_score, + "golden_score_useful_range": gold_score, + "m_star": m_star, + "temp_over_golden_at_200q": ratio_big, + }) + return rows + + +def t2_asymptotic_bar(t1_rows): + """T2: pass/fail -- at m=200q, golden discrepancy < tempered's frozen + m=q value, for EVERY q tested. Reuses T1's rows (no recomputation).""" + ok = all(r["temp_over_golden_at_200q"] > 1.0 for r in t1_rows) + return {"bar": "golden < temp_frozen at m=200q, for every tested q", + "pass": ok, + "per_q": [(r["q"], r["temp_over_golden_at_200q"]) for r in t1_rows]} + + +def t3_closure_occupancy(q, phases): + """T3: at m=q (tempered's own full cycle), count empty bins for both + walks under q equal-width cells, checked at several bin-phase offsets to + rule out a binning artifact. Tempered fill is a PROOF (coprimality => + bijection), included only as an implementation-bug guard.""" + s = best_coprime_stride(q)[1] + temp_fill_by_phase = [] + gold_fill_by_phase = [] + for off in phases: + temp_bins = set(int((((s * i) % q) / q + off) % 1.0 * q) for i in range(q)) + gold_bins = set(int(((i * GOLDEN_FRAC) % 1.0 + off) % 1.0 * q) for i in range(q)) + temp_fill_by_phase.append(len(temp_bins)) + gold_fill_by_phase.append(len(gold_bins)) + return { + "q": q, "stride": s, "phases": phases, + "temp_fill_by_phase": temp_fill_by_phase, + "golden_fill_by_phase": gold_fill_by_phase, + "temp_always_full": all(f == q for f in temp_fill_by_phase), + "golden_ever_short": any(f < q for f in gold_fill_by_phase), + } + + +def t4_naive_rounding_collapse(q_lo, q_hi): + """T4: sweep q in [q_lo, q_hi), round(golden_frac * q) with NO + coprimality check, count how often gcd(s,q) > 1 (the walk collapses to + fewer than q distinct cells).""" + collapses = [] + total = 0 + for q in range(q_lo, q_hi): + total += 1 + s = round(GOLDEN_FRAC * q) + if s == 0: + s = 1 + g = gcd(s, q) + if g > 1: + collapses.append({"q": q, "s": s, "gcd": g, "cells_reached": q // g}) + return { + "q_range": [q_lo, q_hi], "total_q_tested": total, + "n_collapsing": len(collapses), + "collapse_rate": len(collapses) / total, + "examples": collapses[:8], + } + + +def run(): + """Run T1-T4 in order, checkpoint each to the .partial.jsonl, then write + the final combined JSON. Deterministic -- no seed needed anywhere.""" + out_dir = pathlib.Path(__file__).parent + partial = out_dir / "golden_vs_tempered_probe.partial.jsonl" + with open(partial, "w") as pf: + q_list = [12, 17, 34, 55, 64, 89, 144, 233, 377, 987] + t1 = t1_crossover(q_list) + pf.write(json.dumps({"stage": "T1", "rows": t1}) + "\n") + pf.flush() + + t2 = t2_asymptotic_bar(t1) + pf.write(json.dumps({"stage": "T2", "result": t2}) + "\n") + pf.flush() + + t3 = t3_closure_occupancy(140, [0.0, 0.1, 0.37, 0.5, 0.83]) + t3_aside_144 = t3_closure_occupancy(144, [0.0, 0.1, 0.37, 0.5, 0.83]) + pf.write(json.dumps({"stage": "T3", "headline_q140": t3, + "aside_q144_fibonacci": t3_aside_144}) + "\n") + pf.flush() + + t4 = t4_naive_rounding_collapse(8, 300) + pf.write(json.dumps({"stage": "T4", "result": t4}) + "\n") + pf.flush() + + out = { + "T1": t1, + "T2": t2, + "T3": {"headline_q140": t3, "aside_q144_fibonacci": t3_aside_144}, + "T4": t4, + } + with open(out_dir / "golden_vs_tempered_probe.json", "w") as fh: + json.dump(out, fh, indent=2) + return out + + +if __name__ == "__main__": + result = run() + print("=== T1 crossover ===") + for r in result["T1"]: + print(f" q={r['q']:5d} s={r['best_stride']:5d} " + f"temp={r['temp_score_useful_range']:.4f} " + f"gold={r['golden_score_useful_range']:.4f} " + f"m*={r['m_star']} temp/gold@200q={r['temp_over_golden_at_200q']:.1f}x") + print("\n=== T2 asymptotic bar ===", "PASS" if result["T2"]["pass"] else "FAIL") + print("\n=== T3 closure (q=140) ===") + print(" temp fill by phase:", result["T3"]["headline_q140"]["temp_fill_by_phase"]) + print(" gold fill by phase:", result["T3"]["headline_q140"]["golden_fill_by_phase"]) + print("=== T3 aside (q=144, Fibonacci) ===") + print(" gold fill by phase:", result["T3"]["aside_q144_fibonacci"]["golden_fill_by_phase"]) + print("\n=== T4 naive-rounding collapse ===") + print(f" {result['T4']['n_collapsing']}/{result['T4']['total_q_tested']} = " + f"{100*result['T4']['collapse_rate']:.1f}% collapse") + print(" examples:", result["T4"]["examples"]) From c2ebfc68f4ffc522659a5bc2be9ad4badceb476b Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 12 Aug 2026 12:18:44 +0000 Subject: [PATCH 2/8] probes/weather-p1: golden_vs_tempered_probe RUN -- T1-T4 all pass, two real defects caught in the hand-derived numbers Ran the committed probe against the pre-registered bars. All four bars PASS (T2 asymptotic advantage true at all 10 q; T3 tempered closure proven exact; T4 39.0% naive-rounding collapse rate matches the hand-derived figure exactly). Two real methodology defects were caught by actually running the script rather than trusting the scratch-derived plan numbers: 1. T1's m* (crossover point) was computed inconsistently with the plan's own prose in the original draft. The draft recomputed the TEMPERED sequence's discrepancy at each growing m>q -- but a tempered walk past m=q is REPEATING its own q positions, feeding duplicate points into a formula built for distinct order statistics, which spuriously WORSENS instead of staying frozen. Worked example, q=17: at m=18 (where the stride's 18th sample lands exactly back on the 1st) the repeating-sequence recomputation jumps to 0.1111 -- worse than the true frozen 0.0588 -- making golden's 0.0832 look like a win when it is still worse than tempered's real ceiling. Fixed: hold tempered at its true frozen m=q value (matching the plan's own "repeats identically forever" definition). Every corrected m* is >= the draft's value; the qualitative claim survives but "sits almost exactly at m~=q" is corrected to "within roughly 1.0-1.4x of q". 2. T3's original method checked BOTH walks via a float round-trip (k/q then *q then int()), and for the TEMPERED walk (a proven exact bijection by coprimality) this produced a false negative: 138/140 filled instead of 140/140, from pure IEEE-754 truncation (int(46.99999999999999) rounds down to 46). The mathematical fact was never wrong; the measurement code was. Fixed: verify the tempered bijection via pure integer arithmetic ((s*i) mod q, no float round-trip at all), which cannot have this artifact -- confirmed 140/140 always. The golden check (float, inherently continuous) is unaffected and stays legitimate. Removed the completed run's .partial.jsonl checkpoint (no other probe in this repo retains one after clean completion; the stranded-rescue protocol has no purpose once a run finishes in under 5 seconds). Plan updated in place with the corrected numbers, both correction notes inline, and the RUN status throughout. Nothing here weakens the head-vs-gut synthesis; it tightens two specific numbers and demonstrates, twice, why "commit the bars then run" catches things "commit the bars and trust the scratch math" does not. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi --- .claude/plans/golden-vs-tempered-stride-v1.md | 157 ++++++++---- .../weather-p1/golden_vs_tempered_probe.json | 233 ++++++++++++++++++ probes/weather-p1/golden_vs_tempered_probe.py | 28 ++- 3 files changed, 356 insertions(+), 62 deletions(-) create mode 100644 probes/weather-p1/golden_vs_tempered_probe.json diff --git a/.claude/plans/golden-vs-tempered-stride-v1.md b/.claude/plans/golden-vs-tempered-stride-v1.md index c797060d..512a9fbc 100644 --- a/.claude/plans/golden-vs-tempered-stride-v1.md +++ b/.claude/plans/golden-vs-tempered-stride-v1.md @@ -1,10 +1,15 @@ # golden-vs-tempered-stride-v1 — head vs gut, made falsifiable -> **Status:** ACTIVE for the exploratory tier (T1–T4 below). Cross-referenced +> **Status:** T1–T4 RUN (`probes/weather-p1/golden_vs_tempered_probe.py` / +> `.json`, committed with bars before execution, 2026-08-12). Cross-referenced > from `weather-w-probes-v1.md` §0 (the golden-ratio index floor rule) — this > plan is the standalone, substrate-general validation of that rule, not -> weather-specific. Zero fetch, pure arithmetic, runnable by any Sonnet -> worker with `numpy` + `scipy` only. +> weather-specific. Zero fetch, pure arithmetic. +> +> **⚠ RUNNING THE PROBE CAUGHT TWO REAL DEFECTS IN THE HAND-DERIVED NUMBERS +> BELOW — both fixed, both explained where they occurred (T1, T3).** Neither +> changes the qualitative finding; both change specific numbers. This is +> exactly what "commit the bars, then run" is for. ## Why this file exists @@ -47,18 +52,42 @@ first prefix length beyond `q` at which golden's discrepancy drops below the tempered stride's (permanently, since tempered is frozen at its `m=q` value forever after). -**Pre-registered expectation, measured before commit:** - -| q | best coprime s | temp score (median, useful range) | golden score (same range) | m* (golden overtakes) | temp/golden @ m=200q | -|---|---|---|---|---|---| -| 12 | 5 | 0.1667 | 0.1721 | 13 | 89.5× | -| 17 | 14 | 0.1042 | 0.1169 | 18 | 70.4× | -| 34 | 25 | 0.0570 | 0.0654 | 35 | 89.4× | -| 55 | 34 | 0.0384 | 0.0373 | 55 | 106.4× | -| 64 | 41 | 0.0312 | 0.0337 | 66 | 83.3× | -| 89 | 35 | 0.0251 | 0.0275 | 90 | 84.2× | -| 144 | 85 | 0.0160 | 0.0158 | 144 | 103.6× | -| 233 | 149 | 0.0104 | 0.0116 | 234 | 68.2× | +**RUN, committed script, 2026-08-12** (`golden_vs_tempered_probe.json`): + +| q | best coprime s | temp score (median, useful range) | golden score (same range) | m* (golden overtakes) | m*/q | temp/golden @ m=200q | +|---|---|---|---|---|---|---| +| 12 | 5 | 0.1667 | 0.1721 | 16 | 1.33 | 89.5× | +| 17 | 14 | 0.1042 | 0.1169 | 21 | 1.24 | 70.4× | +| 34 | 25 | 0.0570 | 0.0654 | 42 | 1.24 | 89.4× | +| 55 | 34 | 0.0384 | 0.0373 | 55 | 1.00 | 106.4× | +| 64 | 41 | 0.0312 | 0.0337 | 90 | 1.41 | 83.3× | +| 89 | 35 | 0.0251 | 0.0275 | 110 | 1.24 | 84.2× | +| 144 | 85 | 0.0160 | 0.0158 | 144 | 1.00 | 103.6× | +| 233 | 149 | 0.0104 | 0.0116 | 288 | 1.24 | 68.2× | +| 377 | 239 | 0.0066 | 0.0066 | 377 | 1.00 | 96.4× | +| 987 | 722 | 0.0028 | 0.0027 | 987 | 1.00 | 90.0× | + +> **⚠ CORRECTION — `m*` was computed inconsistently with this plan's own +> prose in the pre-registered draft (all rows), caught by actually running +> the script.** The draft's `m*` search recomputed the TEMPERED sequence's +> star discrepancy at each growing `m > q` — but a tempered walk past `m=q` +> is REPEATING its own `q` positions, not sampling new ones, so that +> recomputation feeds duplicate points into a formula built for distinct +> order statistics, and the resulting "discrepancy" **spuriously worsens** +> instead of staying at its true, meaningful value. Worked example at +> `q=17`: at `m=18` (the point where the tempered stride's 18th sample lands +> exactly back on its own first position) the repeating-sequence +> recomputation jumps to 0.1111 — WORSE than tempered's actual frozen +> quality of 0.0588 — making golden's 0.0832 look like a win at `m=18` when +> it is still **worse** than tempered's real ceiling. The draft's `m*=18` +> for `q=17` was an artifact of this; the corrected script holds tempered at +> its true frozen `m=q` value (matching this plan's own stated definition: +> *"then repeats identically forever — a hard ceiling on refinement"*) and +> finds golden's genuine first crossing, `m*=21`. **Every `m*` in the +> corrected table is ≥ the draft's value** — under the correct definition, +> golden takes somewhat LONGER to overtake than the draft suggested, not +> shorter, so nothing here weakens the qualitative claim; it corrects the +> tightness of one specific number per row. **Reading, stated as the finding rather than left implicit:** - **The head is right in the bounded regime.** At every tested `q`, the @@ -68,23 +97,25 @@ value forever after). quality at any finite `m` is a continuous function with no guaranteed floor. - **The gut is right in the unbounded regime.** `m*` — the point where - golden permanently overtakes — sits almost exactly at `m ≈ q` in every - row (crossing within one budget-length of the tempered walk's own - ceiling). Beyond that, tempered is **frozen** at its `m=q` value forever - (coprimality guarantees full closure, not continued refinement), while - golden keeps improving as `O(log m / m)`. By `m = 200q` the gap is - **68–106×** in golden's favor, at every `q` tested. + golden permanently overtakes — sits **within about 1.0–1.4× of `q`** in + every row tested (never more than half a cycle-length beyond `q`; exactly + at `q` for four of the ten rows — `55, 144, 377, 987`). Beyond that, + tempered is **frozen** at its `m=q` value forever (coprimality guarantees + full closure, not continued refinement), while golden keeps improving as + `O(log m / m)`. By `m = 200q` the gap is **68–106×** in golden's favor, at + every `q` tested. - **Neither instinct is wrong; they are answers to different questions.** "Is there ever going to be more data than this fixed budget?" — no ⇒ tempered, exact closure, zero variance, done. "Is more data always coming, indefinitely?" — yes ⇒ golden, no ceiling, strictly better past `m ≈ q`. -**Bar T1 (descriptive, no single pass/fail — the crossover table itself is -the deliverable):** report the table above, regenerated at run time rather -than copied, for the full q list plus **two additional q not yet run**: -`q = 377` and `q = 987` (both Fibonacci, continuing the ladder) — confirm -the `m* ≈ q` pattern holds or report the first `q` where it breaks. +**Bar T1 — RUN, result above:** the extended list (including `q=377,987`) +confirms the qualitative crossover pattern: `m*` never exceeds ~1.41× `q` at +any tested `q`, and lands exactly at `q` whenever the useful-range-optimal +stride's own frozen discrepancy already beats golden's score throughout the +sweep window (the `m*/q = 1.00` rows). No `q` broke the pattern into a +qualitatively different regime. **⚠ CAVEAT, stated up front rather than discovered late (an earlier worst-case-over-all-`m` metric picked DIFFERENT "best" strides for q=17 — @@ -102,8 +133,8 @@ citing a "best stride" number, here or elsewhere. ## T2 — the asymptotic claim, tested not assumed **Bar (pass/fail):** for `m = 200q`, golden discrepancy `<` the tempered -stride's frozen `m=q` value, for **every** `q` in the T1 list. **Measured: -TRUE at all 8 tested q (68.2×–106.4× separation)** — this is the arithmetic +stride's frozen `m=q` value, for **every** `q` in the T1 list. **RUN: PASS +at all 10 tested q (68.2×–106.4× separation)** — this is the arithmetic validation of the intuitive "nature prefers golden ratio" pull, made falsifiable rather than assumed. A single `q` where this bar fails would be a genuine surprise and would need its own investigation before the T2 @@ -119,23 +150,42 @@ Golden's fill count is genuinely **not guaranteed** and must be measured — report it, and check it is not an artifact of bin-boundary phase by re-binning at 5 different phase offsets. -**Measured (non-Fibonacci q=140, avoiding the self-referential case where q -is itself a Fibonacci number — see the aside below):** tempered fills -**140/140** at every phase (proof, not measurement). Golden fills -**124/140 at the canonical phase** — **16 empty cells** — and the count is -**stable across 5 bin-phase offsets tested** (not a binning artifact). +> **⚠ CORRECTION — the FIRST run of this bar produced a false negative on +> tempered's OWN proof, caught by actually running it rather than trusting +> the proof-not-measurement framing.** The original method checked BOTH +> walks via the same float round-trip (`k/q` then `+offset` then `%1.0` +> then `*q` then `int()`) — and for the tempered walk, at `off=0.0`, this +> reported only **138/140** filled, contradicting its own "proof, not +> measurement" claim. Diagnosed: pure IEEE-754 truncation — +> `int(46.99999999999999)` rounds DOWN to 46 instead of 47, because +> `(47/140)*140` does not round-trip to exactly `47.0` in binary floating +> point. This affected only the MEASUREMENT CODE, not the mathematical +> fact (coprimality ⇒ exact bijection, provably true regardless of how it +> is measured). **Fixed: the tempered check now uses pure integer +> arithmetic (`(s·i) mod q`, never divided then re-multiplied) — it cannot +> have this artifact, and correctly reports 140/140 always.** The golden +> check is unaffected by this fix (its positions are inherently +> continuous, so the float phase-offset sweep is the legitimate empirical +> method there, not a proof-verification with a spurious failure mode). + +**RUN, corrected method (non-Fibonacci q=140, avoiding the self-referential +case where q is itself a Fibonacci number — see the aside below):** +tempered fills **140/140** (exact integer check — proof confirmed, not +merely assumed). Golden fills **124–127/140 across 5 phase offsets tested** +(canonical phase: 124) — **13–16 empty cells depending on phase** — real, +not a binning artifact (verified via the phase sweep, and the artifact this +correction removed was in the TEMPERED check, not the golden one). **Aside, reported not judged:** at `q = 144 = F(12)` (a Fibonacci number -itself), golden happened to fill **144/144 at all 5 phases tested** in a -quick check — a special/resonant case worth flagging but not treated as -representative; T3's headline number uses `q=140` specifically to avoid -this Fibonacci-on-Fibonacci confound. +itself), golden fills **144/144 at all 5 phases tested** — a +special/resonant case worth flagging but not treated as representative; +T3's headline number uses `q=140` specifically to avoid this +Fibonacci-on-Fibonacci confound. -**Bar T3 (two-sided by construction):** tempered fill = q/q **always** (a -guard against an implementation bug more than a finding); golden fill `< -q` for **at least** `q=140` (falsifiable — if golden also fills 140/140, -the closure-guarantee argument for T3 is weaker than claimed and must be -restated as "usually" rather than "guaranteed-vs-not"). +**Bar T3 (two-sided by construction) — RUN, PASS on both sides:** tempered +fill = q/q **always** (140/140, exact integer arithmetic — the proof holds +and is now verified without a measurement artifact); golden fill `< q` at +`q=140` (**124–127/140**, well below 140, falsifiable and not falsified). ## T4 — the naive-rounding collapse hazard (the sharpest form of "kollabiert nicht") @@ -261,15 +311,14 @@ happens — and this is the elegant part, not the load-bearing part — that the storm's geography sorts its tasks into exactly the two regimes the T1 crossover table measures. -## Execution - -Zero fetch, pure `numpy`/`scipy.spatial` (only T3's KD-tree-adjacent bucket -counting needs anything beyond stdlib math, and even that is trivial at -these sizes — `q ≤ 987`, no lattice-scale KD-tree needed here at all, -unlike `weather-w-probes-v1`'s W5/W2s-a). Single Sonnet worker, -**~5 minutes**, no `§0` preamble needed (this plan is self-contained and -carries no weather-domain data access). One script, -`probes/weather-p1/golden_vs_tempered_probe.py`, emitting -`golden_vs_tempered_probe.json` with `{T1: [...], T2: {...}, T3: {...}, -T4: {...}}`. Commit the script with its bars BEFORE running, per the -standing discipline. +## Execution — RUN + +Zero fetch, pure stdlib `math`/`statistics` — no `numpy`/`scipy` needed after +all (`q ≤ 987`, no lattice-scale KD-tree required, unlike +`weather-w-probes-v1`'s W5/W2s-a). Committed with bars before execution +(`38c56d00`), then run (< 5 seconds wall time), then two real defects were +caught by the run itself and fixed (T1's `m*` methodology, T3's float +round-trip false negative) — both explained inline above rather than +silently absorbed into the numbers. `probes/weather-p1/ +golden_vs_tempered_probe.py` / `.json` / `.partial.jsonl` are the committed +artifacts; `.json` is the record of truth for every number in this document. diff --git a/probes/weather-p1/golden_vs_tempered_probe.json b/probes/weather-p1/golden_vs_tempered_probe.json new file mode 100644 index 00000000..eedc66c2 --- /dev/null +++ b/probes/weather-p1/golden_vs_tempered_probe.json @@ -0,0 +1,233 @@ +{ + "T1": [ + { + "q": 12, + "best_stride": 5, + "temp_score_useful_range": 0.16666666666666663, + "golden_score_useful_range": 0.17213595499957957, + "m_star": 16, + "temp_over_golden_at_200q": 89.53794482112389 + }, + { + "q": 17, + "best_stride": 14, + "temp_score_useful_range": 0.1042016806722689, + "golden_score_useful_range": 0.11685777865321556, + "m_star": 21, + "temp_over_golden_at_200q": 70.35571368151886 + }, + { + "q": 34, + "best_stride": 25, + "temp_score_useful_range": 0.057009803921568625, + "golden_score_useful_range": 0.06538867268498469, + "m_star": 42, + "temp_over_golden_at_200q": 89.43980989976654 + }, + { + "q": 55, + "best_stride": 34, + "temp_score_useful_range": 0.03838383838383841, + "golden_score_useful_range": 0.037271388032666455, + "m_star": 55, + "temp_over_golden_at_200q": 106.35961099655272 + }, + { + "q": 64, + "best_stride": 41, + "temp_score_useful_range": 0.03125, + "golden_score_useful_range": 0.03366799389590275, + "m_star": 90, + "temp_over_golden_at_200q": 83.30256049149101 + }, + { + "q": 89, + "best_stride": 35, + "temp_score_useful_range": 0.02505140633032238, + "golden_score_useful_range": 0.027539906627747447, + "m_star": 110, + "temp_over_golden_at_200q": 84.2201278187017 + }, + { + "q": 144, + "best_stride": 85, + "temp_score_useful_range": 0.016008771929824583, + "golden_score_useful_range": 0.01578730894249568, + "m_star": 144, + "temp_over_golden_at_200q": 103.57381186938156 + }, + { + "q": 233, + "best_stride": 149, + "temp_score_useful_range": 0.010369816859613802, + "golden_score_useful_range": 0.011612009931471826, + "m_star": 288, + "temp_over_golden_at_200q": 68.19636649519545 + }, + { + "q": 377, + "best_stride": 239, + "temp_score_useful_range": 0.006631061693812973, + "golden_score_useful_range": 0.00658030019036672, + "m_star": 377, + "temp_over_golden_at_200q": 96.43803268023423 + }, + { + "q": 987, + "best_stride": 722, + "temp_score_useful_range": 0.002834401568212852, + "golden_score_useful_range": 0.0027486658306924983, + "m_star": 987, + "temp_over_golden_at_200q": 90.01758793198474 + } + ], + "T2": { + "bar": "golden < temp_frozen at m=200q, for every tested q", + "pass": true, + "per_q": [ + [ + 12, + 89.53794482112389 + ], + [ + 17, + 70.35571368151886 + ], + [ + 34, + 89.43980989976654 + ], + [ + 55, + 106.35961099655272 + ], + [ + 64, + 83.30256049149101 + ], + [ + 89, + 84.2201278187017 + ], + [ + 144, + 103.57381186938156 + ], + [ + 233, + 68.19636649519545 + ], + [ + 377, + 96.43803268023423 + ], + [ + 987, + 90.01758793198474 + ] + ] + }, + "T3": { + "headline_q140": { + "q": 140, + "stride": 103, + "phases": [ + 0.0, + 0.1, + 0.37, + 0.5, + 0.83 + ], + "temp_fill_exact_integer": 140, + "golden_fill_by_phase": [ + 124, + 124, + 126, + 124, + 127 + ], + "temp_always_full": true, + "golden_ever_short": true + }, + "aside_q144_fibonacci": { + "q": 144, + "stride": 85, + "phases": [ + 0.0, + 0.1, + 0.37, + 0.5, + 0.83 + ], + "temp_fill_exact_integer": 144, + "golden_fill_by_phase": [ + 144, + 144, + 144, + 144, + 144 + ], + "temp_always_full": true, + "golden_ever_short": false + } + }, + "T4": { + "q_range": [ + 8, + 300 + ], + "total_q_tested": 292, + "n_collapsing": 114, + "collapse_rate": 0.3904109589041096, + "examples": [ + { + "q": 9, + "s": 3, + "gcd": 3, + "cells_reached": 3 + }, + { + "q": 10, + "s": 4, + "gcd": 2, + "cells_reached": 5 + }, + { + "q": 15, + "s": 6, + "gcd": 3, + "cells_reached": 5 + }, + { + "q": 16, + "s": 6, + "gcd": 2, + "cells_reached": 8 + }, + { + "q": 20, + "s": 8, + "gcd": 4, + "cells_reached": 5 + }, + { + "q": 22, + "s": 8, + "gcd": 2, + "cells_reached": 11 + }, + { + "q": 24, + "s": 9, + "gcd": 3, + "cells_reached": 8 + }, + { + "q": 25, + "s": 10, + "gcd": 5, + "cells_reached": 5 + } + ] + } +} \ No newline at end of file diff --git a/probes/weather-p1/golden_vs_tempered_probe.py b/probes/weather-p1/golden_vs_tempered_probe.py index a6934168..4cd929ba 100644 --- a/probes/weather-p1/golden_vs_tempered_probe.py +++ b/probes/weather-p1/golden_vs_tempered_probe.py @@ -109,21 +109,33 @@ def t2_asymptotic_bar(t1_rows): def t3_closure_occupancy(q, phases): """T3: at m=q (tempered's own full cycle), count empty bins for both walks under q equal-width cells, checked at several bin-phase offsets to - rule out a binning artifact. Tempered fill is a PROOF (coprimality => - bijection), included only as an implementation-bug guard.""" + rule out a binning artifact. + + TWO DIFFERENT VERIFICATION METHODS, DELIBERATELY, per the SAME lesson + caught mid-development of this script: a naive float round-trip + (k/q then *q then int()) truncates values like 46.99999999999999 to 46 + instead of 47 -- a pure IEEE-754 rounding artifact that produced a FALSE + fill-count deficit for the tempered walk even though its bijection is a + mathematical PROOF (coprimality => {s*i mod q} = {0..q-1} exactly, no + floats involved at all). Fixed: the tempered check uses EXACT INTEGER + arithmetic (`(s*i) % q`, never divided then re-multiplied), so it cannot + have this artifact -- it either equals q always (as proven) or the proof + itself would be wrong, which it is not. The golden check legitimately + needs floats (its positions are inherently continuous), so the + phase-offset sweep stays meaningful there -- it is the actual empirical + question, not a verification of an existing proof. + """ s = best_coprime_stride(q)[1] - temp_fill_by_phase = [] + temp_fill_exact = len(set((s * i) % q for i in range(q))) # proof-checking; no floats gold_fill_by_phase = [] for off in phases: - temp_bins = set(int((((s * i) % q) / q + off) % 1.0 * q) for i in range(q)) gold_bins = set(int(((i * GOLDEN_FRAC) % 1.0 + off) % 1.0 * q) for i in range(q)) - temp_fill_by_phase.append(len(temp_bins)) gold_fill_by_phase.append(len(gold_bins)) return { "q": q, "stride": s, "phases": phases, - "temp_fill_by_phase": temp_fill_by_phase, + "temp_fill_exact_integer": temp_fill_exact, "golden_fill_by_phase": gold_fill_by_phase, - "temp_always_full": all(f == q for f in temp_fill_by_phase), + "temp_always_full": temp_fill_exact == q, "golden_ever_short": any(f < q for f in gold_fill_by_phase), } @@ -196,7 +208,7 @@ def run(): f"m*={r['m_star']} temp/gold@200q={r['temp_over_golden_at_200q']:.1f}x") print("\n=== T2 asymptotic bar ===", "PASS" if result["T2"]["pass"] else "FAIL") print("\n=== T3 closure (q=140) ===") - print(" temp fill by phase:", result["T3"]["headline_q140"]["temp_fill_by_phase"]) + print(" temp fill (exact integer):", result["T3"]["headline_q140"]["temp_fill_exact_integer"]) print(" gold fill by phase:", result["T3"]["headline_q140"]["golden_fill_by_phase"]) print("=== T3 aside (q=144, Fibonacci) ===") print(" gold fill by phase:", result["T3"]["aside_q144_fibonacci"]["golden_fill_by_phase"]) From 5d4ea5226d24e907883efdaba16b7cd97bf652ba Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 12 Aug 2026 12:19:18 +0000 Subject: [PATCH 3/8] board: D-GVT-T1..T4 Queued -> RUN with results Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi --- .claude/board/STATUS_BOARD.md | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index c19cab31..91fffab6 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -1,17 +1,20 @@ -## golden-vs-tempered-stride-v1 — head-vs-gut queue (PRE-REGISTERED 2026-08-12) +## golden-vs-tempered-stride-v1 — head-vs-gut queue — RUN 2026-08-12 Plan: `.claude/plans/golden-vs-tempered-stride-v1.md`. Standalone, zero fetch, -~5 min single Sonnet worker. All four bars pre-registered in the plan text -itself with the expected numbers already worked out arithmetically — the -worker's job is to reproduce them from a committed script, not discover them -fresh. - -| D-id | Deliverable | Status | Feeds | +< 5 s wall time (turned out lighter than the ~5 min pre-registered estimate — +pure stdlib arithmetic, no numpy/scipy needed after all). All four bars ran +against `probes/weather-p1/golden_vs_tempered_probe.py`; results in the +matching `.json`. **Two real methodology defects caught by the run itself** +(T1's m* definition, T3's float round-trip false negative) — both fixed in +the committed script, both explained inline in the plan; neither weakens the +qualitative synthesis, both tightened specific numbers. + +| D-id | Deliverable | Status | Result | |---|---|---|---| -| D-GVT-T1 | Crossover sweep across 8+ q, useful-range metric | Queued | the two-regime design rule | -| D-GVT-T2 | Asymptotic golden-advantage pass/fail bar | Queued | validates "gut" instinct | -| D-GVT-T3 | Closure-occupancy guarantee (tempered) vs variable (golden) | Queued | validates "does not collapse" precisely | -| D-GVT-T4 | Naive-rounding collapse hazard rate | Queued | the sharpest form of "does not collapse" | +| D-GVT-T1 | Crossover sweep across 10 q, useful-range metric | **RUN** | m* within 1.0–1.4× q at every q (never exactly ≈q as first drafted — corrected) | +| D-GVT-T2 | Asymptotic golden-advantage pass/fail bar | **RUN — PASS** | golden ahead 68.2–106.4× at m=200q, all 10 q | +| D-GVT-T3 | Closure-occupancy guarantee (tempered) vs variable (golden) | **RUN — PASS** | tempered 140/140 exact (integer-verified); golden 124–127/140 across 5 phases | +| D-GVT-T4 | Naive-rounding collapse hazard rate | **RUN — PASS** | 114/292 = 39.0 % of q∈[8,300) collapse under naive rounding | ## weather-w-probes-v1 — W-probe queue (PRE-REGISTERED 2026-08-12) From 39ac75d51125e8bef73ab8df33c778932b7cad75 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 12 Aug 2026 12:22:36 +0000 Subject: [PATCH 4/8] probes/weather-p1: sunflower_pairing_probe.py -- W2s-a pre-registered BEFORE the run Golden two-lattice pairing on real cos-lat lat/lon geometry vs an axis-aligned grid control, per weather-w-probes-v1.md SS2 (N=F(17)^2 headline + the G4 index-floor sweep). Committed exactly as specified in the brief before execution. Smoke-tested at N=50k first (caught nothing wrong in the code -- but surfaced a real, mechanically-understood geometric property worth flagging before the full run: two IDENTICALLY-SPACED regular grids offset by a pure translation vector are, by lattice symmetry, translation-invariant in their cross-nearest-neighbour distance -- every point of one grid sees the exact same local neighbour configuration in the other, so CV(nearest-pair distance) is near machine-epsilon regardless of the offset (verified: 4 different offsets from 0 to 300km all gave CV ~1e-12 to 1e-13). This may make G2 fail against its pre-registered expectation for a real, structural reason rather than a code defect -- reported honestly either way once the full run completes, not redesigned mid-flight to force the expected answer. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi --- probes/weather-p1/sunflower_pairing_probe.py | 200 +++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 probes/weather-p1/sunflower_pairing_probe.py diff --git a/probes/weather-p1/sunflower_pairing_probe.py b/probes/weather-p1/sunflower_pairing_probe.py new file mode 100644 index 00000000..6a6f6dab --- /dev/null +++ b/probes/weather-p1/sunflower_pairing_probe.py @@ -0,0 +1,200 @@ +"""W2s-a -- golden two-lattice pairing on REAL lat/lon geometry. + +Per weather-w-probes-v1.md SS2 (Sonnet, zero fetch). Tests whether the +collision-node construction's assumed property -- two sunflower lattices +pair generically, no ties, even pair distances -- survives the real cos-lat +metric, not just an idealized disk. The #921 lesson: disk properties do NOT +automatically transfer to a projected lat/lon geometry. + +INDEX FLOOR: N = F(17)^2 = 2,550,409 per lattice for the headline run (the +first draft's N=2048 was six orders of magnitude sub-floor per SS0's rule). + +TIE DEFINITION: per-source near-tie (d1/d2 ratio for each H-point's own two +nearest T-candidates), NOT a global duplicate-distance count -- the first +draft's global count is blind to the actual pairing-ambiguity question and, +at million-point sizes, prone to unrelated-pair float collisions regardless +of mechanism. See SS0's G1/G4 correction note. +""" +import json +import pathlib +from math import gcd + +import numpy as np +from scipy.spatial import cKDTree + +SEED = 20260812 +R_E = 6371.0 +F17_SQ = 1597 * 1597 # = 2_550_409, the headline N + + +def geom_ll(lat_c, lon_c, lat_h, lon_h): + """Project a lat/lon offset from a center to local km via the flat cos-lat + metric this arc uses throughout (dx scaled by cos(lat_c), dy not).""" + dlat = np.deg2rad(lat_h - lat_c) + dlon = np.deg2rad(lon_h - lon_c) + dx = R_E * np.cos(np.deg2rad(lat_c)) * dlon + dy = R_E * dlat + return dx, dy + + +def vogel_lattice(n, radius_km, center_lat, center_lon): + """N-point Vogel spiral (r = c*sqrt(k), golden angle) covering a disk of + the given radius (km), centered at (center_lat, center_lon). Returns + (x_km, y_km, lat, lon) arrays, all length n. c chosen so max radius + equals the requested radius_km.""" + phi = (1 + 5 ** 0.5) / 2 + golden_frac = 2 - phi + k = np.arange(n) + r = radius_km * np.sqrt((k + 0.5) / n) + theta = k * 2 * np.pi * golden_frac + x = r * np.cos(theta) + y = r * np.sin(theta) + lat = center_lat + np.rad2deg(y / R_E) + lon = center_lon + np.rad2deg(x / (R_E * np.cos(np.deg2rad(center_lat)))) + return x, y, lat, lon + + +def grid_lattice(n, radius_km, center_lat, center_lon): + """Axis-aligned square grid control of matching point density, clipped + to the same disk -- identical pairing procedure applies to it below.""" + side = int(np.ceil(np.sqrt(n * 4 / np.pi))) + g = (np.arange(side) + 0.5) / side * 2 * radius_km - radius_km + gx, gy = np.meshgrid(g, g) + gx, gy = gx.ravel(), gy.ravel() + rr = np.hypot(gx, gy) + mask = rr <= radius_km + x, y = gx[mask], gy[mask] + lat = center_lat + np.rad2deg(y / R_E) + lon = center_lon + np.rad2deg(x / (R_E * np.cos(np.deg2rad(center_lat)))) + return x, y, lat, lon + + +def project_to_center(x_src, y_src, lat_src_c, lon_src_c, lat_dst_c, lon_dst_c): + """Re-project a lattice's local (x,y) km coords, built around its own + center, into the OTHER center's local km frame -- needed because the + cos-lat metric is center-dependent (dx scale differs at each center).""" + lat = lat_src_c + np.rad2deg(y_src / R_E) + lon = lon_src_c + np.rad2deg(x_src / (R_E * np.cos(np.deg2rad(lat_src_c)))) + dx, dy = geom_ll(lat_dst_c, lon_dst_c, lat, lon) + return dx, dy + + +def near_tie_count(src_x, src_y, dst_x, dst_y, band_km): + """G1/G4: for each source point within band_km of the dest center, + find its 1st/2nd nearest dest-lattice neighbours and count near-ties + (d1/d2 > 1-1e-6). Returns (n_in_band, n_near_ties, cv_of_d1).""" + tree = cKDTree(np.column_stack([dst_x, dst_y])) + r = np.hypot(src_x, src_y) + in_band = r <= band_km + if in_band.sum() == 0: + return 0, 0, float("nan") + pts = np.column_stack([src_x[in_band], src_y[in_band]]) + d, _ = tree.query(pts, k=2) + d1, d2 = d[:, 0], d[:, 1] + ratio = np.where(d2 > 0, d1 / d2, 0.0) + near_ties = int((ratio > 1 - 1e-6).sum()) + cv = float(d1.std() / d1.mean()) if d1.mean() > 0 else float("nan") + return int(in_band.sum()), near_ties, cv + + +def chi2_midpoint_uniform(src_x, src_y, dst_x, dst_y, band_km, bins=10): + """G3 (descriptive): chi-square of pair-midpoint radial density against + uniform-in-area expectation across the corridor band.""" + tree = cKDTree(np.column_stack([dst_x, dst_y])) + r = np.hypot(src_x, src_y) + in_band = r <= band_km + if in_band.sum() < bins * 5: + return None + pts = np.column_stack([src_x[in_band], src_y[in_band]]) + d, idx = tree.query(pts, k=1) + mid_x = (pts[:, 0] + dst_x[idx]) / 2 + mid_y = (pts[:, 1] + dst_y[idx]) / 2 + mid_r = np.hypot(mid_x, mid_y) + edges = np.linspace(0, band_km, bins + 1) + obs, _ = np.histogram(mid_r, bins=edges) + area = np.pi * (edges[1:] ** 2 - edges[:-1] ** 2) + exp = obs.sum() * area / area.sum() + chi2 = float(((obs - exp) ** 2 / np.maximum(exp, 1e-9)).sum()) + return chi2 + + +def run_pair(n_h, n_t, band_km=900.0, radius_km=1500.0): + """One full G1/G2/G3 run at a given N, both golden and grid, both + directions (H->T and T->H symmetrized by just running H->T since the + construction is deliberately symmetric in this test).""" + lat_h, lon_h = 55.0, 340.0 + lat_t, lon_t = 55.0, 340.0 + np.rad2deg(1400.0 / (R_E * np.cos(np.deg2rad(55.0)))) + + gx_h, gy_h, _, _ = vogel_lattice(n_h, radius_km, lat_h, lon_h) + gx_t, gy_t, _, _ = vogel_lattice(n_t, radius_km, lat_t, lon_t) + qx_h, qy_h, _, _ = grid_lattice(n_h, radius_km, lat_h, lon_h) + qx_t, qy_t, _, _ = grid_lattice(n_t, radius_km, lat_t, lon_t) + + # project H's points into T's local frame for the H->T pairing + gdx, gdy = project_to_center(gx_h, gy_h, lat_h, lon_h, lat_t, lon_t) + qdx, qdy = project_to_center(qx_h, qy_h, lat_h, lon_h, lat_t, lon_t) + + n_band_g, ties_g, cv_g = near_tie_count(gdx, gdy, gx_t, gy_t, band_km) + n_band_q, ties_q, cv_q = near_tie_count(qdx, qdy, qx_t, qy_t, band_km) + chi2_g = chi2_midpoint_uniform(gdx, gdy, gx_t, gy_t, band_km) + chi2_q = chi2_midpoint_uniform(qdx, qdy, qx_t, qy_t, band_km) + + return { + "n_pairs_golden": n_band_g, "n_pairs_grid": n_band_q, + "ties_golden": ties_g, "ties_grid": ties_q, + "cv_golden": cv_g, "cv_grid": cv_q, + "chi2_golden": chi2_g, "chi2_grid": chi2_q, + } + + +def run(): + """Headline run at N=F(17)^2, then the G4 index-floor sweep at + N=F(n)^2 for n in {8,10,12,14,17,19}. Checkpoints each stage.""" + out_dir = pathlib.Path(__file__).parent + partial = out_dir / "sunflower_pairing_probe.partial.jsonl" + + with open(partial, "w") as pf: + headline = run_pair(F17_SQ, F17_SQ) + pf.write(json.dumps({"stage": "headline", "N": F17_SQ, **headline}) + "\n") + pf.flush() + + fibs = {8: 21, 10: 55, 12: 144, 14: 377, 17: 1597, 19: 4181} + sweep = [] + for n_idx, fn in fibs.items(): + N = fn * fn + r = run_pair(N, N) + row = {"n": n_idx, "N": N, "near_ties_golden": r["ties_golden"], + "near_ties_grid": r["ties_grid"], + "cv_golden": r["cv_golden"], "cv_grid": r["cv_grid"]} + sweep.append(row) + pf.write(json.dumps({"stage": "sweep", **row}) + "\n") + pf.flush() + + verdict_g1 = "VOID" if headline["ties_grid"] == 0 else ( + "PASS" if headline["ties_golden"] == 0 else "FAIL") + verdict_g2 = "PASS" if headline["cv_golden"] < headline["cv_grid"] else "FAIL" + verdict_g4 = "PASS" if all(r["near_ties_golden"] == 0 for r in sweep) else "FAIL" + + out = { + "N": F17_SQ, **headline, + "sweep": sweep, + "verdicts": {"G1": verdict_g1, "G2": verdict_g2, "G4": verdict_g4}, + } + with open(out_dir / "sunflower_pairing_probe.json", "w") as fh: + json.dump(out, fh, indent=2) + partial.unlink() # clean up after a successful completion, per repo convention + return out + + +if __name__ == "__main__": + r = run() + print(f"N={r['N']} pairs(golden)={r['n_pairs_golden']} pairs(grid)={r['n_pairs_grid']}") + print(f"ties: golden={r['ties_golden']} grid={r['ties_grid']}") + print(f"CV: golden={r['cv_golden']:.4f} grid={r['cv_grid']:.4f}") + print(f"chi2: golden={r['chi2_golden']} grid={r['chi2_grid']}") + print("verdicts:", r["verdicts"]) + print("\nG4 sweep:") + for row in r["sweep"]: + print(f" n={row['n']:2d} N={row['N']:9d} " + f"ties(g/q)={row['near_ties_golden']}/{row['near_ties_grid']} " + f"CV(g/q)={row['cv_golden']:.4f}/{row['cv_grid']:.4f}") From 8a225e1ccdc4fe7db2a2f33c07a0d8c0e02983d0 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 12 Aug 2026 12:25:49 +0000 Subject: [PATCH 5/8] probes/weather-p1: fix 2 codex findings on #935 (off-by-one, m* not actually permanent) Both real, both caught by codex on the RUN result of golden_vs_tempered_probe.py: 1. (P2) useful_range_lo used q//2 (floor) where the documented range is [ceil(q/2), q]. For every odd q this admits one prefix below the stated floor -- q=17: q//2=8 vs ceil(17/2)=9. Fixed with a single ceiling-division helper (useful_range_lo) that best_coprime_stride and t1_crossover both call, so the two computations can never drift apart again. 2. (P1, the substantive one) m* was a FIRST-crossing search, not a verified PERMANENT one, despite being reported and used throughout the plan as "the point golden permanently overtakes". Golden's raw discrepancy sequence is not monotonic -- only its O(log m/m) envelope is a bound -- so a single dip below the tempered ceiling can be followed by a rise back above it. Codex's exact example reproduced: q=17 reported m*=21, but D*(22)=0.08137 > the frozen ceiling 0.05882 -- not permanent at all. Replaced with verified_permanent_crossover: on finding a candidate crossing, verify it holds at a SAMPLED checkpoint set (every integer for the next 50 steps -- catches exactly this near-term-reversal failure mode -- plus ~15%-geometrically-spaced points out to the m=200q horizon, plus the horizon itself); on any checkpoint violation, restart the scan past it. The checkpoint COUNT is reported alongside every m* so the verification scope is never silently overclaimed as exhaustive. For q=17, the verified m* is 32, not 21 -- reran and confirmed before this commit. Both fixes land in the committed script before rerunning, per the standing discipline (bars/methodology committed, then run, never the other way). Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi --- probes/weather-p1/golden_vs_tempered_probe.py | 88 ++++++++++++++++--- 1 file changed, 77 insertions(+), 11 deletions(-) diff --git a/probes/weather-p1/golden_vs_tempered_probe.py b/probes/weather-p1/golden_vs_tempered_probe.py index 4cd929ba..376ee62e 100644 --- a/probes/weather-p1/golden_vs_tempered_probe.py +++ b/probes/weather-p1/golden_vs_tempered_probe.py @@ -15,7 +15,7 @@ import json import pathlib import statistics -from math import gcd, log2 +from math import gcd PHI = (1 + 5 ** 0.5) / 2 GOLDEN_FRAC = 2 - PHI # = 1/phi^2, the golden-angle fraction in turns @@ -44,6 +44,15 @@ def tempered_pts(m, s, q): return [((s * i) % q) / q for i in range(m)] +def useful_range_lo(q): + """The useful-prefix-range floor, ceil(q/2) -- NOT q//2. codex P2 on + PR #935: floor division shorts every odd q by one (q=17: q//2=8 admits + m=8, outside the documented [ceil(q/2),q]=[9,17]). Fixed here as the + single source both best_coprime_stride and t1_crossover call, so the + two can never drift apart again.""" + return -(-q // 2) # ceiling division, no float rounding + + def best_coprime_stride(q): """The coprime stride s in [1,q) minimizing the MEDIAN star discrepancy over the 'useful' prefix range m in [ceil(q/2), q] -- excludes the @@ -51,7 +60,7 @@ def best_coprime_stride(q): dominate a naive worst-case-over-all-m metric into near-uselessness. Returns (score, stride). """ - lo = max(2, q // 2) + lo = max(2, useful_range_lo(q)) best = None for s in range(1, q): if gcd(s, q) != 1: @@ -64,26 +73,81 @@ def best_coprime_stride(q): return best +def _sampled_checkpoints(m, q, horizon): + """Checkpoint set for verifying a candidate crossover stays below the + tempered ceiling: EVERY integer for the next 50 steps (catches + near-term reversals -- codex P1 on PR #935 found exactly this failure + mode: m*=21 reported as 'permanent' for q=17, but D*(22) rises back + above the frozen value), plus ~15%-geometrically-spaced points out to + the horizon, plus the horizon itself. NOT exhaustive between the sparse + far points -- the return value's length is reported alongside m* so the + verification scope is never silently overclaimed as total. + """ + near = list(range(m, min(m + 50, horizon) + 1)) + far = set() + if horizon > near[-1]: + x = near[-1] + while x < horizon: + x = min(horizon, int(x * 1.15) + 1) + far.add(x) + far.add(horizon) + return sorted(set(near) | far) + + +def verified_permanent_crossover(q, temp_frozen, horizon): + """First m >= q at which golden's discrepancy drops below temp_frozen + AND STAYS below it at every checkpoint in _sampled_checkpoints(m, q, + horizon) -- i.e. verified non-exceeding at a SAMPLED (not exhaustive) + set of points out to `horizon`. On any checkpoint violation, the scan + restarts just past the violating point. Returns (m_star, n_checkpoints) + or (None, 0) if no such m is found within a generous search budget. + + This replaces a FIRST-crossing search (the pre-#935-fix behaviour), + which is a materially different and weaker claim: golden's raw + discrepancy sequence is not monotonic (only its O(log m/m) ENVELOPE + is a bound), so a single dip below the tempered ceiling can be + followed by a rise back above it before the sequence settles for + good. A first-crossing m* can therefore report a point that is not + actually the point after which golden STAYS ahead -- exactly the + q=17, m=21-not-permanent defect codex caught. + """ + m = q + tries = 0 + budget = 40 * q + while m <= 20 * q and tries < budget: + tries += 1 + if star_discrepancy(golden_pts(m)) < temp_frozen: + checkpoints = _sampled_checkpoints(m, q, horizon) + bad = None + for cp in checkpoints: + if star_discrepancy(golden_pts(cp)) >= temp_frozen: + bad = cp + break + if bad is None: + return m, len(checkpoints) + m = bad + 1 + else: + m += 1 + return None, 0 + + def t1_crossover(q_list): """T1: for each q, find the best coprime stride (useful-range metric), the matching golden score in the same range, and m* -- the first prefix - length beyond q where golden's discrepancy permanently drops below the - tempered stride's frozen m=q value. + length beyond q at which golden's discrepancy VERIFIABLY stays below the + tempered stride's frozen m=q value through the m=200q horizon (sampled + checkpoints, not every integer -- see verified_permanent_crossover). """ rows = [] for q in q_list: temp_score, s = best_coprime_stride(q) - lo = max(2, q // 2) + lo = max(2, useful_range_lo(q)) gold_score = statistics.median( star_discrepancy(golden_pts(m)) for m in range(lo, q + 1) ) temp_frozen = star_discrepancy(tempered_pts(q, s, q)) - m_star = None - for m in range(q, 20 * q + 1): - if star_discrepancy(golden_pts(m)) < temp_frozen: - m_star = m - break m_big = 200 * q + m_star, n_checkpoints = verified_permanent_crossover(q, temp_frozen, m_big) ratio_big = star_discrepancy(golden_pts(m_big)) and ( temp_frozen / star_discrepancy(golden_pts(m_big)) ) @@ -92,6 +156,7 @@ def t1_crossover(q_list): "temp_score_useful_range": temp_score, "golden_score_useful_range": gold_score, "m_star": m_star, + "m_star_checkpoints_verified": n_checkpoints, "temp_over_golden_at_200q": ratio_big, }) return rows @@ -205,7 +270,8 @@ def run(): print(f" q={r['q']:5d} s={r['best_stride']:5d} " f"temp={r['temp_score_useful_range']:.4f} " f"gold={r['golden_score_useful_range']:.4f} " - f"m*={r['m_star']} temp/gold@200q={r['temp_over_golden_at_200q']:.1f}x") + f"m*={r['m_star']} (verified @ {r['m_star_checkpoints_verified']} checkpoints) " + f"temp/gold@200q={r['temp_over_golden_at_200q']:.1f}x") print("\n=== T2 asymptotic bar ===", "PASS" if result["T2"]["pass"] else "FAIL") print("\n=== T3 closure (q=140) ===") print(" temp fill (exact integer):", result["T3"]["headline_q140"]["temp_fill_exact_integer"]) From e64a4d4ecfda447e2fab5d5bc19f82aa03daec2b Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 12 Aug 2026 12:31:16 +0000 Subject: [PATCH 6/8] probes: W2s-a RUN (G1 VOID, G2/G4 FAIL -- control degenerate) + T1 rerun with verified-permanent m* + E-A-CONTROL-THAT-CANNOT-LOSE-IS-NO-CONTROL-1 High-signal epiphany first (operator directive): the falsifiability rule's can-it-fire doctrine applies to CONTROL ARMS with the same force as to guards -- a control that cannot lose by construction carries zero information when it wins. Two independent instances in one afternoon: 1. W2s-a's grid control: two IDENTICALLY constructed grids differing only by a pure translation are, by lattice symmetry, translation-invariant in their cross-nearest-neighbour distance -- CV ~1.6e-12 vs golden's 0.368, twelve orders of magnitude, invariant under 4 different center offsets. The control cannot lose ANY evenness comparison against ANY irregular construction. Diagnosed via a 0.1s smoke test at N=50k BEFORE the full 2.55M run; run as-specified anyway (deliberately -- the record shows the specified control failing, not a quiet redesign forcing the expected answer). G2/G4 verdicts: FAIL, with the diagnosis attached. G1: VOID via its own pre-registered escape hatch (grid also 0 ties). G4 honesty note: 3 near-ties in 3.15M golden points at n=19 (ABOVE the floor) -- the fixed 1e-6 relative tolerance admits ~1e-6-rate coincidences at large N; the pre-registered "stays exactly 0" was overclaimed for large N. 2. golden-vs-tempered T1's m* (codex P1 on #935): first-crossing search reported as "permanent" -- an implicit never-reverses control that was never checked. q=17: m*=21 claimed, D*(22)=0.081 back above the 0.059 ceiling. Fixed with verified_permanent_crossover (sampled suffix: every integer for 50 steps + geometric checkpoints to 200q, count REPORTED beside every m* so scope is stated not implied). Also fixed the codex P2 off-by-one (q//2 -> ceil(q/2), single shared helper). Verified m* moved from ~1.0-1.4x q to ~1.9-2.7x q -- the number's third revision, each widening: golden needs ~two tempered cycles before its lead is durable, and its eventual dominance (68-106x at 200q, T2, never exposed to either bug) is unchanged. Plan updates in place (both plans carry full correction trails, three-stage for m*); STATUS_BOARD rows D-W2sA + D-GVT-T1 updated; probe JSONs regenerated; partial checkpoints cleaned after successful completion. The synthesis survives all four corrections in direction and weakens in no cell; what moved is tightness -- and the honest crossover is now: tempered holds its ground for roughly TWO of its own cycles, not one. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi --- .claude/board/EPIPHANIES.md | 69 +++++++ .claude/board/STATUS_BOARD.md | 2 +- .claude/plans/golden-vs-tempered-stride-v1.md | 173 +++++++++++------- .claude/plans/weather-w-probes-v1.md | 61 ++++++ .../weather-p1/golden_vs_tempered_probe.json | 54 +++--- .../weather-p1/sunflower_pairing_probe.json | 66 +++++++ 6 files changed, 331 insertions(+), 94 deletions(-) create mode 100644 probes/weather-p1/sunflower_pairing_probe.json diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index 28c9addd..8f9cf605 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -1,3 +1,72 @@ +## 2026-08-12 — E-A-CONTROL-THAT-CANNOT-LOSE-IS-NO-CONTROL-1 + +**Status:** FINDING `[G]` — both instances measured this session, committed +scripts + JSONs (`sunflower_pairing_probe`, `golden_vs_tempered_probe`). + +**The claim.** The falsifiability rule's can-it-fire / can-it-stay-silent +doctrine applies to CONTROLS with exactly the same force as to guards: **a +control arm that cannot lose by construction carries zero information when +it wins** — and this session produced the shape twice in one afternoon, in +two independent probes, caught two different ways. + +**Instance 1 — the symmetric-grid control (W2s-a, caught by a smoke test +BEFORE the full run).** The pre-registered G2 bar expected golden's +nearest-pair-distance CV to beat an axis-aligned-grid control's. Measured: +grid CV ≈ **1.6e-12** vs golden's 0.368 — twelve orders of magnitude, at +every N in the sweep, invariant under four different center offsets (0 to +300 km, checked in a 50k-point smoke test before committing to the full +2.55M run). Mechanism, not mystery: the brief specified TWO IDENTICALLY +CONSTRUCTED grids differing only by a pure translation — and two identical +periodic tilings offset by a fixed vector are, by lattice symmetry, +**translation-invariant in their cross-nearest-neighbour distance**. Every +point sees the same local geometry; the CV is floating-point noise. **The +control cannot lose ANY evenness comparison against ANY irregular +construction — so G2's FAIL verdict is a fact about the control, not about +the golden lattice.** Reported as FAIL + diagnosis rather than silently +redesigning the control to force the pre-registered answer (the run was +committed as-specified after the smoke test flagged it, deliberately, so +the record shows the specified control failing rather than a quiet swap). + +**Instance 2 — the first-crossing m* (golden-vs-tempered T1, caught by +external review of the RUN's own result, codex P1 on #935).** The +crossover point "where golden permanently overtakes tempered" was computed +as a FIRST crossing — but golden's raw discrepancy sequence is +non-monotonic (only its `O(log m/m)` ENVELOPE is a bound), so a first dip +below the ceiling can reverse. Reproduced exactly: q=17 reported m*=21; +D*(22)=0.081 sits back ABOVE the frozen ceiling 0.059. The "permanent" +claim had no machinery that could catch a reversal — **an implicit +control (nothing rose back above) that was never actually checked**. +Fixed: `verified_permanent_crossover` checks a sampled suffix (every +integer for 50 steps + geometric points to the 200q horizon) and REPORTS +THE CHECKPOINT COUNT beside every m*, so the verification scope is stated +rather than implied exhaustive. The verified m* moved from ~1.0–1.4×q to +**~1.9–2.7×q** — the number's THIRD revision, each widening, each from a +methodological gap the previous pass could not see. + +**The rule, in one line each:** +- *A control must be able to lose.* Before pre-registering a control arm, + ask what result would make the control WIN unfairly — symmetry, + degeneracy, and construction-identity are the usual culprits (two + identical tilings, a rotated referent with the same marginals, a + permutation that preserves the tested statistic). +- *"Permanently" is a claim about a suffix, not a point.* Any + "stays/never/always thereafter" assertion over a non-monotonic sequence + needs suffix verification with a STATED scope (checkpoint count), never + a first-hit search. +- *Smoke-test the control's losability cheaply before paying for the full + run* — instance 1 cost 0.1 s at N=50k to diagnose what would otherwise + have surfaced only as a confusing FAIL after the 2.55M-point run. + +**Cross-refs.** The falsifiability rule (CLAUDE.md P0) — this extends its +guard-doctrine to control arms. `E-THE-CONTROL-SCORED-THE-HEADLINE-1` — the +complementary failure (a control that scored AS WELL as the signal, +exposing the instrument); today's is the inverse (a control that cannot +lose, exposing nothing). W5's B3 control history — three successive control +designs (12/18 wrong-scale, 1500/2600 wrong-scale-in-disguise, +distance-matched-neighbour) are the same lesson approached from the +wrong-scale side. Plan homes: `weather-w-probes-v1.md` §2 RUN note, +`golden-vs-tempered-stride-v1.md` T1 correction notes. + ## 2026-08-12 — E-THE-GOLDEN-STEP-IS-THE-WRONG-STEP-AT-SMALL-Q-1 **Status:** FINDING `[G]` — arithmetic, fully reproducible, no fetch. Operator diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index 91fffab6..d636a466 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -26,7 +26,7 @@ Wave 1 = parallel, no operator gate beyond go-ahead; gated rows named. | D-id | Deliverable | Wave | Status | Feeds | |---|---|---|---|---| | D-W5 | Spiral-ADI anisotropy: Vogel N=4096, iso ≤0.15 & aniso ≤1.25, non-Fibonacci stride control ≥1.5× | 1 | Queued | domino.rs gather design; [H] flags §10.5 | -| D-W2sA | Golden-vs-grid pairing on real cos-lat metric (zero-ties G1, CV G2) | 1 | Queued | facet-node addressing [G] extension | +| D-W2sA | Golden-vs-grid pairing on real cos-lat metric (zero-ties G1, CV G2) | 1 | **RUN — G1 VOID, G2/G4 FAIL (control degenerate: two identical translated grids are symmetry-uniform, CV ~1e-12 — cannot lose any evenness comparison; diagnosed via smoke test, run as-specified, `E-A-CONTROL-THAT-CANNOT-LOSE-IS-NO-CONTROL-1`)** | honest falsifier for evenness DEFERRED (offset/rotation-varied or spacing-mismatched control); §10.5 properties 1–3 untouched | | D-W6 | Two-component deconvolution (geo + bow, global lstsq, 38 eqs / 2 params; B3 = stranded stratification via v_rel) | 1 | Queued | dipole vector-sum identification; F17 gate | | D-W2sB | α-window sweep β∈[0.85,1.15] | gated (W2s-a) | Queued | corridor α discriminator | | D-W7 | Corridor two-regime α field probe | gated (W6) | Queued | §10.3 physics | diff --git a/.claude/plans/golden-vs-tempered-stride-v1.md b/.claude/plans/golden-vs-tempered-stride-v1.md index 512a9fbc..032d9fcd 100644 --- a/.claude/plans/golden-vs-tempered-stride-v1.md +++ b/.claude/plans/golden-vs-tempered-stride-v1.md @@ -6,10 +6,19 @@ > plan is the standalone, substrate-general validation of that rule, not > weather-specific. Zero fetch, pure arithmetic. > -> **⚠ RUNNING THE PROBE CAUGHT TWO REAL DEFECTS IN THE HAND-DERIVED NUMBERS -> BELOW — both fixed, both explained where they occurred (T1, T3).** Neither -> changes the qualitative finding; both change specific numbers. This is -> exactly what "commit the bars, then run" is for. +> **⚠ THE RUN CAUGHT DEFECTS TWICE — once from running it, once from +> external review of the run's own result.** First pass (running the +> script): two real defects in the hand-derived numbers (T1's m* +> methodology, T3's float round-trip false negative). Second pass (codex +> review on PR #935, of the FIRST corrected run): two more real defects, +> both in T1 (an off-by-one in the useful-range floor, and — the +> substantive one — "m*" was a first-crossing search, not a verified +> PERMANENT one, so the corrected-once table was still wrong in a way that +> mattered). All four are fixed and explained inline (T1, T3). None +> changes the qualitative finding; all four change specific numbers, twice +> over for `m*`. This is what "commit the bars, then run, then let review +> hit the run" is for — each pass caught something the previous one +> could not see from the outside. ## Why this file exists @@ -52,70 +61,90 @@ first prefix length beyond `q` at which golden's discrepancy drops below the tempered stride's (permanently, since tempered is frozen at its `m=q` value forever after). -**RUN, committed script, 2026-08-12** (`golden_vs_tempered_probe.json`): - -| q | best coprime s | temp score (median, useful range) | golden score (same range) | m* (golden overtakes) | m*/q | temp/golden @ m=200q | -|---|---|---|---|---|---|---| -| 12 | 5 | 0.1667 | 0.1721 | 16 | 1.33 | 89.5× | -| 17 | 14 | 0.1042 | 0.1169 | 21 | 1.24 | 70.4× | -| 34 | 25 | 0.0570 | 0.0654 | 42 | 1.24 | 89.4× | -| 55 | 34 | 0.0384 | 0.0373 | 55 | 1.00 | 106.4× | -| 64 | 41 | 0.0312 | 0.0337 | 90 | 1.41 | 83.3× | -| 89 | 35 | 0.0251 | 0.0275 | 110 | 1.24 | 84.2× | -| 144 | 85 | 0.0160 | 0.0158 | 144 | 1.00 | 103.6× | -| 233 | 149 | 0.0104 | 0.0116 | 288 | 1.24 | 68.2× | -| 377 | 239 | 0.0066 | 0.0066 | 377 | 1.00 | 96.4× | -| 987 | 722 | 0.0028 | 0.0027 | 987 | 1.00 | 90.0× | - -> **⚠ CORRECTION — `m*` was computed inconsistently with this plan's own -> prose in the pre-registered draft (all rows), caught by actually running -> the script.** The draft's `m*` search recomputed the TEMPERED sequence's -> star discrepancy at each growing `m > q` — but a tempered walk past `m=q` -> is REPEATING its own `q` positions, not sampling new ones, so that -> recomputation feeds duplicate points into a formula built for distinct -> order statistics, and the resulting "discrepancy" **spuriously worsens** -> instead of staying at its true, meaningful value. Worked example at -> `q=17`: at `m=18` (the point where the tempered stride's 18th sample lands -> exactly back on its own first position) the repeating-sequence -> recomputation jumps to 0.1111 — WORSE than tempered's actual frozen -> quality of 0.0588 — making golden's 0.0832 look like a win at `m=18` when -> it is still **worse** than tempered's real ceiling. The draft's `m*=18` -> for `q=17` was an artifact of this; the corrected script holds tempered at -> its true frozen `m=q` value (matching this plan's own stated definition: -> *"then repeats identically forever — a hard ceiling on refinement"*) and -> finds golden's genuine first crossing, `m*=21`. **Every `m*` in the -> corrected table is ≥ the draft's value** — under the correct definition, -> golden takes somewhat LONGER to overtake than the draft suggested, not -> shorter, so nothing here weakens the qualitative claim; it corrects the -> tightness of one specific number per row. - -**Reading, stated as the finding rather than left implicit:** -- **The head is right in the bounded regime.** At every tested `q`, the - best coprime tempered stride is **competitive with or better than** - golden **within its own budget** (`m ≤ q`) — and it achieves this with - **zero variance and a construction-guaranteed closure**, where golden's - quality at any finite `m` is a continuous function with no guaranteed - floor. -- **The gut is right in the unbounded regime.** `m*` — the point where - golden permanently overtakes — sits **within about 1.0–1.4× of `q`** in - every row tested (never more than half a cycle-length beyond `q`; exactly - at `q` for four of the ten rows — `55, 144, 377, 987`). Beyond that, - tempered is **frozen** at its `m=q` value forever (coprimality guarantees - full closure, not continued refinement), while golden keeps improving as - `O(log m / m)`. By `m = 200q` the gap is **68–106×** in golden's favor, at - every `q` tested. -- **Neither instinct is wrong; they are answers to different questions.** - "Is there ever going to be more data than this fixed budget?" — no ⇒ - tempered, exact closure, zero variance, done. "Is more data always - coming, indefinitely?" — yes ⇒ golden, no ceiling, strictly better past - `m ≈ q`. - -**Bar T1 — RUN, result above:** the extended list (including `q=377,987`) -confirms the qualitative crossover pattern: `m*` never exceeds ~1.41× `q` at -any tested `q`, and lands exactly at `q` whenever the useful-range-optimal -stride's own frozen discrepancy already beats golden's score throughout the -sweep window (the `m*/q = 1.00` rows). No `q` broke the pattern into a -qualitatively different regime. +**RUN, committed script, 2026-08-12 — THIRD revision of `m*`, this one +verified rather than assumed** (`golden_vs_tempered_probe.json`): + +| q | best coprime s | temp score (median, useful range) | golden score (same range) | m* (VERIFIED permanent) | m*/q | checkpoints verified | temp/golden @ m=200q | +|---|---|---|---|---|---|---|---| +| 12 | 5 | 0.1667 | 0.1721 | 24 | 2.00 | 76 | 89.5× | +| 17 | 14 | 0.0966 | 0.1087 | 32 | 1.88 | 78 | 70.4× | +| 34 | 25 | 0.0570 | 0.0654 | 68 | 2.00 | 80 | 89.4× | +| 55 | 34 | 0.0383 | 0.0367 | 115 | 2.09 | 81 | 106.4× | +| 64 | 41 | 0.0312 | 0.0337 | 170 | 2.66 | 81 | 83.3× | +| 89 | 35 | 0.0247 | 0.0275 | 212 | 2.38 | 82 | 84.2× | +| 144 | 85 | 0.0160 | 0.0158 | 320 | 2.22 | 83 | 103.6× | +| 233 | 149 | 0.0103 | 0.0116 | 589 | 2.53 | 82 | 68.2× | +| 377 | 239 | 0.0066 | 0.0066 | 929 | 2.46 | 83 | 96.4× | +| 987 | 722 | 0.0028 | 0.0027 | 2521 | 2.55 | 83 | 90.0× | + +> **⚠⚠ TWO CORRECTIONS TO `m*` NOW, NOT ONE — stated plainly rather than +> quietly folded in, because the number has moved twice and a reader +> deserves to see the trajectory.** The pattern each time: a real +> methodological gap the run itself exposed, each fix moving `m*` further +> from `q`, never closer. +> +> **First correction (already recorded here) — the draft recomputed the +> TEMPERED sequence past its own closure**, feeding repeated points into a +> distinct-order-statistic formula, spuriously worsening it and making +> golden look like it won earlier than it did. Fix: hold tempered frozen at +> its true `m=q` value. That produced the (now superseded) `m* ≈ 1.0–1.4×q` +> table. +> +> **Second correction (codex P1 on PR #935) — that "frozen-ceiling" `m*` +> was still only a FIRST crossing, not a verified PERMANENT one.** Golden's +> raw discrepancy sequence is not monotonic — only its `O(log m/m)` +> ENVELOPE is a bound — so a single dip below the tempered ceiling can be +> followed by a rise back above it before the sequence settles for good. +> Codex's exact, reproduced example: `q=17` reported `m*=21`, but +> `D*(22) = 0.08137`, ABOVE the frozen ceiling `0.05882` — not permanent at +> all. Fixed with `verified_permanent_crossover`: a candidate crossing is +> checked against a **sampled checkpoint set** (every integer for the next +> 50 steps — this is what catches near-term reversals exactly like the +> q=17 case — plus ~15 geometrically-spaced points out to the `m=200q` +> horizon, plus the horizon itself); any checkpoint violation restarts the +> scan past it. **The checkpoint count is reported alongside every `m*`** +> (76–83 points per row) so the verification scope is stated, not implied +> as exhaustive — points strictly between checkpoints are not individually +> checked, though the sampling density (every integer for 50 steps right +> after the candidate, where reversals are most likely, per the q=17 +> example) is chosen to make an undetected reversal unlikely. +> +> Also note: the useful-range floor bug (codex P2, `q//2` vs `⌈q/2⌉`) is +> folded into this table too — it shifted `temp/gold score` slightly for +> odd `q` (17, 55, 89, 233), visible above; it did not change any stride +> choice or any pass/fail verdict. + +**Reading, corrected a second time:** +- **The head is right in the bounded regime — this claim is unaffected by + either correction.** At every tested `q`, the best coprime tempered + stride is **competitive with or better than** golden **within its own + budget** (`m ≤ q`) — zero variance, construction-guaranteed closure, + where golden's quality at any finite `m` has no guaranteed floor. +- **The gut is right in the unbounded regime, and its margin is LARGER than + first stated.** The verified-permanent `m*` sits at **roughly 1.9–2.7× `q`** + — golden needs about two tempered cycles' worth of samples, not one, before + it can be trusted never to dip back above the frozen ceiling. This is a + WEAKER claim for tempered's near-term competitiveness than the + once-corrected table suggested (`1.0–1.4×`), and a stronger one for + golden's eventual, durable dominance. Beyond `m*`, tempered is frozen at + its `m=q` value forever, while golden keeps improving as `O(log m/m)`; by + `m=200q` the gap is **68–106×** in golden's favor at every `q` tested, + UNCHANGED by either correction (T2 was always computed at the single + fixed point `m=200q`, never via a crossing search, so it was never + exposed to either bug). +- **Neither instinct is wrong; they are answers to different questions, + and the honest margins are now wider apart than first drafted, not + narrower.** "Is there ever going to be more data than this fixed + budget?" — no ⇒ tempered, exact closure, zero variance, done. "Is more + data always coming, indefinitely?" — yes ⇒ golden, no ceiling, verified + durably better past roughly `2× q`. + +**Bar T1 — RUN, corrected result above:** the extended list (including +`q=377,987`) confirms the qualitative crossover pattern holds at every +tested `q` — golden's advantage is DURABLE once past its verified `m*`, not +merely a lucky first dip. No `q` broke the pattern into a qualitatively +different regime; what changed across both corrections is the TIGHTNESS of +the crossover estimate, not its existence or direction. **⚠ CAVEAT, stated up front rather than discovered late (an earlier worst-case-over-all-`m` metric picked DIFFERENT "best" strides for q=17 — @@ -229,9 +258,11 @@ already does correctly (stride 4, `gcd(4,17)=1`). | **unbounded, growing budget** (`m ≫ q`, e.g. a continuum lattice sampled indefinitely, real phyllotaxis with thousands of florets) | **gut** | golden angle | no ceiling — `O(log m/m)` refinement forever, 68–106× ahead of any frozen tempered walk by `m=200q` (T1, T2) | This is not a tie-breaker between the two intuitions — it is the discovery -that **each is the correct mechanism for its own regime**, and the -crossover sits almost exactly at `m ≈ q` in every case tested. Filed as the -final validation of the two-regime table already committed in +that **each is the correct mechanism for its own regime**, and the VERIFIED +crossover sits at roughly **1.9–2.7× `q`** in every case tested (the +number moved twice under review, both times widening — see T1's two +correction notes above). Filed as the final validation of the two-regime +table already committed in `COMET_TAIL_REPORT.md` §10.5 and `EPIPHANIES.md` `E-THE-GOLDEN-STEP-IS-THE-WRONG-STEP-AT-SMALL-Q-1` — this plan supplies the head-to-head arithmetic that entry asserted but did not yet run as a diff --git a/.claude/plans/weather-w-probes-v1.md b/.claude/plans/weather-w-probes-v1.md index 845d9d0f..f3662c13 100644 --- a/.claude/plans/weather-w-probes-v1.md +++ b/.claude/plans/weather-w-probes-v1.md @@ -344,6 +344,67 @@ chi2_golden, chi2_grid, sweep: [{n, N, ties, cv}], verdicts: {G1, G2, G4}}`. --- +### RUN, 2026-08-12 (`sunflower_pairing_probe.py` / `.json`) — G1 VOID, G2 and G4 FAIL, and the FAIL is real and diagnosed, not a bug + +| bar | verdict | measured | +|---|---|---| +| G1 (ties) | **VOID** (as the pre-registered escape hatch anticipated) | golden 0 ties, grid 0 ties too — no discriminating power on this geometry | +| G2 (evenness) | **FAIL** | golden CV ≈ **0.368**; grid CV ≈ **1.6e-12** — grid is "more even" by twelve orders of magnitude | +| G4 (floor sweep) | **FAIL** | same CV gap holds at every `n∈{8,10,12,14,17,19}` — never closes, never reverses | + +**This is a real result, not a defect in the golden lattice — the CONTROL is +degenerate, and running the probe is what exposed it.** Diagnosed BEFORE the +full run, via a cheap smoke test (N=50k, four different center-offsets from +0 to 300 km): grid CV stayed at **1e-12 to 1e-13 regardless of the offset**. +Mechanism: this brief's two grid controls are constructed **identically** +(same `n`, same `radius_km`) and differ only by a **pure translation** +between centers. Two lattices with **identical spacing**, offset by a fixed +translation vector, are — by ordinary lattice symmetry — **translation- +invariant in their cross-nearest-neighbour distance**: every point of one +grid sees the exact same local neighbour geometry in the other, so the +nearest-pair distance is *the same number* for every point, and its +coefficient of variation is bounded only by floating-point noise. **This has +nothing to do with the merit of golden pairing — it is a property of +comparing two IDENTICAL periodic tilings against each other**, and it would +hold regardless of what irregular construction was tested against it. The +control this brief specified ("TWO axis-aligned square grids of identical +point density") is exactly what was built and exactly what the brief asked +for — the finding is that **this specific control is not discriminating for +G2's evenness question**, which is itself worth knowing rather than +silently redesigning the control to force the pre-registered answer. + +**A second, smaller honesty note on G4.** The pre-registered expectation +said near-ties "stay 0 at every n" — measured, `n=19` (N=17 480 761, ABOVE +the floor, not below it) shows **3 near-ties among golden's ~3.15M in-band +points**, a rate of ≈1e-6. This is consistent with the near-tie definition +itself (`d1/d2 > 1−1e-6`, a FIXED relative tolerance): as N grows into the +millions, even a genuinely generic, irrational-angle point process will +admit a small number of near-coincidences purely from sampling density at a +fixed tolerance — this is a different claim from "the angle stopped being +generic." The pre-registered "stays exactly 0" was slightly overclaimed for +large N under a fixed near-tie tolerance; it should have anticipated this +scaling. Not treated as a floor-crossing failure (n=19 is above the floor, +not below it, and 3-in-3.15M is not the pattern a real mechanism failure +would produce). + +**Consequence for `golden-vs-tempered-stride-v1`'s "controlled chaos" +claim.** G1's VOID means the "no ties" half of that claim is **not +contradicted** here (golden: 0 ties; this grid: also 0, for an unrelated +reason). G2/G4's FAIL means **the specific CV-based "evenness" framing is +not the right test of golden's virtue against a translated regular grid** — +a translated-regular-grid control trivially wins any evenness metric by +symmetry, which is a fact about the control, not a refutation of the +aperiodic/incommensurate/deterministic-address properties (§10.5 properties +1–3) the collision-node architecture actually relies on. Those properties — +prefix-extensibility, zero-geometry-storage, deterministic overlay from two +center coordinates — are untouched by this result. What is now flagged as +needing a better falsifier: a control that tests EVENNESS honestly would +need to vary the relative offset/rotation between the two grids (not just +translate at a fixed relative angle) or compare against a grid genuinely +mismatched in spacing — deferred, not attempted here. + +--- + ## §3 BRIEF W6 — the dipole deconvolution: neighbor + bow, global fit (Sonnet, ~40 chunks) **File:** `comet_tail_w6.py`. **Seed:** 20260812. diff --git a/probes/weather-p1/golden_vs_tempered_probe.json b/probes/weather-p1/golden_vs_tempered_probe.json index eedc66c2..8c2396f8 100644 --- a/probes/weather-p1/golden_vs_tempered_probe.json +++ b/probes/weather-p1/golden_vs_tempered_probe.json @@ -5,15 +5,17 @@ "best_stride": 5, "temp_score_useful_range": 0.16666666666666663, "golden_score_useful_range": 0.17213595499957957, - "m_star": 16, + "m_star": 24, + "m_star_checkpoints_verified": 76, "temp_over_golden_at_200q": 89.53794482112389 }, { "q": 17, "best_stride": 14, - "temp_score_useful_range": 0.1042016806722689, - "golden_score_useful_range": 0.11685777865321556, - "m_star": 21, + "temp_score_useful_range": 0.09663865546218486, + "golden_score_useful_range": 0.10871555730643112, + "m_star": 32, + "m_star_checkpoints_verified": 78, "temp_over_golden_at_200q": 70.35571368151886 }, { @@ -21,15 +23,17 @@ "best_stride": 25, "temp_score_useful_range": 0.057009803921568625, "golden_score_useful_range": 0.06538867268498469, - "m_star": 42, + "m_star": 68, + "m_star_checkpoints_verified": 80, "temp_over_golden_at_200q": 89.43980989976654 }, { "q": 55, "best_stride": 34, - "temp_score_useful_range": 0.03838383838383841, - "golden_score_useful_range": 0.037271388032666455, - "m_star": 55, + "temp_score_useful_range": 0.03825350276963181, + "golden_score_useful_range": 0.036743480219287784, + "m_star": 115, + "m_star_checkpoints_verified": 81, "temp_over_golden_at_200q": 106.35961099655272 }, { @@ -37,15 +41,17 @@ "best_stride": 41, "temp_score_useful_range": 0.03125, "golden_score_useful_range": 0.03366799389590275, - "m_star": 90, + "m_star": 170, + "m_star_checkpoints_verified": 81, "temp_over_golden_at_200q": 83.30256049149101 }, { "q": 89, "best_stride": 35, - "temp_score_useful_range": 0.02505140633032238, - "golden_score_useful_range": 0.027539906627747447, - "m_star": 110, + "temp_score_useful_range": 0.024656679151061178, + "golden_score_useful_range": 0.027474569923002345, + "m_star": 212, + "m_star_checkpoints_verified": 82, "temp_over_golden_at_200q": 84.2201278187017 }, { @@ -53,31 +59,35 @@ "best_stride": 85, "temp_score_useful_range": 0.016008771929824583, "golden_score_useful_range": 0.01578730894249568, - "m_star": 144, + "m_star": 320, + "m_star_checkpoints_verified": 83, "temp_over_golden_at_200q": 103.57381186938156 }, { "q": 233, "best_stride": 149, - "temp_score_useful_range": 0.010369816859613802, - "golden_score_useful_range": 0.011612009931471826, - "m_star": 288, + "temp_score_useful_range": 0.010335982934200169, + "golden_score_useful_range": 0.011608500589898214, + "m_star": 589, + "m_star_checkpoints_verified": 82, "temp_over_golden_at_200q": 68.19636649519545 }, { "q": 377, "best_stride": 239, - "temp_score_useful_range": 0.006631061693812973, - "golden_score_useful_range": 0.00658030019036672, - "m_star": 377, + "temp_score_useful_range": 0.006626613303840012, + "golden_score_useful_range": 0.006579742099070085, + "m_star": 929, + "m_star_checkpoints_verified": 83, "temp_over_golden_at_200q": 96.43803268023423 }, { "q": 987, "best_stride": 722, - "temp_score_useful_range": 0.002834401568212852, - "golden_score_useful_range": 0.0027486658306924983, - "m_star": 987, + "temp_score_useful_range": 0.0028331991239704413, + "golden_score_useful_range": 0.0027479422492533156, + "m_star": 2521, + "m_star_checkpoints_verified": 83, "temp_over_golden_at_200q": 90.01758793198474 } ], diff --git a/probes/weather-p1/sunflower_pairing_probe.json b/probes/weather-p1/sunflower_pairing_probe.json new file mode 100644 index 00000000..dd01021f --- /dev/null +++ b/probes/weather-p1/sunflower_pairing_probe.json @@ -0,0 +1,66 @@ +{ + "N": 2550409, + "n_pairs_golden": 462879, + "n_pairs_grid": 463377, + "ties_golden": 0, + "ties_grid": 0, + "cv_golden": 0.36774607879963067, + "cv_grid": 1.59767288438734e-12, + "chi2_golden": 12788.627749641304, + "chi2_grid": 12897.873968062777, + "sweep": [ + { + "n": 8, + "N": 441, + "near_ties_golden": 0, + "near_ties_grid": 0, + "cv_golden": 0.420048491932172, + "cv_grid": 3.894718389667698e-14 + }, + { + "n": 10, + "N": 3025, + "near_ties_golden": 0, + "near_ties_grid": 0, + "cv_golden": 0.3662413746879478, + "cv_grid": 5.3973259121356446e-14 + }, + { + "n": 12, + "N": 20736, + "near_ties_golden": 0, + "near_ties_grid": 0, + "cv_golden": 0.3758869623955925, + "cv_grid": 8.634972831697149e-13 + }, + { + "n": 14, + "N": 142129, + "near_ties_golden": 0, + "near_ties_grid": 0, + "cv_golden": 0.3671218221439048, + "cv_grid": 7.432943019143699e-13 + }, + { + "n": 17, + "N": 2550409, + "near_ties_golden": 0, + "near_ties_grid": 0, + "cv_golden": 0.36774607879963067, + "cv_grid": 1.59767288438734e-12 + }, + { + "n": 19, + "N": 17480761, + "near_ties_golden": 3, + "near_ties_grid": 0, + "cv_golden": 0.36911926142552787, + "cv_grid": 6.2865117293316446e-12 + } + ], + "verdicts": { + "G1": "VOID", + "G2": "FAIL", + "G4": "FAIL" + } +} \ No newline at end of file From 56ca90832d80e3a70835da2fa7d6703d66fbb3c4 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 12 Aug 2026 12:32:29 +0000 Subject: [PATCH 7/8] board: D-GVT-T1 row was still on the superseded 1.0-1.4x figure The previous commit's message claimed "STATUS_BOARD rows D-W2sA + D-GVT-T1 updated" -- only D-W2sA had actually been edited; D-GVT-T1 still carried the first-crossing 1.0-1.4x range that codex P1 falsified. Caught by verifying the commit message's own claims against the tree immediately after pushing (the #930-arc lesson: prose and patch are written in the same breath and only the patch is checked by anything). Row now carries the verified-permanent 1.9-2.7x figure with both codex fixes named. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi --- .claude/board/STATUS_BOARD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index d636a466..3a435d6f 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -11,7 +11,7 @@ qualitative synthesis, both tightened specific numbers. | D-id | Deliverable | Status | Result | |---|---|---|---| -| D-GVT-T1 | Crossover sweep across 10 q, useful-range metric | **RUN** | m* within 1.0–1.4× q at every q (never exactly ≈q as first drafted — corrected) | +| D-GVT-T1 | Crossover sweep across 10 q, useful-range metric | **RUN (twice-corrected)** | VERIFIED-permanent m* at **1.9–2.7× q** (codex P1 on #935: the earlier 1.0–1.4× figure was a FIRST crossing, not permanent — golden's non-monotonic sequence dips back above the ceiling; now suffix-verified at 76–83 reported checkpoints per row). Also codex P2: useful-range floor `q//2`→`⌈q/2⌉`. | | D-GVT-T2 | Asymptotic golden-advantage pass/fail bar | **RUN — PASS** | golden ahead 68.2–106.4× at m=200q, all 10 q | | D-GVT-T3 | Closure-occupancy guarantee (tempered) vs variable (golden) | **RUN — PASS** | tempered 140/140 exact (integer-verified); golden 124–127/140 across 5 phases | | D-GVT-T4 | Naive-rounding collapse hazard rate | **RUN — PASS** | 114/292 = 39.0 % of q∈[8,300) collapse under naive rounding | From 2c3d4483abc54040398449dc65a4bb49a55a7076 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 12 Aug 2026 12:35:59 +0000 Subject: [PATCH 8/8] probes/weather-p1: spiral_adi_probe.py -- W5 pre-registered BEFORE the run Full post-#933 brief: N=3*F(17)^2=7,651,227 headline, bump at r0=0.75 (local index ~2077 > floor 1597), KD-tree stride discovery with no capped window, band qualification by the INNER-radius >= r_floor rule (bands 4-8; the brief's "bands 3-8" label is off-by-one against its own parenthetical r>=0.6124 -- the rule wins, discrepancy reported not silently adopted), distance-matched shuffled-neighbour B3 control (same 0.25/0.5/0.25 stencil both arms; control prev = reverse map where uniquely defined, else hold -- documented implementation choice), B4 sweep n in {8,10,12,14,17,19} at 3*F(n)^2 each, n=21 recorded NOT RUN per budget. Smoke-tested at n_idx=8 (N=1323, machinery-only, sub-floor by design): discovery correctly returns the F(8)/F(9)=21/34 pair, both arms run, JSON shape complete. The smoke run's poor isotropy at that N is exactly the sub-floor behaviour B4 exists to expose and is not a code defect. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01CcpLeEC3XK8Eye53GKBVvi --- probes/weather-p1/spiral_adi_probe.py | 331 ++++++++++++++++++++++++++ 1 file changed, 331 insertions(+) create mode 100644 probes/weather-p1/spiral_adi_probe.py diff --git a/probes/weather-p1/spiral_adi_probe.py b/probes/weather-p1/spiral_adi_probe.py new file mode 100644 index 00000000..038eedbe --- /dev/null +++ b/probes/weather-p1/spiral_adi_probe.py @@ -0,0 +1,331 @@ +"""W5 -- spiral-ADI: two Fibonacci-stride tridiagonal sweeps ~ one 2D diffusion? + +Per weather-w-probes-v1.md SS1 (post-#933 corrected brief). Vogel lattice at +N = 3*F(17)^2 = 7,651,227 (index-floor rule with 3x margin), Gaussian bump at +r0 = 0.75 (local parastichy index sqrt(r0^2*N) ~ 2077, comfortably above the +floor F(17) = 1597), ADI smoothing along the two emergent parastichy stride +families vs an isotropic heat-kernel reference, plus a distance-matched +shuffled-neighbour control (B3) and the index-floor sweep (B4). + +BAND QUALIFICATION RULE (implemented exactly; one label in the brief is +off-by-one against its own parenthetical and the rule wins): a band +qualifies for B2 judgment iff its INNER radius >= r_floor = F(17)/sqrt(N). +With N = 3*F(17)^2, r_floor = 1/sqrt(3) ~ 0.5774. Under the 8-equal-area +annulus scheme (band i spans [sqrt((i-1)/8), sqrt(i/8)]), band 4's inner +radius sqrt(3/8) ~ 0.6124 >= r_floor, band 3's inner radius 0.5 < r_floor +-- so the qualifying set is bands 4-8 (matching the brief's parenthetical +"r >= 0.6124"), and the brief's "bands 3-8" label is reported as the +off-by-one it is rather than silently adopted. + +B3 CONTROL IMPLEMENTATION NOTE (documented choice, same operator form both +arms): the Fibonacci arm sweeps chains k -> k+j (prev = k-j, next = k+j, +hold at open ends). The control arm replaces each point's next-partner with +its distance-matched non-Fibonacci neighbour (among the 8 real nearest, +closest in physical distance to the true Fibonacci partner, excluding that +partner); prev is the reverse map where uniquely defined, else hold. Both +arms use the identical 0.25/0.5/0.25 stencil, so the comparison isolates +the LINK STRUCTURE (arithmetic coherence vs local distance-matched +shuffle), which is what B3 exists to test. +""" +import json +import pathlib + +import numpy as np +from scipy.spatial import cKDTree + +SEED = 20260812 +PHI = (1 + 5 ** 0.5) / 2 +GOLDEN_FRAC = 2 - PHI +F = {8: 21, 10: 55, 12: 144, 14: 377, 17: 1597, 19: 4181} +SIGMA = 0.08 +N_ADI_ITERS = 8 +N_BANDS = 8 + + +def vogel(n): + """Unit-disk Vogel lattice: returns (x, y, r) arrays of length n.""" + k = np.arange(n) + r = np.sqrt((k + 0.5) / n) + th = k * 2 * np.pi * GOLDEN_FRAC + return r * np.cos(th), r * np.sin(th), r + + +def band_of(r): + """Equal-area band index 1..8 for each radius (band i spans + [sqrt((i-1)/8), sqrt(i/8)]).""" + b = np.floor(r * r * N_BANDS).astype(int) + 1 + return np.clip(b, 1, N_BANDS) + + +def discover_strides(x, y, bands, sample_per_band=4000, rng=None): + """Step 1: per band, the dominant nearest-neighbour index-difference + pair, found GEOMETRICALLY with no capped search window (KD-tree over the + band's own points, 8 nearest neighbours each, histogram |dk|). Sampled + per band for tractability at N in the millions -- the stride pair is a + bulk property; 4000 points per band estimate the histogram mode with + huge margin. Returns {band: (j1, j2, histogram_top5)}.""" + out = {} + for b in range(1, N_BANDS + 1): + idx = np.where(bands == b)[0] + if len(idx) < 100: + out[b] = None + continue + tree = cKDTree(np.column_stack([x[idx], y[idx]])) + take = idx if len(idx) <= sample_per_band else rng.choice( + idx, sample_per_band, replace=False) + sub = np.searchsorted(idx, take) + _, nn = tree.query(np.column_stack([x[take], y[take]]), k=9) + dk = np.abs(idx[nn[:, 1:]] - take[:, None]).ravel() + vals, counts = np.unique(dk, return_counts=True) + order = np.argsort(-counts) + top = [(int(vals[i]), int(counts[i])) for i in order[:5]] + j1, j2 = top[0][0], next(v for v, _ in top[1:] if v != top[0][0]) + out[b] = {"pair": sorted([j1, j2]), "top5": top} + return out + + +def crossing_angles(x, y, bands, strides, n, rng, sample=2000): + """Step 2: per band, the distribution of the angle between the two + stride directions at sampled points (median + IQR, degrees).""" + out = {} + for b, info in strides.items(): + if info is None: + out[b] = None + continue + j1, j2 = info["pair"] + idx = np.where(bands == b)[0] + idx = idx[(idx + max(j1, j2) < n)] + if len(idx) > sample: + idx = rng.choice(idx, sample, replace=False) + v1 = np.column_stack([x[idx + j1] - x[idx], y[idx + j1] - y[idx]]) + v2 = np.column_stack([x[idx + j2] - x[idx], y[idx + j2] - y[idx]]) + cosang = (v1 * v2).sum(1) / ( + np.linalg.norm(v1, axis=1) * np.linalg.norm(v2, axis=1) + 1e-300) + ang = np.rad2deg(np.arccos(np.clip(np.abs(cosang), 0, 1))) + out[b] = {"median_deg": float(np.median(ang)), + "iqr_deg": [float(np.percentile(ang, 25)), + float(np.percentile(ang, 75))]} + return out + + +def sweep_field(y_field, next_idx, prev_idx): + """One tridiagonal smoothing sweep along a link family: + y_i <- 0.25*y[prev] + 0.5*y[i] + 0.25*y[next], holds (self) where a + link is missing (encoded as the point's own index).""" + return 0.25 * y_field[prev_idx] + 0.5 * y_field + 0.25 * y_field[next_idx] + + +def build_fib_links(n, bands, strides, qualifying): + """Fibonacci-arm links: for each qualifying point, next = k+j (same + band, else hold), prev = k-j (same band, else hold), per family. + Returns (nextA, prevA, nextB, prevB) index arrays of length n.""" + idx = np.arange(n) + links = [] + for fam in (0, 1): + nxt = idx.copy() + prv = idx.copy() + for b in qualifying: + info = strides[b] + if info is None: + continue + j = info["pair"][fam] + sel = np.where(bands == b)[0] + tgt = sel + j + ok = (tgt < n) + ok[ok] &= (bands[tgt[ok]] == b) + nxt[sel[ok]] = tgt[ok] + src = sel - j + ok2 = (src >= 0) + ok2[ok2] &= (bands[src[ok2]] == b) + prv[sel[ok2]] = src[ok2] + links.extend([nxt, prv]) + return links + + +def build_control_links(x, y, n, bands, strides, qualifying, rng, + max_pts_per_band=250_000): + """B3 control links: for each point (subsampled per band if huge), the + control partner is the physically-distance-matched real neighbour + (among 8 nearest, closest in distance to the true Fibonacci partner's + distance, excluding that partner). prev = reverse map where uniquely + defined, else hold.""" + idx_all = np.arange(n) + links = [] + for fam in (0, 1): + nxt = idx_all.copy() + for b in qualifying: + info = strides[b] + if info is None: + continue + j = info["pair"][fam] + sel = np.where(bands == b)[0] + if len(sel) > max_pts_per_band: + sel = np.sort(rng.choice(sel, max_pts_per_band, replace=False)) + tree = cKDTree(np.column_stack([x[sel], y[sel]])) + d_nn, nn = tree.query(np.column_stack([x[sel], y[sel]]), k=9) + # true fib partner distance (where it exists in-band) + tgt = sel + j + ok = (tgt < n) + ok[ok] &= (bands[tgt[ok]] == b) + fibd = np.full(len(sel), np.nan) + fibd[ok] = np.hypot(x[tgt[ok]] - x[sel[ok]], y[tgt[ok]] - y[sel[ok]]) + # choose among neighbours 1..8 the one closest in distance to fibd, + # excluding the true partner itself + cand_global = sel[nn[:, 1:]] + is_partner = cand_global == np.where(ok, tgt, -1)[:, None] + dist_diff = np.abs(d_nn[:, 1:] - fibd[:, None]) + dist_diff[is_partner] = np.inf + dist_diff[np.isnan(dist_diff)] = np.inf + pick = np.argmin(dist_diff, axis=1) + good = np.isfinite(dist_diff[np.arange(len(sel)), pick]) + nxt[sel[good]] = cand_global[np.arange(len(sel)), pick][good] + # reverse map: unique preimage -> prev, else hold + prv = idx_all.copy() + src_pts = np.where(nxt != idx_all)[0] + order = np.argsort(nxt[src_pts]) + tgts_sorted = nxt[src_pts][order] + uniq, first, counts = np.unique(tgts_sorted, return_index=True, + return_counts=True) + unique_tgts = uniq[counts == 1] + unique_srcs = src_pts[order][first[counts == 1]] + prv[unique_tgts] = unique_srcs + links.extend([nxt, prv]) + return links + + +def run_adi(field, links, iters=N_ADI_ITERS): + """iters ADI iterations: family-A sweep then family-B sweep each.""" + nxtA, prvA, nxtB, prvB = links + y = field.copy() + for _ in range(iters): + y = sweep_field(y, nxtA, prvA) + y = sweep_field(y, nxtB, prvB) + return y + + +def analyze_bump(x, y, blurred, x0, y0, sel): + """Second-moment tensor of the blurred bump over the qualifying region + -> anisotropy lambda_max/lambda_min; plus a least-squares isotropic + Gaussian fit (over sigma_ref and amplitude) -> relative L2 error.""" + w = np.clip(blurred[sel], 0, None) + if w.sum() <= 0: + return None + xs, ys = x[sel], y[sel] + mx = (w * xs).sum() / w.sum() + my = (w * ys).sum() / w.sum() + cxx = (w * (xs - mx) ** 2).sum() / w.sum() + cyy = (w * (ys - my) ** 2).sum() / w.sum() + cxy = (w * (xs - mx) * (ys - my)).sum() / w.sum() + ev = np.linalg.eigvalsh(np.array([[cxx, cxy], [cxy, cyy]])) + aniso = float(ev[1] / max(ev[0], 1e-300)) + # isotropic reference: fit sigma_ref (grid search then refine) + amplitude + d2 = (xs - x0) ** 2 + (ys - y0) ** 2 + best = None + for s_ref in np.linspace(SIGMA, SIGMA * 3, 60): + g = np.exp(-d2 / (2 * s_ref ** 2)) + a = (g * w).sum() / (g * g).sum() + err = np.sqrt(((w - a * g) ** 2).sum() / (w ** 2).sum()) + if best is None or err < best[0]: + best = (float(err), float(s_ref), float(a)) + return {"aniso": aniso, "iso_rel_l2": best[0], + "sigma_ref": best[1], "amplitude": best[2]} + + +def run_one_n(n_idx, rng, partial_fh): + """The full pipeline at N = 3*F(n_idx)^2: build, discover, sweep both + arms, analyze. Returns the result row; checkpoints to partial_fh.""" + fn = F[n_idx] + n = 3 * fn * fn + x, y, r = vogel(n) + bands = band_of(r) + r_floor = fn / np.sqrt(n) + qualifying = [b for b in range(1, N_BANDS + 1) + if np.sqrt((b - 1) / N_BANDS) >= r_floor] + strides = discover_strides(x, y, bands, rng=rng) + angles = crossing_angles(x, y, bands, strides, n, rng) + # bump at r0 = 0.75 along +x (any azimuth is equivalent by construction) + x0, y0 = 0.75, 0.0 + field = np.exp(-((x - x0) ** 2 + (y - y0) ** 2) / (2 * SIGMA ** 2)) + sel = np.isin(bands, qualifying) + + fib_links = build_fib_links(n, bands, strides, qualifying) + fib_blur = run_adi(field, fib_links) + fib_res = analyze_bump(x, y, fib_blur, x0, y0, sel) + + ctl_links = build_control_links(x, y, n, bands, strides, qualifying, rng) + ctl_blur = run_adi(field, ctl_links) + ctl_res = analyze_bump(x, y, ctl_blur, x0, y0, sel) + + row = { + "n_idx": n_idx, "N": int(n), "r_floor": float(r_floor), + "qualifying_bands": qualifying, + "strides": {str(b): (v["pair"] if v else None) + for b, v in strides.items()}, + "stride_top5_band5": strides.get(5, {}).get("top5") if strides.get(5) else None, + "crossing_angles": {str(b): v for b, v in angles.items()}, + "fib": fib_res, "control": ctl_res, + "aniso_ratio_control_over_fib": ( + float(ctl_res["aniso"] / fib_res["aniso"]) + if fib_res and ctl_res else None), + } + partial_fh.write(json.dumps({"stage": f"n{n_idx}", **row}) + "\n") + partial_fh.flush() + return row + + +def run(): + """Headline at n=17 (N=3*F(17)^2) first, then the B4 sweep ascending + n in {8,10,12,14,19} (17 reused from the headline). n=21 NOT RUN + (N~3.6e8, beyond budget) -- recorded, not silently dropped.""" + rng = np.random.default_rng(SEED) + out_dir = pathlib.Path(__file__).parent + partial = out_dir / "spiral_adi_probe.partial.jsonl" + with open(partial, "w") as pf: + headline = run_one_n(17, rng, pf) + sweep = [] + for n_idx in (8, 10, 12, 14, 19): + sweep.append(run_one_n(n_idx, rng, pf)) + sweep_full = sorted(sweep + [headline], key=lambda r: r["n_idx"]) + + b2 = (headline["fib"] is not None + and headline["fib"]["iso_rel_l2"] <= 0.15 + and headline["fib"]["aniso"] <= 1.25) + b3 = (headline["aniso_ratio_control_over_fib"] is not None + and headline["aniso_ratio_control_over_fib"] >= 1.5) + out = { + "headline": headline, + "sweep": [{k: v for k, v in row.items() + if k in ("n_idx", "N", "qualifying_bands", "fib", + "control", "aniso_ratio_control_over_fib")} + for row in sweep_full], + "n21": "NOT RUN (N=3*F(21)^2 ~ 3.6e8, beyond budget; recorded per brief)", + "verdicts": { + "B2_iso": "PASS" if b2 else "FAIL", + "B3_control": ("PASS" if b3 else + ("VOID -- control smooths as isotropically as " + "Fibonacci; the Fibonacci claim measures nothing" + if headline["aniso_ratio_control_over_fib"] is not None + else "NO-VERDICT")), + }, + } + with open(out_dir / "spiral_adi_probe.json", "w") as fh: + json.dump(out, fh, indent=2) + partial.unlink() + return out + + +if __name__ == "__main__": + res = run() + h = res["headline"] + print(f"N={h['N']} r_floor={h['r_floor']:.4f} qualifying={h['qualifying_bands']}") + print("strides per band:", h["strides"]) + print(f"fib: {h['fib']}") + print(f"control: {h['control']}") + print(f"aniso ratio (control/fib): {h['aniso_ratio_control_over_fib']}") + print("verdicts:", res["verdicts"]) + print("\nB4 sweep:") + for row in res["sweep"]: + f_ = row["fib"] + print(f" n={row['n_idx']:2d} N={row['N']:9d} " + f"iso={f_['iso_rel_l2']:.4f} aniso={f_['aniso']:.4f} " + f"ratio={row['aniso_ratio_control_over_fib']}")