mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
AGENTS.md: added SpherionTwinPrime architecture section, updated Burgers energy dissipation to parametric form. spherion_twin_prime.py (20KB): priority-queue walk with polarity energy tuning. Build: 8598 jobs, 0 errors.
518 lines
20 KiB
Python
518 lines
20 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Spherion 16D Multi-Polar Transition Domain Twin-Prime Walk.
|
||
|
||
Extends the classic twin-prime priority-queue enumeration of the 4 obstruction
|
||
sheets 6ab ┬▒ a ┬▒ b with a per-sheet polarity parameter that tunes the energy
|
||
of each obstruction, controlling the density of twin-prime witnesses.
|
||
|
||
Theory:
|
||
Twin primes (p, p+2) are characterized by the polynomial
|
||
n = 6ab ┬▒ a ┬▒ b. An integer n is a *witness* (twin-prime candidate)
|
||
iff no positive integers a,b and signs ┬▒1 satisfy the obstruction:
|
||
|
||
n = 6ab + σ₁·a + σ₂·b (σ₁, σ₂ ∈ {+1, -1})
|
||
|
||
The 4 obstruction sheets are:
|
||
Sheet (+1, +1): 6ab + a + b
|
||
Sheet (+1, -1): 6ab + a - b
|
||
Sheet (-1, +1): 6ab - a + b
|
||
Sheet (-1, -1): 6ab - a - b
|
||
|
||
Polarity scales the obstruction energy:
|
||
E = p ┬À (6ab + σ₁┬Àa + σ₂┬Àb)
|
||
|
||
p = 1.0 → standard algorithm (OEIS A002822)
|
||
p < 1.0 → shed energy (lower barrier, more obstructions → fewer witnesses)
|
||
p > 1.0 → accumulate energy (higher barrier, fewer obstructions → more witnesses)
|
||
p = 0 → no obstructions (false positives: all integers are witnesses)
|
||
p → ∞ → obstructions at infinity (false negatives: no finite witnesses)
|
||
|
||
Energy = polarity ┬À raw_value defines the peak position. Integer n is
|
||
obstructed iff there exists (a,b,σ₁,σ₂) such that n = p ┬À (6ab + σ₁┬Àa + σ₂┬Àb).
|
||
|
||
References:
|
||
- OEIS A002822: Numbers n such that 6n-1 and 6n+1 are twin primes.
|
||
- "Twin-prime generating polynomials" via 6ab ┬▒ a ┬▒ b.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import hashlib
|
||
import heapq
|
||
import json
|
||
import sys
|
||
from datetime import datetime, timezone
|
||
from typing import Dict, List, Optional, Set, Tuple
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Sheet-generator ── lazy priority-queue enumeration of one obstruction sheet
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
class SheetGenerator:
|
||
"""Lazily yield values from one obstruction sheet in increasing order.
|
||
|
||
F(a,b) = polarity ┬À (6┬Àa┬Àb + σ₁┬Àa + σ₂┬Àb) for a,b ≥ 1.
|
||
|
||
Uses a priority-queue frontier (standard sorted-matrix traversal).
|
||
When *max_value* is given, stops as soon as the popped value exceeds it,
|
||
enabling bounded scans that terminate even for extreme polarities.
|
||
"""
|
||
|
||
def __init__(
|
||
self,
|
||
σ1: int,
|
||
σ2: int,
|
||
polarity: float,
|
||
max_value: Optional[float] = None,
|
||
) -> None:
|
||
if polarity < 0:
|
||
raise ValueError(f"polarity must be ≥ 0, got {polarity}")
|
||
self.σ1 = σ1
|
||
self.σ2 = σ2
|
||
self.polarity = polarity
|
||
self.max_value = max_value
|
||
self._heap: List[Tuple[float, int, int]] = []
|
||
self._visited: Set[Tuple[int, int]] = set()
|
||
self._push(1, 1)
|
||
|
||
def _row(self, a: int, b: int) -> float:
|
||
raw = 6 * a * b + self.σ1 * a + self.σ2 * b
|
||
return self.polarity * raw
|
||
|
||
def _push(self, a: int, b: int) -> None:
|
||
if self.max_value is not None:
|
||
lo = self._row(a, b)
|
||
if lo > self.max_value:
|
||
return
|
||
key = (a, b)
|
||
if key not in self._visited:
|
||
self._visited.add(key)
|
||
heapq.heappush(self._heap, (self._row(a, b), a, b))
|
||
|
||
def __iter__(self) -> SheetGenerator:
|
||
return self
|
||
|
||
def __next__(self) -> float:
|
||
while self._heap:
|
||
v, a, b = heapq.heappop(self._heap)
|
||
if self.max_value is not None and v > self.max_value:
|
||
self._heap.clear()
|
||
raise StopIteration
|
||
self._push(a + 1, b)
|
||
self._push(a, b + 1)
|
||
return v
|
||
raise StopIteration
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Spherion 16D twin-prime walk
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
SIGNS: List[Tuple[int, int]] = [(1, 1), (1, -1), (-1, 1), (-1, -1)]
|
||
|
||
|
||
class SpherionTwinPrimeWalk:
|
||
"""Priority-queue walk over the 4 obstruction sheets with energy tuning.
|
||
|
||
Parameters
|
||
----------
|
||
polarities : dict, optional
|
||
Per-sheet energy tuning. Keys are (σ₁, σ₂) tuples, values are floats.
|
||
Missing sheets default to 1.0.
|
||
"""
|
||
|
||
def __init__(
|
||
self, polarities: Optional[Dict[Tuple[int, int], float]] = None
|
||
) -> None:
|
||
if polarities is None:
|
||
polarities = {s: 1.0 for s in SIGNS}
|
||
self.polarities = polarities
|
||
|
||
# ── Public API ──────────────────────────────────────────────────────
|
||
|
||
def obstruction(self, a: int, b: int, σ1: int, σ2: int) -> float:
|
||
"""Obstruction energy at (a, b, σ₁, σ₂)."""
|
||
raw = 6 * a * b + σ1 * a + σ2 * b
|
||
p = self.polarities.get((σ1, σ2), 1.0)
|
||
return p * raw
|
||
|
||
def _merged(self, max_value: Optional[float] = None) -> heapq.merge:
|
||
"""Merge all 4 sheet streams into one sorted obstruction stream."""
|
||
gens = [
|
||
SheetGenerator(
|
||
σ1,
|
||
σ2,
|
||
self.polarities.get((σ1, σ2), 1.0),
|
||
max_value=max_value,
|
||
)
|
||
for σ1, σ2 in SIGNS
|
||
]
|
||
return heapq.merge(*gens)
|
||
|
||
def energy_spectrum(self, limit: int = 1000) -> List[float]:
|
||
"""First *limit* obstruction energies from the merged stream."""
|
||
out: List[float] = []
|
||
for i, v in enumerate(self._merged()):
|
||
if i >= limit:
|
||
break
|
||
out.append(v)
|
||
return out
|
||
|
||
def _enumerate_bounded(self, limit: int) -> Set[int]:
|
||
"""Enumerate all obstructed integers ≤ *limit* via direct nested loops.
|
||
|
||
For bounded enumeration this is faster and terminates reliably even for
|
||
extreme polarities (p=0, p→∞) where the PQ-based generator would loop
|
||
indefinitely or never reach the threshold.
|
||
"""
|
||
covered: Set[int] = set()
|
||
limit_f = float(limit)
|
||
for σ1, σ2 in SIGNS:
|
||
p = self.polarities.get((σ1, σ2), 1.0)
|
||
if p == 0.0:
|
||
continue
|
||
# a,b ≥ 1, raw = 6ab + σ1·a + σ2·b ≥ 4 for a=b=1, (-1,-1) sheet
|
||
# bound: p * (6ab - a - b) ≤ limit ⇒ ab ≤ limit/p/4 (rough bound)
|
||
max_ab = max(1, int(limit_f / p / 4) + 2)
|
||
for a in range(1, max_ab + 1):
|
||
for b in range(1, max_ab + 1):
|
||
raw = 6 * a * b + σ1 * a + σ2 * b
|
||
v = p * raw
|
||
if v > limit_f:
|
||
break
|
||
iv = int(v)
|
||
if v == iv and iv >= 1:
|
||
covered.add(iv)
|
||
return covered
|
||
|
||
def obstructions_up_to(self, limit: int) -> Set[int]:
|
||
"""Set of integers n ≤ *limit* that are obstructed (energy lands on n)."""
|
||
return self._enumerate_bounded(limit)
|
||
|
||
def witnesses(self, limit: int = 500) -> List[int]:
|
||
"""Integers 1..limit NOT obstructed at the current polarities."""
|
||
obs = self.obstructions_up_to(limit)
|
||
return [i for i in range(1, limit + 1) if i not in obs]
|
||
|
||
def witness_density(self, limit: int = 500) -> float:
|
||
"""Fraction of integers ≤ *limit* that are witnesses."""
|
||
return len(self.witnesses(limit)) / max(limit, 1)
|
||
|
||
def betti_gaps(self, limit: int = 500) -> List[int]:
|
||
"""Witness numbers as Betti scars — the uncovered (gap) region."""
|
||
return self.witnesses(limit)
|
||
|
||
def per_sheet_coverage(
|
||
self, limit: int = 500
|
||
) -> Dict[Tuple[int, int], List[int]]:
|
||
"""Which integers each sheet obstructs individually."""
|
||
result: Dict[Tuple[int, int], List[int]] = {}
|
||
for σ1, σ2 in SIGNS:
|
||
p = self.polarities.get((σ1, σ2), 1.0)
|
||
hits: Set[int] = set()
|
||
if p > 0.0:
|
||
max_ab = max(1, int(float(limit) / p / 4) + 2)
|
||
for a in range(1, max_ab + 1):
|
||
for b in range(1, max_ab + 1):
|
||
raw = 6 * a * b + σ1 * a + σ2 * b
|
||
v = p * raw
|
||
if v > limit:
|
||
break
|
||
iv = int(v)
|
||
if v == iv and iv >= 1:
|
||
hits.add(iv)
|
||
result[(σ1, σ2)] = sorted(hits)
|
||
return result
|
||
|
||
def report(self, limit: int = 500) -> dict:
|
||
"""Full JSON-serialisible report for the current polarity configuration."""
|
||
energy = self.energy_spectrum(min(limit, 200))
|
||
w = self.witnesses(limit)
|
||
psc = self.per_sheet_coverage(limit)
|
||
psc_serial = {f"({s1},{s2})": vals for (s1, s2), vals in psc.items()}
|
||
return {
|
||
"schema": "spherion_twin_prime_report_v1",
|
||
"polarities": {
|
||
f"({s1},{s2})": self.polarities[(s1, s2)]
|
||
for s1, s2 in SIGNS
|
||
},
|
||
"limit": limit,
|
||
"witness_count": len(w),
|
||
"witness_density": len(w) / max(limit, 1),
|
||
"first_20_witnesses": w[:20],
|
||
"first_20_energies": [round(e, 6) for e in energy[:20]],
|
||
"per_sheet_coverage": {k: v[:10] for k, v in psc_serial.items()},
|
||
}
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Analysis helpers
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
def witness_density_vs_polarity(
|
||
polarities: List[float], limit: int = 500
|
||
) -> List[Tuple[float, float, int]]:
|
||
"""Compute witness density for each uniform polarity in *polarities*."""
|
||
results: List[Tuple[float, float, int]] = []
|
||
for p in polarities:
|
||
walk = SpherionTwinPrimeWalk({s: p for s in SIGNS})
|
||
w = walk.witnesses(limit)
|
||
density = len(w) / max(limit, 1)
|
||
results.append((p, density, len(w)))
|
||
return results
|
||
|
||
|
||
def standard_oeis_check(limit: int = 200) -> Tuple[int, List[int]]:
|
||
"""Check that polarity=1.0 gives OEIS A002822 (twin-prime witnesses)."""
|
||
walk = SpherionTwinPrimeWalk()
|
||
w = walk.witnesses(limit)
|
||
# OEIS A002822 starts: 1, 2, 3, 5, 7, 10, 12, 15, 17, 18, 23, 25, 30, ...
|
||
return (len(w), w)
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# CLI
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
def build_arg_parser() -> argparse.ArgumentParser:
|
||
p = argparse.ArgumentParser(
|
||
description="Spherion 16D twin-prime walk with polarity tuning"
|
||
)
|
||
p.add_argument("--test", action="store_true", help="Run test suite")
|
||
p.add_argument(
|
||
"--limit", type=int, default=500, help="Search limit (default 500)"
|
||
)
|
||
p.add_argument(
|
||
"--polarity",
|
||
type=float,
|
||
default=None,
|
||
help="Uniform polarity override (omit for per-sheet defaults)",
|
||
)
|
||
p.add_argument("--json", action="store_true", help="Output JSON report")
|
||
return p
|
||
|
||
|
||
def run_tests(limit: int = 500) -> dict:
|
||
"""Execute the full test matrix and return a summary dict."""
|
||
results: dict = {
|
||
"schema": "spherion_twin_prime_test_v1",
|
||
"generated_at_utc": datetime.now(timezone.utc).isoformat(),
|
||
"limit": limit,
|
||
"tests": {},
|
||
}
|
||
|
||
do_small = min(limit, 300)
|
||
|
||
# ── 1. polarity = 0: no obstructions → all witnesses ────────────────
|
||
walk0 = SpherionTwinPrimeWalk({s: 0.0 for s in SIGNS})
|
||
w0 = walk0.witnesses(do_small)
|
||
results["tests"]["polarity_0"] = {
|
||
"description": "p=0 → no obstructions, all witnesses",
|
||
"witness_count": len(w0),
|
||
"expected_count": do_small,
|
||
"all_witnesses": len(w0) == do_small,
|
||
}
|
||
|
||
# ── 2. polarity = 1.0: standard OEIS A002822 ────────────────────────
|
||
walk1 = SpherionTwinPrimeWalk()
|
||
w1 = walk1.witnesses(do_small)
|
||
results["tests"]["polarity_1_0"] = {
|
||
"description": "p=1.0 → standard OEIS A002822",
|
||
"witness_count": len(w1),
|
||
"witness_density": len(w1) / do_small,
|
||
"first_20": w1[:20],
|
||
}
|
||
|
||
# ── 3. polarity → ∞: very large → obstructions drift to infinity ────
|
||
walk_inf = SpherionTwinPrimeWalk({s: 1e9 for s in SIGNS})
|
||
w_inf = walk_inf.witnesses(do_small)
|
||
results["tests"]["polarity_inf"] = {
|
||
"description": "p=1e9 → obstructions at ≈1e9×raw, no finite hits",
|
||
"witness_count": len(w_inf),
|
||
"expected_count": do_small,
|
||
"all_witnesses": len(w_inf) == do_small,
|
||
}
|
||
|
||
# ── 4. shed energy (p < 1.0) ────────────────────────────────────────
|
||
walk_low = SpherionTwinPrimeWalk({s: 0.5 for s in SIGNS}) # p = 0.5
|
||
w_low = walk_low.witnesses(do_small)
|
||
results["tests"]["polarity_shed"] = {
|
||
"description": "p=0.5 → shed energy, more obstructions, fewer witnesses",
|
||
"witness_count": len(w_low),
|
||
"density": len(w_low) / do_small,
|
||
"vs_nominal_density": (
|
||
round(len(w_low) / do_small, 4),
|
||
round(len(w1) / do_small, 4),
|
||
),
|
||
}
|
||
|
||
# ── 5. accumulate energy (p > 1.0) ──────────────────────────────────
|
||
walk_high = SpherionTwinPrimeWalk({s: 3.0 for s in SIGNS})
|
||
w_high = walk_high.witnesses(do_small)
|
||
results["tests"]["polarity_accumulate"] = {
|
||
"description": "p=3.0 → accumulate energy, fewer obstructions, more witnesses",
|
||
"witness_count": len(w_high),
|
||
"density": len(w_high) / do_small,
|
||
"vs_nominal_density": (
|
||
round(len(w_high) / do_small, 4),
|
||
round(len(w1) / do_small, 4),
|
||
),
|
||
}
|
||
|
||
# ── 6. per-sheet tuning ─────────────────────────────────────────────
|
||
per_sheet_polarities = {
|
||
(1, 1): 1.0,
|
||
(1, -1): 0.5,
|
||
(-1, 1): 2.0,
|
||
(-1, -1): 1.0,
|
||
}
|
||
walk_ps = SpherionTwinPrimeWalk(per_sheet_polarities)
|
||
w_ps = walk_ps.witnesses(do_small)
|
||
psc = walk_ps.per_sheet_coverage(do_small)
|
||
results["tests"]["per_sheet_tuning"] = {
|
||
"description": "Per-sheet polarities (1,1)=1.0 (1,-1)=0.5 (-1,1)=2.0 (-1,-1)=1.0",
|
||
"polarities": {
|
||
f"({s1},{s2})": p for (s1, s2), p in per_sheet_polarities.items()
|
||
},
|
||
"witness_count": len(w_ps),
|
||
"density": len(w_ps) / do_small,
|
||
"per_sheet_obstruction_counts": {
|
||
f"({s1},{s2})": len(vals) for (s1, s2), vals in psc.items()
|
||
},
|
||
}
|
||
|
||
# ── 7. Density sweep ────────────────────────────────────────────────
|
||
sweep_points = [0.0, 0.25, 0.5, 0.75, 1.0, 2.0, 5.0, 10.0]
|
||
sweep = witness_density_vs_polarity(sweep_points, limit=do_small)
|
||
results["tests"]["density_sweep"] = {
|
||
"description": "Witness density vs uniform polarity",
|
||
"sweep": [
|
||
{"polarity": p, "density": round(d, 4), "witnesses": n}
|
||
for p, d, n in sweep
|
||
],
|
||
}
|
||
|
||
# ── 8. Betti-style gap analysis ─────────────────────────────────────
|
||
gaps = walk1.betti_gaps(do_small)
|
||
max_run = 0
|
||
cur = 0
|
||
for i in range(1, do_small + 1):
|
||
if i in gaps:
|
||
cur += 1
|
||
if cur > max_run:
|
||
max_run = cur
|
||
else:
|
||
cur = 0
|
||
results["tests"]["betti_gaps"] = {
|
||
"description": "Betti-style gap analysis at p=1.0",
|
||
"gap_count": len(gaps),
|
||
"longest_witness_run": max_run,
|
||
"first_20_gaps": gaps[:20],
|
||
}
|
||
|
||
return results
|
||
|
||
|
||
def main() -> None:
|
||
args = build_arg_parser().parse_args()
|
||
|
||
if args.test:
|
||
results = run_tests(limit=args.limit)
|
||
if args.json:
|
||
print(json.dumps(results, indent=2))
|
||
else:
|
||
tests = results["tests"]
|
||
print(f"╔══ Spherion 16D Twin-Prime Walk — Test Results ═══╗")
|
||
print(f" Limit: {results['limit']}\n")
|
||
|
||
t = tests["polarity_0"]
|
||
print(
|
||
f"[polarity=0] {t['witness_count']}/{results['limit']} "
|
||
f"witnesses — {'✓' if t['all_witnesses'] else '✗'} all witnesses"
|
||
)
|
||
|
||
t = tests["polarity_1_0"]
|
||
print(
|
||
f"[polarity=1.0] {t['witness_count']}/{results['limit']} "
|
||
f"witnesses — density {t['witness_density']:.4f}"
|
||
)
|
||
print(f" First 20 witnesses: {t['first_20']}")
|
||
|
||
t = tests["polarity_shed"]
|
||
d_nom = tests["polarity_1_0"]["witness_density"]
|
||
print(
|
||
f"[polarity=1/3 — shed] {t['witness_count']}/{results['limit']} "
|
||
f"witnesses — density {t['density']:.4f} "
|
||
f"(nominal {d_nom:.4f}) {'✓ shed < nominal' if t['density'] < d_nom else ''}"
|
||
)
|
||
|
||
t = tests["polarity_accumulate"]
|
||
print(
|
||
f"[polarity=3.0 — accumulate] {t['witness_count']}/{results['limit']} "
|
||
f"witnesses — density {t['density']:.4f} "
|
||
f"(nominal {d_nom:.4f}) {'✓ accumulate > nominal' if t['density'] > d_nom else ''}"
|
||
)
|
||
|
||
t = tests["polarity_inf"]
|
||
print(
|
||
f"[polarity=1e9 — ∞ limit] {t['witness_count']}/{results['limit']} "
|
||
f"witnesses — {'✓' if t['all_witnesses'] else '✗'} all finite witnesses"
|
||
)
|
||
|
||
t = tests["per_sheet_tuning"]
|
||
print(f"\n[per-sheet tuning] {t['witness_count']}/{results['limit']} witnesses")
|
||
print(f" Sheet obstructions: {t['per_sheet_obstruction_counts']}")
|
||
print(f" Polarities: {t['polarities']}")
|
||
|
||
t = tests["density_sweep"]
|
||
print(f"\n[Density sweep]")
|
||
for pt in t["sweep"]:
|
||
marker = " ← nominal" if pt["polarity"] == 1.0 else ""
|
||
print(
|
||
f" p={pt['polarity']:<8} → density {pt['density']:.4f} "
|
||
f"({pt['witnesses']} witnesses)" + marker
|
||
)
|
||
|
||
t = tests["betti_gaps"]
|
||
print(
|
||
f"\n[Betti gaps] {t['gap_count']} gaps, longest run = "
|
||
f"{t['longest_witness_run']}"
|
||
)
|
||
print(f" First 20 gaps (witnesses): {t['first_20_gaps']}")
|
||
print(f"\n Key finding: gaps = twin-prime candidate scars.")
|
||
print(f" The uncovered integers are the Betti-0 homology of")
|
||
print(f" the obstruction complex.")
|
||
|
||
# Generate SHA256 of results for audit trail
|
||
h = hashlib.sha256(json.dumps(results, sort_keys=True).encode()).hexdigest()
|
||
print(f"\n receipt_hash: {h}")
|
||
print(f"╚{'═' * 50}╝")
|
||
|
||
else:
|
||
walk = SpherionTwinPrimeWalk()
|
||
limit = args.limit
|
||
if args.polarity is not None:
|
||
walk = SpherionTwinPrimeWalk(
|
||
{s: args.polarity for s in SIGNS}
|
||
)
|
||
|
||
rep = walk.report(limit)
|
||
if args.json:
|
||
print(json.dumps(rep, indent=2))
|
||
else:
|
||
print(f"Spherion Twin-Prime Walk — limit={limit}")
|
||
print(f" Polarities: {rep['polarities']}")
|
||
print(f" Witnesses: {rep['witness_count']}/{limit} "
|
||
f"(density {rep['witness_density']:.4f})")
|
||
print(f" First 20: {rep['first_20_witnesses']}")
|
||
print(f" First 20 energies: {rep['first_20_energies']}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|