Current task: —

← Session 9 — Phase 4 synthesis assault + paper draft cleanup

#364 assistant 2026-08-19 16:59:53.836 UTC · 354→9317 tok open ⤢
thinking (25377 chars)
E-009 line 231: `Z = np.linalg.qr(np.ones((n,1)))[:,1:]` — projects to the mean-zero subspace, and line 343: "mean-zero constraint enforced (theorem's operator is on L^2_0)". So Suzuki's operator is defined on L²_0 (mean-zero functions) — the constraint is THEORETICAL, part of Suzuki's construction (T_a on L²_0(−a,a)). Good: the bordered solve enforces this, and my K computation must use the same convention. So weights: w_k = 1/K_k, K_k = Re(b_zk^H u_k) where [T e; eᵀ 0][u_k; μ] = [b_zk; 0], b_zk = hat_FT(z_k,h)·e^{−iz_k x}. Now — design E-022 concretely. **Objective (D2):** spectral measure μ_{a,π} = Σ_k w_k δ_{z_k} (z_k = W(a,π) zeros); test convergence toward the γ-target measure as a grows; measure resolvent-convergence rate. **Key design questions:** 1. What is the target measure at γ's? The limit operator D (translation generator in ℋ(T)) has spectrum at γ's with weights 1/K_∞(γ,γ). K_∞ unknown analytically, BUT: the natural comparison is SHAPE convergence: normalized measures. Options: a. Compare cumulative M_a(x) = Σ_{z_k≤x} w_k with N(x)·(average weight) — test whether M_a(x)/x → (1/2π)log(x/2πe)·w̄ shape. b. Weak-star: μ_a(f) for smooth f vs Σ_γ f(γ)·c_γ where c_γ = candidate limit weight. For the limit weights: in Suzuki's Cor 1.6 world, W(a,θ;z) → const·ξ(1/2−iz)·e^{iφ(z)}-ish; the de Branges space of ξ: E_ξ(z) = ξ(1/2−iz); K_ξ(w,w) = (1/π) Im[E′(w)Ē(w)]/|E|²·|E(w)|²... the standard formula: for de Branges space H(E), K(w,w) = (1/π)(|E(w)|² φ′(w)) where φ = −arg E on ℝ (phase). Mass at zero γ: 1/K(γ,γ) = π/(|E(γ)|² φ′(γ)). With E(γ) = ξ(1/2−iγ)·... hmm zeros of E are at z with ξ(1/2−iz) = 0: 1/2−iz = ρ: z = i(ρ−1/2) = i·iγ = −γ... sign conventions; |z| = γ. E′(γ) = ξ′(1/2−iγ)·(−i). The phase φ(w) = −arg ξ(1/2−iw): φ′(w) = ... = Re[(ξ′/ξ)(1/2−iw)]·... on the line this is the argument derivative = related to the zero density: φ′(γ) ≈ log(γ/2π)/... This gives a concrete candidate: limit mass at γ: m_γ = π/(|E(γ)|²φ′(γ)). But E(γ) = ξ(1/2−iγ) — VALUE of ξ at... wait E's zeros ARE the eigenvalues: E(γ) = 0!! For a zero of E, K(γ,γ) = (1/π)|E′(γ)|²/... no: K(w,w) = (1/π)·Im[E′(w)Ē(w)]/(...) hmm at a zero of E this is 0/0. The correct formula at a zero: K(γ,γ) = (1/π)|E′(γ)|²/|... let me just recall the clean identity: for H(E), A = (E+Ē*)/2, B = (E−Ē*)/2i (real on ℝ); zeros of the spectral problem are zeros of B (or A−B cot); K(β,β) = (1/π)(B′(β)A(β) − A′(β)B(β))/... at B-zero: K(β,β) = B′(β)A(β)/π·(1/?)... K(w,w) = (1/π)(A B′ − A′ B)(w) with normalization... Actually: K(w,w) = (1/π)(B′(w)A(w) − A′(w)B(w)) needs a 1/(2π)? conventions vary. At a zero β of B: K(β,β) = (1/π) A(β) B′(β). Hmm sign/factors to fix. For OUR Galerkin model space the K comes straight from the computation — no formula needed. For the LIMIT comparison, compute candidate limit masses directly from ξ data: m_γ ∝ 1/(A(γ)B′(γ)) with E built from ξ. I can compute |ξ′(1/2+iγ)| via mpmath (dps 30, fine for γ ≤ 300) and the phase derivative from the zero-density or directly. Then compare the RATIO structure: e.g., correlation between w_k(a) at zeros near... but atoms don't lock to γ's (E-019: chance-level proximity). So weight-per-atom comparison is meaningless; only WEAK convergence makes sense: μ_a(f) := Σ_k w_k f(z_k) vs ν(f) := Σ_γ m_γ f(γ) for smooth f. 2. Which f? Gaussian bumps at fixed centers, and monotone cumulative M_a(x) vs M_∞(x) = Σ_{γ≤x} m_γ. 3. Also the ORIGIN double-zero artifact: Galerkin gives a double zero at z≈0 (±iε pair) — E-016b found it; weights there will be garbage/huge. Exclude |z| < z_min (e.g. first real zero z₁ ≈ 4.5/a·... z₁·a ≈ 4.5; at a=4: z₁ ≈ 1.1; exclude |z| < 0.5·z₁ or handle explicitly). 4. Normalization subtlety: weights depend on the norm scaling of T (λ shift etc.). Cross-a comparisons need a consistent gauge. The measures μ_a and ν might differ by an overall a-dependent scale; handle by normalizing: compare μ_a(f)/μ_a(1) vs ν(f)/ν(1) over the window, or fix gauge via... Total mass in window [0, Z]: Σ_k w_k — its a-scaling is itself an observable (should grow like window·density·mean-weight). Also λ = LAM_SHIFT = −1.0 fixed across all a ✓ consistent gauge already. 5. Ranges: a ∈ {4, 6, 8, 10, 12}; zeros in (0, Z_MAX = 300] (matches E-019); at a=4: #zeros ≈ aT/π = 4·300/π ≈ 382... hmm wait z-density: zeros of W ~ lattice spacing π/a → count to Z: aZ/π. At a=4, Z=300: 382 zeros; a=12: 1146. But zeros only up to... E-019: 1145/1146 at a=12 ✓. But CAREFUL: the prime-sum backend validity: g̃ approximated with primes to e^{2a}; the symbol tail truncation error at level e^{−a}-ish (D-019: λ_a cliff; the Galerkin T_a uses primes ≤ e^{2a}). The measured zeros are zeros of the TRUNCATED model — for the measure-convergence question that's exactly what D2 wants (the a-dependent model converging to the limit). 6. Runtime: build(a) at h=0.01: n = 2400a−1 → a=12: n = 28799?? WAIT: n_elem = 2a/h = 2400 at a=12, n = 2399. solve_toeplitz per zero: n = 2399, O(n²) ≈ 5.8M per solve × 1146 zeros × 5 a-values ≈ 33 Gflops — fine (~1 min). But build(a) itself: stiffness_first_col does S_full per k (n_elem quadratures) — E-016 ran a=10; E-019 a=12 — cache? _BUILD is in-memory only; each E-022 run rebuilds (E-016 sieve e^20 takes time! "runs the e^20 sieve" at import — E-019 noted import cost). The prime_backend_v2 monkeypatch avoids recompute. E-019 loads E16 which runs the sieve to e^20 — for a=12 need e^24 (backend v2 cache exists: E-019-backend-cache.npz). Follow E-019's pattern: import E16 (sieve e^20 ~ minutes?), then monkeypatch prime_sum_vec with PrimeBackendV2. Hmm wait, actually for build(a, h) the expensive part is stiffness_first_col → S_full per k → quad over log-segments with g_float evaluations (prime_sum_vec inside). With v2 backend monkeypatched, g_float uses the fast blockwise sums ✓. 7. What about θ=π only? Yes (θ=π has all-real zeros, E-016 F1; θ=0 has complex zeros — measure not real-positive). 8. Checks: V1 (gauge/self-consistency): weights positive, symmetric: w(−z_k) = w(z_k) — compute only z>0 and mirror. V2 (refinement): at a=4, weights with h=0.01 vs h=0.005 agree to tolerance (Galerkin convergence of K). V3 (cross-check vs E-016/E-019 zero lists): z₁ and count match. V4 (mass scaling): total mass M_a(Z) vs a — report scaling (expect ~ linear in window count × mean weight; just document). V5 (the headline): weak-convergence metric vs a — e.g., discrepancy D(a) = sup over test set |μ̂_a(f) − ν̂(f)|/ν̂(1) or Wasserstein-ish distance between cumulative mass functions normalized. If D(a) DECREASES with a: evidence of measure-level convergence (resolvent convergence signal); if flat/increasing: no convergence yet (consistent with D-035's honest reading "nothing is converging yet" — F5 slow increase of leftover). Hmm wait, I should think about what the target measure ν actually is, more carefully, because E-016 already compared ρ_W vs ρ_F (Herglotz densities on the IMAGINARY axis) — the Herglotz density ρ_W(iy) = −Im(W′/W)(iy)/π vs ρ_F (from F = z²ξ/ξ′...). That was the zero-density comparison in Herglotz form. E-022's novelty: the WEIGHTED atomic measure on the REAL axis (spectral/Clark measure) — genuinely different observable (weights carry the operator metric, not just zero positions). For the target: the limit de Branges space (if it exists under (★)) has E-function related to ξ. From R-003: W = F·(R₀+R_τ), F = z²ξ/ξ′... hmm — W itself converges to... M = W/F → e^{−φ}? Under (★), W ~ F·R₀·(1+o(1))... no wait (★) says M = W/F → e^{−ϕ}·... hmm: (b) function form: e^{ϕ(a,z)}M(a,θ;z) → 1. So W ≈ F·e^{−ϕ}·(1+o(1))... wait M = W/F → e^{−ϕ}? "R_τ = −2e^{a+iθ/2}sin(az−θ/2) + e^{−ϕ(a,z)} + o(1)" and "e^{ϕ}M → 1": M → e^{−ϕ}·1... anyway the LIMIT zeros are where F·(stuff) = 0 — F = z²ξ/ξ′: zeros of F on ℝ... ξ(1/2−iz) zeros at z = ±γ (γ's) — WAIT: F = z²ξ/ξ′: F has zeros where ξ = 0 (z = γ) AND poles where ξ′ = 0. Hmm, and z=0 double. So the limit measure atoms at γ's — with weights from the metric. Candidate limit weights: m_γ = 1/K_∞(γ,γ). For a first numerics pass, we don't need the exact limit theory — the informative comparisons: (i) normalized cumulative: M_a(x)/M_a(Z) vs N(x)/N(Z) — shape only. (ii) weight-profile: log w_k vs z_k smooth trend; compare to log(1/K_∞-candidate) using |ξ′(1/2+iγ)|-based candidate: K_∞(γ,γ) ∝ |ξ′|²·... I'll compute the candidate m_γ = π/(A(γ)B′(γ)) with E := F evaluated... let me define via F directly: F(z) = z²ξ(1/2−iz)/ξ′(1/2−iz) — has zeros at γ. Write F = A − iB on ℝ? F is real on... ξ(1/2−iz) for real z: ξ(1/2−iz) = conj(ξ(1/2+iz))... and ξ real on the critical line ⟹ ξ(1/2−iz) = conj(ξ(1/2+iz)): for real z, ξ(1/2+iz) = Ξ(z) real (Ξ reality, resources note) ⟹ conj = same: ξ(1/2−iz) = Ξ(z) real! So ξ(1/2−iz) is REAL for real z ✓. ξ′(1/2−iz): derivative w.r.t. its argument... (d/dz)ξ(1/2−iz) = −i ξ′(1/2−iz); and Ξ′(z) = −iξ′(1/2−iz)·... Ξ(z) real on ℝ ⟹ Ξ′(z) real on ℝ ⟹ ξ′(1/2−iz) = iΞ′(z) pure imaginary! So F(z) = z²·Ξ(z)/(iΞ′(z)) = −i·z²Ξ(z)/Ξ′(z) — pure imaginary on ℝ (away from zeros/poles). So F = −iB_F with B_F real. The de Branges E-function convention: E = A − iB with A,B real: here A = 0 (like the W case: W = 2i·g real gauge). For the spectral problem: eigenvalue equation W(z)=0 with W = 2ig → zeros of g. The masses: for the model space of W-type: K(β,β) = (1/π)·∂_z[phase]·... Honestly: rather than derive the exact limit-weight formula under time pressure, E-022 should: - compute μ_a (weights from Galerkin — the actual operator-defined measure); - compare against the UNIT-weight zero measure (counting) and against a ξ′-based candidate; - report convergence metrics vs a. The derivation of the exact limit K_∞ can be D-038/2.9-D1 theory work. The numerics stand alone: deliver the μ_a measures + convergence metrics; the interpretation (which limit weights) can be refined analytically later. D2 says "vs the target atomic measure at γ's" — unit atoms at γ's is the minimal target (zero positions); weight-profile comparison is bonus. Hmm, wait — actually there's a cleaner theoretical anchor I'm missing. The spectral measure μ_θ of 𝒟̄_{a,θ} satisfies: m_θ(z) = ∫ dμ/(t−z) = the Weyl m-function = ratio involving W: m_θ(z) = −(something)/(W)... For rank-one perturbation theories (Clark): m(z) = (Θ(z)−...)/(Θ(z)+...)... and crucially: residues of m at poles = masses = 1/K(z_k,z_k) ✓. So I can VALIDATE the Galerkin weights against residues of the explicit m-function built from W, V±! The m-function for Suzuki's operator: from the boundary-value form, m_θ(z) = −[V+(z)·...]/W(a,θ;z)-ish. If I can get the exact m formula: mass_k = Res_{z_k} m = numerator(z_k)/W′(z_k). I have dW machinery (W_and_dW) ✓. Need numerator... From the theory of the first-order operator with boundary condition: m_θ = −(z−i)V+... hmm W = (z−i)V+ + e^{iθ}(z+i)V−; the m-function is likely m_θ(z) = i·[(z−i)V+ − e^{iθ}(z+i)V−·...]/W — at θ=π: numerator = (z−i)V+ + (z+i)V−? Then mass_k = num(z_k)/W′(z_k). Not sure of exact form — VALIDATION by identity instead: Σ_k mass_k/(t−z_k) should equal m(iy) for sample y — check both sides numerically. If the guessed numerator is wrong, try alternatives... this could burn time. PRECISION DECISION: skip the m-function cross-check in v1; use refinement check (h=0.01 vs 0.005 at a=4) as the validation, plus unit-sanity (weights > 0, symmetric). If time permits add m-check later. **E-022 script plan:** ``` Import E16 (sieve e^20), monkeypatch backend v2 (e^24) — a=12 needs primes to e^24. mp.mp.dps = 40. gammas = first 200 zeta zeros (mp.zetazero, float ok for statistics — for measure comparison at float64 level, fine; note E-021 caveat applies only to high-precision binomial sums). for a in [4, 6, 8, 10, 12]: x, h, Q, M, b = E16.build(a, H) # H=0.01 cp, T = solve_v(...); cm = mirrored zeros = locate_zeros(...) # z>0, brentq # exclude origin artifact: keep zeros > z1/2 for each z_k: b_z = hat_FT(z_k,h)*exp(-1j z_k x) solve bordered [T e; e' 0] u = [b_z; 0] K_k = Re(b_z^H u); w_k = 1/K_k record (z_k, w_k) V2 refinement at a=4 with h=0.005 for first 50 zeros. Observables: O1: M_a(x) cumulative vs N(x)·w̄_a — correlation/shape O2: log w_k vs z_k trend per a (smooth? outliers?) O3: weak metric: f_j = Gaussian bumps at 20 log-spaced centers, σ = mean gap·2: d_a = max_j |μ_a(f_j)/μ_a(1) − ν(f_j)/ν(1)| where ν = unit weights at γ's in window. O4: compare d_a vs a → convergence rate estimate. ``` Wait, one problem: the bordered solve per zero with scipy dense: n=2399 at a=12, B is (n+1)² dense solve O(n³) = 1.4×10¹⁰ per zero — too slow × 1146 zeros. Need Toeplitz-aware solve. solve_v uses scipy.linalg.solve on the bordered matrix — dense O(n³). E-016 did this only twice (cp, cm) per a. For 1146 zeros need the structure: bordered Toeplitz system. Options: use scipy.linalg.solve_toeplitz for T (O(n²) Levinson) — but the border breaks Toeplitz. Solve via Schur: with T u₀ = b_z and T u₁ = e: u = u₀ − μu₁, μ = (e·u₀)/(e·u₁). Two Toeplitz solves per zero, u₁ shared across zeros (e fixed) → per zero: ONE solve_toeplitz (b_z varies) + shared u₁. solve_toeplitz O(n²) = 5.8M flops → ×1146×5 ≈ 33 Gflop — OK (~30–60 s numpy). Levinson via solve_toeplitz allocates fine. But numerically Levinson for n=2400 SPD Toeplitz — stable enough for statistics (check residual for a few). Alternatively use the Toeplitz + conjugate: validate residuals |T u − b| small on a sample. Also MEMORY: Q, M Toeplitz stored as full n×n dense in build() — 2399² × 8B = 46 MB each — fine. Hmm wait — actually T = Q + M: Q Toeplitz from stiffness_first_col, M tridiagonal Toeplitz → T Toeplitz with first column t = q + m where m = [2h/3·...]. Careful: T = Q − λM, λ = −1: T = Q + M: t_col = q + [2h/3, h/6, 0...]. Solve with solve_toeplitz((t_col, t_col), b) — scipy solve_toeplitz takes (c, r) column+row; symmetric: c = r = t_col. Validation V-resid: for ~10 random zeros compute ‖Tu + μe − b‖/‖b‖ < 1e-8. Refinement check h=0.005 at a=4: n = 2·4/0.005−1 = 1599 — fine. Runtime estimate: builds at each a: S_full per k — E-016 did a ∈ {4,10} sweep... E-016's A_SWEEP presumably [4,10]; each build took? E-016 runtime total unknown; E-019 did a=12 build in sub-stage A validation "full pipeline at a=10 ... Q vs E16.build(10)" and a=12. Sieve e^20 at import (E16 module exec) — that was the slow part historically; v2 backend cache covers e^24. Total runtime maybe 10–30 min. Run detached per long-run protocol. Zero location: N_GRID=40000 over (0,300] — step 0.0075; lattice spacing π/a ≥ π/12 = 0.26 ✓ safe. g evaluation: V_and_dV on 40000-point grid with E matrix 40000×2400 complex = 768 MB — hmm, E-019 did exactly this (N_GRID=40000, n=2399): 40000×2399×16B = 1.5 GB — they ran it, 64GB RAM fine. b_z vectors: hat_FT(z_k,h)·exp(−1j·z_k·x) — cheap. Also z₁·a ≈ 4.5 law: z₁ at a=4: ≈1.125. Origin artifact: double zero at z≈0 excluded by grid start 1e-3 and... the ±iε pair is OFF-axis (Galerkin artifact: pair ±iε, ε~2–6e-9) — the real-axis grid misses it ✓ but there may be a real double-zero AT 0? E-016b: "Galerkin W has a double zero at z≈0 (pair ±iε)" — off-axis by tiny ε. g(0) = 0·ImV − ReV(0) = −ReV(0); V(0) = hat_FT(0)·Σcp — generically nonzero. So no real zero at 0 located. But the FIRST located real zero z₁ — E-016 family ✓. Edge: g grid sign-change detection misses tangent double zeros — W's real zeros are simple (interlacing, E-016 F1) except the origin artifact. Fine. Also for O1/O3 need γ's up to Z=300: first ~200 zeros (N(300) ≈ 300/2π·log(300/2πe) ≈ 47.7·log(47.7/e·...) ≈ 47.7·2.86 ≈ 136; take 200 to be safe — mp.zetazero fast). Let me also decide the weak-metric window: z ∈ (0, Z] with Z = 300 but exclude the top edge (located zeros truncated at 300; measure sums need complete neighborhoods — use f centers up to 250, and cumulative to 280; and Z_MAX=300 grid; fine). One more: μ_a(1) total mass — includes only located zeros in window; normalized metrics handle it. And gauge: weights have absolute scale from T (λ=−1 shift, h-normalization) — consistent across a since same conventions; but the b_z load has hat_FT factor (h·sinc²) — h same for all a ✓ consistent. Actually hmm: K computed as b_z^H u where u from bordered solve: as h→0 this converges to ⟨e_z, T_a^{-1}e_z⟩ ✓ (Galerkin consistent). Deliverables: CSV rows (a, k, z_k, w_k), JSON with metrics (d_a per a, mass totals, refinement error, V checks). Then D-file? The analysis write-up can go into the experiment JSON + a short D-038? No — D-038 is reserved for task 2.11 (transition scale). E-022 findings get summarized in the journal + maybe feed D-035 D2 conclusion. Per protocol: experiment = E-022 with CSV/JSON + db registration; analysis conclusions in journal + NGUYEN/STATE; if substantial, a derivation file D-039... let me keep D-039 for the digamma upgrade as already planned — hmm, I used "D-039 (digamma)" in D-037 follow-ups. So E-022 analysis → could be filed as part of D-035 D2 completion note in journal + index. If the analysis is meaty, file as D-040 to avoid collision. Decide after seeing results. Before writing the script, double-check the bordered-solve Schur decomposition against solve_v: solve_v returns sol[:n] solving B [v;μ] = [b;0]: T v + e μ = b, e·v = 0. My version: u₀ = T⁻¹b, u₁ = T⁻¹e; v = u₀ − μ u₁ with μ = (e·u₀)/(e·u₁). Check: T v + eμ = b − μe + μe = b ✓; e·v = e·u₀ − μ(e·u₁) = 0 ✓. And K = Re(b_z^H v). For cp validation: my Schur solve for b[+1] should reproduce E16.solve_v's cp — quick check in-script (V0: max |v_schur − cp| < tol). Wait, actually — e = ones(n): e·v = Σ v_j = 0 — this is a nodal-sum constraint (approx mean-zero via mass lumping? not exactly M-inner-product-zero but close enough — it's what the machinery uses; consistency is what matters). Also: b_z for z=0? Not needed (no zero at 0). Also should I compute weights for z_k AND −z_k? Symmetric by construction (mirror); compute z>0 only, mirror in analysis. Let me also confirm E-016's A_SWEEP / Z_MAX_ZERO and whether E-016 stored located zeros to CSV (for V3 cross-check at a=10). E-016 CSV rows are Herglotz densities; zeros used internally for V3. E-019 CSV has a=12 zero data. V3: compare a=10 z₁ and count vs E-016... E-016 ran a ∈ {4,10}? A_SWEEP at top of E-016 — check. Whatever: V3 = match z₁(a) against the stiff law z₁·a ≈ 4.495 (E-019) and counts ≈ aZ/π (maximal lattice) — sufficient. Runtime concern: E16 import runs the e^20 sieve — E-019 noted "runs the e^20 sieve" at import. How long? mangoldt_sieve_fast(e^20)? e^20 = 4.85×10⁸ sieve — E-016 did it; time maybe 1–5 min. Then monkeypatch to v2 (e^24 cache). Fine detached. Actually — do I even need backend v2/e^24? build(a) uses g_float(t) for t ≤ 2a: prime_sum_vec(t) = sums over primes ≤ e^t ≤ e^{2a}: a=12 → e^24 needed ✓ v2 required. a ≤ 10 → e^20 = in-memory sieve OK. Use v2 for all (E-019 pattern). One more design point: h. E-016/E-019 used H_MAIN = 0.01. n = 2a/h − 1: a=12 → 2399 ✓. Toeplitz solves O(n²)·#zeros — at a=12: n² = 5.75M × 1146 zeros = 6.6G × 2 (complex128) — ~minutes. OK. Also — V2 refinement at a=4 h=0.005: rebuild at h=0.005 (stiffness_first_col cost ~2× of h=0.01 build at a=4) fine. Write the script now. Structure: ```python """E-022 — task 2.10 (D-035 deliverable D2): spectral-measure convergence. ... """ import importlib.util, json, sys, time from pathlib import Path import mpmath as mp, numpy as np, scipy.optimize, scipy.linalg from scipy.signal import ... no load E16, backend v2 monkeypatch A_LIST = [4,6,8,10,12]; H = E16.H_MAIN; LAM = E16.LAM_SHIFT; Z_MAX=300; N_GRID=40000 mp.mp.dps = 40 def bordered_toeplitz_setup(T_col): u1 = solve_toeplitz((T_col, T_col), ones) return u1, e·u1 def solve_bordered(T_col, u1, eTu1, b): u0 = solve_toeplitz((T_col,T_col), b) mu = (u0.sum())/eTu1 ... wait e·u₀ = sum(u0) (e=ones); complex sum fine. return u0 - (u0.sum()/eTu1_sum)*u1 # hmm: e·u₁ = u1.sum() per a: x, h, Q, M, b = E16.build(a, H) n = len(x); q_col = Q[:,0]; m_col = [2h/3, h/6, 0...] T_col = q_col - LAM*m_col (LAM=-1 → +) Verify vs solve_v: cp_check via solve_bordered(b[+1]) vs E16.solve_v → V0 cp, cm for zeros (use E16.solve_v directly — consistent with E-016/019) zeros = locate (same as E-019 locate_zeros) weights: for each z_k: b_z = hat_FT(z_k,h)*exp(-1j z_k x); v = solve_bordered; K = Re(conj(b_z) @ v); w = 1/K store refinement at a=4, h=0.005, first 50 zeros → rel dev metrics + JSON/CSV ``` Wait — Q[:,0] full column: build returns Q as full dense Toeplitz; take column 0 ✓. M likewise tridiagonal Toeplitz: M[0,:2] etc: m_col = [2h/3, h/6, 0,...] ✓ (from build: M = toeplitz([2h/3, h/6, 0...])). T = Q − LAM·M ✓ matches solve_v's T = Q − lam*M. V0 check: solve_bordered(T_col, b[+1.0]) vs E16.solve_v(Q,M,b[+1],LAM,n) → max dev < 1e-8. Weight positivity: K > 0 (T SPD on constrained subspace; K should be > 0). Count K ≤ 0 as failures. Zero locate: reuse E-019's collect_t_points + g grid approach — simpler: copy E-019's locate_zeros (grid sign-change + brentq with g_direct). It needs cp and x, h. ✓ gamma zeros: mp.zetazero(k).imag for k=1..200 at dps 30 → float64 list ✓ (fine for statistics). Metrics: - O1: normalized cumulative shape: for grid xs: M_a(x)/M_a(Z') vs N_γ(x)/N_γ(Z') (unit weights), Z' = 280: L∞ and L2 discrepancy. - O2: weight profile: log w vs z — report slope/spread per a (linear fit). - O3: Gaussian bumps: centers 30 log-spaced in [15, 250], σ = 2π·(2/log...) — σ = 2·mean local gap ≈ 2·2π/log(center/2π)... use fixed σ = 3·(2π/log(50/2π)) ≈ 9? Keep simple: σ = 8 fixed. d_a = max_j |μ̃_a(f_j) − ν̃(f_j)| with μ̃ = μ(f)/μ(1-window)... normalize by total mass in [0,Z']: μ̃_a(f) = μ_a(f)/M_a(Z'), ν̃(f) = ν(f)/N_γ(Z'). - O4: d_a vs a table; fit d_a ~ c·a^{−ρ} or exp(−ρa). Also record total mass M_a(Z') scaling. Edge cases: zeros located up to 300 but weighted measure uses zeros ≤ 280 for metrics. Also — IMPORTANT: are weights w_k = 1/K_k with K from the CONSTRAINED solve the right spectral masses? The constrained space L²_0: evaluation functional restricted; reproducing kernel of the constrained model space: k_z = P(T^{-1}e_z) with P the projection... The masses = 1/‖k_z‖²_T. Our bordered solve gives v ⟂ e (in lumped sense) with T v + μe = b_z: v = T^{-1}(b_z − μe): the representer IN the constrained space of the restricted functional. ‖v‖²_T = v^H T v = v^H(b_z − μe) = v^H b_z − μ v^H e = v^H b_z (since e·v = 0) = conj(b_z^H v)... K = Re(b_z^H v) ✓ = Re(v^H b_z). ✓ consistent. Let me also double-check hat_FT sign/convention: b_z[j] should approximate ∫ e^{−izx} φ_j dx = e^{−iz x_j}·h·sinc²(zh/2π) = hat_FT(z,h)·e^{−izx_j} ✓ (hat_FT = h·sinc² ✓ real). But NOTE: build's b[±1] loads were computed with EXACT quadrature of e^{±x}φ_j (leggauss 8), not the sinc² formula — because e^{±x} isn't oscillatory the sinc² approx... anyway for e^{±x} they used quadrature; for e^{−izx} I use the exact-integrable form: ∫ e^{−izx}φ_j dx has a CLOSED form = e^{−izx_j}·h·sinc²(zh/2π)·(correction?) — let me verify: φ_j piecewise linear hat; ∫_{x_j−h}^{x_j} e^{−izx}(x−(x_j−h))/h dx + ∫_{x_j}^{x_j+h} e^{−izx}((x_j+h)−x)/h dx. Substitute u = x−x_j: ∫_{−h}^{0} e^{−izu}(u+h)/h du + ∫_{0}^{h} e^{−izu}(h−u)/h du = ∫_{−h}^{h} e^{−izu}(1−|u|/h) du = 2∫_0^h cos(zu)(1−u/h)du = 2[sin(zh)/z − (1/h)·(sin(zh)/z²·z − ...)] let me just compute: ∫_0^h cos(zu)du = sin(zh)/z. ∫_0^h u cos(zu) du = [u sin(zu)/z]_0^h + [cos(zu)/z²]_0^h = h sin(zh)/z + (cos(zh)−1)/z². So 2[sin(zh)/z − (1/h)(h sin(zh)/z + (cos(zh)−1)/z²)] = 2[sin(zh)/z − sin(zh)/z − (cos(zh)−1)/(hz²)] = 2(1−cos(zh))/(hz²) = 4 sin²(zh/2)/(h z²) = h·[sin(zh/2)/(zh/2)]² ✓ = h·sinc²(zh/(2π)) exactly (numpy sinc(x) = sin(πx)/(πx): sinc(zh/2π) = sin(zh/2)/(zh/2) ✓). EXACT — great, b_z = hat_FT(z,h)·e^{−izx} exact for the hat basis. This is consistent with V_and_dV using hat_FT for the P1 interpolant's FT ✓ — the whole Galerkin FT machinery uses this factor. NOW — one more sanity issue: cp are nodal values of v_+, and V+(z) = ∫ e^{izx} v_+(x)dx ≈ hat_FT·Σ e^{izx_j}cp_j. But my b_z convention uses e^{−izx}: V+(z) uses e^{+izx}. For the weight at z_k: K(z_k,z_k) = ⟨T^{-1}e_{?}, e_{?}⟩ — which sign? ‖v_z‖²_T = ⟨v_z, e^{−izx}⟩ where T v_z = e^{−izx}. V+ machinery uses e^{+izx} — by symmetry (z→−z, conj) K(z,z) = K(−z,−z) so sign choice is consistent as long as I'm uniform; use b_z = hat_FT·e^{−izx} per the definition. ✓ OK. Also compute for validation at a=10: compare zeros with E-016 family... skip (z₁·a law check suffices). Total runtime: builds (S_full × n_elem per a) — E-016 at a=10 took how long? Unknown; E-019 (a=10 rebuild + a=12 build + zeros) ran in some minutes. With v2 backend it should be faster. Detached run with log. Actually, one thing to double check: E16 import runs the sieve to e^20 ("runs the e^20 sieve" comment at E-019 import). mangoldt_sieve_fast(n_max) with n_max = e^20 = 4.85e8: sieve of that size — E-016 ran it once (minutes). It computes C1, C0 cumsum arrays of size 4.85e8 × 8B = 3.9 GB ×2 = 7.8 GB — hmm big but 64GB fine. E-019 did this import. OK. Then backend v2: PrimeBackendV2 — monkeypatch E16.prime_sum_vec and E16._BUILD.clear() per STATE.md note. E-019 line: "from prime_backend_v2 import PrimeBackendV2" then presumably monkeypatches. Let me check how E-019 does it (need exact pattern) — grep.
I have the full machinery picture. Let me check how E-019 patches in backend v2, then write E-022.
tool: Bash
input
{
  "command": "cd /d/Python/Quantum && grep -n \"PrimeBackendV2\\|prime_sum_vec\\|_BUILD\" research/numerics/scripts/E-019-zeroflow-a12.py | head && sed -n '/backend = /,+8p' research/numerics/scripts/E-019-zeroflow-a12.py | head -12 && grep -n \"Z_MAX_ZERO\\|A_SWEEP\\|GL_N\" research/numerics/scripts/E-016-herglotz-fine-structure.py | head -6",
  "description": "Check backend v2 monkeypatch pattern in E-019"
}
raw content_json (database row)
[{"type":"thinking","thinking":"E-009 line 231: `Z = np.linalg.qr(np.ones((n,1)))[:,1:]` — projects to the mean-zero subspace, and line 343: \"mean-zero constraint enforced (theorem's operator is on L^2_0)\". So Suzuki's operator is defined on L²_0 (mean-zero functions) — the constraint is THEORETICAL, part of Suzuki's construction (T_a on L²_0(−a,a)). Good: the bordered solve enforces this, and my K computation must use the same convention.\n\nSo weights: w_k = 1/K_k, K_k = Re(b_zk^H u_k) where [T e; eᵀ 0][u_k; μ] = [b_zk; 0], b_zk = hat_FT(z_k,h)·e^{−iz_k x}.\n\nNow — design E-022 concretely.\n\n**Objective (D2):** spectral measure μ_{a,π} = Σ_k w_k δ_{z_k} (z_k = W(a,π) zeros); test convergence toward the γ-target measure as a grows; measure resolvent-convergence rate.\n\n**Key design questions:**\n1. What is the target measure at γ's? The limit operator D (translation generator in ℋ(T)) has spectrum at γ's with weights 1/K_∞(γ,γ). K_∞ unknown analytically, BUT: the natural comparison is SHAPE convergence: normalized measures. Options:\n   a. Compare cumulative M_a(x) = Σ_{z_k≤x} w_k with N(x)·(average weight) — test whether M_a(x)/x → (1/2π)log(x/2πe)·w̄ shape.\n   b. Weak-star: μ_a(f) for smooth f vs Σ_γ f(γ)·c_γ where c_γ = candidate limit weight.\n   \n   For the limit weights: in Suzuki's Cor 1.6 world, W(a,θ;z) → const·ξ(1/2−iz)·e^{iφ(z)}-ish; the de Branges space of ξ: E_ξ(z) = ξ(1/2−iz); K_ξ(w,w) = (1/π) Im[E′(w)Ē(w)]/|E|²·|E(w)|²... the standard formula: for de Branges space H(E), K(w,w) = (1/π)(|E(w)|² φ′(w)) where φ = −arg E on ℝ (phase). Mass at zero γ: 1/K(γ,γ) = π/(|E(γ)|² φ′(γ)). With E(γ) = ξ(1/2−iγ)·... hmm zeros of E are at z with ξ(1/2−iz) = 0: 1/2−iz = ρ: z = i(ρ−1/2) = i·iγ = −γ... sign conventions; |z| = γ. E′(γ) = ξ′(1/2−iγ)·(−i). The phase φ(w) = −arg ξ(1/2−iw): φ′(w) = ... = Re[(ξ′/ξ)(1/2−iw)]·... on the line this is the argument derivative = related to the zero density: φ′(γ) ≈ log(γ/2π)/... \n\n   This gives a concrete candidate: limit mass at γ: m_γ = π/(|E(γ)|²φ′(γ)). But E(γ) = ξ(1/2−iγ) — VALUE of ξ at... wait E's zeros ARE the eigenvalues: E(γ) = 0!! For a zero of E, K(γ,γ) = (1/π)|E′(γ)|²/... no: K(w,w) = (1/π)·Im[E′(w)Ē(w)]/(...) hmm at a zero of E this is 0/0. The correct formula at a zero: K(γ,γ) = (1/π)|E′(γ)|²/|... let me just recall the clean identity: for H(E), A = (E+Ē*)/2, B = (E−Ē*)/2i (real on ℝ); zeros of the spectral problem are zeros of B (or A−B cot); K(β,β) = (1/π)(B′(β)A(β) − A′(β)B(β))/... at B-zero: K(β,β) = B′(β)A(β)/π·(1/?)... K(w,w) = (1/π)(A B′ − A′ B)(w) with normalization... Actually: K(w,w) = (1/π)(B′(w)A(w) − A′(w)B(w)) needs a 1/(2π)? conventions vary. At a zero β of B: K(β,β) = (1/π) A(β) B′(β). Hmm sign/factors to fix. \n\n   For OUR Galerkin model space the K comes straight from the computation — no formula needed. For the LIMIT comparison, compute candidate limit masses directly from ξ data: m_γ ∝ 1/(A(γ)B′(γ)) with E built from ξ. I can compute |ξ′(1/2+iγ)| via mpmath (dps 30, fine for γ ≤ 300) and the phase derivative from the zero-density or directly. Then compare the RATIO structure: e.g., correlation between w_k(a) at zeros near... but atoms don't lock to γ's (E-019: chance-level proximity). So weight-per-atom comparison is meaningless; only WEAK convergence makes sense:\n   \n   μ_a(f) := Σ_k w_k f(z_k) vs ν(f) := Σ_γ m_γ f(γ) for smooth f.\n   \n2. Which f? Gaussian bumps at fixed centers, and monotone cumulative M_a(x) vs M_∞(x) = Σ_{γ≤x} m_γ.\n\n3. Also the ORIGIN double-zero artifact: Galerkin gives a double zero at z≈0 (±iε pair) — E-016b found it; weights there will be garbage/huge. Exclude |z| < z_min (e.g. first real zero z₁ ≈ 4.5/a·... z₁·a ≈ 4.5; at a=4: z₁ ≈ 1.1; exclude |z| < 0.5·z₁ or handle explicitly).\n\n4. Normalization subtlety: weights depend on the norm scaling of T (λ shift etc.). Cross-a comparisons need a consistent gauge. The measures μ_a and ν might differ by an overall a-dependent scale; handle by normalizing: compare μ_a(f)/μ_a(1) vs ν(f)/ν(1) over the window, or fix gauge via... Total mass in window [0, Z]: Σ_k w_k — its a-scaling is itself an observable (should grow like window·density·mean-weight).\n\nAlso λ = LAM_SHIFT = −1.0 fixed across all a ✓ consistent gauge already.\n\n5. Ranges: a ∈ {4, 6, 8, 10, 12}; zeros in (0, Z_MAX = 300] (matches E-019); at a=4: #zeros ≈ aT/π = 4·300/π ≈ 382... hmm wait z-density: zeros of W ~ lattice spacing π/a → count to Z: aZ/π. At a=4, Z=300: 382 zeros; a=12: 1146. But zeros only up to... E-019: 1145/1146 at a=12 ✓.\n\nBut CAREFUL: the prime-sum backend validity: g̃ approximated with primes to e^{2a}; the symbol tail truncation error at level e^{−a}-ish (D-019: λ_a cliff; the Galerkin T_a uses primes ≤ e^{2a}). The measured zeros are zeros of the TRUNCATED model — for the measure-convergence question that's exactly what D2 wants (the a-dependent model converging to the limit).\n\n6. Runtime: build(a) at h=0.01: n = 2400a−1 → a=12: n = 28799?? WAIT: n_elem = 2a/h = 2400 at a=12, n = 2399. solve_toeplitz per zero: n = 2399, O(n²) ≈ 5.8M per solve × 1146 zeros × 5 a-values ≈ 33 Gflops — fine (~1 min). But build(a) itself: stiffness_first_col does S_full per k (n_elem quadratures) — E-016 ran a=10; E-019 a=12 — cache? _BUILD is in-memory only; each E-022 run rebuilds (E-016 sieve e^20 takes time! \"runs the e^20 sieve\" at import — E-019 noted import cost). The prime_backend_v2 monkeypatch avoids recompute. E-019 loads E16 which runs the sieve to e^20 — for a=12 need e^24 (backend v2 cache exists: E-019-backend-cache.npz). Follow E-019's pattern: import E16 (sieve e^20 ~ minutes?), then monkeypatch prime_sum_vec with PrimeBackendV2.\n\nHmm wait, actually for build(a, h) the expensive part is stiffness_first_col → S_full per k → quad over log-segments with g_float evaluations (prime_sum_vec inside). With v2 backend monkeypatched, g_float uses the fast blockwise sums ✓.\n\n7. What about θ=π only? Yes (θ=π has all-real zeros, E-016 F1; θ=0 has complex zeros — measure not real-positive).\n\n8. Checks:\n   V1 (gauge/self-consistency): weights positive, symmetric: w(−z_k) = w(z_k) — compute only z>0 and mirror.\n   V2 (refinement): at a=4, weights with h=0.01 vs h=0.005 agree to tolerance (Galerkin convergence of K).\n   V3 (cross-check vs E-016/E-019 zero lists): z₁ and count match.\n   V4 (mass scaling): total mass M_a(Z) vs a — report scaling (expect ~ linear in window count × mean weight; just document).\n   V5 (the headline): weak-convergence metric vs a — e.g., discrepancy D(a) = sup over test set |μ̂_a(f) − ν̂(f)|/ν̂(1) or Wasserstein-ish distance between cumulative mass functions normalized. If D(a) DECREASES with a: evidence of measure-level convergence (resolvent convergence signal); if flat/increasing: no convergence yet (consistent with D-035's honest reading \"nothing is converging yet\" — F5 slow increase of leftover).\n\nHmm wait, I should think about what the target measure ν actually is, more carefully, because E-016 already compared ρ_W vs ρ_F (Herglotz densities on the IMAGINARY axis) — the Herglotz density ρ_W(iy) = −Im(W′/W)(iy)/π vs ρ_F (from F = z²ξ/ξ′...). That was the zero-density comparison in Herglotz form. E-022's novelty: the WEIGHTED atomic measure on the REAL axis (spectral/Clark measure) — genuinely different observable (weights carry the operator metric, not just zero positions).\n\nFor the target: the limit de Branges space (if it exists under (★)) has E-function related to ξ. From R-003: W = F·(R₀+R_τ), F = z²ξ/ξ′... hmm — W itself converges to... M = W/F → e^{−φ}? Under (★), W ~ F·R₀·(1+o(1))... no wait (★) says M = W/F → e^{−ϕ}·... hmm: (b) function form: e^{ϕ(a,z)}M(a,θ;z) → 1. So W ≈ F·e^{−ϕ}·(1+o(1))... wait M = W/F → e^{−ϕ}? \"R_τ = −2e^{a+iθ/2}sin(az−θ/2) + e^{−ϕ(a,z)} + o(1)\" and \"e^{ϕ}M → 1\": M → e^{−ϕ}·1... anyway the LIMIT zeros are where F·(stuff) = 0 — F = z²ξ/ξ′: zeros of F on ℝ... ξ(1/2−iz) zeros at z = ±γ (γ's) — WAIT: F = z²ξ/ξ′: F has zeros where ξ = 0 (z = γ) AND poles where ξ′ = 0. Hmm, and z=0 double. So the limit measure atoms at γ's — with weights from the metric.\n\nCandidate limit weights: m_γ = 1/K_∞(γ,γ). For a first numerics pass, we don't need the exact limit theory — the informative comparisons:\n(i) normalized cumulative: M_a(x)/M_a(Z) vs N(x)/N(Z) — shape only.\n(ii) weight-profile: log w_k vs z_k smooth trend; compare to log(1/K_∞-candidate) using |ξ′(1/2+iγ)|-based candidate: K_∞(γ,γ) ∝ |ξ′|²·... I'll compute the candidate m_γ = π/(A(γ)B′(γ)) with E := F evaluated... let me define via F directly: F(z) = z²ξ(1/2−iz)/ξ′(1/2−iz) — has zeros at γ. Write F = A − iB on ℝ? F is real on... ξ(1/2−iz) for real z: ξ(1/2−iz) = conj(ξ(1/2+iz))... and ξ real on the critical line ⟹ ξ(1/2−iz) = conj(ξ(1/2+iz)): for real z, ξ(1/2+iz) = Ξ(z) real (Ξ reality, resources note) ⟹ conj = same: ξ(1/2−iz) = Ξ(z) real! So ξ(1/2−iz) is REAL for real z ✓. ξ′(1/2−iz): derivative w.r.t. its argument... (d/dz)ξ(1/2−iz) = −i ξ′(1/2−iz); and Ξ′(z) = −iξ′(1/2−iz)·... Ξ(z) real on ℝ ⟹ Ξ′(z) real on ℝ ⟹ ξ′(1/2−iz) = iΞ′(z) pure imaginary! So F(z) = z²·Ξ(z)/(iΞ′(z)) = −i·z²Ξ(z)/Ξ′(z) — pure imaginary on ℝ (away from zeros/poles). So F = −iB_F with B_F real. The de Branges E-function convention: E = A − iB with A,B real: here A = 0 (like the W case: W = 2i·g real gauge). For the spectral problem: eigenvalue equation W(z)=0 with W = 2ig → zeros of g. The masses: for the model space of W-type: K(β,β) = (1/π)·∂_z[phase]·... \n\nHonestly: rather than derive the exact limit-weight formula under time pressure, E-022 should:\n- compute μ_a (weights from Galerkin — the actual operator-defined measure);\n- compare against the UNIT-weight zero measure (counting) and against a ξ′-based candidate;\n- report convergence metrics vs a.\nThe derivation of the exact limit K_∞ can be D-038/2.9-D1 theory work. The numerics stand alone: deliver the μ_a measures + convergence metrics; the interpretation (which limit weights) can be refined analytically later. D2 says \"vs the target atomic measure at γ's\" — unit atoms at γ's is the minimal target (zero positions); weight-profile comparison is bonus.\n\nHmm, wait — actually there's a cleaner theoretical anchor I'm missing. The spectral measure μ_θ of 𝒟̄_{a,θ} satisfies: m_θ(z) = ∫ dμ/(t−z) = the Weyl m-function = ratio involving W: m_θ(z) = −(something)/(W)... For rank-one perturbation theories (Clark): m(z) = (Θ(z)−...)/(Θ(z)+...)... and crucially: residues of m at poles = masses = 1/K(z_k,z_k) ✓. So I can VALIDATE the Galerkin weights against residues of the explicit m-function built from W, V±! The m-function for Suzuki's operator: from the boundary-value form, m_θ(z) = −[V+(z)·...]/W(a,θ;z)-ish. If I can get the exact m formula: mass_k = Res_{z_k} m = numerator(z_k)/W′(z_k). I have dW machinery (W_and_dW) ✓. Need numerator... From the theory of the first-order operator with boundary condition: m_θ = −(z−i)V+... hmm W = (z−i)V+ + e^{iθ}(z+i)V−; the m-function is likely m_θ(z) = i·[(z−i)V+ − e^{iθ}(z+i)V−·...]/W — at θ=π: numerator = (z−i)V+ + (z+i)V−? Then mass_k = num(z_k)/W′(z_k). Not sure of exact form — VALIDATION by identity instead: Σ_k mass_k/(t−z_k) should equal m(iy) for sample y — check both sides numerically. If the guessed numerator is wrong, try alternatives... this could burn time. \n\nPRECISION DECISION: skip the m-function cross-check in v1; use refinement check (h=0.01 vs 0.005 at a=4) as the validation, plus unit-sanity (weights > 0, symmetric). If time permits add m-check later.\n\n**E-022 script plan:**\n\n```\nImport E16 (sieve e^20), monkeypatch backend v2 (e^24) — a=12 needs primes to e^24.\nmp.mp.dps = 40.\ngammas = first 200 zeta zeros (mp.zetazero, float ok for statistics — \n    for measure comparison at float64 level, fine; note E-021 caveat \n    applies only to high-precision binomial sums).\n\nfor a in [4, 6, 8, 10, 12]:\n    x, h, Q, M, b = E16.build(a, H)   # H=0.01\n    cp, T = solve_v(...); cm = mirrored\n    zeros = locate_zeros(...)  # z>0, brentq\n    # exclude origin artifact: keep zeros > z1/2\n    for each z_k: b_z = hat_FT(z_k,h)*exp(-1j z_k x)\n        solve bordered [T e; e' 0] u = [b_z; 0]\n        K_k = Re(b_z^H u); w_k = 1/K_k\n    record (z_k, w_k)\n    V2 refinement at a=4 with h=0.005 for first 50 zeros.\n\nObservables:\nO1: M_a(x) cumulative vs N(x)·w̄_a — correlation/shape\nO2: log w_k vs z_k trend per a (smooth? outliers?)\nO3: weak metric: f_j = Gaussian bumps at 20 log-spaced centers, σ = \n    mean gap·2: d_a = max_j |μ_a(f_j)/μ_a(1) − ν(f_j)/ν(1)| where \n    ν = unit weights at γ's in window.\nO4: compare d_a vs a → convergence rate estimate.\n```\n\nWait, one problem: the bordered solve per zero with scipy dense: n=2399 at a=12, B is (n+1)² dense solve O(n³) = 1.4×10¹⁰ per zero — too slow × 1146 zeros. Need Toeplitz-aware solve. solve_v uses scipy.linalg.solve on the bordered matrix — dense O(n³). E-016 did this only twice (cp, cm) per a. For 1146 zeros need the structure: bordered Toeplitz system. Options: use scipy.linalg.solve_toeplitz for T (O(n²) Levinson) — but the border breaks Toeplitz. Solve via Schur: with T u₀ = b_z and T u₁ = e: u = u₀ − μu₁, μ = (e·u₀)/(e·u₁). Two Toeplitz solves per zero, u₁ shared across zeros (e fixed) → per zero: ONE solve_toeplitz (b_z varies) + shared u₁. solve_toeplitz O(n²) = 5.8M flops → ×1146×5 ≈ 33 Gflop — OK (~30–60 s numpy). Levinson via solve_toeplitz allocates fine. But numerically Levinson for n=2400 SPD Toeplitz — stable enough for statistics (check residual for a few). Alternatively use the Toeplitz + conjugate: validate residuals |T u − b| small on a sample.\n\nAlso MEMORY: Q, M Toeplitz stored as full n×n dense in build() — 2399² × 8B = 46 MB each — fine.\n\nHmm wait — actually T = Q + M: Q Toeplitz from stiffness_first_col, M tridiagonal Toeplitz → T Toeplitz with first column t = q + m where m = [2h/3·...]. Careful: T = Q − λM, λ = −1: T = Q + M: t_col = q + [2h/3, h/6, 0...]. Solve with solve_toeplitz((t_col, t_col), b) — scipy solve_toeplitz takes (c, r) column+row; symmetric: c = r = t_col.\n\nValidation V-resid: for ~10 random zeros compute ‖Tu + μe − b‖/‖b‖ < 1e-8.\n\nRefinement check h=0.005 at a=4: n = 2·4/0.005−1 = 1599 — fine.\n\nRuntime estimate: builds at each a: S_full per k — E-016 did a ∈ {4,10} sweep... E-016's A_SWEEP presumably [4,10]; each build took? E-016 runtime total unknown; E-019 did a=12 build in sub-stage A validation \"full pipeline at a=10 ... Q vs E16.build(10)\" and a=12. Sieve e^20 at import (E16 module exec) — that was the slow part historically; v2 backend cache covers e^24. Total runtime maybe 10–30 min. Run detached per long-run protocol.\n\nZero location: N_GRID=40000 over (0,300] — step 0.0075; lattice spacing π/a ≥ π/12 = 0.26 ✓ safe. g evaluation: V_and_dV on 40000-point grid with E matrix 40000×2400 complex = 768 MB — hmm, E-019 did exactly this (N_GRID=40000, n=2399): 40000×2399×16B = 1.5 GB — they ran it, 64GB RAM fine.\n\nb_z vectors: hat_FT(z_k,h)·exp(−1j·z_k·x) — cheap.\n\nAlso z₁·a ≈ 4.5 law: z₁ at a=4: ≈1.125. Origin artifact: double zero at z≈0 excluded by grid start 1e-3 and... the ±iε pair is OFF-axis (Galerkin artifact: pair ±iε, ε~2–6e-9) — the real-axis grid misses it ✓ but there may be a real double-zero AT 0? E-016b: \"Galerkin W has a double zero at z≈0 (pair ±iε)\" — off-axis by tiny ε. g(0) = 0·ImV − ReV(0) = −ReV(0); V(0) = hat_FT(0)·Σcp — generically nonzero. So no real zero at 0 located. But the FIRST located real zero z₁ — E-016 family ✓.\n\nEdge: g grid sign-change detection misses tangent double zeros — W's real zeros are simple (interlacing, E-016 F1) except the origin artifact. Fine.\n\nAlso for O1/O3 need γ's up to Z=300: first ~200 zeros (N(300) ≈ 300/2π·log(300/2πe) ≈ 47.7·log(47.7/e·...) ≈ 47.7·2.86 ≈ 136; take 200 to be safe — mp.zetazero fast).\n\nLet me also decide the weak-metric window: z ∈ (0, Z] with Z = 300 but exclude the top edge (located zeros truncated at 300; measure sums need complete neighborhoods — use f centers up to 250, and cumulative to 280; and Z_MAX=300 grid; fine).\n\nOne more: μ_a(1) total mass — includes only located zeros in window; normalized metrics handle it.\n\nAnd gauge: weights have absolute scale from T (λ=−1 shift, h-normalization) — consistent across a since same conventions; but the b_z load has hat_FT factor (h·sinc²) — h same for all a ✓ consistent. Actually hmm: K computed as b_z^H u where u from bordered solve: as h→0 this converges to ⟨e_z, T_a^{-1}e_z⟩ ✓ (Galerkin consistent).\n\nDeliverables: CSV rows (a, k, z_k, w_k), JSON with metrics (d_a per a, mass totals, refinement error, V checks). Then D-file? The analysis write-up can go into the experiment JSON + a short D-038? No — D-038 is reserved for task 2.11 (transition scale). E-022 findings get summarized in the journal + maybe feed D-035 D2 conclusion. Per protocol: experiment = E-022 with CSV/JSON + db registration; analysis conclusions in journal + NGUYEN/STATE; if substantial, a derivation file D-039... let me keep D-039 for the digamma upgrade as already planned — hmm, I used \"D-039 (digamma)\" in D-037 follow-ups. So E-022 analysis → could be filed as part of D-035 D2 completion note in journal + index. If the analysis is meaty, file as D-040 to avoid collision. Decide after seeing results.\n\nBefore writing the script, double-check the bordered-solve Schur decomposition against solve_v: solve_v returns sol[:n] solving B [v;μ] = [b;0]: T v + e μ = b, e·v = 0. My version: u₀ = T⁻¹b, u₁ = T⁻¹e; v = u₀ − μ u₁ with μ = (e·u₀)/(e·u₁). Check: T v + eμ = b − μe + μe = b ✓; e·v = e·u₀ − μ(e·u₁) = 0 ✓. And K = Re(b_z^H v). For cp validation: my Schur solve for b[+1] should reproduce E16.solve_v's cp — quick check in-script (V0: max |v_schur − cp| < tol). \n\nWait, actually — e = ones(n): e·v = Σ v_j = 0 — this is a nodal-sum constraint (approx mean-zero via mass lumping? not exactly M-inner-product-zero but close enough — it's what the machinery uses; consistency is what matters).\n\nAlso: b_z for z=0? Not needed (no zero at 0).\n\nAlso should I compute weights for z_k AND −z_k? Symmetric by construction (mirror); compute z>0 only, mirror in analysis.\n\nLet me also confirm E-016's A_SWEEP / Z_MAX_ZERO and whether E-016 stored located zeros to CSV (for V3 cross-check at a=10). E-016 CSV rows are Herglotz densities; zeros used internally for V3. E-019 CSV has a=12 zero data. V3: compare a=10 z₁ and count vs E-016... E-016 ran a ∈ {4,10}? A_SWEEP at top of E-016 — check. Whatever: V3 = match z₁(a) against the stiff law z₁·a ≈ 4.495 (E-019) and counts ≈ aZ/π (maximal lattice) — sufficient.\n\nRuntime concern: E16 import runs the e^20 sieve — E-019 noted \"runs the e^20 sieve\" at import. How long? mangoldt_sieve_fast(e^20)? e^20 = 4.85×10⁸ sieve — E-016 did it; time maybe 1–5 min. Then monkeypatch to v2 (e^24 cache). Fine detached.\n\nActually — do I even need backend v2/e^24? build(a) uses g_float(t) for t ≤ 2a: prime_sum_vec(t) = sums over primes ≤ e^t ≤ e^{2a}: a=12 → e^24 needed ✓ v2 required. a ≤ 10 → e^20 = in-memory sieve OK. Use v2 for all (E-019 pattern).\n\nOne more design point: h. E-016/E-019 used H_MAIN = 0.01. n = 2a/h − 1: a=12 → 2399 ✓. Toeplitz solves O(n²)·#zeros — at a=12: n² = 5.75M × 1146 zeros = 6.6G × 2 (complex128) — ~minutes. OK.\n\nAlso — V2 refinement at a=4 h=0.005: rebuild at h=0.005 (stiffness_first_col cost ~2× of h=0.01 build at a=4) fine.\n\nWrite the script now. Structure:\n\n```python\n\"\"\"E-022 — task 2.10 (D-035 deliverable D2): spectral-measure convergence.\n...\n\"\"\"\nimport importlib.util, json, sys, time\nfrom pathlib import Path\nimport mpmath as mp, numpy as np, scipy.optimize, scipy.linalg\nfrom scipy.signal import ... no\n\nload E16, backend v2 monkeypatch\nA_LIST = [4,6,8,10,12]; H = E16.H_MAIN; LAM = E16.LAM_SHIFT; Z_MAX=300; N_GRID=40000\nmp.mp.dps = 40\n\ndef bordered_toeplitz_setup(T_col):\n    u1 = solve_toeplitz((T_col, T_col), ones)\n    return u1, e·u1\n\ndef solve_bordered(T_col, u1, eTu1, b):\n    u0 = solve_toeplitz((T_col,T_col), b)\n    mu = (u0.sum())/eTu1 ... wait e·u₀ = sum(u0) (e=ones); complex sum fine.\n    return u0 - (u0.sum()/eTu1_sum)*u1  # hmm: e·u₁ = u1.sum()\n    \nper a:\n    x, h, Q, M, b = E16.build(a, H)\n    n = len(x); q_col = Q[:,0]; m_col = [2h/3, h/6, 0...] \n    T_col = q_col - LAM*m_col  (LAM=-1 → +)\n    Verify vs solve_v: cp_check via solve_bordered(b[+1]) vs E16.solve_v → V0\n    cp, cm for zeros (use E16.solve_v directly — consistent with E-016/019)\n    zeros = locate (same as E-019 locate_zeros)\n    weights: for each z_k: b_z = hat_FT(z_k,h)*exp(-1j z_k x); v = solve_bordered; K = Re(conj(b_z) @ v); w = 1/K\n    store\n\nrefinement at a=4, h=0.005, first 50 zeros → rel dev\n\nmetrics + JSON/CSV\n```\n\nWait — Q[:,0] full column: build returns Q as full dense Toeplitz; take column 0 ✓. M likewise tridiagonal Toeplitz: M[0,:2] etc: m_col = [2h/3, h/6, 0,...] ✓ (from build: M = toeplitz([2h/3, h/6, 0...])). T = Q − LAM·M ✓ matches solve_v's T = Q − lam*M.\n\nV0 check: solve_bordered(T_col, b[+1.0]) vs E16.solve_v(Q,M,b[+1],LAM,n) → max dev < 1e-8.\n\nWeight positivity: K > 0 (T SPD on constrained subspace; K should be > 0). Count K ≤ 0 as failures.\n\nZero locate: reuse E-019's collect_t_points + g grid approach — simpler: copy E-019's locate_zeros (grid sign-change + brentq with g_direct). It needs cp and x, h. ✓ \n\ngamma zeros: mp.zetazero(k).imag for k=1..200 at dps 30 → float64 list ✓ (fine for statistics).\n\nMetrics:\n- O1: normalized cumulative shape: for grid xs: M_a(x)/M_a(Z') vs N_γ(x)/N_γ(Z') (unit weights), Z' = 280: L∞ and L2 discrepancy.\n- O2: weight profile: log w vs z — report slope/spread per a (linear fit).\n- O3: Gaussian bumps: centers 30 log-spaced in [15, 250], σ = 2π·(2/log...) — σ = 2·mean local gap ≈ 2·2π/log(center/2π)... use fixed σ = 3·(2π/log(50/2π)) ≈ 9? Keep simple: σ = 8 fixed. d_a = max_j |μ̃_a(f_j) − ν̃(f_j)| with μ̃ = μ(f)/μ(1-window)... normalize by total mass in [0,Z']: μ̃_a(f) = μ_a(f)/M_a(Z'), ν̃(f) = ν(f)/N_γ(Z').\n- O4: d_a vs a table; fit d_a ~ c·a^{−ρ} or exp(−ρa).\n\nAlso record total mass M_a(Z') scaling.\n\nEdge cases: zeros located up to 300 but weighted measure uses zeros ≤ 280 for metrics.\n\nAlso — IMPORTANT: are weights w_k = 1/K_k with K from the CONSTRAINED solve the right spectral masses? The constrained space L²_0: evaluation functional restricted; reproducing kernel of the constrained model space: k_z = P(T^{-1}e_z) with P the projection... The masses = 1/‖k_z‖²_T. Our bordered solve gives v ⟂ e (in lumped sense) with T v + μe = b_z: v = T^{-1}(b_z − μe): the representer IN the constrained space of the restricted functional. ‖v‖²_T = v^H T v = v^H(b_z − μe) = v^H b_z − μ v^H e = v^H b_z (since e·v = 0) = conj(b_z^H v)... K = Re(b_z^H v) ✓ = Re(v^H b_z). ✓ consistent.\n\nLet me also double-check hat_FT sign/convention: b_z[j] should approximate ∫ e^{−izx} φ_j dx = e^{−iz x_j}·h·sinc²(zh/2π) = hat_FT(z,h)·e^{−izx_j} ✓ (hat_FT = h·sinc² ✓ real).\n\nBut NOTE: build's b[±1] loads were computed with EXACT quadrature of e^{±x}φ_j (leggauss 8), not the sinc² formula — because e^{±x} isn't oscillatory the sinc² approx... anyway for e^{±x} they used quadrature; for e^{−izx} I use the exact-integrable form: ∫ e^{−izx}φ_j dx has a CLOSED form = e^{−izx_j}·h·sinc²(zh/2π)·(correction?) — let me verify: φ_j piecewise linear hat; ∫_{x_j−h}^{x_j} e^{−izx}(x−(x_j−h))/h dx + ∫_{x_j}^{x_j+h} e^{−izx}((x_j+h)−x)/h dx. Substitute u = x−x_j: ∫_{−h}^{0} e^{−izu}(u+h)/h du + ∫_{0}^{h} e^{−izu}(h−u)/h du = ∫_{−h}^{h} e^{−izu}(1−|u|/h) du = 2∫_0^h cos(zu)(1−u/h)du = 2[sin(zh)/z − (1/h)·(sin(zh)/z²·z − ...)] let me just compute: ∫_0^h cos(zu)du = sin(zh)/z. ∫_0^h u cos(zu) du = [u sin(zu)/z]_0^h + [cos(zu)/z²]_0^h = h sin(zh)/z + (cos(zh)−1)/z². So 2[sin(zh)/z − (1/h)(h sin(zh)/z + (cos(zh)−1)/z²)] = 2[sin(zh)/z − sin(zh)/z − (cos(zh)−1)/(hz²)] = 2(1−cos(zh))/(hz²) = 4 sin²(zh/2)/(h z²) = h·[sin(zh/2)/(zh/2)]² ✓ = h·sinc²(zh/(2π)) exactly (numpy sinc(x) = sin(πx)/(πx): sinc(zh/2π) = sin(zh/2)/(zh/2) ✓). EXACT — great, b_z = hat_FT(z,h)·e^{−izx} exact for the hat basis.\n\nThis is consistent with V_and_dV using hat_FT for the P1 interpolant's FT ✓ — the whole Galerkin FT machinery uses this factor.\n\nNOW — one more sanity issue: cp are nodal values of v_+, and V+(z) = ∫ e^{izx} v_+(x)dx ≈ hat_FT·Σ e^{izx_j}cp_j. But my b_z convention uses e^{−izx}: V+(z) uses e^{+izx}. For the weight at z_k: K(z_k,z_k) = ⟨T^{-1}e_{?}, e_{?}⟩ — which sign? ‖v_z‖²_T = ⟨v_z, e^{−izx}⟩ where T v_z = e^{−izx}. V+ machinery uses e^{+izx} — by symmetry (z→−z, conj) K(z,z) = K(−z,−z) so sign choice is consistent as long as I'm uniform; use b_z = hat_FT·e^{−izx} per the definition. ✓\n\nOK. Also compute for validation at a=10: compare zeros with E-016 family... skip (z₁·a law check suffices).\n\nTotal runtime: builds (S_full × n_elem per a) — E-016 at a=10 took how long? Unknown; E-019 (a=10 rebuild + a=12 build + zeros) ran in some minutes. With v2 backend it should be faster. Detached run with log.\n\nActually, one thing to double check: E16 import runs the sieve to e^20 (\"runs the e^20 sieve\" comment at E-019 import). mangoldt_sieve_fast(n_max) with n_max = e^20 = 4.85e8: sieve of that size — E-016 ran it once (minutes). It computes C1, C0 cumsum arrays of size 4.85e8 × 8B = 3.9 GB ×2 = 7.8 GB — hmm big but 64GB fine. E-019 did this import. OK.\n\nThen backend v2: PrimeBackendV2 — monkeypatch E16.prime_sum_vec and E16._BUILD.clear() per STATE.md note. E-019 line: \"from prime_backend_v2 import PrimeBackendV2\" then presumably monkeypatches. Let me check how E-019 does it (need exact pattern) — grep.","signature":""},{"type":"text","text":"I have the full machinery picture. Let me check how E-019 patches in backend v2, then write E-022."},{"type":"tool_use","id":"Bash_84","name":"Bash","input":{"command":"cd /d/Python/Quantum && grep -n \"PrimeBackendV2\\|prime_sum_vec\\|_BUILD\" research/numerics/scripts/E-019-zeroflow-a12.py | head && sed -n '/backend = /,+8p' research/numerics/scripts/E-019-zeroflow-a12.py | head -12 && grep -n \"Z_MAX_ZERO\\|A_SWEEP\\|GL_N\" research/numerics/scripts/E-016-herglotz-fine-structure.py | head -6","description":"Check backend v2 monkeypatch pattern in E-019"},"extra_content":null}]