mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
Lean proof fixes: - N3L_Energy.lean: fully close gaussian_line_integral_unit_dir (nlinarith+hab for unit-circle quadratic, sqrt_mul+neg_div for integral_gaussian_1d match, exp_sum_of_sq order fix, add_assoc for h_gauss_shift, sq_sqrt for field_simp, sq_abs for perpDistance hd) - Add Adapters/AlphaProofNexus: 12 Erdos/graph adapter stubs (AlphaProof nexus) - Add Adapters/ErgodicAdditive.lean, SidonMatroid.lean - Add AntiDiophantine.lean, EffectiveBoundDQ.lean, PVGS_DQ_Bridge.lean - Add FormalConjectures/Util/ProblemImports.lean - Add RRC/EntropyCandidates/Candidates.lean - Add OTOM external project (lakefile.toml, lake-manifest.json, lean-toolchain) Infrastructure: - Add 4-Infrastructure/shim/: 17 Python probes (RRC manifold, Sidon kernel, Wannier, arxiv harvest, math_symbols DB, coverage density, geometric entropy) - Add 4-Infrastructure/NoDupeLabs/: Node server + package files - Add 6-Documentation/docs/specs/DP_RRC_RECEIPT_ENCODING_SPEC.md - Add fix_offloat.py Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
208 lines
7.5 KiB
Python
208 lines
7.5 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
rrc_root_system_probe.py — Which root system does the RRC equation corpus sit on?
|
|
|
|
Pipeline:
|
|
1. Fingerprint each equation into a symbol-ROLE vector (15 axes) via the
|
|
math-symbol matrix (math_symbols.CHAR_INFO) on normalize_math(text).
|
|
2. Build the role COUPLING matrix C = corr(role_i, role_j) across the corpus.
|
|
3. Spectrum of C → effective rank (participation ratio) = the data's intrinsic
|
|
dimension.
|
|
4. Minimum spanning tree of roles under distance 1-|corr|. Dynkin diagrams are
|
|
TREES, so the coupling skeleton's tree-topology is directly comparable to
|
|
the A / D / E_n Dynkin diagrams:
|
|
max-degree ≤ 2 → A_n (path)
|
|
one deg-3 node, arms 1,1,k → D_n (fork)
|
|
one deg-3 node, arms 1,2,2 → E6
|
|
one deg-3 node, arms 1,2,3 → E7
|
|
one deg-3 node, arms 1,2,4 → E8
|
|
5. Emits a receipt; the per-equation role-vectors + coupling tree are the probe
|
|
we can then apply to NEW math (anomaly = lands in a coupling-tree "hole").
|
|
|
|
HONEST SCOPE: this matches the coupling *skeleton* to Dynkin *trees* (a real
|
|
graph comparison) and reports the effective rank. It does NOT claim C is a Cartan
|
|
matrix; that stronger claim would need the ±1/±2 integer inner-product spectrum.
|
|
|
|
Usage: python3 4-Infrastructure/shim/rrc_root_system_probe.py
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
import time
|
|
from collections import Counter
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
from math_symbols import CHAR_INFO, normalize_math # noqa: E402
|
|
|
|
ROOT = Path("/home/allaun/Research Stack")
|
|
RECEIPT_IN = ROOT / "archive/experimental-shim-probes/rrc_equation_classifier_receipt.json"
|
|
RECEIPT_OUT = ROOT / "shared-data/data/rrc_root_system_probe_receipt.json"
|
|
|
|
# ADE Dynkin diagrams as (branch arm-length signatures). Arms measured in EDGES
|
|
# from the unique degree-3 node; A_n has no branch node.
|
|
DYNKIN = {
|
|
(1, 1): "D", # fork: two length-1 arms + a tail of length k → D_{k+2}
|
|
(1, 2, 2): "E6",
|
|
(1, 2, 3): "E7",
|
|
(1, 2, 4): "E8",
|
|
}
|
|
|
|
|
|
def role_vector(text: str, roles: list[str]) -> np.ndarray:
|
|
"""Count symbol roles in an equation after LaTeX/Unicode normalization."""
|
|
norm = normalize_math(text)
|
|
idx = {r: i for i, r in enumerate(roles)}
|
|
v = np.zeros(len(roles))
|
|
for ch in norm:
|
|
info = CHAR_INFO.get(ch)
|
|
if info and info["role"] in idx:
|
|
v[idx[info["role"]]] += 1.0
|
|
return v
|
|
|
|
|
|
def load_equations() -> list[str]:
|
|
d = json.loads(RECEIPT_IN.read_text())
|
|
out = []
|
|
for e in d.get("compiled_equations", []):
|
|
r = e.get("equation_record", {})
|
|
txt = (r.get("name", "") + " " + r.get("equation", "")).strip()
|
|
if txt:
|
|
out.append(txt)
|
|
return out
|
|
|
|
|
|
def mst_prim(dist: np.ndarray) -> list[tuple[int, int, float]]:
|
|
"""Prim's MST on a dense distance matrix → list of (i, j, dist) edges."""
|
|
n = dist.shape[0]
|
|
in_tree = [False] * n
|
|
in_tree[0] = True
|
|
edges = []
|
|
for _ in range(n - 1):
|
|
best = (None, None, np.inf)
|
|
for i in range(n):
|
|
if not in_tree[i]:
|
|
continue
|
|
for j in range(n):
|
|
if in_tree[j]:
|
|
continue
|
|
if dist[i, j] < best[2]:
|
|
best = (i, j, dist[i, j])
|
|
i, j, w = best
|
|
if j is None:
|
|
break
|
|
in_tree[j] = True
|
|
edges.append((i, j, float(w)))
|
|
return edges
|
|
|
|
|
|
def classify_tree(n: int, adj: dict[int, list[int]]) -> tuple[str, dict]:
|
|
"""Classify a tree's topology against the A/D/E Dynkin families."""
|
|
deg = {v: len(adj[v]) for v in adj}
|
|
branch = [v for v in deg if deg[v] >= 3]
|
|
info = {"n_nodes": n, "max_degree": max(deg.values()) if deg else 0,
|
|
"n_branch_nodes": len(branch)}
|
|
if info["max_degree"] <= 2:
|
|
info["arms"] = []
|
|
return f"A_{n}", info
|
|
if len(branch) != 1 or info["max_degree"] != 3:
|
|
info["arms"] = []
|
|
return "irregular (not simply-laced ADE tree)", info
|
|
|
|
b = branch[0]
|
|
# measure each arm length (in edges) from the branch node out to a leaf
|
|
arms = []
|
|
for start in adj[b]:
|
|
length, prev, cur = 1, b, start
|
|
while True:
|
|
nxts = [x for x in adj[cur] if x != prev]
|
|
if len(nxts) != 1: # leaf (0) or another branch (>1)
|
|
break
|
|
prev, cur = cur, nxts[0]
|
|
length += 1
|
|
arms.append(length)
|
|
arms.sort()
|
|
info["arms"] = arms
|
|
key2 = tuple(arms[:2])
|
|
if tuple(arms) in DYNKIN:
|
|
return f"{DYNKIN[tuple(arms)]}", info
|
|
if key2 == (1, 1):
|
|
return f"D_{n}", info
|
|
return f"branched (arms={arms}; nearest E-series by long arm)", info
|
|
|
|
|
|
def main() -> None:
|
|
eqs = load_equations()
|
|
# roles present in the matrix, ordered by global frequency for readability
|
|
all_roles = sorted({i["role"] for i in CHAR_INFO.values()})
|
|
|
|
M = np.array([role_vector(e, all_roles) for e in eqs]) # (N, R)
|
|
present = M.sum(axis=0) > 0
|
|
roles = [r for r, p in zip(all_roles, present) if p]
|
|
M = M[:, present]
|
|
totals = M.sum(axis=0)
|
|
|
|
# drop zero-variance columns (corr undefined)
|
|
var = M.var(axis=0)
|
|
keep = var > 1e-9
|
|
roles = [r for r, k in zip(roles, keep) if k]
|
|
M = M[:, keep]
|
|
R = M.shape[1]
|
|
|
|
C = np.corrcoef(M, rowvar=False)
|
|
eig = np.sort(np.linalg.eigvalsh(C))[::-1]
|
|
pos = eig[eig > 1e-9]
|
|
participation = (pos.sum() ** 2) / (np.square(pos).sum()) # effective rank
|
|
|
|
dist = 1.0 - np.abs(C)
|
|
np.fill_diagonal(dist, 0.0)
|
|
edges = mst_prim(dist)
|
|
adj: dict[int, list[int]] = {i: [] for i in range(R)}
|
|
for i, j, _ in edges:
|
|
adj[i].append(j)
|
|
adj[j].append(i)
|
|
dynkin, tinfo = classify_tree(R, adj)
|
|
|
|
# ---- report ----
|
|
print("=" * 66)
|
|
print(f"RRC ROOT-SYSTEM PROBE — {len(eqs)} equations, {R} active role axes")
|
|
print("=" * 66)
|
|
print("\nRole totals across corpus:")
|
|
for r, t in sorted(zip(roles, totals[keep] if keep.shape[0] == totals.shape[0] else totals),
|
|
key=lambda x: -x[1]):
|
|
print(f" {r:16s} {int(t)}")
|
|
print(f"\nCoupling-matrix eigenvalues (desc): "
|
|
+ ", ".join(f"{e:.2f}" for e in eig))
|
|
print(f"Effective rank (participation ratio): {participation:.2f} "
|
|
f"→ compare E6=6, E7=7, E8=8")
|
|
print("\nRole coupling MST (Dynkin skeleton):")
|
|
for i, j, w in edges:
|
|
print(f" {roles[i]:16s} —{1-w:+.2f}— {roles[j]}")
|
|
print(f"\nTree topology: nodes={tinfo['n_nodes']}, max_degree={tinfo['max_degree']}, "
|
|
f"branch_nodes={tinfo['n_branch_nodes']}, arms={tinfo.get('arms')}")
|
|
print(f"\n>>> NEAREST DYNKIN TYPE: {dynkin}")
|
|
print(f">>> effective dim {participation:.2f} vs rank({R}) skeleton {dynkin}")
|
|
|
|
receipt = {
|
|
"schema": "rrc_root_system_probe_v1",
|
|
"generated_at": time.strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
"n_equations": len(eqs),
|
|
"roles": roles,
|
|
"role_totals": {r: int(t) for r, t in zip(roles, totals[keep])},
|
|
"coupling_eigenvalues": [float(x) for x in eig],
|
|
"effective_rank": float(participation),
|
|
"mst_edges": [[roles[i], roles[j], float(1 - w)] for i, j, w in edges],
|
|
"tree_topology": tinfo,
|
|
"nearest_dynkin": dynkin,
|
|
"caveat": "coupling-skeleton vs Dynkin-tree topology; not a Cartan-matrix claim",
|
|
}
|
|
RECEIPT_OUT.write_text(json.dumps(receipt, indent=2, ensure_ascii=False))
|
|
print(f"\nReceipt: {RECEIPT_OUT}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|