Research-Stack/4-Infrastructure/shim/rrc_self_classify.py
allaun 77488ac0ae feat(lean): close gaussian_line_integral_unit_dir + consolidate infrastructure
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>
2026-06-18 16:53:23 -05:00

248 lines
9.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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
"""
rrc_self_classify.py — Self-classifying RRC pipeline.
Takes a new equation (name + LaTeX text + route_hint), runs it through
all 6 kernel stages, assigns manifold location + regime, and emits a receipt.
Usage:
# Classify a single equation
python3 4-Infrastructure/shim/rrc_self_classify.py \\
--name "my_sidon_test" \\
--equation "|A| ≤ √(2N) + 1" \\
--route "number_theory"
# Batch classify from JSONL
python3 4-Infrastructure/shim/rrc_self_classify.py --batch new_eqs.jsonl
# Self-test: classify all kernel titles against themselves
python3 4-Infrastructure/shim/rrc_self_classify.py --self-test
"""
from __future__ import annotations
import json
import re
import subprocess
import sys
import time
from collections import defaultdict
from pathlib import Path
from typing import Any
NEON_HOST = "neon-64gb"
CONTAINER = "arxiv-pg"
DB = "arxiv"
# Import the kernel detection logic
sys.path.insert(0, str(Path(__file__).resolve().parent))
from rrc_arxiv_kernel_refine import (
DIOPHANTINE_KERNEL, COMBINATORICS_KERNEL,
detect_diophantine_type, detect_combinatorics_type,
detect_obscure_type, detect_dataset_type, detect_sidon_type,
detect_geometry_type, detect_reconstruction_type,
extract_keywords, search_papers,
RECEIPT_PATH,
)
RECEIPT_PATH = Path("archive/experimental-shim-probes/rrc_equation_classifier_receipt.json")
def classify_equation(name: str, eq_text: str, route_hint: str = "") -> dict:
"""Run an equation through all 6 kernel stages and return the best match."""
stages = [
# v0: graph-reconstruction kernel — fires FIRST (matches the batch
# kernel_refine ordering), so reconstruction-conjecture equations are
# tagged before the generic combinatorics/sidon stages claim them.
("kernel_refine_v0", lambda: detect_reconstruction_type(name, eq_text)),
("kernel_refine_v6", lambda: detect_sidon_type(name, eq_text)),
("kernel_refine_v3", lambda: detect_combinatorics_type(name, eq_text)),
("kernel_refine_v4", lambda: detect_dataset_type(name, eq_text)),
("kernel_refine_v2", lambda: detect_diophantine_type(name, eq_text)),
("kernel_refine_v5", lambda: detect_obscure_type(name, eq_text)),
# v7: geometry/topology kernel — last kernel stage before the keyword
# fallback, so existing number-theory matches are untouched and only
# otherwise-unmatched eqs (e.g. the geodesic equation) reach it.
("kernel_refine_v7", lambda: detect_geometry_type(name, eq_text)),
]
best_paper_id = None
best_match = None
best_stage = None
for stage_name, detector in stages:
try:
matches = detector()
if matches:
best = matches[0]
best_paper_id = best.get("paper_id")
best_match = best
best_stage = stage_name
break
except Exception:
continue
# Fallback: generic keyword search
if not best_paper_id:
kw = extract_keywords(name + " " + eq_text + " " + route_hint)
results = search_papers(kw)
if results and results[0]["score"] >= 3:
best_paper_id = results[0]["paper_id"]
best_match = results[0]
best_stage = "kernel_refine_v1"
# Compute manifold assignment
slack, regime = _compute_regime(best_match)
sidon_label, strand = _assign_label(name)
manifold_route = _guess_route(name, eq_text, route_hint)
# Coherence: a geometry-kernel match implies the geometry/topology route,
# overriding the keyword route-guess (which lacks geometry vocabulary like
# "perelman"/"chern"). Only applied when the caller gave no explicit hint.
if (best_match and str(best_match.get("match_type", "")).startswith("geometry")
and (not route_hint or route_hint == "?")):
manifold_route = "geometry_topology"
result = {
"name": name,
"equation_snippet": eq_text[:100],
"classified_at": time.strftime("%Y-%m-%dT%H:%M:%SZ"),
"match": {
"paper_id": best_paper_id,
"title": best_match.get("title", "") if best_match else "",
"score": best_match.get("score", 0) if best_match else 0,
"stage": best_stage,
"match_type": best_match.get("match_type", "") if best_match else "",
"signals": best_match.get("signals", []) if best_match else [],
},
"manifold": {
"route": manifold_route,
"regime": regime,
"slack": slack,
"sidon_label": sidon_label,
"strand": strand,
},
"classification": "classified" if best_paper_id else "unmatched",
}
return result
def _compute_regime(match: dict | None) -> tuple[int, str]:
if match is None:
return 0, "unclassified"
score = match.get("score", 0)
if isinstance(score, str):
try:
score = int(score)
except ValueError:
score = 0
# Geometry/topology eqs sit off the Sidon diophantine axis — label them
# by curvature regime instead of (anti_)diophantine slack.
if str(match.get("match_type", "")).startswith("geometry"):
return (64 if score >= 6 else 16), "riemannian"
if score >= 100:
return 128, "anti_diophantine"
elif score >= 20:
return 64, "transition"
elif score >= 5:
return 16, "transition_tight"
return 4, "diophantine"
def _assign_label(name: str) -> tuple[int, int]:
labels = [1, 2, 4, 8, 16, 32, 64, 128]
idx = hash(name) % len(labels)
return labels[idx], idx
def _guess_route(name: str, eq_text: str, route_hint: str) -> str:
if route_hint and route_hint != "?":
return route_hint
combined = (name + " " + eq_text).lower()
route_patterns = [
("thermodynamic_energy", ["energy", "entropy", "heat", "temperature", "thermo"]),
("geometry_topology", ["geometry", "metric", "manifold", "curvature", "geodesic"]),
("cognitive_load", ["cognitive", "load", "emotional", "signal", "gate"]),
("compression_route", ["compress", "encoding", "codec", "hutter", "entropy"]),
("magnetic_signal", ["magnetic", "field", "plasma", "wave"]),
("control_signal", ["control", "overflow", "gain", "threshold", "tuning"]),
("number_theory", ["prime", "modulo", "sidon", "sumset", "additive", "bound"]),
("chaotic_couch", ["chaotic", "couch", "soliton", "turbulence"]),
]
best_route, best_score = "unclassified", 0
for route, kws in route_patterns:
score = sum(3 for kw in kws if kw in combined)
if score > best_score:
best_score = score
best_route = route
return best_route
def self_test():
"""Self-test: classify a set of known equations to verify pipeline."""
test_cases = [
{"name": "sidon_maximum_bound", "equation": "|A| ≤ √(2N) + 1", "route": "number_theory"},
{"name": "sumset_growth", "equation": "|A+A| ≥ |A|(|A|1)/2", "route": "combinatorics"},
{"name": "baker_lower_bound", "equation": "log|Λ| > C·log(H₁)·log(H₂)", "route": "number_theory"},
{"name": "entropy_rate", "equation": "H(X|Y) = H(X) I(X;Y)", "route": "thermodynamic_energy"},
{"name": "geodesic_equation", "equation": "d²x^i/ds² + Γ^i_jk dx^j/ds dx^k/ds = 0", "route": "geometry_topology"},
{"name": "sidon_set_collision", "equation": "a + b = c + d ⇒ {a,b} = {c,d}", "route": "number_theory"},
{"name": "singer_construction", "equation": "|D| = q+1, D ⊂ _{q²+q+1}", "route": "number_theory"},
{"name": "cap_set_bound", "equation": "|A| ≤ 3·(2.756)^n", "route": "number_theory"},
{"name": "CAUCHY_DAVENPORT", "equation": "|A+B| ≥ min(p, |A|+|B|1)", "route": "number_theory"},
{"name": "emotional_gate", "equation": "G_em = max(0, L_em T_em)", "route": "cognitive_load"},
]
print("=" * 60)
print("RRC Self-Classification Test")
print("=" * 60)
results = []
for tc in test_cases:
result = classify_equation(tc["name"], tc["equation"], tc["route"])
results.append(result)
stage = result["match"]["stage"] or "NONE"
paper = result["match"]["paper_id"] or ""
route = result["manifold"]["route"]
regime = result["manifold"]["regime"]
status = "" if result["classification"] == "classified" else ""
print(f"\n {status} {tc['name']:35s} {stage:20s} {route:25s} {regime:15s}")
print(f" → paper={paper}")
print(f"{tc['equation'][:60]}")
signals = result["match"].get("signals", [])
if signals:
print(f" → signals: {', '.join(signals)}")
# Summary
classified = sum(1 for r in results if r["classification"] == "classified")
print(f"\n{'='*60}")
print(f" Classified: {classified}/{len(results)}")
for stage in set(r["match"]["stage"] for r in results if r["match"]["stage"]):
cnt = sum(1 for r in results if r["match"]["stage"] == stage)
print(f" {stage:25s} {cnt}")
def main():
import argparse
ap = argparse.ArgumentParser(description="RRC Self-Classifying Pipeline")
ap.add_argument("--name", type=str, help="Equation name")
ap.add_argument("--equation", type=str, help="Equation LaTeX")
ap.add_argument("--route", type=str, default="", help="Route hint")
ap.add_argument("--self-test", action="store_true", help="Run self-test")
args = ap.parse_args()
if args.self_test:
self_test()
return
if not args.name or not args.equation:
print("ERROR: --name and --equation required (or --self-test)", file=sys.stderr)
sys.exit(1)
result = classify_equation(args.name, args.equation, args.route)
print(json.dumps(result, indent=2, ensure_ascii=False))
if __name__ == "__main__":
main()