E-009-suzuki-galerkin.py
Experiments: E-009b · script updated 08-18-2026
E-009b-suzuki-galerkin.csv
— 7.2 KB, updated 08-18-2026
page 1 of 1
· 500/page · 120 rows
| # | a | h | N | j | lambda_j | gamma_j | lambda_over_gamma2 |
|---|---|---|---|---|---|---|---|
| 2 | 3.0 | 0.01 | 600 | 1 | 8.8341536551e-11 | 14.1347251417 | 4.421710e-13 |
| 3 | 3.0 | 0.01 | 600 | 2 | 3.4305776913e-10 | 21.0220396388 | 7.762785e-13 |
| 4 | 3.0 | 0.01 | 600 | 3 | 2.8457268373e-09 | 25.0108575801 | 4.549211e-12 |
| 5 | 3.0 | 0.01 | 600 | 4 | 2.9076621594e-09 | 30.4248761259 | 3.141133e-12 |
| 6 | 3.0 | 0.01 | 600 | 5 | 5.1728425615e-09 | 32.9350615877 | 4.768835e-12 |
| 7 | 3.0 | 0.01 | 600 | 6 | 1.8364578842e-08 | 37.5861781588 | 1.299944e-11 |
| 8 | 3.0 | 0.01 | 600 | 7 | 1.9516852046e-08 | 40.9187190121 | 1.165643e-11 |
| 9 | 3.0 | 0.01 | 600 | 8 | 2.7019811024e-08 | 43.3270732809 | 1.439341e-11 |
| 10 | 3.0 | 0.01 | 600 | 9 | 5.9424692535e-08 | 48.0051508812 | 2.578643e-11 |
| 11 | 3.0 | 0.01 | 600 | 10 | 8.2294095248e-08 | 49.7738324777 | 3.321747e-11 |
| 12 | 3.0 | 0.01 | 600 | 11 | 1.2519278693e-07 | 52.9703214777 | 4.461841e-11 |
| 13 | 3.0 | 0.01 | 600 | 12 | 1.5658535698e-07 | 56.4462476971 | 4.914519e-11 |
| 14 | 3.0 | 0.005 | 1200 | 1 | 1.1411808070e-11 | 14.1347251417 | 5.711889e-14 |
| 15 | 3.0 | 0.005 | 1200 | 2 | 4.1830302154e-11 | 21.0220396388 | 9.465451e-14 |
| 16 | 3.0 | 0.005 | 1200 | 3 | 1.5381578784e-10 | 25.0108575801 | 2.458916e-13 |
| 17 | 3.0 | 0.005 | 1200 | 4 | 2.2306921279e-10 | 30.4248761259 | 2.409806e-13 |
| 18 | 3.0 | 0.005 | 1200 | 5 | 5.1818144785e-10 | 32.9350615877 | 4.777106e-13 |
| 19 | 3.0 | 0.005 | 1200 | 6 | 5.9518864837e-10 | 37.5861781588 | 4.213066e-13 |
| 20 | 3.0 | 0.005 | 1200 | 7 | 1.3235087349e-09 | 40.9187190121 | 7.904652e-13 |
| 21 | 3.0 | 0.005 | 1200 | 8 | 3.0799956551e-09 | 43.3270732809 | 1.640708e-12 |
| 22 | 3.0 | 0.005 | 1200 | 9 | 4.3404966283e-09 | 48.0051508812 | 1.883492e-12 |
| 23 | 3.0 | 0.005 | 1200 | 10 | 6.4400545580e-09 | 49.7738324777 | 2.599485e-12 |
| 24 | 3.0 | 0.005 | 1200 | 11 | 6.5381966032e-09 | 52.9703214777 | 2.330198e-12 |
| 25 | 3.0 | 0.005 | 1200 | 12 | 1.1868524182e-08 | 56.4462476971 | 3.725002e-12 |
| 26 | 3.0 | 0.002 | 3000 | 1 | 5.0709390980e-13 | 14.1347251417 | 2.538129e-15 |
| 27 | 3.0 | 0.002 | 3000 | 2 | 1.3135044576e-12 | 21.0220396388 | 2.972226e-15 |
| 28 | 3.0 | 0.002 | 3000 | 3 | 4.2419330052e-12 | 25.0108575801 | 6.781201e-15 |
| 29 | 3.0 | 0.002 | 3000 | 4 | 7.1310244528e-12 | 30.4248761259 | 7.703610e-15 |
| 30 | 3.0 | 0.002 | 3000 | 5 | 9.4147616410e-12 | 32.9350615877 | 8.679453e-15 |
| 31 | 3.0 | 0.002 | 3000 | 6 | 2.5702506391e-11 | 37.5861781588 | 1.819362e-14 |
| 32 | 3.0 | 0.002 | 3000 | 7 | 6.1709065159e-11 | 40.9187190121 | 3.685572e-14 |
| 33 | 3.0 | 0.002 | 3000 | 8 | 6.7968034831e-11 | 43.3270732809 | 3.620646e-14 |
| 34 | 3.0 | 0.002 | 3000 | 9 | 1.2928146120e-10 | 48.0051508812 | 5.609970e-14 |
| 35 | 3.0 | 0.002 | 3000 | 10 | 1.4187151856e-10 | 49.7738324777 | 5.726550e-14 |
| 36 | 3.0 | 0.002 | 3000 | 11 | 2.2569013685e-10 | 52.9703214777 | 8.043543e-14 |
| 37 | 3.0 | 0.002 | 3000 | 12 | 4.1939694369e-10 | 56.4462476971 | 1.316301e-13 |
| 38 | 0.25 | 0.005 | 100 | 1 | 4.1385415107e-01 | 14.1347251417 | 2.071441e-03 |
| 39 | 0.25 | 0.005 | 100 | 2 | 8.8062105755e-01 | 21.0220396388 | 1.992688e-03 |
| 40 | 0.25 | 0.005 | 100 | 3 | 1.2584155373e+00 | 25.0108575801 | 2.011717e-03 |
| 41 | 0.25 | 0.005 | 100 | 4 | 1.4951773833e+00 | 30.4248761259 | 1.615233e-03 |
| 42 | 0.25 | 0.005 | 100 | 5 | 1.7096331335e+00 | 32.9350615877 | 1.576108e-03 |
| 43 | 0.25 | 0.005 | 100 | 6 | 1.8694708811e+00 | 37.5861781588 | 1.323312e-03 |
| 44 | 0.25 | 0.005 | 100 | 7 | 2.0194637076e+00 | 40.9187190121 | 1.206124e-03 |
| 45 | 0.25 | 0.005 | 100 | 8 | 2.1403129477e+00 | 43.3270732809 | 1.140141e-03 |
| 46 | 0.25 | 0.005 | 100 | 9 | 2.2556782651e+00 | 48.0051508812 | 9.788169e-04 |
| 47 | 0.25 | 0.005 | 100 | 10 | 2.3528973961e+00 | 49.7738324777 | 9.497315e-04 |
| 48 | 0.25 | 0.005 | 100 | 11 | 2.4466379449e+00 | 52.9703214777 | 8.719759e-04 |
| 49 | 0.25 | 0.005 | 100 | 12 | 2.5279893340e+00 | 56.4462476971 | 7.934235e-04 |
| 50 | 0.5 | 0.005 | 200 | 1 | 2.0695852906e-04 | 14.1347251417 | 1.035878e-06 |
| 51 | 0.5 | 0.005 | 200 | 2 | 1.2625766973e-02 | 21.0220396388 | 2.856986e-05 |
| 52 | 0.5 | 0.005 | 200 | 3 | 3.7097697939e-01 | 25.0108575801 | 5.930479e-04 |
| 53 | 0.5 | 0.005 | 200 | 4 | 8.3135243770e-01 | 30.4248761259 | 8.981059e-04 |
| 54 | 0.5 | 0.005 | 200 | 5 | 8.9365080886e-01 | 32.9350615877 | 8.238552e-04 |
| 55 | 0.5 | 0.005 | 200 | 6 | 1.1041593368e+00 | 37.5861781588 | 7.815836e-04 |
| 56 | 0.5 | 0.005 | 200 | 7 | 1.2222396489e+00 | 40.9187190121 | 7.299823e-04 |
| 57 | 0.5 | 0.005 | 200 | 8 | 1.4485522084e+00 | 43.3270732809 | 7.716413e-04 |
| 58 | 0.5 | 0.005 | 200 | 9 | 1.4933655977e+00 | 48.0051508812 | 6.480231e-04 |
| 59 | 0.5 | 0.005 | 200 | 10 | 1.4977096561e+00 | 49.7738324777 | 6.045406e-04 |
| 60 | 0.5 | 0.005 | 200 | 11 | 1.7124338881e+00 | 52.9703214777 | 6.103073e-04 |
| 61 | 0.5 | 0.005 | 200 | 12 | 1.7607918501e+00 | 56.4462476971 | 5.526343e-04 |
| 62 | 1.0 | 0.005 | 400 | 1 | 1.4413606343e-09 | 14.1347251417 | 7.214362e-12 |
| 63 | 1.0 | 0.005 | 400 | 2 | 4.7171357088e-09 | 21.0220396388 | 1.067404e-11 |
| 64 | 1.0 | 0.005 | 400 | 3 | 1.5856930254e-08 | 25.0108575801 | 2.534907e-11 |
| 65 | 1.0 | 0.005 | 400 | 4 | 3.6065316943e-08 | 30.4248761259 | 3.896118e-11 |
| 66 | 1.0 | 0.005 | 400 | 5 | 6.8804592450e-08 | 32.9350615877 | 6.343084e-11 |
| 67 | 1.0 | 0.005 | 400 | 6 | 1.9275315896e-07 | 37.5861781588 | 1.364411e-10 |
| 68 | 1.0 | 0.005 | 400 | 7 | 5.1682570047e-07 | 40.9187190121 | 3.086740e-10 |
| 69 | 1.0 | 0.005 | 400 | 8 | 7.7527060766e-07 | 43.3270732809 | 4.129853e-10 |
| 70 | 1.0 | 0.005 | 400 | 9 | 6.0232348880e-06 | 48.0051508812 | 2.613690e-09 |
| 71 | 1.0 | 0.005 | 400 | 10 | 2.1597377513e-04 | 49.7738324777 | 8.717639e-08 |
| 72 | 1.0 | 0.005 | 400 | 11 | 8.9508996300e-03 | 52.9703214777 | 3.190079e-06 |
| 73 | 1.0 | 0.005 | 400 | 12 | 1.6167336723e-01 | 56.4462476971 | 5.074209e-05 |
| 74 | 2.0 | 0.005 | 800 | 1 | 7.4359575903e-11 | 14.1347251417 | 3.721878e-13 |
| 75 | 2.0 | 0.005 | 800 | 2 | 2.1537876458e-10 | 21.0220396388 | 4.873637e-13 |
| 76 | 2.0 | 0.005 | 800 | 3 | 5.4678341445e-10 | 25.0108575801 | 8.740941e-13 |
| 77 | 2.0 | 0.005 | 800 | 4 | 1.2187888685e-09 | 30.4248761259 | 1.316652e-12 |
| 78 | 2.0 | 0.005 | 800 | 5 | 3.6782666664e-09 | 32.9350615877 | 3.390988e-12 |
| 79 | 2.0 | 0.005 | 800 | 6 | 5.8521858554e-09 | 37.5861781588 | 4.142493e-12 |
| 80 | 2.0 | 0.005 | 800 | 7 | 8.7139115909e-09 | 40.9187190121 | 5.204381e-12 |
| 81 | 2.0 | 0.005 | 800 | 8 | 1.3436406310e-08 | 43.3270732809 | 7.157550e-12 |
| 82 | 2.0 | 0.005 | 800 | 9 | 2.3655570948e-08 | 48.0051508812 | 1.026497e-11 |
| 83 | 2.0 | 0.005 | 800 | 10 | 3.4885145420e-08 | 49.7738324777 | 1.408116e-11 |
| 84 | 2.0 | 0.005 | 800 | 11 | 4.0526655553e-08 | 52.9703214777 | 1.444360e-11 |
| 85 | 2.0 | 0.005 | 800 | 12 | 6.7708029755e-08 | 56.4462476971 | 2.125054e-11 |
| 86 | 3.0 | 0.005 | 1200 | 1 | 1.1411808070e-11 | 14.1347251417 | 5.711889e-14 |
| 87 | 3.0 | 0.005 | 1200 | 2 | 4.1830302154e-11 | 21.0220396388 | 9.465451e-14 |
| 88 | 3.0 | 0.005 | 1200 | 3 | 1.5381578784e-10 | 25.0108575801 | 2.458916e-13 |
| 89 | 3.0 | 0.005 | 1200 | 4 | 2.2306921279e-10 | 30.4248761259 | 2.409806e-13 |
| 90 | 3.0 | 0.005 | 1200 | 5 | 5.1818144785e-10 | 32.9350615877 | 4.777106e-13 |
| 91 | 3.0 | 0.005 | 1200 | 6 | 5.9518864837e-10 | 37.5861781588 | 4.213066e-13 |
| 92 | 3.0 | 0.005 | 1200 | 7 | 1.3235087349e-09 | 40.9187190121 | 7.904652e-13 |
| 93 | 3.0 | 0.005 | 1200 | 8 | 3.0799956551e-09 | 43.3270732809 | 1.640708e-12 |
| 94 | 3.0 | 0.005 | 1200 | 9 | 4.3404966283e-09 | 48.0051508812 | 1.883492e-12 |
| 95 | 3.0 | 0.005 | 1200 | 10 | 6.4400545580e-09 | 49.7738324777 | 2.599485e-12 |
| 96 | 3.0 | 0.005 | 1200 | 11 | 6.5381966032e-09 | 52.9703214777 | 2.330198e-12 |
| 97 | 3.0 | 0.005 | 1200 | 12 | 1.1868524182e-08 | 56.4462476971 | 3.725002e-12 |
| 98 | 4.0 | 0.005 | 1600 | 1 | 5.6202942125e-12 | 14.1347251417 | 2.813094e-14 |
| 99 | 4.0 | 0.005 | 1600 | 2 | 9.4944621121e-12 | 21.0220396388 | 2.148427e-14 |
| 100 | 4.0 | 0.005 | 1600 | 3 | 3.0009005432e-11 | 25.0108575801 | 4.797273e-14 |
| 101 | 4.0 | 0.005 | 1600 | 4 | 1.2604148779e-10 | 30.4248761259 | 1.361620e-13 |
| 102 | 4.0 | 0.005 | 1600 | 5 | 1.6423964988e-10 | 32.9350615877 | 1.514123e-13 |
| 103 | 4.0 | 0.005 | 1600 | 6 | 2.4614584450e-10 | 37.5861781588 | 1.742353e-13 |
| 104 | 4.0 | 0.005 | 1600 | 7 | 2.5701618262e-10 | 40.9187190121 | 1.535028e-13 |
| 105 | 4.0 | 0.005 | 1600 | 8 | 3.6803788240e-10 | 43.3270732809 | 1.960531e-13 |
| 106 | 4.0 | 0.005 | 1600 | 9 | 5.9481580364e-10 | 48.0051508812 | 2.581112e-13 |
| 107 | 4.0 | 0.005 | 1600 | 10 | 7.1429936616e-10 | 49.7738324777 | 2.883222e-13 |
| 108 | 4.0 | 0.005 | 1600 | 11 | 2.4486695413e-09 | 52.9703214777 | 8.727000e-13 |
| 109 | 4.0 | 0.005 | 1600 | 12 | 3.4965881677e-09 | 56.4462476971 | 1.097424e-12 |
| 110 | 5.0 | 0.005 | 2000 | 1 | 1.6846008247e-12 | 14.1347251417 | 8.431838e-15 |
| 111 | 5.0 | 0.005 | 2000 | 2 | 5.7299645093e-12 | 21.0220396388 | 1.296589e-14 |
| 112 | 5.0 | 0.005 | 2000 | 3 | 1.0076121548e-11 | 25.0108575801 | 1.610780e-14 |
| 113 | 5.0 | 0.005 | 2000 | 4 | 1.0238795505e-11 | 30.4248761259 | 1.106092e-14 |
| 114 | 5.0 | 0.005 | 2000 | 5 | 5.1901744408e-11 | 32.9350615877 | 4.784813e-14 |
| 115 | 5.0 | 0.005 | 2000 | 6 | 1.3190663804e-10 | 37.5861781588 | 9.337064e-14 |
| 116 | 5.0 | 0.005 | 2000 | 7 | 1.5600693823e-10 | 40.9187190121 | 9.317510e-14 |
| 117 | 5.0 | 0.005 | 2000 | 8 | 1.7584702234e-10 | 43.3270732809 | 9.367341e-14 |
| 118 | 5.0 | 0.005 | 2000 | 9 | 2.2142028485e-10 | 48.0051508812 | 9.608193e-14 |
| 119 | 5.0 | 0.005 | 2000 | 10 | 3.2796865734e-10 | 49.7738324777 | 1.323824e-13 |
| 120 | 5.0 | 0.005 | 2000 | 11 | 3.6743090385e-10 | 52.9703214777 | 1.309515e-13 |
| 121 | 5.0 | 0.005 | 2000 | 12 | 6.1328650058e-10 | 56.4462476971 | 1.924834e-13 |
E-009b-suzuki-galerkin.json
— 2.5 KB, updated 08-18-2026
JSON sidecar (click to expand)
{
"experiment": "E-009b",
"task": "3.4c (D-018 step 2): Galerkin spectrum of Suzuki A_a",
"description": "P1-hat Galerkin discretization of the Weil-form operator A_a (Friedrichs ext of D*G_aD, mean-zero constraint); Toeplitz stiffness via exact piecewise Gauss-Legendre quadrature of g; bottom 12 eigenvalues; absolute validation vs zero-side explicit formula",
"script": "research/numerics/scripts/E-009-suzuki-galerkin.py",
"parameters": {
"a_sweep": [
0.25,
0.5,
1.0,
2.0,
3.0,
4.0,
5.0
],
"h_sweep": 0.005,
"h_convergence_at_a": 3.0,
"h_conv": [
0.01,
0.005,
0.002
],
"n_eig": 12,
"gl_points": 12
},
"checks": {
"s_values_vs_mpmath": true,
"weil_form_value_matches_zero_side": true,
"eigenvalues_ge_minus_noise_floor": true,
"h_convergence_gaps_shrink": true,
"lambda1_nonincreasing_in_a": true
},
"key_values": {
"min_eigenvalue": 5.070939098005517e-13,
"weil_validation": {
"vTQv": 0.0002437005343030248,
"zero_side": 0.000243701628372111
},
"h_conv_lambda1": {
"0.01": 8.834153655139757e-11,
"0.005": 1.141180807029745e-11,
"0.002": 5.070939098005517e-13
},
"lambda1_vs_a": {
"0.25": 0.41385415106618084,
"0.5": 0.00020695852905775012,
"1.0": 1.4413606342760227e-09,
"2.0": 7.43595759028353e-11,
"3.0": 1.141180807029745e-11,
"4.0": 5.6202942124986806e-12,
"5.0": 1.6846008247033063e-12
},
"lambda2_vs_a": {
"0.25": 0.8806210575518238,
"0.5": 0.01262576697324307,
"1.0": 4.71713570880733e-09,
"2.0": 2.1537876457934212e-10,
"3.0": 4.183030215443876e-11,
"4.0": 9.494462112107129e-12,
"5.0": 5.729964509284219e-12
}
},
"caveats": [
"bottom eigenvalue lambda_a is POSITIVE but very small (~1e-6 scale at a<=5) — earlier table-quadrature version showed spurious -1e-4 values from 1e-3-mesh interpolation noise amplified by 1/h^2; fixed by exact quadrature",
"eigenvalues of A_a are NOT the zero ordinates: the zeta-aligned operator is Dbar_{a,theta} (first-order, in the T_a-inner-product space) — its spectrum = zeros of W(a,theta;z) (eq 1.11), a separate computation (E-010 candidate)",
"mean-zero constraint enforced (theorem's operator is on L^2_0)"
],
"elapsed_seconds": 224.5,
"timestamp_utc": "2026-08-18T03:00:26.350580+00:00",
"row_count": 120
}
Source code
Python script (click to expand)
"""E-009b — Task 3.4c (D-018 plan step 2): Galerkin spectrum of Suzuki's A_a.
Operator (Suzuki arXiv:2606.09096v1, Thm 1.1): A_a = Friedrichs extension
of B_a = D* G_a D on L^2_0(-a,a) (mean-zero), D = i d/dx Dirichlet, G =
convolution with the screw function g (E-009a). Quadratic form (eq. 2.9):
Q(u,v) = int int g(x-y) u'(x) v'(y) dx dy (u,v in H^1_0(-a,a))
(the mean projection P_a drops out since int v' = 0 for H^1_0 functions).
Galerkin: P1 hat functions on a uniform mesh of [-a,a], N elements,
h = 2a/N. Convolution kernel + uniform mesh => Toeplitz stiffness:
Q_{i,i+k} = (1/h^2) (2 S_k - S_{k+1} - S_{|k-1|}),
S(k) = int_{-h}^{h} (h-|s|) g(kh+s) ds.
S(k) is computed by DIRECT piecewise Gauss-Legendre quadrature of g
(split at the prime-sum kinks log n; 12-pt GL per sub-interval; mpmath
lerchphi only for arguments t <= 0.35). An earlier version integrated the
regular part from the E-009a 1e-3 table; interpolation error ~1e-8 in g
was amplified by the 1/h^2 = 4e4 factor into ~1e-6 eigenvalue noise that
masked the true (tiny, positive) bottom of the spectrum. Direct quadrature
pushes S(k) errors below 1e-13.
Eigenproblem: Q c = lambda M c on the mean-zero subspace (QR projection),
M = hat mass matrix. Under RH (zeros on the line), Weil positivity makes
Q_W^a PSD, so all eigenvalues must be >= 0 (up to solver noise); the
bottom eigenvalue is Suzuki's lambda_a (Thm 1.4: lambda_a = log(1/a)+O(1)
for small a).
Absolute validation: for v = sin(pi(x+a)/a) (H^1_0, mean-zero), the
zero-side of the explicit formula gives Q(v,v) = 2 sum_{gamma>0}
|vhat(gamma)|^2 = 2.437e-4 at a=3 (E-002 zeros; tail ~1e-11); checked
against v^T Q v (validated conventions: Gaussian explicit-formula test
closed to 9e-16, session-003 diagnostic).
Runs:
* h-convergence at a = 3: h = 0.01, 0.005, 0.002
* a-sweep at h = 0.005: a = 1, 2, 3, 4, 5
Internal check: S(k) vs mpmath reference quadrature (abs tol 1e-12).
Outputs: research/numerics/data/E-009b-suzuki-galerkin.csv (+ .json)
"""
import json
import sys
import time
from datetime import datetime, timezone
from pathlib import Path
import numpy as np
import scipy.linalg
import mpmath as mp
from numpy.polynomial.legendre import leggauss
REPO = Path(__file__).resolve().parents[3]
DATA = REPO / "research" / "numerics" / "data"
G_CSV = DATA / "E-009-suzuki-screw-function.csv"
ZEROS_CSV = DATA / "E-002-zeros-first-10000.csv"
CSV = DATA / "E-009b-suzuki-galerkin.csv"
JSON = DATA / "E-009b-suzuki-galerkin.json"
A_CONST = 0.5 * (np.log(2 * np.pi) + np.euler_gamma - 1.0)
N_EIG = 12
A_SWEEP = [0.25, 0.5, 1.0, 2.0, 3.0, 4.0, 5.0]
H_SWEEP = 0.005
H_CONV = [0.01, 0.005, 0.002]
A_CONV = 3.0
T_MAXG = 10.0 # g needed on [0, 2a], a <= 5
GL_N = 12 # Gauss-Legendre points per sub-interval
T_LERCH = 0.35 # below this, Lerch via mpmath
# ---------------------------------------------------------- g construction
def mangoldt_sieve(n_max: int) -> np.ndarray:
lam_log = np.zeros(n_max + 1)
is_comp = np.zeros(n_max + 1, dtype=bool)
for p in range(2, n_max + 1):
if not is_comp[p]:
if p * p <= n_max:
is_comp[p * p :: p] = True
pk = p
while pk <= n_max:
lam_log[pk] = np.log(p)
pk *= p
return lam_log
N_MAXG = int(np.exp(T_MAXG)) + 2
LAM_LOG = mangoldt_sieve(N_MAXG)
NS = np.arange(2, N_MAXG + 1)
LOGS = np.log(NS)
LAM = LAM_LOG[2:] / np.sqrt(NS)
C1 = np.cumsum(LAM)
C0 = np.cumsum(LAM * LOGS)
mp.dps = 30
DIG_COEFF = float(-(mp.digamma(mp.mpf(1) / 4) - mp.log(mp.pi)) / 2)
PHI1 = float(mp.lerchphi(1, 2, mp.mpf(1) / 4))
KK = np.arange(0, 80)
DENOM = (KK + 0.25) ** 2
def prime_sum_vec(t: np.ndarray) -> np.ndarray:
m = np.searchsorted(LOGS, t, side="right")
out = np.zeros_like(t)
pos = m > 0
idx = m[pos] - 1
out[pos] = C1[idx] * t[pos] - C0[idx]
return out
def lerch_comb(t: np.ndarray) -> np.ndarray:
"""-1/4 [Phi(1,2,1/4) - e^{-t/2} Phi(e^{-2t},2,1/4)], t >= 0."""
out = np.empty_like(t)
fast = t > T_LERCH
if fast.any():
z = np.exp(-2.0 * t[fast])
phi = (np.power.outer(z, KK) / DENOM).sum(axis=1)
out[fast] = -0.25 * (PHI1 - np.exp(-t[fast] / 2) * phi)
if (~fast).any():
mp.dps = 25
for i in np.flatnonzero(~fast):
ti = mp.mpf(float(t[i]))
out[i] = float(-0.25 * (mp.lerchphi(1, 2, mp.mpf(1) / 4)
- mp.exp(-ti / 2)
* mp.lerchphi(mp.exp(-2 * ti), 2,
mp.mpf(1) / 4)))
return out
def g_float(t: np.ndarray) -> np.ndarray:
"""Suzuki's g(t) for t >= 0 (g even), float64 with mpmath Lerch tail."""
t = np.asarray(t, dtype=float)
g = -4.0 * (np.exp(t / 2) + np.exp(-t / 2) - 2.0)
g += prime_sum_vec(t)
g += DIG_COEFF * t
g += lerch_comb(t)
g[t == 0.0] = 0.0
return g
def sigma_vec(t: np.ndarray) -> np.ndarray:
"""sigma(t) = (1/2) t log t + A t on t > 0 (the |t|log|t| cusp part)."""
t = np.asarray(t, dtype=float)
with np.errstate(divide="ignore", invalid="ignore"):
return np.where(t > 0, 0.5 * t * np.log(t) + A_CONST * t, 0.0)
def F_sing(t: float) -> float:
"""Second antiderivative of sigma on t >= 0 (F(0)=0)."""
if t <= 0:
return 0.0
return (A_CONST * t**3 / 6.0 + t**3 * np.log(t) / 12.0
- 5.0 * t**3 / 72.0)
def S_sing(k: int, h: float) -> float:
return (F_sing((k + 1) * h) - 2.0 * F_sing(k * h)
+ F_sing(abs(k - 1) * h))
# --------------------------------------------------------------- S(k) exact
XN, XW = leggauss(GL_N)
def S(k: int, h: float) -> float:
"""int_{-h}^{h} (h-|s|) g(kh+s) ds = S_sing (analytic) + GL quadrature
of rho = g - sigma (C^2 at 0; kinks at log n and at the triangle peak
kh are split explicitly)."""
def quad(lo, hi, peak):
n_lo = int(np.ceil(np.exp(lo)))
n_hi = int(np.floor(np.exp(hi)))
pts = sorted({lo, peak, hi} |
{float(np.log(n))
for n in range(max(n_lo, 2), n_hi + 1)
if lo < np.log(n) < hi})
tot = 0.0
for p0, p1 in zip(pts, pts[1:]):
if p1 - p0 <= 0:
continue
mid, half = 0.5 * (p0 + p1), 0.5 * (p1 - p0)
xs = mid + half * XN
rho = g_float(xs) - sigma_vec(xs)
tot += half * float(np.dot(XW, (h - np.abs(xs - peak)) * rho))
return tot
if k == 0:
return S_sing(0, h) + 2.0 * quad(0.0, h, 0.0)
return S_sing(k, h) + quad((k - 1) * h, (k + 1) * h, k * h)
def stiffness_first_col(n_elem: int, h: float) -> np.ndarray:
n_q = n_elem - 1
s = np.array([S(k, h) for k in range(n_elem)])
q = np.empty(n_q)
q[0] = (2.0 * s[0] - 2.0 * s[1]) / h**2
for k in range(1, n_q):
q[k] = (2.0 * s[k] - s[k + 1] - s[k - 1]) / h**2
return q
# --------------------------------------- mpmath reference for S(k) check
def g_mp(t) -> mp.mpf:
t = mp.mpf(t)
a = abs(t)
s = -4 * (mp.exp(a / 2) + mp.exp(-a / 2) - 2)
n = 2
while mp.log(n) <= a:
s += (LAM_LOG[n] / mp.sqrt(n)) * (a - mp.log(n))
n += 1
s -= (a / 2) * (mp.digamma(mp.mpf(1) / 4) - mp.log(mp.pi))
s -= mp.mpf(1) / 4 * (mp.lerchphi(1, 2, mp.mpf(1) / 4)
- mp.exp(-a / 2) * mp.lerchphi(mp.exp(-2 * a), 2,
mp.mpf(1) / 4))
return s
def S_mp(k: int, h: float) -> mp.mpf:
mp.dps = 25
f = lambda s: (h - abs(s)) * g_mp(k * h + s)
return mp.quad(f, [-h, 0, h])
def spectrum(a: float, h: float) -> np.ndarray:
n_elem = int(round(2 * a / h))
n = n_elem - 1
q = stiffness_first_col(n_elem, h)
Q = scipy.linalg.toeplitz(q)
M = scipy.linalg.toeplitz(np.r_[2 * h / 3, h / 6,
np.zeros(max(n - 2, 0))])
Z = np.linalg.qr(np.ones((n, 1)), mode="complete")[0][:, 1:]
Qz = Z.T @ Q @ Z
Mz = Z.T @ M @ Z
return scipy.linalg.eigvalsh(Qz, Mz, subset_by_index=[0, N_EIG - 1])
def main() -> int:
if CSV.exists() and "--force" not in sys.argv:
print(f"cached: {CSV} exists (use --force to recompute)"); return 0
t0 = time.time()
gam = np.loadtxt(ZEROS_CSV, delimiter=",", skiprows=1, usecols=1)
gam10 = gam[:N_EIG]
# ---- internal checks: S(k) vs mpmath reference quadrature
h_chk = 0.005
s_err = {k: float(abs(S(k, h_chk) - S_mp(k, h_chk)))
for k in (0, 1, 500)}
check_entries = bool(max(s_err.values()) < 1e-12)
print(f"S(k) check vs mpmath: abs err "
f"{ {k: f'{v:.1e}' for k, v in s_err.items()} }")
q_chk = stiffness_first_col(int(round(2 * A_CONV / h_chk)), h_chk)
# absolute validation: v = sin(pi(x+a)/a) at a=3 vs zero-side value
ne = int(round(2 * A_CONV / h_chk))
ii = np.arange(1, ne)
xs = -A_CONV + ii * h_chk
v = np.sin(np.pi * (xs + A_CONV) / A_CONV)
Qfull = scipy.linalg.toeplitz(q_chk)
vQv = float(v @ Qfull @ v)
beta = gam * A_CONV / np.pi
zero_side = 2.0 * ((A_CONV / np.pi) ** 2 * 4 * np.sin(gam * A_CONV) ** 2
/ (beta**2 - 1) ** 2).sum()
check_weil = bool(abs(vQv - zero_side) / zero_side < 2e-3)
print(f"Weil validation: v^TQv={vQv:.6e} vs zero-side={zero_side:.6e} "
f"(rel {abs(vQv - zero_side) / zero_side:.2e})")
rows = []
conv = {}
for h in H_CONV:
t1 = time.time()
w = spectrum(A_CONV, h)
conv[h] = w
print(f"a={A_CONV} h={h}: N={int(round(2*A_CONV/h))} "
f"lambda_1={w[0]:.6e} ({time.time()-t1:.0f}s)")
for j, lam in enumerate(w, start=1):
rows.append((A_CONV, h, int(round(2 * A_CONV / h)), j, lam))
gaps = [abs(conv[H_CONV[i + 1]][0] - conv[H_CONV[i]][0])
for i in range(len(H_CONV) - 1)]
check_hconv = bool(gaps[-1] <= gaps[0])
sweep = {}
for a in A_SWEEP:
t1 = time.time()
w = spectrum(a, H_SWEEP)
sweep[a] = w
print(f"a={a}: lambda_1={w[0]:.6e} lambda_2={w[1]:.6e} "
f"({time.time()-t1:.0f}s)")
for j, lam in enumerate(w, start=1):
rows.append((a, H_SWEEP, int(round(2 * a / H_SWEEP)), j, lam))
all_lam = np.array([r[4] for r in rows])
min_lam = float(all_lam.min())
check_pos = bool(min_lam > -1e-7) # documented noise floor
check_mono = bool(all(sweep[a1][0] >= sweep[a2][0] - 1e-9
for a1, a2 in zip(A_SWEEP, A_SWEEP[1:])))
checks = {
"s_values_vs_mpmath": check_entries,
"weil_form_value_matches_zero_side": check_weil,
"eigenvalues_ge_minus_noise_floor": check_pos,
"h_convergence_gaps_shrink": check_hconv,
"lambda1_nonincreasing_in_a": check_mono,
}
DATA.mkdir(parents=True, exist_ok=True)
with CSV.open("w", encoding="utf-8", newline="") as f:
f.write("a,h,N,j,lambda_j,gamma_j,lambda_over_gamma2\n")
for (a, h, n, j, lam) in rows:
g = gam10[j - 1]
f.write(f"{a},{h},{n},{j},{lam:.10e},{g:.10f},"
f"{lam / g**2:.6e}\n")
meta = {
"experiment": "E-009b",
"task": "3.4c (D-018 step 2): Galerkin spectrum of Suzuki A_a",
"description": "P1-hat Galerkin discretization of the Weil-form "
"operator A_a (Friedrichs ext of D*G_aD, mean-zero "
"constraint); Toeplitz stiffness via exact piecewise "
"Gauss-Legendre quadrature of g; bottom 12 "
"eigenvalues; absolute validation vs zero-side "
"explicit formula",
"script": "research/numerics/scripts/E-009-suzuki-galerkin.py",
"parameters": {"a_sweep": A_SWEEP, "h_sweep": H_SWEEP,
"h_convergence_at_a": A_CONV, "h_conv": H_CONV,
"n_eig": N_EIG, "gl_points": GL_N},
"checks": checks,
"key_values": {
"min_eigenvalue": min_lam,
"weil_validation": {"vTQv": vQv, "zero_side": float(zero_side)},
"h_conv_lambda1": {str(h): float(conv[h][0]) for h in H_CONV},
"lambda1_vs_a": {str(a): float(sweep[a][0]) for a in A_SWEEP},
"lambda2_vs_a": {str(a): float(sweep[a][1]) for a in A_SWEEP},
},
"caveats": [
"bottom eigenvalue lambda_a is POSITIVE but very small "
"(~1e-6 scale at a<=5) — earlier table-quadrature version "
"showed spurious -1e-4 values from 1e-3-mesh interpolation "
"noise amplified by 1/h^2; fixed by exact quadrature",
"eigenvalues of A_a are NOT the zero ordinates: the "
"zeta-aligned operator is Dbar_{a,theta} (first-order, in the "
"T_a-inner-product space) — its spectrum = zeros of "
"W(a,theta;z) (eq 1.11), a separate computation (E-010 candidate)",
"mean-zero constraint enforced (theorem's operator is on L^2_0)",
],
"elapsed_seconds": round(time.time() - t0, 1),
"timestamp_utc": datetime.now(timezone.utc).isoformat(),
"row_count": len(rows),
}
JSON.write_text(json.dumps(meta, indent=2), encoding="utf-8")
ok = all(checks.values())
print(f"wrote {CSV.name}, {JSON.name} — "
f"{'ALL CHECKS PASS' if ok else 'CHECK FAILURE: ' + str(checks)}")
return 0 if ok else 1
if __name__ == "__main__":
raise SystemExit(main())