Research-Stack/4-Infrastructure/shim/math_repository_eigenvector_audit.py
2026-05-11 22:18:31 -05:00

434 lines
16 KiB
Python

#!/usr/bin/env python3
"""Eigenvector audit for major math/formalism repositories.
This is a lightweight structural pass, not a theorem checker. It turns each
math-heavy repo/module into a feature vector, centers the matrix, and reports
the leading covariance eigenvectors as "math axes" for routing compression and
formalism work.
"""
from __future__ import annotations
import hashlib
import json
import math
import re
from collections import Counter
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
import numpy as np
ROOT = Path(__file__).resolve().parents[2]
SHIM = ROOT / "4-Infrastructure" / "shim"
WIKI = ROOT / "6-Documentation" / "tiddlywiki-local" / "wiki" / "tiddlers"
OUT_JSON = SHIM / "math_repository_eigenvector_audit.json"
OUT_JSONL = SHIM / "math_repository_eigenvector_audit_curriculum.jsonl"
OUT_TID = WIKI / "Math Repository Eigenvector Audit.tid"
REPO_CANDIDATES = [
"0-Core-Formalism/lean/Semantics",
"0-Core-Formalism/lean/LeanGPT",
"0-Core-Formalism/otom/formal",
"2-Search-Space/PIST",
"2-Search-Space/FAMM",
"2-Search-Space/tardygrada/proofs",
"3-Mathematical-Models/AMMR",
"3-Mathematical-Models/manifold_compression",
"3-Mathematical-Models/hutter_manifold",
"6-Documentation/docs/formal_verification",
"6-Documentation/docs/semantics",
"6-Documentation/invention_record",
"ai-math-discovery-systems/AI-Feynman",
"ai-math-discovery-systems/AI-Newton",
"ai-math-discovery-systems/Goedel-Prover-V2",
"ai-math-discovery-systems/PINNs",
"ai-math-discovery-systems/alphageometry",
"ai-math-discovery-systems/neural-conservation-law",
]
TEXT_EXTENSIONS = {
".lean",
".agda",
".v",
".thy",
".py",
".rs",
".jl",
".md",
".txt",
".json",
".yaml",
".yml",
".toml",
}
SKIP_DIRS = {
".git",
".lake",
".venv",
"__pycache__",
"node_modules",
"build",
"dist",
"target",
".mypy_cache",
".pytest_cache",
}
FEATURES = [
"file_count",
"byte_count_log",
"lean_files",
"proof_files",
"python_files",
"markdown_files",
"definitions",
"theorems",
"lemmas",
"axioms",
"imports",
"evals",
"sorry_markers",
"admit_markers",
"math_symbols",
"unicode_symbols",
"compression_terms",
"geometry_terms",
"spectrum_terms",
"proof_terms",
"model_terms",
"hardware_terms",
"dataset_terms",
"avg_line_length",
]
TERM_PATTERNS = {
"compression_terms": re.compile(r"\b(compress|codec|encode|decode|entropy|hutter|residual|packet|lut|dictionary)\b", re.I),
"geometry_terms": re.compile(r"\b(manifold|topolog|braid|torsion|shear|field|metric|projection|eigen|vector|genus)\b", re.I),
"spectrum_terms": re.compile(r"\b(spectrum|spectral|frequency|wave|phase|fourier|signal|eigenvalue)\b", re.I),
"proof_terms": re.compile(r"\b(theorem|lemma|proof|axiom|decide|native_decide|invariant|lawful)\b", re.I),
"model_terms": re.compile(r"\b(model|neural|pinn|feynman|newton|solver|optimizer|training|loss)\b", re.I),
"hardware_terms": re.compile(r"\b(fpga|verilog|rtl|uart|bram|q16|fixed|silicon|hardware)\b", re.I),
"dataset_terms": re.compile(r"\b(dataset|corpus|sample|benchmark|receipt|jsonl|manifest)\b", re.I),
}
@dataclass
class RepoVector:
name: str
path: str
exists: bool
features: dict[str, float]
top_terms: list[tuple[str, int]]
file_families: dict[str, int]
content_hash: str | None
def iter_files(root: Path) -> list[Path]:
files: list[Path] = []
for path in root.rglob("*"):
if any(part in SKIP_DIRS for part in path.relative_to(root).parts):
continue
if path.is_file() and path.suffix.lower() in TEXT_EXTENSIONS:
files.append(path)
return files
def read_text(path: Path) -> str:
try:
return path.read_text(encoding="utf-8", errors="ignore")
except OSError:
return ""
def count_regex(pattern: str, text: str, flags: int = re.MULTILINE) -> int:
return len(re.findall(pattern, text, flags))
def repo_vector(rel: str) -> RepoVector:
path = ROOT / rel
if not path.exists():
return RepoVector(rel, rel, False, {key: 0.0 for key in FEATURES}, [], {}, None)
files = iter_files(path)
family_counter: Counter[str] = Counter()
term_counter: Counter[str] = Counter()
byte_count = 0
line_count = 0
line_length_sum = 0
all_hash = hashlib.sha256()
accum = {key: 0.0 for key in FEATURES}
accum["file_count"] = float(len(files))
for file_path in files:
rel_file = file_path.relative_to(ROOT).as_posix()
text = read_text(file_path)
encoded = text.encode("utf-8", errors="ignore")
all_hash.update(rel_file.encode("utf-8"))
all_hash.update(b"\0")
all_hash.update(hashlib.sha256(encoded).digest())
byte_count += len(encoded)
family_counter[file_path.suffix.lower() or "<none>"] += 1
lines = text.splitlines()
line_count += len(lines)
line_length_sum += sum(len(line) for line in lines)
suffix = file_path.suffix.lower()
if suffix == ".lean":
accum["lean_files"] += 1
if suffix in {".lean", ".agda", ".v", ".thy"}:
accum["proof_files"] += 1
if suffix == ".py":
accum["python_files"] += 1
if suffix == ".md":
accum["markdown_files"] += 1
accum["definitions"] += count_regex(r"^\s*(def|abbrev|structure|inductive|class|instance|#let)\b", text)
accum["theorems"] += count_regex(r"^\s*(theorem|example)\b", text)
accum["lemmas"] += count_regex(r"^\s*lemma\b", text)
accum["axioms"] += count_regex(r"^\s*(axiom|constant)\b", text)
accum["imports"] += count_regex(r"^\s*(import|from\s+\S+\s+import|#import)\b", text)
accum["evals"] += count_regex(r"^\s*(#eval|#check|native_decide)\b", text)
accum["sorry_markers"] += len(re.findall(r"\bsorry\b", text))
accum["admit_markers"] += len(re.findall(r"\badmit\b", text))
accum["math_symbols"] += len(re.findall(r"[∀∃λΛΣΠα-ωΑ-Ω≤≥≠≈→←↔⊢⊨∂∇∑∏√∞]", text))
accum["unicode_symbols"] += sum(1 for ch in text if ord(ch) > 127)
for key, pattern in TERM_PATTERNS.items():
hits = pattern.findall(text)
accum[key] += len(hits)
term_counter.update(str(hit).lower() for hit in hits)
accum["byte_count_log"] = math.log2(byte_count + 1)
accum["avg_line_length"] = line_length_sum / line_count if line_count else 0.0
content_hash = all_hash.hexdigest() if files else hashlib.sha256(rel.encode("utf-8")).hexdigest()
return RepoVector(
name=rel.split("/")[-1] or rel,
path=rel,
exists=True,
features=accum,
top_terms=term_counter.most_common(12),
file_families=dict(sorted(family_counter.items())),
content_hash=content_hash,
)
def zscore_matrix(vectors: list[RepoVector]) -> np.ndarray:
matrix = np.array([[vec.features[key] for key in FEATURES] for vec in vectors], dtype=float)
means = matrix.mean(axis=0)
stds = matrix.std(axis=0)
stds[stds == 0] = 1.0
return (matrix - means) / stds
def eigen_audit(vectors: list[RepoVector]) -> dict[str, Any]:
active = [vec for vec in vectors if vec.exists and vec.features["file_count"] > 0]
if len(active) < 2:
return {"axes": [], "repo_scores": []}
zmat = zscore_matrix(active)
cov = np.cov(zmat, rowvar=False)
eigenvalues, eigenvectors = np.linalg.eigh(cov)
order = np.argsort(eigenvalues)[::-1]
eigenvalues = eigenvalues[order]
eigenvectors = eigenvectors[:, order]
total = float(np.sum(np.maximum(eigenvalues, 0.0))) or 1.0
axes = []
repo_scores: dict[str, list[float]] = {vec.path: [] for vec in active}
max_axes = min(6, eigenvectors.shape[1])
projections = zmat @ eigenvectors[:, :max_axes]
for axis_i in range(max_axes):
weights = eigenvectors[:, axis_i]
ranked = sorted(
[(FEATURES[i], float(weights[i])) for i in range(len(FEATURES))],
key=lambda item: abs(item[1]),
reverse=True,
)
repo_rank = sorted(
[(active[i].path, float(projections[i, axis_i])) for i in range(len(active))],
key=lambda item: abs(item[1]),
reverse=True,
)
axes.append(
{
"axis": axis_i + 1,
"eigenvalue": float(eigenvalues[axis_i]),
"explained_ratio": float(eigenvalues[axis_i] / total),
"top_features": ranked[:8],
"top_repositories": repo_rank[:8],
"interpretation": interpret_axis(ranked[:8]),
}
)
for repo_i, vec in enumerate(active):
repo_scores[vec.path].append(float(projections[repo_i, axis_i]))
return {"axes": axes, "repo_scores": repo_scores}
def interpret_axis(top_features: list[tuple[str, float]]) -> str:
names = {name for name, _ in top_features[:5]}
if {"proof_terms", "theorems", "lemmas"} & names:
if {"sorry_markers", "admit_markers"} & names:
return "proof-density vs proof-debt axis"
return "formal proof-density axis"
if {"model_terms", "python_files", "dataset_terms"} & names:
return "empirical model/dataset axis"
if {"compression_terms", "geometry_terms"} <= names or {"compression_terms", "spectrum_terms"} <= names:
return "compression-geometry carrier axis"
if {"hardware_terms", "evals"} & names:
return "executable/hardware witness axis"
if {"math_symbols", "unicode_symbols"} & names:
return "symbolic notation density axis"
return "mixed structural axis"
def stable_payload(data: Any) -> bytes:
return json.dumps(data, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
def write_jsonl(vectors: list[RepoVector], audit: dict[str, Any], receipt_hash: str) -> None:
with OUT_JSONL.open("w", encoding="utf-8") as handle:
for vec in vectors:
record = {
"task": "math_repository_eigenvector_audit",
"repo": vec.path,
"exists": vec.exists,
"feature_vector": vec.features,
"top_terms": vec.top_terms,
"content_hash": vec.content_hash,
"teaching_boundary": "Structural eigenvectors route repositories; they do not prove mathematical truth.",
}
handle.write(json.dumps(record, sort_keys=True, ensure_ascii=False) + "\n")
handle.write(
json.dumps(
{
"task": "math_repository_eigenvector_axis_summary",
"axes": audit["axes"],
"receipt_hash": receipt_hash,
"teaching_boundary": "Use axes as compressor route priors, then require exact byte rehydration receipts.",
},
sort_keys=True,
ensure_ascii=False,
)
+ "\n"
)
def format_feature_list(items: list[list[Any] | tuple[str, float]]) -> str:
return ", ".join(f"{name}={value:.3f}" for name, value in items)
def write_tiddler(vectors: list[RepoVector], audit: dict[str, Any], receipt_hash: str) -> None:
existing = [vec for vec in vectors if vec.exists and vec.features["file_count"] > 0]
missing = [vec.path for vec in vectors if not vec.exists or vec.features["file_count"] == 0]
axis_lines = []
for axis in audit["axes"]:
repos = ", ".join(f"{path} ({score:.2f})" for path, score in axis["top_repositories"][:5])
axis_lines.append(
"\n".join(
[
f"!! Axis {axis['axis']}: {axis['interpretation']}",
f"* Explained ratio: {axis['explained_ratio']:.4f}",
f"* Top features: {format_feature_list(axis['top_features'][:6])}",
f"* Dominant repositories: {repos}",
]
)
)
repo_lines = []
scores = audit.get("repo_scores", {})
for vec in existing:
axis_score = scores.get(vec.path, [])
score_text = ", ".join(f"a{i + 1}={score:.2f}" for i, score in enumerate(axis_score[:4]))
repo_lines.append(
f"|{vec.path}|{int(vec.features['file_count'])}|{int(vec.features['lean_files'])}|"
f"{int(vec.features['proof_files'])}|{int(vec.features['python_files'])}|"
f"{score_text}|{(vec.content_hash or '')[:16]}|"
)
text = f"""created: 20260507160000000
modified: 20260507160000000
tags: Compression Eigenvectors Formalism MathRepositories ProjectableGeometry
title: Math Repository Eigenvector Audit
type: text/vnd.tiddlywiki
This tiddler records the first structural eigenvector pass over the major math/formalism repositories in the stack.
The purpose is route selection, not truth promotion. A repository eigenvector says which mathematical surface a module resembles: proof-heavy, model-heavy, compression-geometric, symbolic, hardware-witness, or dataset/receipt oriented. Any compressor candidate still has to pass exact rehydration.
!! Scope
* Active repositories/modules scanned: {len(existing)}
* Missing or empty candidates: {len(missing)}
* Receipt: `4-Infrastructure/shim/math_repository_eigenvector_audit.json`
* Curriculum: `4-Infrastructure/shim/math_repository_eigenvector_audit_curriculum.jsonl`
* Receipt hash: `{receipt_hash}`
!! Eigen Axes
{chr(10).join(axis_lines)}
!! Repository Scores
|!Repository |!Files |!Lean |!Proof files |!Python |!Leading scores |!Content hash |
{chr(10).join(repo_lines)}
!! Compressor Tuning Implication
This fills a missing bucket in the projectable-geometry compressor. Before this pass, we had vectors for finance claims, genetics, molecular/PDE priors, and symbolic Standard Model reductions, but not for the math repositories themselves.
The immediate use is a route prior:
# Proof-dense repositories should feed theorem/witness tokenbooks and exact proof-state sidecars.
# Model/dataset repositories should feed numeric residual lanes and benchmark-corpus metadata.
# Compression-geometry repositories should feed carrier primitives, residual boat rules, and tokenbook transforms.
# Hardware-witness repositories should feed fixed-point and byte-lane constraints.
!! Boundary
This audit is lexical/structural. It does not certify that a theorem compiles, that an equation is correct, or that a repository is more important. It gives the compressor a coarse coordinate system for choosing which specialized route to try first.
"""
OUT_TID.write_text(text, encoding="utf-8")
def main() -> None:
vectors = [repo_vector(rel) for rel in REPO_CANDIDATES]
audit = eigen_audit(vectors)
body = {
"generated_at": datetime.now(timezone.utc).isoformat(),
"root": str(ROOT),
"feature_schema": FEATURES,
"candidate_count": len(REPO_CANDIDATES),
"active_count": sum(1 for vec in vectors if vec.exists and vec.features["file_count"] > 0),
"vectors": [
{
"name": vec.name,
"path": vec.path,
"exists": vec.exists,
"features": vec.features,
"top_terms": vec.top_terms,
"file_families": vec.file_families,
"content_hash": vec.content_hash,
}
for vec in vectors
],
"audit": audit,
"claim_boundary": "Structural eigenvector audit only; compressor routing prior, not mathematical verification.",
}
receipt_hash = hashlib.sha256(stable_payload({k: v for k, v in body.items() if k != "generated_at"})).hexdigest()
body["receipt_hash"] = receipt_hash
OUT_JSON.write_text(json.dumps(body, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8")
write_jsonl(vectors, audit, receipt_hash)
write_tiddler(vectors, audit, receipt_hash)
print(json.dumps({"active_count": body["active_count"], "axes": len(audit["axes"]), "receipt_hash": receipt_hash}, indent=2))
if __name__ == "__main__":
main()