← Session 8 — Regroup + R-004 finite-height Li theorem; resources atlas ingested
thinking (7346 chars)
The setter works fine here — η_24 at dps 243 vs 1000: identical to 40 digits, diff 3.96e-72. So η_24 computed correctly in isolation!
So why did compute_eta produce η_24 with 2.66e-29 error?? Look at compute_eta code again:
```
mp.mp.dps = full_digits + 50
n_zeta = max(zeta_str)
for j in range(K_SPLIT, n_zeta):
z = mp.mpf(zeta_str[j + 1])
eta = (-1) ** (j + 1) * ((1 - mp.mpf(2) ** (-(j + 1))) * z - 1)
eta_str[j] = mp.nstr(eta, full_digits)
```
Hmm wait — `(-1) ** (j + 1) * ((1 - mp.mpf(2) ** (-(j + 1))) * z - 1)`. Operator precedence: (-1)**(j+1) is Python int ±1, times mpf → fine.
`mp.mpf(2) ** (-(j + 1))`: mpf ** int → fine.
BUT WAIT: mp.nstr(eta, full_digits) — with full_digits=193: η_24 = 1.18e-12 stored to 193 SIG digits → abs error 1e-205. But measured 2.66e-29... And just now in isolation it worked!
Difference between compute_eta context and this test: in compute_eta, BEFORE the loop, mp.mp.dps was set to full_digits+100 (Stieltjes recursion at 293), then `mp.mp.dps = full_digits + 50` (243)... Wait — actually, look at the ORDER in compute_eta:
```
flint.ctx.prec = ...
gam_s = [...]
mp.mp.dps = full_digits + 100
[recursion]
eta_str = {j: mp.nstr(Ev[j+1], full_digits) for j in range(K_SPLIT)}
print eta_0, eta_1
print(...)
mp.mp.dps = full_digits + 50
n_zeta = max(zeta_str)
for j in range(K_SPLIT, n_zeta): ...
```
That's what I wrote. Setter works (just tested). Hmm, but in the smoke test, compute_eta was called with full_digits = 193 — WAIT. NO!!! Look at get_zeta_strings: it returns full_digits computed from n_max=300 → 193. But in compute_eta... wait, but which full_digits did compute_eta receive? ns["compute_eta"](zeta_str, fd) with fd = 193 ✓. Recursion at dps 293, loop at 243.
Hmm hold on, in the measured comparison "max |eta_new − ref| j 0-23: worst j=0, diff=4.94e-18" — η_0 from the STIELTJES part! At dps 293 recursion, stability test said error ~1e-295. But measured 4.94e-18!!! So even the Stieltjes part was wrong at 1e-18 in the actual compute_eta run, but fine in the isolated stability test!!! The difference between stability test and compute_eta: compute_eta calls `flint.acb.stieltjes(k).real.str(full_digits + 100)` — stability test: `flint.acb.stieltjes(k).real.str(dps+10)`. Wait — compute_eta: strings at full_digits+100 = 293 digits with flint.ctx.prec = int((full_digits+100)*3.33)+200. Stability test dps 293: strings at 303, ctx.prec = int(293*3.33)+400. Both fine.
And then η_new[j]=mp.nstr(Ev[j+1], full_digits) — 193 digits. Fine.
So WHY was η_0 wrong at 4.94e-18 in compute_eta's output?? η_0 = Ev[1] = N_[1] − D_[1]Ev[0] = N_[1] − D_[1]. N_[1]: for m=1..: N_[m+1] = −(−1)^m γ_m/(m−1)! → N_[2] = γ_1... wait η_0 = [u^1](N/D) = N_[1]/D_[0] = N_[1]. N_[1] stays 0? N_[m+1] set for m=1..K_SPLIT+1 → N_[2..]; N_[1] = 0 (initialized). D_[1] = γ_0. E[0] = N_[0] = 1. E[1] = N_[1] − D_[1]E[0] = −γ_0 = −γ ✓. η_0 = Ev[1] = −γ. diff vs ref 4.94e-18?!?! −γ computed from gam_s[0] = flint stieltjes(0) string at 293 digits: −0.57721566490153286060651209008240243104... The ref at 1900 digits. diff 4.94e-18?! A 293-digit string of γ_0 can't be wrong at 1e-17!! Unless flint.acb.stieltjes(0) at that ctx.prec returned garbage!!! ctx.prec = int(293*3.33)+200 = 1175 bits ≈ 353 digits ✓ > 293 ✓.
Hmm wait, actually — hold on: γ = 0.577215664901532860606512090082... The printed η_0 in compute_eta output: "eta_0 = -0.577215664901533 (should be -gamma)" ✓ 15 digits fine. The measured diff 4.94e-18... wait, actually, maybe the diff measurement: mp.mp.dps = 800 during comparison; mp.mpf(eta_str[0]) — eta_str[0] is a 193-char STRING. −γ to 193 digits. ref[0] = −γ to ~1900 digits. |diff| should be ~1e-193!!! Measured 4.94e-18!!! So eta_str[0]'s string is WRONG at digit 18!?!? The string from mp.nstr(Ev[1], 193) where Ev[1] = −gam_s[0], gam_s[0] = mp.mpf(flint.acb.stieltjes(0).real.str(293)) at mp.mp.dps = 293...
WAIT!!! mp.mp.dps = full_digits + 100 = 293 was set AFTER gam_s was computed? Look at my code:
```
flint.ctx.prec = int((full_digits + 100) * 3.33) + 200
gam_s = [mp.mpf(flint.acb.stieltjes(k).real.str(
full_digits + 100, radius=False)) for k in range(K_SPLIT + 2)]
mp.mp.dps = full_digits + 100
```
gam_s computed BEFORE setting mp.mp.dps = 293!!! At that point, what was mp.mp.dps? Whatever the caller left it at! In the smoke test: before compute_eta, get_zeta_strings → loaded cache (no dps change)... before that, the exec of the module: module-level code — imports, constants... mp.mp.dps default = 15!!! So gam_s = mp.mpf(293-digit string) parsed at dps 15!!! → 53-bit doubles!!! η errors ~1e-16-1e-17 relative — MATCHES the 4.94e-18 at η_0 (0.577·1e-17 ~ 6e-18 ✓✓) and 2.66e-29 at η_24... wait η_24 came from the zeta loop which set dps 243 first... hmm, but 2.66e-29 at η_24 ~ 1.18e-12: relative 2.3e-17 = 53-bit! In the zeta loop, mp.mp.dps = full_digits+50 = 243 set before... z = mp.mpf(zeta_str[j+1]) parsed at 243 ✓... η computed at 243 ✓. So η_24 should be fine?! But measured 2.66e-29. Hmm.
Hmm wait, maybe the zeta loop's mp.mp.dps = 243 ALSO didn't take effect because... no — the isolated test just now showed the same operations working at dps 243 (diff 3.96e-72 — limited by... hmm interesting, why 3.96e-72 not 1e-231? mp.mpf(zeta_str[25]) at dps 243 → 243 digits; η_24 ~ 1e-12: relative 1e-231... the isolated test diff was 3.96e-72: that's 243−12−... = relative 3.4e-60?? Hmm, mp.nstr(eta24, 60) — I compared nstr(60 digits) vs dps-1000 value: the 60-digit string limits the diff to 1e-60·1e-12 = 1e-72 ✓. OK fine.)
So in isolation everything works. But inside compute_eta it didn't. WHY? THE ORDER: in compute_eta, `mp.mp.dps = full_digits + 50` comes after the Stieltjes part... but for j ≥ 24 the values should still be right...
UNLESS the smoke-test comparison itself was at fault: "max |eta_new − ref| j 24-59: worst j=24, diff=2.66e-29" — mp.mp.dps = 800 during comparison; eta_str[24] is a STRING; mp.mpf(eta_str[24]) at dps 800 — parses full 193 digits ✓. ref[24] from dps-2000 recursion ✓. diff 2.66e-29 means eta_str[24] string is wrong at 1e-29. The string was produced by mp.nstr(eta, 193) with eta computed at dps 243 from a 1650-digit ζ string... in the smoke run. But isolated, same ops give correct value!!! What's different?!?
OH MY GOD. I think I finally see it. `zeta_str[j + 1]` — in compute_eta, zeta_str came from get_zeta_strings — the CACHE. Keys are ints (converted in loader ✓). ζ(25) string... fine.
Hmm, what else... `mp.mpf(2) ** (-(j + 1))` — fine.
Let me just add a debug print inside a copy of compute_eta and run it: print eta_24 at full precision right after computing, then parse eta_str[24] and print. See where it degrades.
ACTUALLY WAIT. I bet it's the `mp.nstr(eta, full_digits)` at... mp.nstr doesn't depend on dps. Hmm.
OR — THE CACHE!! Was the zeta cache actually built by flint, or could the PREVIOUS session's cache contain 53-bit strings??? get_zeta_strings built the cache... in the PREVIOUS session (the buggy run). THAT run also had a dps-set ordering bug? Look at the OLD code: flint.ctx.prec set, then zeta_str[j] = flint.arb(j).zeta().str(full_digits, ...) — flint, no mpmath involved. Strings 1650 digits. The isolated test used zeta_str[25] from the cache and got the RIGHT answer (diff 3.96e-72 ✓). So cache fine.
Let me just instrument compute_eta directly.
The setter works in isolation — so something inside compute_eta's flow degrades the values. Let me instrument it directly.
tool: Bash
input
{
"command": "cd /d/Python/Quantum && ./.venv/Scripts/python.exe - <<'EOF'\nimport mpmath as mp, flint, json\n# replicate compute_eta exactly, with instrumentation\nzeta_str = {int(k): v for k, v in json.loads(open(\"research/numerics/data/E-021-zeta-cache.json\").read()).items()}\nfull_digits = 193\nK_SPLIT = 24\nprint(\"dps at entry:\", mp.mp.dps)\nflint.ctx.prec = int((full_digits + 100) * 3.33) + 200\ngam_s = [mp.mpf(flint.acb.stieltjes(k).real.str(full_digits + 100, radius=False)) for k in range(K_SPLIT + 2)]\nprint(\"dps during gam_s parse:\", mp.mp.dps)\nmp.mp.dps = full_digits + 100\nprint(\"gamma_0 parsed:\", mp.nstr(gam_s[0], 25))\nN_=[mp.mpf(0)]*(K_SPLIT+2); D_=[mp.mpf(0)]*(K_SPLIT+2); N_[0]=D_[0]=1\nfor m in range(0,K_SPLIT+1): D_[m+1]=(-1)**m*gam_s[m]/mp.factorial(m)\nfor m in range(1,K_SPLIT+1): N_[m+1]=-(-1)**m*gam_s[m]/mp.factorial(m-1)\nEv=[mp.mpf(0)]*(K_SPLIT+2)\nfor k in range(0,K_SPLIT+2):\n s=N_[k]\n for i in range(1,k+1): s-=D_[i]*Ev[k-i]\n Ev[k]=s\neta_str={j: mp.nstr(Ev[j+1], full_digits) for j in range(K_SPLIT)}\nmp.mp.dps = full_digits + 50\nn_zeta = 300\nfor j in range(K_SPLIT, 60):\n z = mp.mpf(zeta_str[j+1])\n eta = (-1)**(j+1)*((1-mp.mpf(2)**(-(j+1)))*z - 1)\n eta_str[j] = mp.nstr(eta, full_digits)\nprint(\"eta_24 raw:\", mp.nstr((-1)**25*((1-mp.mpf(2)**(-25))*mp.mpf(zeta_str[25])-1), 40))\nprint(\"eta_24 str:\", eta_str[24][:50])\n# now the actual eta_0 string\nprint(\"eta_0 str:\", eta_str[0][:50])\nprint(\"gamma true:\", mp.nstr(-mp.euler, 30))\nEOF",
"description": "Instrumented compute_eta replication",
"timeout": 300000
}
raw content_json (database row)
[{"type":"thinking","thinking":"The setter works fine here — η_24 at dps 243 vs 1000: identical to 40 digits, diff 3.96e-72. So η_24 computed correctly in isolation!\n\nSo why did compute_eta produce η_24 with 2.66e-29 error?? Look at compute_eta code again:\n\n```\nmp.mp.dps = full_digits + 50\nn_zeta = max(zeta_str)\nfor j in range(K_SPLIT, n_zeta):\n z = mp.mpf(zeta_str[j + 1])\n eta = (-1) ** (j + 1) * ((1 - mp.mpf(2) ** (-(j + 1))) * z - 1)\n eta_str[j] = mp.nstr(eta, full_digits)\n```\n\nHmm wait — `(-1) ** (j + 1) * ((1 - mp.mpf(2) ** (-(j + 1))) * z - 1)`. Operator precedence: (-1)**(j+1) is Python int ±1, times mpf → fine.\n\n`mp.mpf(2) ** (-(j + 1))`: mpf ** int → fine.\n\nBUT WAIT: mp.nstr(eta, full_digits) — with full_digits=193: η_24 = 1.18e-12 stored to 193 SIG digits → abs error 1e-205. But measured 2.66e-29... And just now in isolation it worked!\n\nDifference between compute_eta context and this test: in compute_eta, BEFORE the loop, mp.mp.dps was set to full_digits+100 (Stieltjes recursion at 293), then `mp.mp.dps = full_digits + 50` (243)... Wait — actually, look at the ORDER in compute_eta:\n\n```\nflint.ctx.prec = ...\ngam_s = [...]\nmp.mp.dps = full_digits + 100\n[recursion]\neta_str = {j: mp.nstr(Ev[j+1], full_digits) for j in range(K_SPLIT)}\nprint eta_0, eta_1\nprint(...)\nmp.mp.dps = full_digits + 50\nn_zeta = max(zeta_str)\nfor j in range(K_SPLIT, n_zeta): ...\n```\n\nThat's what I wrote. Setter works (just tested). Hmm, but in the smoke test, compute_eta was called with full_digits = 193 — WAIT. NO!!! Look at get_zeta_strings: it returns full_digits computed from n_max=300 → 193. But in compute_eta... wait, but which full_digits did compute_eta receive? ns[\"compute_eta\"](zeta_str, fd) with fd = 193 ✓. Recursion at dps 293, loop at 243.\n\nHmm hold on, in the measured comparison \"max |eta_new − ref| j 0-23: worst j=0, diff=4.94e-18\" — η_0 from the STIELTJES part! At dps 293 recursion, stability test said error ~1e-295. But measured 4.94e-18!!! So even the Stieltjes part was wrong at 1e-18 in the actual compute_eta run, but fine in the isolated stability test!!! The difference between stability test and compute_eta: compute_eta calls `flint.acb.stieltjes(k).real.str(full_digits + 100)` — stability test: `flint.acb.stieltjes(k).real.str(dps+10)`. Wait — compute_eta: strings at full_digits+100 = 293 digits with flint.ctx.prec = int((full_digits+100)*3.33)+200. Stability test dps 293: strings at 303, ctx.prec = int(293*3.33)+400. Both fine.\n\nAnd then η_new[j]=mp.nstr(Ev[j+1], full_digits) — 193 digits. Fine.\n\nSo WHY was η_0 wrong at 4.94e-18 in compute_eta's output?? η_0 = Ev[1] = N_[1] − D_[1]Ev[0] = N_[1] − D_[1]. N_[1]: for m=1..: N_[m+1] = −(−1)^m γ_m/(m−1)! → N_[2] = γ_1... wait η_0 = [u^1](N/D) = N_[1]/D_[0] = N_[1]. N_[1] stays 0? N_[m+1] set for m=1..K_SPLIT+1 → N_[2..]; N_[1] = 0 (initialized). D_[1] = γ_0. E[0] = N_[0] = 1. E[1] = N_[1] − D_[1]E[0] = −γ_0 = −γ ✓. η_0 = Ev[1] = −γ. diff vs ref 4.94e-18?!?! −γ computed from gam_s[0] = flint stieltjes(0) string at 293 digits: −0.57721566490153286060651209008240243104... The ref at 1900 digits. diff 4.94e-18?! A 293-digit string of γ_0 can't be wrong at 1e-17!! Unless flint.acb.stieltjes(0) at that ctx.prec returned garbage!!! ctx.prec = int(293*3.33)+200 = 1175 bits ≈ 353 digits ✓ > 293 ✓.\n\nHmm wait, actually — hold on: γ = 0.577215664901532860606512090082... The printed η_0 in compute_eta output: \"eta_0 = -0.577215664901533 (should be -gamma)\" ✓ 15 digits fine. The measured diff 4.94e-18... wait, actually, maybe the diff measurement: mp.mp.dps = 800 during comparison; mp.mpf(eta_str[0]) — eta_str[0] is a 193-char STRING. −γ to 193 digits. ref[0] = −γ to ~1900 digits. |diff| should be ~1e-193!!! Measured 4.94e-18!!! So eta_str[0]'s string is WRONG at digit 18!?!? The string from mp.nstr(Ev[1], 193) where Ev[1] = −gam_s[0], gam_s[0] = mp.mpf(flint.acb.stieltjes(0).real.str(293)) at mp.mp.dps = 293... \n\nWAIT!!! mp.mp.dps = full_digits + 100 = 293 was set AFTER gam_s was computed? Look at my code:\n\n```\nflint.ctx.prec = int((full_digits + 100) * 3.33) + 200\ngam_s = [mp.mpf(flint.acb.stieltjes(k).real.str(\n full_digits + 100, radius=False)) for k in range(K_SPLIT + 2)]\nmp.mp.dps = full_digits + 100\n```\n\ngam_s computed BEFORE setting mp.mp.dps = 293!!! At that point, what was mp.mp.dps? Whatever the caller left it at! In the smoke test: before compute_eta, get_zeta_strings → loaded cache (no dps change)... before that, the exec of the module: module-level code — imports, constants... mp.mp.dps default = 15!!! So gam_s = mp.mpf(293-digit string) parsed at dps 15!!! → 53-bit doubles!!! η errors ~1e-16-1e-17 relative — MATCHES the 4.94e-18 at η_0 (0.577·1e-17 ~ 6e-18 ✓✓) and 2.66e-29 at η_24... wait η_24 came from the zeta loop which set dps 243 first... hmm, but 2.66e-29 at η_24 ~ 1.18e-12: relative 2.3e-17 = 53-bit! In the zeta loop, mp.mp.dps = full_digits+50 = 243 set before... z = mp.mpf(zeta_str[j+1]) parsed at 243 ✓... η computed at 243 ✓. So η_24 should be fine?! But measured 2.66e-29. Hmm.\n\nHmm wait, maybe the zeta loop's mp.mp.dps = 243 ALSO didn't take effect because... no — the isolated test just now showed the same operations working at dps 243 (diff 3.96e-72 — limited by... hmm interesting, why 3.96e-72 not 1e-231? mp.mpf(zeta_str[25]) at dps 243 → 243 digits; η_24 ~ 1e-12: relative 1e-231... the isolated test diff was 3.96e-72: that's 243−12−... = relative 3.4e-60?? Hmm, mp.nstr(eta24, 60) — I compared nstr(60 digits) vs dps-1000 value: the 60-digit string limits the diff to 1e-60·1e-12 = 1e-72 ✓. OK fine.)\n\nSo in isolation everything works. But inside compute_eta it didn't. WHY? THE ORDER: in compute_eta, `mp.mp.dps = full_digits + 50` comes after the Stieltjes part... but for j ≥ 24 the values should still be right...\n\nUNLESS the smoke-test comparison itself was at fault: \"max |eta_new − ref| j 24-59: worst j=24, diff=2.66e-29\" — mp.mp.dps = 800 during comparison; eta_str[24] is a STRING; mp.mpf(eta_str[24]) at dps 800 — parses full 193 digits ✓. ref[24] from dps-2000 recursion ✓. diff 2.66e-29 means eta_str[24] string is wrong at 1e-29. The string was produced by mp.nstr(eta, 193) with eta computed at dps 243 from a 1650-digit ζ string... in the smoke run. But isolated, same ops give correct value!!! What's different?!?\n\nOH MY GOD. I think I finally see it. `zeta_str[j + 1]` — in compute_eta, zeta_str came from get_zeta_strings — the CACHE. Keys are ints (converted in loader ✓). ζ(25) string... fine.\n\nHmm, what else... `mp.mpf(2) ** (-(j + 1))` — fine.\n\nLet me just add a debug print inside a copy of compute_eta and run it: print eta_24 at full precision right after computing, then parse eta_str[24] and print. See where it degrades.\n\nACTUALLY WAIT. I bet it's the `mp.nstr(eta, full_digits)` at... mp.nstr doesn't depend on dps. Hmm.\n\nOR — THE CACHE!! Was the zeta cache actually built by flint, or could the PREVIOUS session's cache contain 53-bit strings??? get_zeta_strings built the cache... in the PREVIOUS session (the buggy run). THAT run also had a dps-set ordering bug? Look at the OLD code: flint.ctx.prec set, then zeta_str[j] = flint.arb(j).zeta().str(full_digits, ...) — flint, no mpmath involved. Strings 1650 digits. The isolated test used zeta_str[25] from the cache and got the RIGHT answer (diff 3.96e-72 ✓). So cache fine.\n\nLet me just instrument compute_eta directly.","signature":""},{"type":"text","text":"The setter works in isolation — so something inside compute_eta's flow degrades the values. Let me instrument it directly."},{"type":"tool_use","id":"Bash_54","name":"Bash","input":{"command":"cd /d/Python/Quantum && ./.venv/Scripts/python.exe - <<'EOF'\nimport mpmath as mp, flint, json\n# replicate compute_eta exactly, with instrumentation\nzeta_str = {int(k): v for k, v in json.loads(open(\"research/numerics/data/E-021-zeta-cache.json\").read()).items()}\nfull_digits = 193\nK_SPLIT = 24\nprint(\"dps at entry:\", mp.mp.dps)\nflint.ctx.prec = int((full_digits + 100) * 3.33) + 200\ngam_s = [mp.mpf(flint.acb.stieltjes(k).real.str(full_digits + 100, radius=False)) for k in range(K_SPLIT + 2)]\nprint(\"dps during gam_s parse:\", mp.mp.dps)\nmp.mp.dps = full_digits + 100\nprint(\"gamma_0 parsed:\", mp.nstr(gam_s[0], 25))\nN_=[mp.mpf(0)]*(K_SPLIT+2); D_=[mp.mpf(0)]*(K_SPLIT+2); N_[0]=D_[0]=1\nfor m in range(0,K_SPLIT+1): D_[m+1]=(-1)**m*gam_s[m]/mp.factorial(m)\nfor m in range(1,K_SPLIT+1): N_[m+1]=-(-1)**m*gam_s[m]/mp.factorial(m-1)\nEv=[mp.mpf(0)]*(K_SPLIT+2)\nfor k in range(0,K_SPLIT+2):\n s=N_[k]\n for i in range(1,k+1): s-=D_[i]*Ev[k-i]\n Ev[k]=s\neta_str={j: mp.nstr(Ev[j+1], full_digits) for j in range(K_SPLIT)}\nmp.mp.dps = full_digits + 50\nn_zeta = 300\nfor j in range(K_SPLIT, 60):\n z = mp.mpf(zeta_str[j+1])\n eta = (-1)**(j+1)*((1-mp.mpf(2)**(-(j+1)))*z - 1)\n eta_str[j] = mp.nstr(eta, full_digits)\nprint(\"eta_24 raw:\", mp.nstr((-1)**25*((1-mp.mpf(2)**(-25))*mp.mpf(zeta_str[25])-1), 40))\nprint(\"eta_24 str:\", eta_str[24][:50])\n# now the actual eta_0 string\nprint(\"eta_0 str:\", eta_str[0][:50])\nprint(\"gamma true:\", mp.nstr(-mp.euler, 30))\nEOF","description":"Instrumented compute_eta replication","timeout":300000},"extra_content":null}]