Skip to content

Commit fb43f70

Browse files
committed
Mathematica oracle + E/D campaign artifacts: cross-CAS confirms [3,6,17,116] and harmonic [3,6,13,15,15]
Independent Wolfram Language 14.3 reproduction of the headline N=3 d=2 dimension sequences. Different CAS, different rank algorithm (MatrixRank over Rationals on a SparseArray), same Lie-closure filtration as nbody/symbolic_rank_nbody.py. Phase F results: - 1/r: [3, 6, 17, 116] MATCH (40.4 s) - 1/r^2: [3, 6, 17, 116] MATCH (50.0 s) - r^2: [3, 6, 13, 15, 15] MATCH (31.8 s, L=4 closure verified via 11,937 candidate brackets) mathematica/ contains the shared engine, three sanity runners (1/r and 1/r^2, harmonic, L=4 1/r backup), committed reference JSONs, and conventions doc. Recorded in bench_flint/validation_summary.md Phases F + F.2, docs/session_log.md, README.md, and registry entries mathematica_oracle_n3_d2, mathematica_oracle_n3_d2_harmonic, mathematica_l4_backup. Also lands the supporting Python work from the E and D campaigns: - nbody/symbolic_rank_nbody.py: hot-path simplify cancel -> together patch (E1) with full provenance comment - bench_flint/test_simplify_poly_compat.py (E0), validate_symbolic_ rank_patch.py (E2), bench_symbolic_rank_n4.py (E3), test_simplify_ identity.py, probe_l4{,_30min}.py and their JSON outputs - bench_flint/validate_simplify_patch.py + worker: extended Stage 2 battery with Yukawa mu=1/2 case - scripts/hf_jobs/: bootstrap, smoke, L=4 N=3 (D2), and N=5 L=3 (E5) HF Jobs runners - bench_flint/hf_jobs_{d2,e5}.json: job metadata - results/schwarzschild/: rename dimseq_sweep.json to dimseq_sweep_cancel_apr18.json, add dimseq_l3_key.json - .gitignore: whitelist mathematica reference JSONs
1 parent c430ebf commit fb43f70

38 files changed

Lines changed: 9732 additions & 27 deletions

.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,14 @@ bench_flint/results.jsonl
162162

