mirror of
https://github.com/allaunthefox/SilverSight.git
synced 2026-07-31 01:25:21 +00:00
feat: q-profile sweep + Gerver Sidon design + report
Three deliverables: 1. scripts/crt_qprofile_sweep.py Safety factor optimization (R1 from toroidal refinement). Replaces brute-force modulus selection with systematic q-profile sweep. Tests: q < 1 (poloidal) vs q > 1 (toroidal) Sidon rate, simple rational q vs non-simple (R2 cross-pair coprimality). All integer arithmetic (Fraction for q). 2. docs/research/GERVER_SIDON_DESIGN.md Direction B design: actual Gerver sofa (18 arcs) with CRT Sidon boundary in ℤ², high-resolution motion (T=100), justified tolerance. Explains why Direction A failed and what Direction B fixes. Honest assessment: long shot, but more promising than v2/v3. 3. (Report in /tmp — uploaded separately) Negative result write-up: sofa coloring doesn't detect q=1 at justified tolerance. HN spectral database: Hoffman tight for regular graphs, gap=1 for unit-distance. CRT n-moduli generalization.
This commit is contained in:
parent
79433aadfc
commit
55453b05cb
2 changed files with 518 additions and 0 deletions
155
docs/research/GERVER_SIDON_DESIGN.md
Normal file
155
docs/research/GERVER_SIDON_DESIGN.md
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
# Direction B: Gerver Sofa as Sidon — Design Document
|
||||
|
||||
**Status:** DESIGNED — not yet implemented
|
||||
**Date:** 2026-07-04
|
||||
**Depends on:** `SIDON_SOFA_COLORING.md`, `sidon_preservation_creation.md`,
|
||||
`TOROIDAL_POLOIDAL_REFINEMENT.md`
|
||||
|
||||
## Why Direction A Failed and Direction B Might Not
|
||||
|
||||
Direction A used arbitrary shapes (half-disc, rectangle, Sidon-polar,
|
||||
Gerver-like, Hammersley) with CRT Sidon boundary points mapped via polar
|
||||
coordinates. At justified tolerance (EPS=1e-5), these shapes don't produce
|
||||
dense conflict graphs because:
|
||||
|
||||
1. **Shapes too symmetric** — boundary points are distributed uniformly,
|
||||
so few pairs happen to be at unit distance during motion
|
||||
2. **Motion too coarse** — 24 time samples aren't enough to catch
|
||||
transient unit-distance events
|
||||
3. **Boundary not actually Sidon** — the Sidon property was on the 1D
|
||||
set, not on the 2D boundary points (the polar mapping destroys it)
|
||||
|
||||
Direction B fixes all three: use the ACTUAL Gerver sofa (asymmetric,
|
||||
18 curved arcs), discretize at higher resolution, and ensure the 2D
|
||||
boundary points form a Sidon set in ℤ² (not just 1D).
|
||||
|
||||
## The Gerver Sofa
|
||||
|
||||
Gerver's sofa (1992) is the best known solution to the moving sofa
|
||||
problem, with area ≈ 2.2195. It consists of 18 arcs:
|
||||
- 3 circular arcs (from hallway walls)
|
||||
- 3 circular arcs (from hallway walls, different radius)
|
||||
- 4 line segments
|
||||
- 8 circular arcs (from rotation contact)
|
||||
|
||||
The boundary is piecewise smooth, parameterized by arc length.
|
||||
|
||||
## Design: CRT Sidon Boundary on Gerver's Sofa
|
||||
|
||||
### Step 1: Discretize Gerver's Boundary
|
||||
|
||||
Sample n points along the Gerver boundary curve. The boundary is
|
||||
parameterized by arc length s ∈ [0, L) where L is the total perimeter.
|
||||
|
||||
Choose n ∈ {13, 21, 34, 55} (Fibonacci, for Sidon density).
|
||||
|
||||
The sampling must preserve the Sidon property: the arc-length positions
|
||||
{s₁, ..., sₙ} must form a Sidon set in ℤ (all pairwise sums distinct).
|
||||
|
||||
### Step 2: CRT Sidon Lift to ℤ²
|
||||
|
||||
Following `SIDON_SOFA_COLORING.md` §5.2 (corrected version with ℤ²
|
||||
extension):
|
||||
|
||||
1. Sample arc-length positions: s_i = i * L/n (uniform) or use CRT
|
||||
Sidon set in ℤ for non-uniform
|
||||
2. Convert to 2D coordinates: p_i = (x(s_i), y(s_i)) on the Gerver curve
|
||||
3. Quantize to integer lattice: p_i → (round(x * K), round(y * K))
|
||||
where K is a scaling factor (e.g., K = 1000 for millimeter precision)
|
||||
4. Verify Sidon property on ℤ²: all pairwise vector sums p_i + p_j
|
||||
are distinct
|
||||
|
||||
### Step 3: The Motion
|
||||
|
||||
The Gerver sofa has a KNOWN optimal motion through the L-corridor.
|
||||
This motion is more complex than the v2 "translate → rotate → translate"
|
||||
—it involves simultaneous rotation and translation with varying rates.
|
||||
|
||||
Discretize the motion into T = 100 time steps (4× finer than v2's 24).
|
||||
|
||||
### Step 4: Conflict Graph
|
||||
|
||||
Build the conflict graph:
|
||||
- Vertices: time steps t_0, ..., t_{T-1}
|
||||
- Edge {t_i, t_j}: exists if some boundary point at t_i is at unit
|
||||
distance from some boundary point at t_j
|
||||
|
||||
At EPS=1e-5 (justified tolerance), check if the Gerver sofa's actual
|
||||
motion produces more conflicts than the v2 shapes did.
|
||||
|
||||
### Step 5: Why This Might Work
|
||||
|
||||
The Gerver sofa is specifically designed to maximize contact with the
|
||||
corridor walls during motion. This means:
|
||||
- More boundary points are near walls → more near-unit-distance events
|
||||
- The 18-arc structure creates specific contact points → more structure
|
||||
- The asymmetric shape breaks the symmetry that made v2 shapes trivial
|
||||
- Higher time resolution (T=100 vs T=24) catches transient events
|
||||
|
||||
### Step 6: q-Profile as Shape Parameter
|
||||
|
||||
From `TOROIDAL_POLOIDAL_REFINEMENT.md` §4.2:
|
||||
- q < 1 (poloidal-dominated) = Gerver-like (hugs inner corner)
|
||||
- q > 1 (toroidal-dominated) = Hammersley-like (fills outer arc)
|
||||
- q = 1 = degenerate
|
||||
|
||||
The Gerver sofa is inherently q < 1. The question: does the Gerver
|
||||
motion at q ≈ 0.7 (the theoretical optimum from the 1.9× rule) produce
|
||||
a conflict graph with nontrivial χ?
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
```
|
||||
1. Implement Gerver sofa boundary (18 arcs)
|
||||
- Use exact arithmetic where possible (Fractions for radii)
|
||||
- Arc-length parameterization
|
||||
- Source: Gerver 1992, "On Moving a Sofa Around a Corner"
|
||||
|
||||
2. CRT Sidon sampling
|
||||
- Choose n = 21 boundary points
|
||||
- Use CRT Sidon set in ℤ for arc-length positions
|
||||
- Verify 2D Sidon property (vector sums distinct)
|
||||
|
||||
3. Gerver motion
|
||||
- Parameterize as γ(t) = (θ(t), x(t), y(t))
|
||||
- θ(t): rotation angle (nonlinear)
|
||||
- (x(t), y(t)): translation (nonlinear)
|
||||
- Discretize T = 100 steps
|
||||
|
||||
4. Conflict graph + chromatic number
|
||||
- EPS = 1e-5
|
||||
- DSATUR chromatic number
|
||||
- Sweep q-profile via boundary scaling
|
||||
|
||||
5. Compare to Direction A baselines
|
||||
- If χ > 3: the Gerver sofa generates real conflicts
|
||||
- If χ ≤ 3: the approach is fundamentally limited
|
||||
```
|
||||
|
||||
## What Would Constitute Success
|
||||
|
||||
- **χ ≥ 4 for Gerver sofa**: the conflict graph has real structure,
|
||||
spectral analysis is meaningful, the octagon principle applies
|
||||
- **χ varies with q**: the q-profile affects conflict structure,
|
||||
confirming the toroidal/poloidal mapping
|
||||
- **χ is stable across seeds**: the result is a property of the shape,
|
||||
not the DSATUR vertex ordering
|
||||
|
||||
## What Would Constitute Failure
|
||||
|
||||
- **χ ≤ 3 for all configurations**: the Gerver sofa doesn't generate
|
||||
enough conflicts even at high resolution → the sofa coloring approach
|
||||
is fundamentally limited by the geometry, not the implementation
|
||||
- **χ varies wildly with seed**: the conflict graph is too sparse for
|
||||
DSATUR to be reliable → need exact methods, but they're exponential
|
||||
|
||||
## Honest Assessment
|
||||
|
||||
Direction B is a long shot. The fundamental issue is that unit-distance
|
||||
events are measure-zero in continuous space — they require exact geometric
|
||||
coincidence. Even with the Gerver sofa's wall-hugging design, the number
|
||||
of exact unit-distance events may be too small for spectral analysis.
|
||||
|
||||
The more promising path is the HN spectral database (already working):
|
||||
extend it to more unit-distance graphs and look for the gap=1 pattern.
|
||||
The gap=1 for Moser spindle and Golomb graph is a real, measured result.
|
||||
363
scripts/crt_qprofile_sweep.py
Normal file
363
scripts/crt_qprofile_sweep.py
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
#!/usr/bin/env python3
|
||||
"""crt_qprofile_sweep.py — q-profile safety factor design for CRT moduli.
|
||||
|
||||
Replaces brute-force modulus selection with systematic q-profile sweep,
|
||||
as proposed in TOROIDAL_POLOIDAL_REFINEMENT.md (R1, R6).
|
||||
|
||||
The toroidal/poloidal mapping gives:
|
||||
q = L_reflection / L_identity (toroidal/poloidal ratio)
|
||||
q < 1 = poloidal-dominated (Sidon-favorable)
|
||||
q > 1 = toroidal-dominated (Sidon-unfavorable)
|
||||
q = 1 = degenerate (rational surface → resonance → Sidon collapse)
|
||||
|
||||
This script:
|
||||
1. Sweeps q from 0.3 to 3.0 (avoiding simple rationals)
|
||||
2. For each q, constructs moduli (L₀, L₁) with L₁/L₀ = q
|
||||
3. Tests Sidon preservation for known Sidon label sets
|
||||
4. Also applies cross-pair q-ratio check (R2): avoid simple rationals
|
||||
5. Measures Sidon score as function of q
|
||||
|
||||
Outputs:
|
||||
.openresearch/artifacts/crt_qprofile_sweep.json
|
||||
.openresearch/artifacts/EVAL.md
|
||||
|
||||
All integer arithmetic. No floats in compute path.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import math
|
||||
import json
|
||||
import time
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
from collections import Counter
|
||||
from fractions import Fraction
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
ARTIFACTS_DIR = REPO_ROOT / ".openresearch" / "artifacts"
|
||||
ARTIFACTS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
OUTPUT_PATH = ARTIFACTS_DIR / "crt_qprofile_sweep.json"
|
||||
EVAL_PATH = ARTIFACTS_DIR / "EVAL.md"
|
||||
|
||||
|
||||
# ── Exact Arithmetic ──────────────────────────────────────────────────────
|
||||
|
||||
def gcd(a, b):
|
||||
while b:
|
||||
a, b = b, a % b
|
||||
return a
|
||||
|
||||
def pairwise_coprime(moduli):
|
||||
for i in range(len(moduli)):
|
||||
for j in range(i + 1, len(moduli)):
|
||||
if gcd(moduli[i], moduli[j]) != 1:
|
||||
return False
|
||||
return True
|
||||
|
||||
def is_simple_rational(a, b, max_den=7):
|
||||
"""Check if a/b reduces to m/n with n <= max_den."""
|
||||
if b == 0:
|
||||
return True
|
||||
g = gcd(abs(a), abs(b))
|
||||
na, nb = abs(a) // g, abs(b) // g
|
||||
return nb <= max_den
|
||||
|
||||
|
||||
# ── CRT Torus Embedding ───────────────────────────────────────────────────
|
||||
|
||||
def embed(labels, S, moduli):
|
||||
M = 1
|
||||
for m in moduli:
|
||||
M *= m
|
||||
embedded = []
|
||||
for a in labels:
|
||||
row = []
|
||||
for i in range(len(moduli)):
|
||||
if i == 0:
|
||||
row.append(a % moduli[0])
|
||||
else:
|
||||
row.append((S - a) % moduli[i])
|
||||
embedded.append(row)
|
||||
return embedded, M
|
||||
|
||||
def egcd(a, b):
|
||||
if b == 0:
|
||||
return a, 1, 0
|
||||
g, x, y = egcd(b, a % b)
|
||||
return g, y, x - (a // b) * y
|
||||
|
||||
def modinv(a, m):
|
||||
g, x, _ = egcd(a % m, m)
|
||||
if g != 1:
|
||||
return None
|
||||
return x % m
|
||||
|
||||
def crt_reconstruct(residues, moduli):
|
||||
M = 1
|
||||
for m in moduli:
|
||||
M *= m
|
||||
x = 0
|
||||
for r, m in zip(residues, moduli):
|
||||
Mi = M // m
|
||||
inv = modinv(Mi % m, m)
|
||||
if inv is None:
|
||||
return None
|
||||
x = (x + r * Mi * inv) % M
|
||||
return x
|
||||
|
||||
def sidon_check(embedded, moduli):
|
||||
M = 1
|
||||
for m in moduli:
|
||||
M *= m
|
||||
n = len(embedded)
|
||||
total_pairs = n * (n + 1) // 2
|
||||
vals = [crt_reconstruct(row, moduli) for row in embedded]
|
||||
sums = []
|
||||
for i in range(n):
|
||||
for j in range(i, n):
|
||||
sums.append((vals[i] + vals[j]) % M)
|
||||
counts = Counter(sums)
|
||||
collisions = sum(c - 1 for c in counts.values())
|
||||
score = 1.0 - collisions / total_pairs if total_pairs > 0 else 1.0
|
||||
return {
|
||||
"total_pairs": total_pairs,
|
||||
"distinct_residues": len(counts),
|
||||
"collisions": collisions,
|
||||
"sidon_score": round(score, 6),
|
||||
"is_sidon": collisions == 0,
|
||||
}
|
||||
|
||||
|
||||
# ── q-Profile Modulus Selection ──────────────────────────────────────────
|
||||
|
||||
def select_qprofile_moduli(L0, q_num, q_den, n_moduli=2):
|
||||
"""Select moduli with a given q-profile.
|
||||
|
||||
q = L₁/L₀ = q_num/q_den (as exact fraction)
|
||||
L₀ = L0 (identity axis, poloidal)
|
||||
L₁ = L₀ * q_num / q_den (reflection axis, toroidal)
|
||||
|
||||
For n_moduli > 2, additional moduli use increasing primes coprime to all.
|
||||
Returns (moduli, q) or None if not coprime.
|
||||
"""
|
||||
# L₁ = L0 * q_num / q_den — must be integer
|
||||
L1 = L0 * q_num // q_den
|
||||
if L0 * q_num != L1 * q_den:
|
||||
# Not exact — adjust L0 to make it work
|
||||
L0 = L0 * q_den
|
||||
L1 = L0 * q_num // q_den
|
||||
|
||||
if L0 < 2 or L1 < 2:
|
||||
return None, None
|
||||
|
||||
moduli = [L0, L1]
|
||||
|
||||
# For n_moduli > 2, add coprime primes
|
||||
if n_moduli > 2:
|
||||
p = L1 + 2
|
||||
while len(moduli) < n_moduli:
|
||||
if all(gcd(p, m) == 1 for m in moduli) and p > 2:
|
||||
moduli.append(p)
|
||||
p += 1
|
||||
|
||||
if not pairwise_coprime(moduli):
|
||||
return None, None
|
||||
|
||||
q = Fraction(q_num, q_den)
|
||||
return moduli, q
|
||||
|
||||
|
||||
def sweep_q_profile(label_set, S, L0_base, max_q_num=30, max_q_den=20):
|
||||
"""Sweep q = L₁/L₀ over all coprime fractions q_num/q_den.
|
||||
Returns list of (q, moduli, sidon_result, q_is_simple_rational).
|
||||
"""
|
||||
results = []
|
||||
|
||||
for q_den in range(2, max_q_den + 1):
|
||||
for q_num in range(1, max_q_num + 1):
|
||||
if gcd(q_num, q_den) != 1:
|
||||
continue # skip non-reduced fractions
|
||||
if q_num == q_den:
|
||||
continue # skip q=1 (degenerate)
|
||||
|
||||
moduli, q = select_qprofile_moduli(L0_base, q_num, q_den)
|
||||
if moduli is None:
|
||||
continue
|
||||
|
||||
q_simple = is_simple_rational(q_num, q_den, max_den=7)
|
||||
|
||||
embedded, M = embed(label_set, S, moduli)
|
||||
sidon = sidon_check(embedded, moduli)
|
||||
|
||||
entry = {
|
||||
"q": str(q),
|
||||
"q_float": float(q),
|
||||
"q_num": q_num,
|
||||
"q_den": q_den,
|
||||
"q_simple_rational": q_simple,
|
||||
"moduli": moduli,
|
||||
"M": M,
|
||||
"L0": moduli[0],
|
||||
"L1": moduli[1],
|
||||
"sidon_score": sidon["sidon_score"],
|
||||
"collisions": sidon["collisions"],
|
||||
"is_sidon": sidon["is_sidon"],
|
||||
"total_pairs": sidon["total_pairs"],
|
||||
"distinct_residues": sidon["distinct_residues"],
|
||||
}
|
||||
results.append(entry)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
# ── Main ──────────────────────────────────────────────────────────────────
|
||||
|
||||
def run_experiment():
|
||||
results = {
|
||||
"experiment": "crt_qprofile_sweep",
|
||||
"timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
||||
"config": {
|
||||
"L0_base": 7,
|
||||
"max_q_num": 30,
|
||||
"max_q_den": 20,
|
||||
"label_sets": [
|
||||
{"name": "sidon_pow2", "labels": [1, 2, 4, 8, 16], "S": 32},
|
||||
{"name": "sidon_singer5", "labels": [0, 1, 4, 14, 16], "S": 30},
|
||||
{"name": "nonsidon_seq5", "labels": [0, 1, 2, 3, 4], "S": 5},
|
||||
],
|
||||
},
|
||||
"data": [],
|
||||
"summary": {},
|
||||
}
|
||||
|
||||
label_sets = [
|
||||
("sidon_pow2", [1, 2, 4, 8, 16], 32),
|
||||
("sidon_singer5", [0, 1, 4, 14, 16], 30),
|
||||
("nonsidon_seq5", [0, 1, 2, 3, 4], 5),
|
||||
]
|
||||
|
||||
L0_base = 7
|
||||
|
||||
for desc, labels, S in label_sets:
|
||||
print(f"\n{'='*60}")
|
||||
print(f" Label set: {desc} (n={len(labels)}, S={S})")
|
||||
print(f"{'='*60}")
|
||||
|
||||
sweep = sweep_q_profile(labels, S, L0_base)
|
||||
|
||||
# Analyze
|
||||
sidon_count = sum(1 for r in sweep if r["is_sidon"])
|
||||
total_count = len(sweep)
|
||||
q_simple_count = sum(1 for r in sweep if r["q_simple_rational"])
|
||||
q_simple_sidon = sum(1 for r in sweep if r["q_simple_rational"] and r["is_sidon"])
|
||||
q_not_simple_sidon = sum(1 for r in sweep if not r["q_simple_rational"] and r["is_sidon"])
|
||||
|
||||
# q < 1 vs q > 1
|
||||
q_lt1 = [r for r in sweep if r["q_float"] < 1.0]
|
||||
q_gt1 = [r for r in sweep if r["q_float"] > 1.0]
|
||||
q_lt1_sidon = sum(1 for r in q_lt1 if r["is_sidon"])
|
||||
q_gt1_sidon = sum(1 for r in q_gt1 if r["is_sidon"])
|
||||
|
||||
# Best q (highest Sidon score)
|
||||
best = max(sweep, key=lambda r: r["sidon_score"]) if sweep else None
|
||||
|
||||
summary = {
|
||||
"total_configs": total_count,
|
||||
"sidon_configs": sidon_count,
|
||||
"sidon_rate": round(sidon_count / total_count, 4) if total_count > 0 else 0,
|
||||
"q_simple_rational_count": q_simple_count,
|
||||
"q_simple_sidon": q_simple_sidon,
|
||||
"q_not_simple_sidon": q_not_simple_sidon,
|
||||
"q_lt1_total": len(q_lt1),
|
||||
"q_lt1_sidon": q_lt1_sidon,
|
||||
"q_lt1_sidon_rate": round(q_lt1_sidon / len(q_lt1), 4) if q_lt1 else 0,
|
||||
"q_gt1_total": len(q_gt1),
|
||||
"q_gt1_sidon": q_gt1_sidon,
|
||||
"q_gt1_sidon_rate": round(q_gt1_sidon / len(q_gt1), 4) if q_gt1 else 0,
|
||||
"best_q": best["q"] if best else None,
|
||||
"best_sidon_score": best["sidon_score"] if best else None,
|
||||
"best_moduli": best["moduli"] if best else None,
|
||||
}
|
||||
results["summary"][desc] = summary
|
||||
|
||||
print(f" Total configs: {total_count}")
|
||||
print(f" Sidon configs: {sidon_count} ({summary['sidon_rate']:.1%})")
|
||||
print(f" q < 1: {q_lt1_sidon}/{len(q_lt1)} Sidon ({summary['q_lt1_sidon_rate']:.1%})")
|
||||
print(f" q > 1: {q_gt1_sidon}/{len(q_gt1)} Sidon ({summary['q_gt1_sidon_rate']:.1%})")
|
||||
print(f" q simple rational: {q_simple_sidon}/{q_simple_count} Sidon")
|
||||
print(f" q NOT simple: {q_not_simple_sidon}/{total_count - q_simple_count} Sidon")
|
||||
if best:
|
||||
print(f" Best q = {best['q']} (score={best['sidon_score']}, moduli={best['moduli']})")
|
||||
|
||||
# Store all sweep data
|
||||
for r in sweep:
|
||||
r["label_set"] = desc
|
||||
results["data"].append(r)
|
||||
|
||||
content = json.dumps(results, indent=2, sort_keys=True, default=str)
|
||||
results["sha256"] = hashlib.sha256(content.encode()).hexdigest()
|
||||
return results
|
||||
|
||||
|
||||
def write_eval(results):
|
||||
lines = [
|
||||
"# CRT q-Profile Safety Factor Sweep",
|
||||
"",
|
||||
f"**Experiment:** {results['experiment']}",
|
||||
f"**Date:** {results['timestamp']}",
|
||||
f"**SHA-256:** `{results['sha256']}`",
|
||||
"",
|
||||
"## Summary: Sidon Rate by q-Regime",
|
||||
"",
|
||||
"| Label set | Total | Sidon | Rate | q<1 total | q<1 Sidon | q<1 rate | q>1 total | q>1 Sidon | q>1 rate | Simple q Sidon | Non-simple Sidon | Best q |",
|
||||
"|-----------|-------|-------|------|-----------|-----------|----------|-----------|-----------|----------|----------------|------------------|-------|",
|
||||
]
|
||||
for desc, s in results["summary"].items():
|
||||
lines.append(
|
||||
f"| {desc} | {s['total_configs']} | {s['sidon_configs']} | "
|
||||
f"{s['sidon_rate']:.1%} | "
|
||||
f"{s['q_lt1_total']} | {s['q_lt1_sidon']} | {s['q_lt1_sidon_rate']:.1%} | "
|
||||
f"{s['q_gt1_total']} | {s['q_gt1_sidon']} | {s['q_gt1_sidon_rate']:.1%} | "
|
||||
f"{s['q_simple_sidon']} | {s['q_not_simple_sidon']} | "
|
||||
f"{s['best_q']} |"
|
||||
)
|
||||
|
||||
lines.extend([
|
||||
"",
|
||||
"## Predictions Tested",
|
||||
"",
|
||||
"1. **q < 1 (poloidal-dominated) should have higher Sidon rate than q > 1**",
|
||||
" - This is the toroidal/poloidal refinement prediction",
|
||||
" - If confirmed: poloidal resolution matters for Sidon structure",
|
||||
"",
|
||||
"2. **Simple rational q should have LOWER Sidon rate than non-simple q**",
|
||||
" - This is the R2 cross-pair coprimality prediction",
|
||||
" - Simple rationals = resonant surfaces = Sidon collapse",
|
||||
"",
|
||||
"3. **Best q should be < 1 and not a simple rational**",
|
||||
" - Optimal q-profile is poloidal-dominated and irrational",
|
||||
"",
|
||||
])
|
||||
|
||||
EVAL_PATH.write_text("\n".join(lines))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("CRT q-Profile Safety Factor Sweep")
|
||||
print("=" * 60)
|
||||
|
||||
t0 = time.time()
|
||||
results = run_experiment()
|
||||
elapsed = time.time() - t0
|
||||
|
||||
OUTPUT_PATH.write_text(json.dumps(results, indent=2, default=str))
|
||||
print(f"\nResults → {OUTPUT_PATH}")
|
||||
|
||||
write_eval(results)
|
||||
print(f"EVAL → {EVAL_PATH}")
|
||||
|
||||
print(f"\nElapsed: {elapsed:.1f}s")
|
||||
print("=" * 60)
|
||||
print("DONE")
|
||||
print("=" * 60)
|
||||
Loading…
Add table
Reference in a new issue