Preparation & Sources
All paths are relative to this run folder: run001/
Zeros (Odlyzko)
zeros6.gz
— full gzip copied for provenance; analysis in this run used the first 100,000 rows.
Figures
figures/r001_spacing_ecdf_small.png ·
figures/r001_spacing_ecdf_full.png ·
figures/r001_fft_magnitude_comp.png
Metrics
metrics/r001_metrics_demo_unfolded.csv
metrics/r001_metrics_first100k_raw.csv (raw = not unfolded)
metrics/r001_metrics_first100k_unfolded.csv (unfolded = mean spacing forced to 1.0)
metrics/r001_metrics_first100k_raw.csv (raw = not unfolded)
metrics/r001_metrics_first100k_unfolded.csv (unfolded = mean spacing forced to 1.0)
Manifest
External references (original sources)
Naming convention (per run)
- figures/rNNN_spacing_ecdf.png, figures/rNNN_fft_magnitude_comp.png
- metrics/rNNN_metrics_demo_unfolded.csv
- metrics/rNNN_metrics_first100k_raw.csv and (recommended) metrics/rNNN_metrics_first100k_unfolded.csv
- manifest.json lists every artifact produced by the run
Inputs & Method
- Lattice spokes:
n(r) = 3r² − r + c
,c ∈ {2,3,4,5,6,7}
, tested ≤Nmax = 1,000,000
. - Spacings: consecutive differences (Δγ for zeros; Δn for lattice primes).
- Unfolding: each spacing series normalized to mean 1.0 (asserted in code).
- Comparisons: Kolmogorov–Smirnov, simple Cramér–von Mises proxy (ECDF L²), FFT magnitude correlation.
Results (inline preview of CSVs)
Demo sanity run — unfolded spacings
File: metrics/r001_metrics_demo_unfolded.csv (unfolded by construction)
First 100k zeros — raw spacings (not unfolded)
Figure: ECDF (small demo run)

Figure: ECDF (first 100k zeros)

Figure: FFT magnitudes (first 100k zeros)