163163
# Per-(M,L) checkpoints from nbody/run_schwarzschild.py (regeneratable)
164164
nbody/checkpoints_schwarzschild/
165+
166+
# HF Jobs scratch (Phase D/E)
167+
hf_jobs_payload/
168+
hf_results/
169+
170+
171+
# Mathematica run output (committed reference: see registry/experiments.yaml)
172+
mathematica/results/*.json
173+
!mathematica/results/n3_d2_dimseq.json
174+
!mathematica/results/n3_d2_harmonic.json
175+

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ The N=3 sequence is:
3030
- **d-independent** — identical at d = 1, 2, 3 spatial dimensions
3131
- **Mass-invariant** — proved symbolically over ℚ(m₁,m₂,m₃); true for all mass ratios simultaneously (also confirmed numerically 0.001–10⁶)
3232
- **Charge-magnitude-sensitive at level 3** — integer charge magnitudes q=1..20 all give [3,6,17,116]; however mixed-sign geometries Li⁺ (+3,−1,−1) → 111 and H₂⁺ (+1,+1,−1) → 115 are confirmed departures (levels 0–2 remain universal)
33+
- **Cross-CAS confirmed (Apr 21, 2026)**`[3, 6, 17, 116]` reproduced end-to-end in Wolfram Language 14.3 for both 1/r and 1/r² potentials, using an independent rank algorithm (`MatrixRank` over `Rationals` on a `SparseArray`). The harmonic closure `[3, 6, 13, 15, 15]` is also confirmed through L=4 in the same oracle. The headline numbers now stand on two unrelated CAS implementations and two unrelated rank algorithms, for both the open (singular) and closed (harmonic) halves of the universality picture. See [`mathematica/`](mathematica/) and [`bench_flint/validation_summary.md`](bench_flint/validation_summary.md) Phases F and F.2.
3334

3435
The N=4 sequence is mass-invariant and potential-invariant: 1/r², 1/r³, and log(r) all give [6, 14, 62, 1,260] (L3 exact, Apr 11, 2026), and d-independent (d = 1, 2, 3). The old L2 scaling formula (13N³−42N²+83N−120)/6 is **falsified** at N=7, 8; the corrected formula is **L2(N) = N(4N²−9N+3)/2** (N≥4, equivalent to new_L2 = 12·C(N,3)). N=9 confirms all three formulas: L0=36, L1=99, L2=1107 (Apr 14, 2026). The L1 formula **dim(L1) = N(3N−5)/2** is now verified for N=3 through N=26 (23 exact data points). L0 = N(N−1)/2 confirmed through N=50.
3536

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#!/usr/bin/env python3
2+
"""
3+
E3 — N=4 d=1 1/r cancel-vs-together speedup bench.
4+
5+
Two passes:
6+
Pass 1: head-to-head at L=2 (both cancel and together, same engine
7+
freshly instantiated; per-level wall time + per-bracket counts)
8+
Pass 2: together-only at L=3 (compared against the pinned cancel time
9+
recorded in results/symbolic_rank/rank_N4_d1_1r.json:
10+
computation_time_seconds = 3137.6s)
11+
12+
Generation phase only (no extract_monomial_matrix, no rank). The patch only
13+
touches generation; the rank phase is identical for both engines.
14+
15+
Output: bench_flint/symrank_n4_timing.json
16+
"""
17+
from __future__ import annotations
18+
19+
import json
20+
import os
21+
import sys
22+
import time
23+
from pathlib import Path
24+
25+
REPO_ROOT = Path(__file__).resolve().parents[1]
26+
sys.path.insert(0, str(REPO_ROOT / "nbody"))
27+
28+
import sympy as sp # noqa: E402
29+
from sympy import cancel, expand, together # noqa: E402
30+
31+
import symbolic_rank_nbody as srn # noqa: E402
32+
from symbolic_rank_nbody import NBodySymbolicRank # noqa: E402
33+
34+
35+
def _make_simplify(mode):
36+
"""Return a _simplify implementation that uses the given mode."""
37+
if mode == "cancel":
38+
def _simplify(self, expr):
39+
if self.uses_u:
40+
return cancel(expr)
41+
return expand(expr)
42+
elif mode == "together":
43+
def _simplify(self, expr):
44+
if self.uses_u:
45+
return together(expr)
46+
return expand(expr)
47+
else:
48+
raise ValueError(mode)
49+
return _simplify
50+
51+
52+
def run_one(mode, n_bodies, d_spatial, potential, max_level):
53+
print(f"\n{'='*68}")
54+
print(f" N={n_bodies} d={d_spatial} potential={potential} "
55+
f"max_level={max_level} mode={mode}")
56+
print(f"{'='*68}")
57+
# Monkeypatch _simplify on the class, then build engine afresh.
58+
NBodySymbolicRank._simplify = _make_simplify(mode)
59+
engine = NBodySymbolicRank(
60+
n_bodies=n_bodies, d_spatial=d_spatial, potential=potential)
61+
62+
t0 = time.time()
63+
exprs, names, levels = engine.build_generators(
64+
max_level, checkpoint_dir=None, n_workers=1)
65+
elapsed = time.time() - t0
66+
67+
# Per-level counts
68+
by_level = {}
69+
for lv in levels:
70+
by_level[lv] = by_level.get(lv, 0) + 1
71+
72+
# Average term count of generated brackets at the top level
73+
from sympy import Add
74+
top_lv = max(levels)
75+
top_exprs = [e for e, lv in zip(exprs, levels) if lv == top_lv]
76+
top_nterms = [len(Add.make_args(e)) for e in top_exprs]
77+
avg_nterms = (sum(top_nterms) / len(top_nterms)) if top_nterms else 0.0
78+
79+
return {
80+
"mode": mode,
81+
"n_bodies": n_bodies,
82+
"d_spatial": d_spatial,
83+
"potential": potential,
84+
"max_level": max_level,
85+
"elapsed_s": round(elapsed, 2),
86+
"n_generators": len(exprs),
87+
"by_level": by_level,
88+
"top_level_avg_nterms": round(avg_nterms, 1),
89+
"top_level_max_nterms": max(top_nterms) if top_nterms else 0,
90+
}
91+
92+
93+
def main():
94+
print(f"E3 — N=4 d=1 1/r cancel-vs-together (sympy {sp.__version__})")
95+
t_global = time.time()
96+
97+
# Pass 1: head-to-head at L=2 (small enough that cancel won't hang)
98+
pass1 = []
99+
pass1.append(run_one("cancel", n_bodies=4, d_spatial=1,
100+
potential="1/r", max_level=2))
101+
pass1.append(run_one("together", n_bodies=4, d_spatial=1,
102+
potential="1/r", max_level=2))
103+
104+
if pass1[0]["elapsed_s"] > 0:
105+
l2_speedup = pass1[0]["elapsed_s"] / max(pass1[1]["elapsed_s"], 1e-6)
106+
else:
107+
l2_speedup = None
108+
109+
# Pass 2: together-only at L=3, compared against pin
110+
pin_path = REPO_ROOT / "results" / "symbolic_rank" / "rank_N4_d1_1r.json"
111+
with open(pin_path, "r", encoding="utf-8") as f:
112+
pin = json.load(f)
113+
pin_t_l3 = pin["computation_time_seconds"]
114+
115+
pass2 = run_one("together", n_bodies=4, d_spatial=1,
116+
potential="1/r", max_level=3)
117+
l3_speedup = pin_t_l3 / max(pass2["elapsed_s"], 1e-6)
118+
119+
summary = {
120+
"sympy_version": sp.__version__,
121+
"pass1_l2_head_to_head": pass1,
122+
"pass1_l2_speedup_cancel_over_together": round(l2_speedup, 2)
123+
if l2_speedup is not None else None,
124+
"pass2_l3_together_only": pass2,
125+
"pass2_l3_pin_cancel_elapsed_s": pin_t_l3,
126+
"pass2_l3_speedup_pin_over_together": round(l3_speedup, 2),
127+
"elapsed_total_s": round(time.time() - t_global, 2),
128+
"completed_at": time.strftime("%Y-%m-%dT%H:%M:%S"),
129+
}
130+
131+
out = REPO_ROOT / "bench_flint" / "symrank_n4_timing.json"
132+
out.parent.mkdir(parents=True, exist_ok=True)
133+
tmp = out.with_suffix(".tmp.json")
134+
with open(tmp, "w", encoding="utf-8") as f:
135+
json.dump(summary, f, indent=2)
136+
os.replace(tmp, out)
137+
138+
print("\n" + "=" * 68)
139+
print(" E3 SUMMARY")
140+
print("=" * 68)
141+
print(f" L=2 cancel: {pass1[0]['elapsed_s']}s")
142+
print(f" L=2 together: {pass1[1]['elapsed_s']}s")
143+
print(f" L=2 speedup: x{summary['pass1_l2_speedup_cancel_over_together']}")
144+
print(f" L=3 pin (cancel, 3137.6s reference)")
145+
print(f" L=3 together: {pass2['elapsed_s']}s")
146+
print(f" L=3 speedup: x{summary['pass2_l3_speedup_pin_over_together']}")
147+
print(f" Output: {out.relative_to(REPO_ROOT)}")
148+
return 0
149+
150+
151+
if __name__ == "__main__":
152+
sys.exit(main())

bench_flint/hf_jobs_d2.json

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"submitted_at_utc": "2026-04-22T01:14Z",
3+
"campaign": "phase_d_l4_n3_symbolic_rank",
4+
"engine_patch": "together (Phase E1, 8.49x faster vs cancel at N=4 L=3)",
5+
"flavor": "cpu-xl",
6+
"vcpu": 16,
7+
"memory_gb": 124,
8+
"rate_usd_per_hour": 1.0,
9+
"timeout": "6h",
10+
"max_cost_usd_per_job": 6.0,
11+
"max_cost_usd_total_d2": 12.0,
12+
"jobs": {
13+
"n5_l3_probe_resubmit": {
14+
"purpose": "Phase E5 - N=5 d=1 1/r generation-only (resubmit after attribute-name fix)",
15+
"id": "69e820eccd8c002f31e01409",
16+
"view": "https://huggingface.co/jobs/bshepp/69e820eccd8c002f31e01409",
17+
"bucket": "hf://buckets/bshepp/3body-n5-l3-gen-probe",
18+
"target": "run_n5_l3_gen",
19+
"args": [],
20+
"expected_outputs": [
21+
"results/n5_l3.json (complete)",
22+
"results/n5_l3.partial.json (sigterm/oom)"
23+
],
24+
"first_attempt_failure": {
25+
"id": "69e81f3eac288e522d8f0752",
26+
"exit_code": 137,
27+
"underlying_python_exit": 1,
28+
"cause": "AttributeError: NBodySymbolicRank has no attribute PHASE_VARS (engine API used uppercase; correct name is phase_vars; algebra is at engine.algebra.hamiltonian_list)",
29+
"fix": "scripts/hf_jobs/run_n5_l3_gen.py L104-105 patched; resynced to bucket"
30+
}
31+
},
32+
"l4_1r_d1": {
33+
"purpose": "Phase D2 - N=3 L=4 d=1 1/r symbolic rank (extends L=3 [3,6,17,116] pin to L=4)",
34+
"id": "69e820f1cd8c002f31e0140b",
35+
"view": "https://huggingface.co/jobs/bshepp/69e820f1cd8c002f31e0140b",
36+
"bucket": "hf://buckets/bshepp/3body-l4-1r",
37+
"target": "run_l4_symbolic_rank",
38+
"args": ["--potential", "1/r", "--d-spatial", "1"],
39+
"expected_outputs": [
40+
"results/l4_1r_d1.json (complete)",
41+
"results/l4_1r_d1.partial.json (sigterm/oom)",
42+
"checkpoints/* (per-level pickled state)"
43+
],
44+
"baseline_local_cost_pin_l3": "3137.6s for N=4 L=3 with cancel; together gave 369.5s (8.49x). Local pin for N=3 L=4 1/r not measured (engine timed out)."
45+
},
46+
"l4_log_d2": {
47+
"purpose": "Phase D2 - N=3 L=4 d=2 log symbolic rank (extends L=2 [3,6,17] pin; L=3 + L=4 both open)",
48+
"id": "69e820f6ac288e522d8f075c",
49+
"view": "https://huggingface.co/jobs/bshepp/69e820f6ac288e522d8f075c",
50+
"bucket": "hf://buckets/bshepp/3body-l4-log",
51+
"target": "run_l4_symbolic_rank",
52+
"args": ["--potential", "log", "--d-spatial", "2"],
53+
"expected_outputs": [
54+
"results/l4_log_d2.json (complete)",
55+
"results/l4_log_d2.partial.json (sigterm/oom)",
56+
"checkpoints/* (per-level pickled state)"
57+
]
58+
}
59+
},
60+
"monitoring": [
61+
"hf jobs inspect <id> # status / stage",
62+
"hf jobs logs <id> -f # streaming logs",
63+
"hf buckets sync hf://buckets/bshepp/<bucket> ./hf_results/<bucket>"
64+
]
65+
}

bench_flint/hf_jobs_e5.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"phase": "E5",
3+
"purpose": "N=5 d=1 1/r generation-only probe on patched (together) symbolic_rank engine",
4+
"job_id": "69e81f3eac288e522d8f0752",
5+
"url": "https://huggingface.co/jobs/bshepp/69e81f3eac288e522d8f0752",
6+
"submitted_at": "2026-04-22T01:07:10Z",
7+
"flavor": "cpu-xl",
8+
"rate_usd_per_h": 1.0,
9+
"timeout_h": 6,
10+
"worst_case_cost_usd": 6.0,
11+
"expected_cost_usd_estimate": "~1",
12+
"bucket": "bshepp/3body-n5-l3-gen-probe",
13+
"target": "run_n5_l3_gen.py",
14+
"deliverable_path_in_bucket": "results/n5_l3.json (or n5_l3.partial.json on SIGTERM)",
15+
"smoke_validated": {
16+
"local_windows": "OK (0.1s, 18 generators)",
17+
"hf_cpu_basic": "OK (0.8s)"
18+
}
19+
}

0 commit comments

Comments
 (0)