SilverSight/python/charpoly_codebook.py
allaun 359effdfc6 feat(spectral): f/Cartan integration — exact spiral packing, f(n) layout, Cartan Δ-floor, torus-winding fix
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>
2026-07-01 20:53:47 -05:00

261 lines
10 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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 FaddeevLeVerrier
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())