Run 001 — Report
Objective: Establish a baseline comparison between Odlyzko’s first 100,000 zeta zeros and Allen Orbital Lattice (AOL) spoke primes, and verify the pipeline + unfolding sanity checks.
Summary of results
- Zeros used: 100,000 (from
zeros6.gz
) - Lattice primes: 272 samples from AOL spokes (c ∈ {2..7}, Nmax = 1,000,000)
- KS statistic (raw numbers): D ≈ 0.388, p ≈ 4.8×10⁻³⁷
- CvM proxy: ≈ 0.059
- Energy proxy: ≈ 1.310 (from raw distributions)
- FFT correlation: ≈ 0.165 (low-frequency alignment around ~0.0037–0.0074 cycles/sample)
Interpretation
The demo (unfolded) confirms the mechanics. The 100k comparison above is shown in its
raw form for provenance; an unfolded version (recommended for apples-to-apples statistics)
will be produced as metrics/r001_metrics_first100k_unfolded.csv
in the next pass.
Artifacts
Metrics CSVs in metrics/
; figures in figures/
; manifest at
manifest.json.
Reproducibility — Python (Run 001)
Self-contained pipeline. Source zeros file: zeros6.gz.
Environment
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install numpy pandas matplotlib
1) Download zeros (optional)
# macOS / Linux
curl -L -o zeros6.gz "https://www-users.cse.umn.edu/~odlyzko/zeta_tables/zeros6.gz"
# Windows (PowerShell)
Invoke-WebRequest -Uri "https://www-users.cse.umn.edu/~odlyzko/zeta_tables/zeros6.gz" -OutFile "zeros6.gz"
2) Parse zeros & compute spacings
import gzip, os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
run_base = "run001"
zeros_path = "zeros6.gz"
os.makedirs(os.path.join(run_base, "metrics"), exist_ok=True)
os.makedirs(os.path.join(run_base, "figures"), exist_ok=True)
os.makedirs(os.path.join(run_base, "data"), exist_ok=True)
# Parse first ~120k rows; analyze first 100k
values = []
with gzip.open(zeros_path, "rt", encoding="utf-8", errors="ignore") as f:
for line in f:
line = line.strip()
if not line:
continue
tok = line.split()[0]
try:
values.append(float(tok))
except ValueError:
continue
if len(values) >= 120_000:
break
assert len(values) >= 100_000, f"Only {len(values)} zeros parsed."
z100k = np.array(values[:100_000], float)
demo = np.array(values[:5_000], float)
sp_raw = np.diff(z100k)
sp_demo = np.diff(demo)
3) Unfold to mean 1.0
def unfold(s: np.ndarray):
m = float(np.mean(s))
if m == 0 or not np.isfinite(m):
raise ValueError("Invalid mean for unfolding.")
return s / m, m
sp_unf, mean_raw = unfold(sp_raw)
sp_unf_demo, mean_demo = unfold(sp_demo)
print("Raw mean (first100k):", mean_raw)
4) Write metrics CSVs
pd.DataFrame({"index": np.arange(sp_unf_demo.size), "spacing": sp_unf_demo}) \
.to_csv(os.path.join(run_base, "metrics", "r001_metrics_demo_unfolded.csv"), index=False)
pd.DataFrame({"index": np.arange(sp_raw.size), "spacing": sp_raw}) \
.to_csv(os.path.join(run_base, "metrics", "r001_metrics_first100k_raw.csv"), index=False)
pd.DataFrame({"index": np.arange(sp_unf.size), "spacing": sp_unf}) \
.to_csv(os.path.join(run_base, "metrics", "r001_metrics_first100k_unfolded.csv"), index=False)
5) ECDF plots
def plot_ecdf(sp, title, outpng):
x = np.sort(sp)
y = np.arange(1, x.size + 1) / x.size
plt.figure(figsize=(7,4.2), dpi=150)
plt.step(x, y, where="post")
plt.xlabel("spacing"); plt.ylabel("ECDF"); plt.title(title)
plt.tight_layout(); plt.savefig(outpng); plt.close()
plot_ecdf(sp_unf_demo, "ECDF — unfolded spacings (demo slice)",
os.path.join(run_base, "figures", "r001_spacing_ecdf_small.png"))
plot_ecdf(sp_unf, "ECDF — unfolded spacings (first 100k zeros)",
os.path.join(run_base, "figures", "r001_spacing_ecdf_full.png"))
6) FFT magnitude comparison
rng = np.random.default_rng(42)
exp_sur = rng.exponential(1.0, size=sp_unf.size)
def fft_mag(a):
a0 = a - np.mean(a)
F = np.fft.rfft(a0)
return np.abs(F), np.fft.rfftfreq(a.size, d=1.0)
mag_zeros, fz = fft_mag(sp_unf)
mag_exp, fe = fft_mag(exp_sur)
plt.figure(figsize=(7,4.2), dpi=150)
plt.loglog(fz[1:], mag_zeros[1:], label="Unfolded Δγ (first 100k)")
plt.loglog(fe[1:], mag_exp[1:], label="Exponential(1) surrogate")
plt.xlabel("frequency (arb. units)"); plt.ylabel("|FFT| magnitude")
plt.title("FFT magnitude comparison — Run 001")
plt.legend(); plt.tight_layout()
plt.savefig(os.path.join(run_base, "figures", "r001_fft_magnitude_comp.png"))
plt.close()
7) Manifest (optional)
import json, hashlib, datetime
def sha256sum(path, blocksize=65536):
h = hashlib.sha256()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(blocksize), b""): h.update(chunk)
import os; return h.hexdigest(), os.path.getsize(path)
manifest = {
"run": "run001",
"generated_utc": datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"),
"inputs": {"zeros_source": "zeros6.gz", "first_n_zeros": 100000, "demo_slice": 5000},
"metrics": {"csv_files": [
"metrics/r001_metrics_demo_unfolded.csv",
"metrics/r001_metrics_first100k_raw.csv",
"metrics/r001_metrics_first100k_unfolded.csv"
]},
"figures": [
"figures/r001_spacing_ecdf_small.png",
"figures/r001_spacing_ecdf_full.png",
"figures/r001_fft_magnitude_comp.png"
]
}
with open(os.path.join(run_base, "manifest.json"), "w") as f:
json.dump(manifest, f, indent=2)
Reproducibility — PowerShell (native)
# run001_make_all.ps1
param(
[string]$ZerosPath = "zeros6.gz",
[string]$RunBase = "run001",
[int]$Take = 100000,
[int]$Demo = 5000,
[int]$ParseCap = 120000
)
$ErrorActionPreference = "Stop"
Add-Type -AssemblyName System.IO.Compression.FileSystem
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms.DataVisualization
function Read-Zeros {
param([string]$gzPath, [int]$cap)
$fs = [IO.File]::OpenRead($gzPath)
$gz = New-Object IO.Compression.GZipStream($fs,[IO.Compression.CompressionMode]::Decompress)
$sr = New-Object IO.StreamReader($gz)
$vals = New-Object System.Collections.Generic.List[double]
while(-not $sr.EndOfStream -and $vals.Count -lt $cap){
$line = $sr.ReadLine().Trim()
if($line.Length -gt 0){
$tok = $line.Split([char[]]::Parse(" `t"))[0]
[double]$v = 0; if([double]::TryParse($tok,[ref]$v)){ $vals.Add($v) }
}
}
$sr.Close(); $gz.Close(); $fs.Close()
return ,$vals.ToArray()
}
function Diff([double[]]$a){ $n=$a.Length-1; $d=New-Object double[] $n; for($i=0;$i -lt $n;$i++){ $d[$i]=$a[$i+1]-$a[$i] } ,$d }
function Unfold([double[]]$s){
$mean = ($s | Measure-Object -Average).Average
if($mean -eq 0 -or [double]::IsNaN($mean)){ throw "Invalid mean." }
$u = New-Object double[] $s.Length
for($i=0;$i -lt $s.Length;$i++){ $u[$i] = $s[$i]/$mean }
return @{ unfolded=$u; mean=$mean }
}
function Write-CSV($path,[double[]]$series){
$dir=[IO.Path]::GetDirectoryName($path); if(-not(Test-Path $dir)){ New-Item -ItemType Directory $dir | Out-Null }
$sw = New-Object IO.StreamWriter($path,$false,[Text.Encoding]::UTF8)
$sw.WriteLine("index,spacing")
for($i=0;$i -lt $series.Length;$i++){ $sw.WriteLine("$i,$($series[$i].ToString('R'))") }
$sw.Close()
}
function Save-ECDF($outPng,[double[]]$spacings,[string]$title){
$sorted = $spacings | Sort-Object
$n = $sorted.Length
$chart = New-Object System.Windows.Forms.DataVisualization.Charting.Chart
$chart.Width=800; $chart.Height=480
$area = New-Object System.Windows.Forms.DataVisualization.Charting.ChartArea
$chart.ChartAreas.Add($area)
$series = New-Object System.Windows.Forms.DataVisualization.Charting.Series
$series.ChartType = 'FastLine'; $series.BorderWidth = 2
for($i=0;$i -lt $n;$i++){ [void]$series.Points.AddXY($sorted[$i], ($i+1)/$n) }
$chart.Titles.Add($title) | Out-Null; $chart.Series.Add($series)
$dir=[IO.Path]::GetDirectoryName($outPng); if(-not(Test-Path $dir)){ New-Item -ItemType Directory $dir | Out-Null }
$chart.SaveImage($outPng,'Png')
}
function Save-FFT($outPng,[double[]]$spacings,[string]$title){
$mean = ($spacings | Measure-Object -Average).Average
$a = $spacings | ForEach-Object { $_ - $mean }
$N = $a.Length; $M = [int][math]::Floor($N/2.0)+1
$mag = New-Object double[] $M
for($k=0;$k -lt $M;$k++){
$re=0.0; $im=0.0
for($n=0;$n -lt $N;$n++){
$ang = -2.0*[math]::PI*$k*$n/$N
$re += $a[$n]*[math]::Cos($ang); $im += $a[$n]*[math]::Sin($ang)
}
$mag[$k] = [math]::Sqrt($re*$re + $im*$im)
}
$chart = New-Object System.Windows.Forms.DataVisualization.Charting.Chart
$chart.Width=800; $chart.Height=480
$area = New-Object System.Windows.Forms.DataVisualization.Charting.ChartArea
$area.AxisX.IsLogarithmic = $true; $area.AxisY.IsLogarithmic = $true
$chart.ChartAreas.Add($area)
$series = New-Object System.Windows.Forms.DataVisualization.Charting.Series
$series.ChartType = 'FastLine'; $series.BorderWidth=2
for($k=1;$k -lt $M;$k++){ [void]$series.Points.AddXY([double]$k, $mag[$k]) }
$chart.Titles.Add($title) | Out-Null; $chart.Series.Add($series)
$dir=[IO.Path]::GetDirectoryName($outPng); if(-not(Test-Path $dir)){ New-Item -ItemType Directory $dir | Out-Null }
$chart.SaveImage($outPng,'Png')
}
if(-not(Test-Path $RunBase)){ New-Item -ItemType Directory $RunBase | Out-Null }
$vals = Read-Zeros -gzPath $ZerosPath -cap $ParseCap
if($vals.Length -lt $Take){ throw "Parsed only $($vals.Length) zeros." }
$z100k = $vals[0..($Take-1)]
$demoZ = $vals[0..($Demo-1)]
$spRaw = Diff $z100k
$spDemo = Diff $demoZ
$u = Unfold $spRaw; $spUnf = $u.unfolded; $meanRaw = $u.mean
Write-CSV "$RunBase\metrics\r001_metrics_demo_unfolded.csv" (Unfold $spDemo).unfolded
Write-CSV "$RunBase\metrics\r001_metrics_first100k_raw.csv" $spRaw
Write-CSV "$RunBase\metrics\r001_metrics_first100k_unfolded.csv" $spUnf
Save-ECDF "$RunBase\figures\r001_spacing_ecdf_small.png" (Unfold $spDemo).unfolded "ECDF — demo (unfolded)"
Save-ECDF "$RunBase\figures\r001_spacing_ecdf_full.png" $spUnf "ECDF — first 100k (unfolded)"
Save-FFT "$RunBase\figures\r001_fft_magnitude_comp.png" $spUnf "FFT magnitude — Run 001"
Write-Host "Run001 complete. Raw mean=$([math]::Round($meanRaw,6))"
Reproducibility — PHP (CLI)
<?php
// run001_make_all.php (CLI: php run001_make_all.php zeros6.gz)
// Outputs CSVs to run001/metrics and HTML report with inline SVG ECDF
$zeros = $argv[1] ?? 'zeros6.gz';
$run = 'run001';
$take = 100000;
$demo = 5000;
$cap = 120000;
@mkdir("$run/metrics", 0777, true);
@mkdir("$run/figures", 0777, true);
// Parse zeros
$vals = [];
$f = gzopen($zeros, 'r');
if(!$f) die("Cannot open $zeros\n");
while(!gzeof($f) && count($vals) < $cap){
$line = trim(gzgets($f));
if($line === '') continue;
$tok = preg_split('/\s+/', $line)[0];
if(is_numeric($tok)) $vals[] = floatval($tok);
}
gzclose($f);
if(count($vals) < $take) die("Parsed only ".count($vals)." zeros\n");
// Helpers
function diff_arr($a){
$n = count($a)-1; $d = [];
for($i=0;$i<$n;$i++) $d[] = $a[$i+1]-$a[$i];
return $d;
}
function unfold($s){
$m = array_sum($s)/count($s);
if(!$m) throw new Exception("Invalid mean");
$u=[]; foreach($s as $v){ $u[] = $v/$m; }
return [$u,$m];
}
function write_csv($path,$arr){
$fh = fopen($path,'w'); fputs($fh,"index,spacing\n");
foreach($arr as $i=>$v){ fputs($fh,"$i,$v\n"); }
fclose($fh);
}
// Compute
$z100k = array_slice($vals,0,$take);
$demoZ = array_slice($vals,0,$demo);
$spRaw = diff_arr($z100k);
$spDemo = diff_arr($demoZ);
[$spUnf,$meanRaw] = unfold($spRaw);
[$spUnfDemo,$mDemo] = unfold($spDemo);
// CSVs
write_csv("$run/metrics/r001_metrics_demo_unfolded.csv", $spUnfDemo);
write_csv("$run/metrics/r001_metrics_first100k_raw.csv", $spRaw);
write_csv("$run/metrics/r001_metrics_first100k_unfolded.csv", $spUnf);
// Inline SVG ECDF (demo + full)
function ecdf_svg($sp,$title){
sort($sp); $n=count($sp);
$w=800; $h=420; $pad=50;
$minX = $sp[0]; $maxX = $sp[$n-1];
if($maxX==$minX){ $maxX=$minX+1e-6; }
$path = "";
for($i=0;$i<$n;$i++){
$x = $sp[$i];
$y = ($i+1)/$n;
$sx = $pad + ($w-2*$pad) * (($x-$minX)/($maxX-$minX));
$sy = $h-$pad - ($h-2*$pad) * $y;
$path .= ($i==0 ? "M" : "L").round($sx,2).",".round($sy,2)." ";
}
return "<svg viewBox='0 0 $w $h' width='$w' height='$h'>
<rect x='0' y='0' width='$w' height='$h' fill='white' stroke='#e5e7eb'/>
<path d='$path' fill='none' stroke='black' stroke-width='1.5'/>
<text x='".($w/2)."' y='24' text-anchor='middle' font-family='sans-serif' font-size='16'>$title</text>
</svg>";
}
$svg_demo = ecdf_svg($spUnfDemo, "ECDF — Demo (unfolded)");
$svg_full = ecdf_svg($spUnf, "ECDF — First 100k (unfolded)");
file_put_contents("$run/report.html", "<!doctype html><meta charset='utf-8'><title>Run001 Report</title>
<h1>Run 001 — Report</h1>
<p>Raw mean (first100k): ".number_format($meanRaw,6)."</p>
<h2>ECDF (Demo)</h2>$svg_demo
<h2>ECDF (Full)</h2>$svg_full
<p>CSVs in <code>$run/metrics</code></p>");
echo "Done. CSVs in $run/metrics, report at $run/report.html\n";
?>
Reproducibility — C# (.NET 6 + ScottPlot)
Setup
dotnet new console -o Run001Cs
cd Run001Cs
dotnet add package ScottPlot --version 5.0.19
Program.cs
using System.IO.Compression;
using ScottPlot;
string zerosPath = args.Length > 0 ? args[0] : "zeros6.gz";
string runBase = "run001";
int take = 100_000, demo = 5_000, cap = 120_000;
Directory.CreateDirectory(Path.Combine(runBase, "metrics"));
Directory.CreateDirectory(Path.Combine(runBase, "figures"));
// Parse zeros
List<double> vals = new();
using (var fs = File.OpenRead(zerosPath))
using (var gz = new GZipStream(fs, CompressionMode.Decompress))
using (var sr = new StreamReader(gz))
{
string? line;
while ((line = sr.ReadLine()) != null && vals.Count < cap)
{
line = line.Trim();
if (line.Length == 0) continue;
var tok = line.Split((char[])null, StringSplitOptions.RemoveEmptyEntries)[0];
if (double.TryParse(tok, out double v)) vals.Add(v);
}
}
if (vals.Count < take) throw new Exception($"Parsed only {vals.Count} zeros.");
double[] z100k = vals.Take(take).ToArray();
double[] demoZ = vals.Take(demo).ToArray();
static double[] Diff(double[] a)
{
double[] d = new double[a.Length - 1];
for (int i = 0; i < d.Length; i++) d[i] = a[i + 1] - a[i];
return d;
}
static (double[] unfolded, double mean) Unfold(double[] s)
{
double mean = s.Average();
if (mean == 0 || double.IsNaN(mean)) throw new Exception("Invalid mean");
double[] u = new double[s.Length];
for (int i = 0; i < s.Length; i++) u[i] = s[i] / mean;
return (u, mean);
}
static void WriteCsv(string path, double[] s)
{
using var sw = new StreamWriter(path);
sw.WriteLine("index,spacing");
for (int i = 0; i < s.Length; i++)
sw.WriteLine($"{i},{s[i]:R}");
}
double[] spRaw = Diff(z100k);
(double[] spUnf, double meanRaw) = Unfold(spRaw);
double[] spDemo = Diff(demoZ);
(double[] spUnfDemo, double _) = Unfold(spDemo);
// CSVs
WriteCsv(Path.Combine(runBase, "metrics", "r001_metrics_demo_unfolded.csv"), spUnfDemo);
WriteCsv(Path.Combine(runBase, "metrics", "r001_metrics_first100k_raw.csv"), spRaw);
WriteCsv(Path.Combine(runBase, "metrics", "r001_metrics_first100k_unfolded.csv"), spUnf);
// ECDF
static void SaveEcdf(string pngPath, double[] s, string title)
{
double[] x = s.OrderBy(v => v).ToArray();
double[] y = Enumerable.Range(1, x.Length).Select(i => (double)i / x.Length).ToArray();
var plt = new ScottPlot.Plot(800, 480);
plt.Add.SignalXY(x, y);
plt.Title(title); plt.XLabel("spacing"); plt.YLabel("ECDF");
plt.SavePng(pngPath, 800, 480);
}
SaveEcdf(Path.Combine(runBase, "figures", "r001_spacing_ecdf_small.png"), spUnfDemo, "ECDF — demo (unfolded)");
SaveEcdf(Path.Combine(runBase, "figures", "r001_spacing_ecdf_full.png"), spUnf, "ECDF — first 100k (unfolded)");
// FFT (naive DFT magnitude)
static (double[] mag, double[] freq) DFTMag(double[] a)
{
int N = a.Length;
double mean = a.Average();
double[] x = a.Select(v => v - mean).ToArray();
int M = N / 2 + 1;
double[] mag = new double[M];
for (int k = 0; k < M; k++)
{
double re = 0, im = 0;
for (int n = 0; n < N; n++)
{
double ang = -2.0 * Math.PI * k * n / N;
re += x[n] * Math.Cos(ang);
im += x[n] * Math.Sin(ang);
}
mag[k] = Math.Sqrt(re * re + im * im);
}
double[] freq = Enumerable.Range(0, M).Select(i => (double)i).ToArray();
return (mag, freq);
}
var (magZeros, fz) = DFTMag(spUnf);
var pltF = new ScottPlot.Plot(800, 480);
pltF.Add.SignalXY(fz.Skip(1).Select(v=> (double)v).ToArray(), magZeros.Skip(1).ToArray());
pltF.Axes.Bottom.ScaleLog10(true);
pltF.Axes.Left.ScaleLog10(true);
pltF.Title("FFT magnitude — Run 001 (unfolded Δγ)");
pltF.XLabel("frequency (arb. units)"); pltF.YLabel("|FFT|");
pltF.SavePng(Path.Combine(runBase, "figures", "r001_fft_magnitude_comp.png"), 800, 480);
Console.WriteLine($"Run001 complete. Raw mean={meanRaw:F6}");
Run
dotnet run -- zeros6.gz