mirror of
https://github.com/allaunthefox/SilverSight.git
synced 2026-07-31 01:25:21 +00:00
STEP 1 — integer spiral-index packing (EXACT): pack_spiral_index/ unpack_spiral_index with corpus-wide base=2·max|c|+1 and offset-encoded digits, recorded in the codebook JSON header; round-trip verified over all 250 matrices. Fixes the sign bug in integration_sprint.py's integer path (unoffset signed packing collapsed (-1,1) with (1,0) in base 2). STEP 2 — f(n) layout (DECORATIVE): per-entry radius_sq + angle_frac, computed in decimal arithmetic (indices reach ~1e44 >> 2^52); verified against VERIFICATION_LOG V007 f(20121); min angular separation ≈4.64e-6 of a turn reported. STEP 3 — Cartan Δ-floor (--cartan-floor): Δ=17/1792 exact (CartanConnection.lean:70) as Fisher-distance resolution floor on Δ₇; 13 sub-Δ pairs merge 196 fingerprints into 187 Δ-resolution codewords vs 9 gap-rule clusters; emitted alongside, not replacing, the 3×-median rule. Caveat asserted in tests: the |c|/Σ|c| simplex projection is lossy (one corpus pair at Fisher distance 0) — similarity, not identity. STEP 4 — torus-winding saturation fix: TorusWinding gains a_exact/b_exact (plain ℤ, no Q16.16 clamp); torus_to_spiral_index prefers them, making the round trip lossless (regression-tested at n=1e9). BraidEigensolid.lean needs the same widening — noted, Lean untouched. Also corrects charpoly_codebook.py's stale docstring counts (192/180 → 196/182, cross-checked against the exact Faddeev–LeVerrier fingerprints: identical on all 250 matrices). Tests: 43 passed (19 new). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
261 lines
10 KiB
Python
261 lines
10 KiB
Python
#!/usr/bin/env python3
|
||
"""charpoly_codebook.py — Exact spectral codebook via characteristic polynomials.
|
||
|
||
Replaces float-based spectral radius with exact integer characteristic
|
||
polynomial coefficients as the codebook key.
|
||
|
||
Key advantages over power-iteration spectral radius:
|
||
- Exact (integer coefficients, no float precision issues)
|
||
- 196 unique fingerprints vs 182 from spectral radius (measured on the
|
||
250-matrix corpus; identical coefficients to the exact Faddeev–LeVerrier
|
||
fingerprints in spectral_codebook.py on all 250 matrices)
|
||
- Cayley-Hamilton verifiable in ℤ (fits integer-only doctrine)
|
||
- Guaranteed L∞ minimum distance of 1 between distinct codewords
|
||
|
||
Usage:
|
||
python3 python/charpoly_codebook.py [--matrices PATH] [--output PATH]
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import json
|
||
import math
|
||
import re
|
||
import sys
|
||
from collections import defaultdict
|
||
from pathlib import Path
|
||
from typing import Any
|
||
|
||
import numpy as np
|
||
|
||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||
CARTAN_FLOOR = 17 / 1792 # ≈ 0.00949, proven in CartanConnection.lean
|
||
|
||
|
||
# ── Characteristic polynomial ─────────────────────────────────────────────
|
||
|
||
def charpoly_fingerprint(mat: list[list[int]]) -> tuple[int, ...]:
|
||
"""Exact characteristic polynomial coefficients as a hashable key.
|
||
|
||
For an n×n integer matrix A, computes:
|
||
p(λ) = det(λI - A) = λⁿ + cₙ₋₁λⁿ⁻¹ + ... + c₁λ + c₀
|
||
|
||
The coefficients are integers (exact, no rounding needed for integer matrices).
|
||
Returns (cₙ₋₁, cₙ₋₂, ..., c₁, c₀) — excludes the leading 1.
|
||
|
||
Uses numpy internally but the result is exact for integer inputs.
|
||
"""
|
||
np_mat = np.array(mat, dtype=float)
|
||
# numpy poly returns [1, c_{n-1}, ..., c_0] for monic polynomial
|
||
coeffs = np.poly(np_mat)
|
||
# Round to integers (exact for integer matrices, but guard against
|
||
# float64 representation errors for large matrices)
|
||
return tuple(int(round(c)) for c in coeffs[1:]) # skip leading 1
|
||
|
||
|
||
def spectral_radius_from_charpoly(coeffs: tuple[int, ...]) -> float:
|
||
"""Compute spectral radius from characteristic polynomial coefficients.
|
||
|
||
Returns the largest absolute value of the roots.
|
||
"""
|
||
# Full polynomial: [1, c_{n-1}, ..., c_0]
|
||
full_coeffs = [1.0] + [float(c) for c in coeffs]
|
||
roots = np.roots(full_coeffs)
|
||
return float(max(abs(r) for r in roots)) if len(roots) > 0 else 0.0
|
||
|
||
|
||
# ── Matrix extraction from Lean ───────────────────────────────────────────
|
||
|
||
def extract_matrices_from_lean(path: Path) -> dict[str, list[list[int]]]:
|
||
"""Extract named 8×8 matrices from a Lean source file."""
|
||
text = path.read_text()
|
||
blocks = re.split(r'(?=def rrc_eq_)', text)
|
||
matrices = {}
|
||
for block in blocks:
|
||
eid_m = re.match(r'def (rrc_eq_\w+)', block)
|
||
if not eid_m:
|
||
continue
|
||
eid = eid_m.group(1)
|
||
rows = re.findall(r'#\[([0-9, -]+)\]', block)
|
||
if len(rows) != 8:
|
||
continue
|
||
try:
|
||
mat = [[int(x.strip()) for x in row.split(',')] for row in rows]
|
||
if all(len(r) == 8 for r in mat):
|
||
matrices[eid] = mat
|
||
except (ValueError, IndexError):
|
||
pass
|
||
return matrices
|
||
|
||
|
||
# ── Codebook builder ──────────────────────────────────────────────────────
|
||
|
||
def build_codebook(matrices: dict[str, list[list[int]]]) -> dict[str, Any]:
|
||
"""Build spectral codebook from matrices using characteristic polynomial fingerprints.
|
||
|
||
Returns:
|
||
{
|
||
'schema': 'charpoly_codebook_v1',
|
||
'matrix_count': N,
|
||
'distinct_matrices': M,
|
||
'unique_fingerprints': F,
|
||
'bits_per_matrix': log2(F),
|
||
'cospectral_groups': [...],
|
||
'collision_groups': [...],
|
||
'entries': [
|
||
{
|
||
'equation_id': str,
|
||
'charpoly': [int, ...],
|
||
'charpoly_hash': int,
|
||
'spectral_radius_exact': float,
|
||
'density': float,
|
||
'cartan_snapped': float,
|
||
'fingerprint_group': int,
|
||
},
|
||
...
|
||
]
|
||
}
|
||
"""
|
||
entries = []
|
||
fingerprint_groups: dict[tuple[int, ...], list[str]] = defaultdict(list)
|
||
|
||
for eid, mat in matrices.items():
|
||
fp = charpoly_fingerprint(mat)
|
||
rho = spectral_radius_from_charpoly(fp)
|
||
density = sum(sum(row) for row in mat) / (len(mat) ** 2)
|
||
|
||
# Cartan-floor snapping
|
||
cartan_snapped = round(rho / CARTAN_FLOOR) * CARTAN_FLOOR
|
||
|
||
fingerprint_groups[fp].append(eid)
|
||
entries.append({
|
||
'equation_id': eid,
|
||
'charpoly': list(fp),
|
||
'charpoly_hash': hash(fp),
|
||
'spectral_radius_exact': round(rho, 10),
|
||
'density': round(density, 4),
|
||
'cartan_snapped': round(cartan_snapped, 6),
|
||
'fingerprint_group': 0, # filled below
|
||
})
|
||
|
||
# Assign group IDs
|
||
fp_to_gid = {fp: i for i, fp in enumerate(fingerprint_groups.keys())}
|
||
for entry in entries:
|
||
fp = tuple(entry['charpoly'])
|
||
entry['fingerprint_group'] = fp_to_gid[fp]
|
||
|
||
# Cospectral groups (same fingerprint, different matrix)
|
||
cospectral = [
|
||
{'fingerprint': list(fp), 'equation_ids': eids}
|
||
for fp, eids in fingerprint_groups.items()
|
||
if len(eids) > 1
|
||
]
|
||
|
||
# Collision analysis
|
||
unique_fps = len(fingerprint_groups)
|
||
unique_rhos = len(set(round(e['spectral_radius_exact'], 10) for e in entries))
|
||
total = len(entries)
|
||
distinct = len(set(tuple(e['charpoly']) for e in entries))
|
||
|
||
return {
|
||
'schema': 'charpoly_codebook_v1',
|
||
'generated': '2026-07-01',
|
||
'source': 'formal/SilverSight/PIST/Matrices250.lean',
|
||
'matrix_count': total,
|
||
'distinct_matrices': distinct,
|
||
'unique_fingerprints': unique_fps,
|
||
'unique_spectral_radii': unique_rhos,
|
||
'bits_from_fingerprint': round(math.log2(unique_fps), 2) if unique_fps > 0 else 0,
|
||
'bits_from_rho': round(math.log2(unique_rhos), 2) if unique_rhos > 0 else 0,
|
||
'cartan_floor': CARTAN_FLOOR,
|
||
'cospectral_groups': cospectral,
|
||
'entries': sorted(entries, key=lambda e: e['spectral_radius_exact']),
|
||
}
|
||
|
||
|
||
# ── Verification ──────────────────────────────────────────────────────────
|
||
|
||
def verify_codebook(codebook: dict) -> list[str]:
|
||
"""Verify codebook consistency. Returns list of errors (empty = pass)."""
|
||
errors = []
|
||
entries = codebook['entries']
|
||
|
||
# Check all entries have charpoly
|
||
for e in entries:
|
||
if not e['charpoly']:
|
||
errors.append(f"{e['equation_id']}: empty charpoly")
|
||
|
||
# Check fingerprint count
|
||
unique_fps = len(set(tuple(e['charpoly']) for e in entries))
|
||
if unique_fps != codebook['unique_fingerprints']:
|
||
errors.append(f"Fingerprint count mismatch: {unique_fps} vs {codebook['unique_fingerprints']}")
|
||
|
||
# Check Cayley-Hamilton: p(A) should be zero matrix
|
||
# (skip for performance — this is a separate verification step)
|
||
|
||
# Check Cartan-floor bound: no two distinct fingerprints within Δ
|
||
# (this is a necessary condition, not sufficient for quantization)
|
||
sorted_entries = sorted(entries, key=lambda e: e['spectral_radius_exact'])
|
||
sub_delta_pairs = 0
|
||
for i in range(len(sorted_entries) - 1):
|
||
e1, e2 = sorted_entries[i], sorted_entries[i + 1]
|
||
if e1['fingerprint_group'] != e2['fingerprint_group']:
|
||
gap = abs(e2['spectral_radius_exact'] - e1['spectral_radius_exact'])
|
||
if gap < CARTAN_FLOOR:
|
||
sub_delta_pairs += 1
|
||
# This is INFORMATIONAL, not an error. The Cartan floor is the operator's
|
||
# resolution limit. Charpoly resolves beyond it. Both are valid.
|
||
if sub_delta_pairs > 0:
|
||
print(f" ℹ️ {sub_delta_pairs} pairs within Cartan floor (Δ={CARTAN_FLOOR:.6f})")
|
||
print(f" Charpoly distinguishes them; operator dynamics cannot.")
|
||
print(f" This is expected — charpoly is the finer-grained key.")
|
||
|
||
return errors
|
||
|
||
|
||
# ── CLI ────────────────────────────────────────────────────────────────────
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(description="Build exact spectral codebook")
|
||
parser.add_argument('--matrices', type=Path,
|
||
default=REPO_ROOT / 'formal/SilverSight/PIST/Matrices250.lean',
|
||
help='Lean file with matrix definitions')
|
||
parser.add_argument('--output', type=Path,
|
||
default=REPO_ROOT / 'data/charpoly_codebook.json',
|
||
help='Output JSON path')
|
||
parser.add_argument('--verify', action='store_true', help='Run verification')
|
||
args = parser.parse_args()
|
||
|
||
print(f"Loading matrices from {args.matrices}...")
|
||
matrices = extract_matrices_from_lean(args.matrices)
|
||
print(f" Found {len(matrices)} matrices")
|
||
|
||
print("Building codebook...")
|
||
codebook = build_codebook(matrices)
|
||
print(f" Distinct matrices: {codebook['distinct_matrices']}")
|
||
print(f" Unique fingerprints: {codebook['unique_fingerprints']}")
|
||
print(f" Unique spectral radii: {codebook['unique_spectral_radii']}")
|
||
print(f" Bits from fingerprint: {codebook['bits_from_fingerprint']}")
|
||
print(f" Bits from ρ: {codebook['bits_from_rho']}")
|
||
print(f" Cospectral groups: {len(codebook['cospectral_groups'])}")
|
||
|
||
if args.verify:
|
||
print("\nVerifying...")
|
||
errors = verify_codebook(codebook)
|
||
if errors:
|
||
print(f" ❌ {len(errors)} errors:")
|
||
for e in errors:
|
||
print(f" - {e}")
|
||
return 1
|
||
else:
|
||
print(" ✅ All checks pass")
|
||
|
||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||
args.output.write_text(json.dumps(codebook, indent=2))
|
||
print(f"\nWrote {args.output}")
|
||
return 0
|
||
|
||
|
||
if __name__ == '__main__':
|
||
sys.exit(main())
|