diff --git a/python/chiral_spectral_lut.py b/python/chiral_spectral_lut.py new file mode 100644 index 00000000..cae37c6e --- /dev/null +++ b/python/chiral_spectral_lut.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +""" +Chiral Spectral LUT — mapping 8-strand chiral configuration → spectral state. + +8 strands × 4 chiral labels = 4⁸ = 65,536 states +Collapses to exactly 5 distinct spectral fingerprints. + +The LUT maps any configuration to its eigenvalue pair {λ_min, λ_max} +using pure integer arithmetic (zero floats). +""" +import json, time + +# Chiral weights as rational pairs (num, den) — integer arithmetic, zero floats +CHIRAL = {"A": (1, 1), "S": (1, 2), "L": (3, 2), "R": (3, 2)} +NAMES = ["A", "S", "L", "R"] # achiral, scarred, left_bias, right_bias + +def compute_spectral(chi8): + """ + Compute {λ_min, λ_max} for an 8-char chiral string like 'AASSSLLR'. + Integer arithmetic only — zero floats. + + The Cartan matrix is 4×2×2 block-diagonal: pairs (0,1), (2,3), (4,5), (6,7). + Each block [[273, w], [w, 273]] has eigenvalues {273+w, 273-w} + where w = 128 × (chiral[num]/chiral[den] average of the pair). + """ + pairs = [(0,1), (2,3), (4,5), (6,7)] + all_lo, all_hi = [], [] + for a, b in pairs: + an, ad = CHIRAL[chi8[a]] + bn, bd = CHIRAL[chi8[b]] + # w = 128 × (an/ad + bn/bd) = 128 × (an×bd + bn×ad) / (ad×bd) + num = 128 * (an * bd + bn * ad) + den = ad * bd + w = num // den + all_lo.append(273 + w) + all_hi.append(273 - w) + return max(all_lo), min(all_hi) + +def build_lut(save=True): + """Build the full 65,536-entry LUT.""" + t0 = time.time() + lut = {} + for mask_int in range(4**8): + chi = "".join(NAMES[(mask_int >> (i*2)) & 3] for i in range(8)) + lam_max, lam_min = compute_spectral(chi) + lut[chi] = {"lambda_max": lam_max, "lambda_min": lam_min} + + t1 = time.time() + + # Classify into regimes + regimes = {} + for chi, spec in lut.items(): + key = (spec["lambda_min"], spec["lambda_max"]) + regimes.setdefault(key, []) + regimes[key].append(chi) + + print(f"LUT built in {t1-t0:.1f}s — {len(lut):,} entries, zero floats") + print(f"\nSpectral regime distribution ({len(regimes)} regimes):") + for (lo, hi), configs in sorted(regimes.items(), key=lambda x: -len(x[1])): + regime = "CANONICAL" if lo == 17 else "ROSSBY" if lo < 0 else "SCARRED" if lo > 17 else "OTHER" + name = {"CANONICAL": "achiral ground state", + "ROSSBY": "nonabelian (Rossby-active)", + "SCARRED": "sub-critical (scarred)"}.get(regime, regime) + print(f" λ=[{lo:>4}, {hi:>4}] {len(configs):>5} configs ({len(configs)/len(lut)*100:5.1f}%) {name}") + + if save: + receipt = { + "schema": "chiral_spectral_lut_v1", "zero_float": True, + "entries": len(lut), "regimes": len(regimes), + "regime_labels": ["CANONICAL", "ROSSBY", "SCARRED"], + "compute_time_s": round(t1-t0, 2), + "note": "Integer arithmetic only. Block eigenvalues = 273 ± w, w = 128 × avg(chi)" + } + with open("signatures/chiral_spectral_lut.json", "w") as f: + json.dump(receipt, f, indent=2) + print(f"\nReceipt: signatures/chiral_spectral_lut.json") + + return lut, regimes + +if __name__ == "__main__": + lut, regimes = build_lut() + + # Example lookups + print("\nExample lookups:") + for chi in ["AAAAAAAA", "SSSSSSSS", "LLLLLLLL", "AASSLRRL", "AASSAASS"]: + spec = lut.get(chi, {}) + regime = "CANONICAL" if spec.get("lambda_min") == 17 else "ROSSBY" if spec.get("lambda_min", 0) < 0 else "SCARRED" + print(f" {chi} → λ=[{spec.get('lambda_min')}, {spec.get('lambda_max')}] {regime}") diff --git a/signatures/chiral_spectral_lut.json b/signatures/chiral_spectral_lut.json new file mode 100644 index 00000000..341d5df7 --- /dev/null +++ b/signatures/chiral_spectral_lut.json @@ -0,0 +1,13 @@ +{ + "schema": "chiral_spectral_lut_v1", + "zero_float": true, + "entries": 65536, + "regimes": 5, + "regime_labels": [ + "CANONICAL", + "ROSSBY", + "SCARRED" + ], + "compute_time_s": 0.09, + "note": "Integer arithmetic only. Block eigenvalues = 273 \u00b1 w, w = 128 \u00d7 avg(chi)" +} \ No newline at end of file