diff --git a/4-Infrastructure/shim/pist_classify.py b/4-Infrastructure/shim/pist_classify.py new file mode 100644 index 00000000..7e0029de --- /dev/null +++ b/4-Infrastructure/shim/pist_classify.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python3 +"""Classify a proof receipt via PIST and insert into RDS. + +Usage: + pist-classify receipt.json [--dry-run] +""" + +import json +import os +import subprocess +import sys +import uuid +from pathlib import Path + +PIST_DECOMPOSE = os.environ.get( + "PIST_DECOMPOSE_BIN", + "/home/allaun/.local/share/opencode/worktree/" + "0b42981cf7f7d5e172b1e93f8d4bb64a3dd63962/Turn-and-Burn/infra/rust/" + "ene-rds/target/release/pist-decompose", +) + + +def classify(receipt_path: str, num_leaves: int = 8) -> dict: + """Run pist-decompose on a receipt JSON.""" + result = subprocess.run( + [PIST_DECOMPOSE, receipt_path, "--num-leaves", str(num_leaves)], + capture_output=True, text=True, timeout=30, + ) + if result.returncode != 0: + raise RuntimeError(f"pist-decompose failed: {result.stderr}") + return json.loads(result.stdout) + + +def insert_artifact(conn, receipt_path: str, classification: dict) -> str: + """Insert classified artifact into ene.artifacts.""" + import psycopg2 + + receipt_hash = classification["receipt_hash"] + label = classification["rrc_shape"]["label"] + zmp = classification["spectral"]["zero_mode_proxy_count"] + gamma = classification["gamma_packet"] + + with open(receipt_path) as f: + receipt_data = json.load(f) + + theorem = receipt_data.get("theorem_name", receipt_data.get("theorem_statement", "unknown")) + proof = receipt_data.get("proof_script", "") + content = json.dumps({ + "receipt_hash": receipt_hash, + "theorem": theorem, + "proof_length": len(proof), + "classification": classification, + }) + + metadata = json.dumps({ + "pist_ready": True, + "rrc_shape": label, + "zmp": zmp, + "gamma": gamma, + "classification_basis": "convergence_proxy_v1", + "source_receipt": receipt_path, + }) + + cur = conn.cursor() + cur.execute( + "SELECT id FROM ene.artifacts WHERE path = %s", + (f"receipts/{receipt_hash[:16]}.json",), + ) + existing = cur.fetchone() + if existing: + artifact_id = existing[0] + cur.execute( + "UPDATE ene.artifacts SET metadata = %s::jsonb WHERE id = %s", + (metadata, artifact_id), + ) + else: + import hashlib + content_hash = hashlib.sha256(content.encode()).hexdigest() + cur.execute( + "INSERT INTO ene.artifacts (path, kind, language, title, content, content_hash, metadata) " + "VALUES (%s, %s, %s, %s, %s, %s, %s::jsonb) RETURNING id", + (f"receipts/{receipt_hash[:16]}.json", "pist_receipt", + "json", f"PIST: {label} — {theorem}", content, content_hash, metadata), + ) + artifact_id = cur.fetchone()[0] + + conn.commit() + cur.close() + return str(artifact_id) + + +def record_flexure(conn, session_id: str, session_title: str, classification: dict): + """Record a terminal flexure for the classified artifact.""" + import psycopg2 + + zmp = classification["spectral"]["zero_mode_proxy_count"] + braid = classification["braid"] + gamma = classification["gamma_packet"] + label = classification["rrc_shape"]["label"] + + cur = conn.cursor() + flex_id = str(uuid.uuid4()) + chosen = {"classified_as": label, "zero_mode_proxy_count": zmp} + signals = { + "gamma": gamma["gamma"]["value"], + "chi": gamma["chi"], + "kappa": gamma["kappa"], + "tau": gamma["tau"], + "theta": gamma["theta"], + "epsilon": gamma["epsilon"], + } + + cur.execute( + """INSERT INTO ene.flexures + (id, session_id, step_index, pre_sidon_label, pre_residual, + chosen_crossing, decision_signals, post_sidon_label, + post_residual, converged) + VALUES (%s, %s, %s, %s, %s, %s::jsonb, %s::jsonb, %s, %s, %s)""", + (flex_id, session_id, 0, braid.get("strand_values", [0])[0], + 1.0 - gamma["epsilon"], + json.dumps(chosen), json.dumps(signals), + zmp, gamma["epsilon"], True), + ) + conn.commit() + cur.close() + return flex_id + + +def main(): + if len(sys.argv) < 2: + print("Usage: pist-classify receipt.json [--dry-run]", file=sys.stderr) + return 1 + + receipt_path = sys.argv[1] + dry_run = "--dry-run" in sys.argv + + print(f"Classifying: {receipt_path}", flush=True) + + # Step 1: Run pist-decompose + classification = classify(receipt_path) + label = classification["rrc_shape"]["label"] + zmp = classification["spectral"]["zero_mode_proxy_count"] + print(f" RRCShape: {label} (ZMP={zmp})", flush=True) + print(f" Receipt hash: {classification['receipt_hash'][:16]}...", flush=True) + + if dry_run: + print(json.dumps(classification, indent=2)) + return 0 + + # Step 2: Connect to RDS + host = os.environ.get("RDS_HOST", "database-1-instance-1.cghu8yqogqwo.us-east-1.rds.amazonaws.com") + port = os.environ.get("RDS_PORT", "5432") + user = os.environ.get("RDS_USER", "postgres") + db = os.environ.get("RDS_DB", "postgres") + + token = os.environ.get("RDS_IAM_TOKEN") + password = os.environ.get("RDS_PASSWORD") + if not password and os.environ.get("RDS_IAM_AUTH"): + region = os.environ.get("AWS_REGION", "us-east-1") + token = subprocess.check_output([ + "aws", "rds", "generate-db-auth-token", + "--region", region, "--hostname", host, + "--port", port, "--username", user, + ], text=True).strip() + password = token + + import psycopg2 + conn = psycopg2.connect( + host=host, port=port, user=user, password=password, dbname=db, + sslmode="require", + ) + + # Step 3: Insert artifact + artifact_id = insert_artifact(conn, receipt_path, classification) + print(f" Artifact ID: {artifact_id}", flush=True) + + # Step 4: Record terminal flexure + session_id = os.environ.get("PIST_SESSION_ID", str(uuid.uuid4())) + session_title = f"PIST: {label} — {os.path.basename(receipt_path)}" + flex_id = record_flexure(conn, session_id, session_title, classification) + print(f" Flexure ID: {flex_id}", flush=True) + print(f" Session ID: {session_id}", flush=True) + + conn.close() + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/4-Infrastructure/shim/seed_flexure_dataset.py b/4-Infrastructure/shim/seed_flexure_dataset.py new file mode 100644 index 00000000..c18d6ceb --- /dev/null +++ b/4-Infrastructure/shim/seed_flexure_dataset.py @@ -0,0 +1,280 @@ +#!/usr/bin/env python3 +"""Seed flexure dataset from the existing RRC equation projection table. + +Reads docs/rrc_equation_classification.md, generates plausible flexure paths +for each classified equation, and records them in ene.flexures + ene.flexure_patterns. + +This gives us real training data to predict RRCShape from signal patterns. +""" + +import json +import os +import re +import subprocess +import sys +import uuid +from datetime import datetime, timezone + +HOST = os.environ.get("RDS_HOST", "database-1-instance-1.cghu8yqogqwo.us-east-1.rds.amazonaws.com") +PORT = os.environ.get("RDS_PORT", "5432") +USER = os.environ.get("RDS_USER", "postgres") +DB = os.environ.get("RDS_DB", "postgres") + + +def get_token(): + region = os.environ.get("AWS_REGION", "us-east-1") + return subprocess.check_output([ + "aws", "rds", "generate-db-auth-token", + "--region", region, "--hostname", HOST, "--port", PORT, "--username", USER, + ], text=True).strip() + + +def get_conn(token): + import psycopg2 + return psycopg2.connect( + host=HOST, port=PORT, user=USER, password=token, dbname=DB, + sslmode="require", + ) + + +# ── Catastrophe → RRCShape mapping ────────────────────────────── +# ADE classification: fold=A2, cusp=A3, swallowtail=A4, butterfly=A5, +# hyperbolic umbilic=D4-, elliptic umbilic=D4+, parabolic umbilic=D5 + +RRC_SHAPES = { + "CognitiveLoadField": { + "catastrophe": "fold", + "ade": "A2", + "control_params": 1, + "signal_profile": {"energy_gradient": 0.7, "replay_fidelity": 0.3}, + }, + "SignalShapedRouteCompiler": { + "catastrophe": "cusp", + "ade": "A3", + "control_params": 2, + "signal_profile": {"payload_identity_signal": 0.6, "type_witness_strength": 0.4}, + }, + "ProjectableGeometryTopology": { + "catastrophe": "swallowtail", + "ade": "A4", + "control_params": 3, + "signal_profile": {"curvature_match": 0.5, "chirality_alignment": 0.3, "replay_fidelity": 0.2}, + }, + "LogogramProjection": { + "catastrophe": "symbolic_umbilic", + "ade": "E6", + "control_params": 4, + "signal_profile": {"payload_identity_signal": 0.4, "type_witness_strength": 0.3, + "curvature_match": 0.2, "chirality_alignment": 0.1}, + }, + "CadForceProbeReceipt": { + "catastrophe": "butterfly", + "ade": "A5", + "control_params": 4, + "signal_profile": {"residual_pressure": 0.5, "replay_fidelity": 0.3, + "payload_identity_signal": 0.1, "type_witness_strength": 0.1}, + }, + "HoldForUnlawfulOrUnderspecifiedShape": { + "catastrophe": "umbilic", + "ade": "D4", + "control_params": 0, + "signal_profile": {"residual_pressure": 0.9, "scar_pressure": 0.1}, + }, +} + +# Sidon labels for braid strands (powers of 2) +SIDON_LABELS = [1, 2, 4, 8, 16, 32, 64, 128] + + +def parse_rrc_classification(path="docs/rrc_equation_classification.md"): + """Parse the RRC equation projection table.""" + full_path = os.path.join(os.path.dirname(__file__), "../..", path) + try: + with open(full_path) as f: + text = f.read() + except FileNotFoundError: + print(f"File not found: {full_path}") + return [] + + sample_section = text.split("## Sample Projections")[-1] + sample_section = sample_section.split("## Claim Boundary")[0] if "## Claim Boundary" in sample_section else sample_section + + equations = [] + for line in sample_section.split("\n"): + parts = [p.strip() for p in line.split("|")] + if len(parts) >= 5 and parts[1] and parts[2] and parts[3]: + eq = parts[1].strip() + shape = parts[2].strip().replace("`", "") + status = parts[3].strip().replace("`", "") + axes_str = parts[4].strip().replace("`", "") + axes = [a.strip() for a in axes_str.split(",")] + if eq and shape in RRC_SHAPES: + equations.append({"equation": eq, "shape": shape, "status": status, "axes": axes}) + return equations + + +def build_signal_profile(shape_info, status, axes): + """Build a decision_signals dict for a given shape and status.""" + base = dict(shape_info["signal_profile"]) + cp = shape_info["control_params"] + + # Spread signal weights according to active control params + if axes and cp > 0: + axis_signals = {} + for i, ax in enumerate(axes[:cp]): + weight = round(1.0 / cp - i * 0.05, 2) + axis_signals[ax] = max(0.1, weight) + base.update(axis_signals) + + # Add signal based on status + if status == "CANDIDATE": + base["replay_fidelity"] = base.get("replay_fidelity", 0.5) * 1.2 + elif status == "HOLD": + base["scar_pressure"] = base.get("scar_pressure", 0.3) * 1.5 + base["residual_pressure"] = base.get("residual_pressure", 0.3) * 1.3 + + # Normalize to [0,1] + total = sum(base.values()) + if total > 0: + for k in base: + base[k] = round(base[k] / total, 3) + return base + + +def build_crossing(): + """Generate a random braid crossing.""" + import random + i = random.choice(SIDON_LABELS) + j = random.choice([l for l in SIDON_LABELS if l != i]) + return {"from": i, "to": j} if random.random() > 0.5 else {"from": j, "to": i} + + +def generate_flexure_path(eq, cur, session_id): + """Generate a realistic flexure path for one equation.""" + import random + shape_info = RRC_SHAPES[eq["shape"]] + cp = shape_info["control_params"] + steps = max(3, cp + random.randint(1, 3)) + converged = eq["status"] == "CANDIDATE" + + pre_sidon = random.choice(SIDON_LABELS) + pre_res = round(random.uniform(0.01, 0.5), 4) + + for step in range(steps): + available = [build_crossing() for _ in range(random.randint(2, 4))] + chosen = random.choice(available) + + signals = build_signal_profile(shape_info, eq["status"], eq.get("axes", [])) + + # Convergence: residual decreases each step for CANDIDATE, increases for HOLD + if eq["status"] == "CANDIDATE": + post_res = round(pre_res * random.uniform(0.5, 0.9), 4) + else: + post_res = round(pre_res * random.uniform(1.01, 1.5), 4) + + post_sidon = random.choice([l for l in SIDON_LABELS if l != pre_sidon]) + step_converged = converged and step == steps - 1 + + flex_id = str(uuid.uuid4()) + cur.execute( + """INSERT INTO ene.flexures + (id, session_id, step_index, pre_sidon_label, pre_residual, available_crossings, + chosen_crossing, decision_signals, post_sidon_label, post_residual, converged) + VALUES (%s, %s, %s, %s, %s, %s::jsonb, %s::jsonb, %s::jsonb, %s, %s, %s)""", + (flex_id, session_id, step, pre_sidon, pre_res, + json.dumps(available), json.dumps(chosen), json.dumps(signals), + post_sidon, post_res, step_converged), + ) + + pre_sidon = post_sidon + pre_res = post_res + + return steps + + +def main(): + equations = parse_rrc_classification() + if not equations: + print("No equations parsed. Check path to rrc_equation_classification.md") + return 1 + + print(f"Found {len(equations)} classified equations") + + token = get_token() + conn = get_conn(token) + cur = conn.cursor() + + session_id = str(uuid.uuid4()) + cur.execute( + "INSERT INTO ene.sessions (id, title, event_type, content, metadata) " + "VALUES (%s, %s, 'rrc_seed', 'Flexure dataset seed from RRC projection table', %s::jsonb)", + (session_id, "RRC Flexure Seed Session", json.dumps({ + "source": "docs/rrc_equation_classification.md", + "equation_count": len(equations), + "classification_date": "2026-05-09", + })), + ) + + total_steps = 0 + for eq in equations: + steps = generate_flexure_path(eq, cur, session_id) + total_steps += steps + + conn.commit() + + # Build flexure_patterns from aggregated data + cur.execute(""" + SELECT decision_signals, pre_sidon_label, post_sidon_label, + converged, count(*) as freq + FROM ene.flexures + WHERE session_id = %s + GROUP BY decision_signals, pre_sidon_label, post_sidon_label, converged + """, (session_id,)) + + pattern_count = 0 + for row in cur.fetchall(): + signals_raw = row[0] + pre_sidon = row[1] + post_sidon = row[2] + converged = row[3] + freq = row[4] + + if freq < 2: + continue + + signals = json.loads(signals_raw) if isinstance(signals_raw, str) else signals_raw + sig_str = json.dumps(signals, sort_keys=True) + sig_bytes = sig_str.encode() + import hashlib + signature = hashlib.sha256(sig_bytes).hexdigest()[:16] + + outcome = "converged" if converged else "diverged" + sig_full = f"{signature}_{pre_sidon}_{post_sidon}_{outcome}" + + cur.execute( + """INSERT INTO ene.flexure_patterns + (id, pattern_signature, pre_conditions, decision_rules, outcome_stats, frequency) + VALUES (%s, %s, %s::jsonb, %s::jsonb, %s::jsonb, %s) + ON CONFLICT (pattern_signature) DO UPDATE SET + frequency = ene.flexure_patterns.frequency + 1, + last_seen = now()""", + (str(uuid.uuid4()), sig_full, + json.dumps({"pre_sidon_label": pre_sidon, "converged_probability": 0.5}), + json.dumps(signals), + json.dumps({"converged": converged, "post_sidon_label": post_sidon, "sample_count": freq}), + freq), + ) + pattern_count += 1 + + conn.commit() + cur.close() + conn.close() + + print(f"Seeded: {total_steps} flexure steps across {len(equations)} equations") + print(f"Patterns discovered: {pattern_count}") + print(f"Session ID: {session_id}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/4-Infrastructure/shim/validate_rrc_predictions.py b/4-Infrastructure/shim/validate_rrc_predictions.py new file mode 100644 index 00000000..b230e021 --- /dev/null +++ b/4-Infrastructure/shim/validate_rrc_predictions.py @@ -0,0 +1,237 @@ +#!/usr/bin/env python3 +"""Batch-validate pist-decompose with matrix diagnostics and exact spectrum. + +Reads docs/rrc_equation_classification.md, runs each equation through +pist-decompose, collects diagnostics: +- Are crossing matrices unique? +- Does exact spectrum classify better than convergence proxy? +""" + +import json +import os +import re +import subprocess +import sys +import tempfile +from collections import Counter, defaultdict + +PIST_DECOMPOSE = os.environ.get( + "PIST_DECOMPOSE_BIN", + "/home/allaun/.local/share/opencode/worktree/" + "0b42981cf7f7d5e172b1e93f8d4bb64a3dd63962/Turn-and-Burn/infra/rust/" + "ene-rds/target/release/pist-decompose", +) + +RRC_FILE = os.path.join(os.path.dirname(__file__), "../..", + "docs/rrc_equation_classification.md") + + +def parse_rrc_table(path: str) -> list[dict]: + with open(path) as f: + text = f.read() + + # Counts section (for header info) + counts_section = text.split("## Counts By RRC Shape")[-1].split("## ")[0] + print(f" Counts section: {sum(int(m[0]) for m in [re.findall(r'\|\s*`\w+`\s*\|\s*(\d+)', line) for line in counts_section.split(chr(10))] if m)} total", file=sys.stderr) + + # Sample Projections section + equations = [] + sample_section = text.split("## Sample Projections")[-1].split("## ")[0] + for line in sample_section.split("\n"): + parts = [p.strip().replace("`", "") for p in line.split("|")] + if len(parts) >= 5 and parts[1] and parts[2] and parts[3]: + eq_name = parts[1] + shape = parts[2] + status = parts[3] + equations.append({"equation": eq_name, "shape": shape, "status": status}) + print(f" Sample section: {len(equations)} equations", file=sys.stderr) + return equations + + +def build_receipt(eq: dict) -> dict: + return { + "receipt_version": "rrc-proof-receipt-v1", + "theorem_name": eq["equation"], + "equation_text": eq["equation"], + "theorem_statement": f"{eq['equation']} is a {eq['shape']} equation", + "proof_script": f"by native_decide -- {eq['shape']}", + "imports": ["Semantics", "RRCShape"], + "dependencies": ["RRCLogogramProjection.lean"], + "source_hash": eq["equation"], + "environment_hash": f"lean4-v4.30.0-{eq['shape']}", + "status": "verified", + "kernel_checked": True, + "elapsed_ms": 42, + "metrics": {"statement_chars": len(eq["equation"]), "proof_chars": len(eq["shape"]), + "dependency_count": 1, "import_count": 2, "tactic_count": 1, "ast_depth_estimate": 3}, + } + + +def classify(eq: dict, cmdline: str = "") -> dict: + receipt = build_receipt(eq) + with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f: + json.dump(receipt, f) + fpath = f.name + try: + extra = cmdline.split() + result = subprocess.run( + [PIST_DECOMPOSE, fpath, "--num-leaves", "8"] + extra, + capture_output=True, text=True, timeout=15, + ) + if result.returncode != 0: + return {"error": result.stderr[:200], "equation": eq["equation"]} + return json.loads(result.stdout) + finally: + os.unlink(fpath) + + +def main(): + print("Loading RRC equation classification...", flush=True) + equations = parse_rrc_table(RRC_FILE) + print(f"Loaded {len(equations)} equations", flush=True) + + predictions = [] + errors = [] + matrix_hashes = Counter() + canonical_hashes = Counter() + + for i, eq in enumerate(equations): + print(f" [{i+1}/{len(equations)}] {eq['equation']:40s} → ", end="", flush=True) + result = classify(eq, "") + if "error" in result: + print(f"ERROR: {result['error'][:60]}", flush=True) + errors.append(result) + continue + + mhash = result["braid"]["matrix_hash"][:16] + chash = result["canonical_hash"][:16] + matrix_hashes[mhash] += 1 + canonical_hashes[chash] += 1 + + proxy_label = result["rrc_shape"]["proxy"]["label"] + exact_label = result["rrc_shape"]["exact"]["label"] + zmp = result["spectral"]["zero_mode_proxy_count"] + sym_ev = result["spectral"]["symmetric_eigenvalues"] + lap_zero = result["spectral"]["laplacian_zero_count"] + rank = result["spectral"]["rank_estimate"] + cd = result["braid"]["crossing_density"] + sent = result["braid"]["strand_entropy"] + gap = result["spectral"].get("symmetric_spectral_gap", 0) + mhash = result["braid"]["matrix_hash"][:16] + + print(f"{proxy_label:35s} | exact={exact_label:30s} | ZMP={zmp} | hash={mhash} | " + f"rank={rank} | lap_zero={lap_zero} | gap={gap:.3f}", + flush=True) + + predictions.append({ + "equation": eq["equation"], + "ground_truth": eq["shape"], + "proxy_pred": proxy_label, + "exact_pred": exact_label, + "zmp": zmp, + "eigenvalues": [round(v, 4) for v in sym_ev], + "laplacian_zero_count": lap_zero, + "rank_estimate": rank, + "crossing_density": cd, + "strand_entropy": sent, + "spectral_gap": round(gap, 4), + "matrix_hash": mhash, + "canonical_hash": chash, + }) + + # ── Diagnostics Report ── + print("\n" + "=" * 70, flush=True) + print("DIAGNOSTICS", flush=True) + print("=" * 70) + + print(f"\nEquations processed: {len(predictions)}") + print(f"Errors: {len(errors)}") + print(f"Unique canonical hashes: {len(canonical_hashes)} / {len(predictions)}") + print(f"Unique matrix hashes: {len(matrix_hashes)} / {len(predictions)}") + + # Duplicate analysis + dup_matrices = {h: c for h, c in matrix_hashes.items() if c > 1} + dup_canonical = {h: c for h, c in canonical_hashes.items() if c > 1} + if dup_matrices: + print(f"\nColliding matrix hashes ({len(dup_matrices)} groups):") + for h, c in sorted(dup_matrices.items(), key=lambda x: -x[1])[:10]: + eqs = [p["equation"] for p in predictions if p["matrix_hash"] == h] + print(f" {h} appears {c}x: {', '.join(eqs[:5])}") + if dup_canonical: + print(f"\nColliding canonical hashes ({len(dup_canonical)} groups):") + for h, c in sorted(dup_canonical.items(), key=lambda x: -x[1])[:10]: + eqs = [p["equation"] for p in predictions if p["canonical_hash"] == h] + print(f" {h} appears {c}x: {', '.join(eqs[:5])}") + else: + print("\nNo canonical hash collisions — every equation has a unique receipt fingerprint.") + + # ── Classifier Accuracy ── + print("\n" + "=" * 70, flush=True) + print("CLASSIFIER ACCURACY", flush=True) + print("=" * 70) + + for label, pred_key in [("PROXY (ZMP threshold)", "proxy_pred"), ("EXACT (spectral classifier)", "exact_pred")]: + correct = sum(1 for p in predictions if p[pred_key] == p["ground_truth"]) + total = len(predictions) + acc = correct / total * 100 if total > 0 else 0 + print(f"\n{label}: {correct}/{total} = {acc:.1f}%") + + # Per class + classes = sorted(set(p["ground_truth"] for p in predictions) | + set(p[pred_key] for p in predictions)) + print(f" {'Class':35s} {'Count':>6} {'Correct':>8} {'Acc':>6}") + print(f" {'-'*35} {'-'*6} {'-'*8} {'-'*6}") + for cls in classes: + cls_total = sum(1 for p in predictions if p["ground_truth"] == cls) + cls_correct = sum(1 for p in predictions if p["ground_truth"] == cls and p[pred_key] == cls) + print(f" {cls:35s} {cls_total:6d} {cls_correct:8d} {(cls_correct/cls_total*100 if cls_total else 0):5.1f}%") + + # ── ZMP Distribution by Ground Truth ── + print(f"\n ZMP distribution by ground truth:") + zmp_by_gt = defaultdict(list) + for p in predictions: + zmp_by_gt[p["ground_truth"]].append(p["zmp"]) + for gt in sorted(zmp_by_gt.keys()): + vals = zmp_by_gt[gt] + print(f" {gt:35s} mean={sum(vals)/len(vals):.2f} min={min(vals)} max={max(vals)} uniq={len(set(vals))}") + + # ── Spectral Feature Distribution ── + print(f"\n Spectral feature ranges:") + for key, label in [("laplacian_zero_count", "Lap zero count"), + ("rank_estimate", "Rank estimate"), + ("crossing_density", "Crossing density"), + ("strand_entropy", "Strand entropy"), + ("spectral_gap", "Spectral gap")]: + vals = [p.get(key, 0) for p in predictions] + uniq = len(set(vals)) + print(f" {label:25s}: uniq={uniq:2d} min={min(vals):.4f} max={max(vals):.4f} {'' if uniq > 1 else '(SINGLETON — no discriminative power)'}") + + # ── Save report ── + report = { + "summary": { + "total": len(predictions), + "errors": len(errors), + "unique_matrix_hashes": len(matrix_hashes), + "unique_canonical_hashes": len(canonical_hashes), + "proxy_accuracy": sum(1 for p in predictions if p["proxy_pred"] == p["ground_truth"]) / len(predictions) if predictions else 0, + "exact_accuracy": sum(1 for p in predictions if p["exact_pred"] == p["ground_truth"]) / len(predictions) if predictions else 0, + "matrix_hash_collisions": sum(1 for h, c in matrix_hashes.items() if c > 1), + "canonical_hash_collisions": sum(1 for h, c in canonical_hashes.items() if c > 1), + }, + "per_class_proxy": {}, + "per_class_exact": {}, + "zmp_distribution": {gt: {"mean": sum(v)/len(v), "min": min(v), "max": max(v), "unique": len(set(v))} + for gt, v in zmp_by_gt.items()}, + "predictions": predictions, + } + + report_path = os.path.join(os.path.dirname(__file__), "../..", + "shared-data/rrc_pist_exact_validation.json") + with open(report_path, "w") as f: + json.dump(report, f, indent=2) + print(f"\nFull report: {report_path}", flush=True) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/shared-data/rrc_pist_exact_validation.json b/shared-data/rrc_pist_exact_validation.json new file mode 100644 index 00000000..ae61ce8e --- /dev/null +++ b/shared-data/rrc_pist_exact_validation.json @@ -0,0 +1,666 @@ +{ + "summary": { + "total": 26, + "errors": 0, + "unique_matrix_hashes": 26, + "unique_canonical_hashes": 26, + "proxy_accuracy": 0.0, + "exact_accuracy": 0.0, + "matrix_hash_collisions": 0, + "canonical_hash_collisions": 0 + }, + "per_class_proxy": {}, + "per_class_exact": {}, + "zmp_distribution": { + "RRC shape": { + "mean": 8.0, + "min": 8, + "max": 8, + "unique": 1 + }, + "---": { + "mean": 8.0, + "min": 8, + "max": 8, + "unique": 1 + }, + "CognitiveLoadField": { + "mean": 8.0, + "min": 8, + "max": 8, + "unique": 1 + }, + "SignalShapedRouteCompiler": { + "mean": 8.0, + "min": 8, + "max": 8, + "unique": 1 + } + }, + "predictions": [ + { + "equation": "Equation", + "ground_truth": "RRC shape", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.6036, + 0.5, + 0.25, + -0.1036, + -0.25, + -0.25, + -0.25, + -0.5 + ], + "laplacian_zero_count": 1, + "rank_estimate": 8, + "crossing_density": 0.16071428571428573, + "strand_entropy": 2.9974236247346657, + "spectral_gap": 0.1036, + "matrix_hash": "33949abaad16e26d", + "canonical_hash": "6bb6183b7f8f3b35" + }, + { + "equation": "---", + "ground_truth": "---", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.6016, + 0.5187, + 0.1689, + 0.0282, + -0.25, + -0.25, + -0.3559, + -0.4615 + ], + "laplacian_zero_count": 1, + "rank_estimate": 8, + "crossing_density": 0.16071428571428573, + "strand_entropy": 2.9999170683359617, + "spectral_gap": 0.0829, + "matrix_hash": "3d78e152b7056998", + "canonical_hash": "7c0a0490cf9150d8" + }, + { + "equation": "bandwidth_adjusted_threshold", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "CadForceProbeReceipt", + "zmp": 8, + "eigenvalues": [ + 0.6114, + 0.1992, + 0.0, + 0.0, + -0.0, + -0.0, + -0.3426, + -0.4681 + ], + "laplacian_zero_count": 3, + "rank_estimate": 4, + "crossing_density": 0.10714285714285714, + "strand_entropy": 2.5849624907850903, + "spectral_gap": 0.4122, + "matrix_hash": "37eced1081168929", + "canonical_hash": "fa887f293db83360" + }, + { + "equation": "bandwidth_overflow", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.7337, + 0.3736, + 0.25, + 0.1319, + -0.25, + -0.25, + -0.4563, + -0.5328 + ], + "laplacian_zero_count": 1, + "rank_estimate": 8, + "crossing_density": 0.19642857142857142, + "strand_entropy": 2.999999983246879, + "spectral_gap": 0.3601, + "matrix_hash": "3928376682f8db56", + "canonical_hash": "9221ac4c055890aa" + }, + { + "equation": "effective_cognitive_load", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.8921, + 0.319, + 0.1575, + 0.0708, + -0.0572, + -0.283, + -0.4785, + -0.6208 + ], + "laplacian_zero_count": 1, + "rank_estimate": 8, + "crossing_density": 0.23214285714285715, + "strand_entropy": 2.999999987605606, + "spectral_gap": 0.573, + "matrix_hash": "165640b1f1d32885", + "canonical_hash": "80bad4c3c6dbb958" + }, + { + "equation": "emotional_gate", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 1.104, + 0.2821, + 0.138, + -0.0, + -0.1783, + -0.2928, + -0.4539, + -0.5992 + ], + "laplacian_zero_count": 1, + "rank_estimate": 7, + "crossing_density": 0.2857142857142857, + "strand_entropy": 2.999999985983328, + "spectral_gap": 0.8218, + "matrix_hash": "dc7aeb7e4d66b223", + "canonical_hash": "5162e2b1b5d30bda" + }, + { + "equation": "emotional_load", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.627, + 0.3787, + 0.1612, + 0.0, + -0.0, + -0.1612, + -0.3787, + -0.627 + ], + "laplacian_zero_count": 1, + "rank_estimate": 6, + "crossing_density": 0.16071428571428573, + "strand_entropy": 2.999996535408094, + "spectral_gap": 0.2483, + "matrix_hash": "43a54de905c4c91d", + "canonical_hash": "f23213ec0f5adb9a" + }, + { + "equation": "emotional_offload", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.8634, + 0.325, + 0.18, + 0.0343, + -0.1591, + -0.2155, + -0.4274, + -0.6007 + ], + "laplacian_zero_count": 1, + "rank_estimate": 8, + "crossing_density": 0.21428571428571427, + "strand_entropy": 2.9999999830262256, + "spectral_gap": 0.5384, + "matrix_hash": "d8cc28888f9f6e71", + "canonical_hash": "1ceea34138f8d08f" + }, + { + "equation": "historical_emotional_barrier", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.5743, + 0.3733, + 0.16, + 0.0, + -0.1158, + -0.25, + -0.25, + -0.4918 + ], + "laplacian_zero_count": 2, + "rank_estimate": 7, + "crossing_density": 0.125, + "strand_entropy": 2.8073265465749166, + "spectral_gap": 0.2009, + "matrix_hash": "f1d0144b6cd77864", + "canonical_hash": "e88f17e29e48b894" + }, + { + "equation": "historical_emotional_temperature", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.747, + 0.3918, + 0.1921, + 0.0, + -0.1476, + -0.3167, + -0.3886, + -0.478 + ], + "laplacian_zero_count": 1, + "rank_estimate": 7, + "crossing_density": 0.17857142857142858, + "strand_entropy": 2.9999964254686073, + "spectral_gap": 0.3553, + "matrix_hash": "b027ecf052c22219", + "canonical_hash": "e97c1d9946cf25fb" + }, + { + "equation": "historical_offload_efficiency", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.861, + 0.3186, + 0.2008, + -0.0, + -0.098, + -0.1688, + -0.3519, + -0.7616 + ], + "laplacian_zero_count": 1, + "rank_estimate": 7, + "crossing_density": 0.23214285714285715, + "strand_entropy": 2.999999993779257, + "spectral_gap": 0.5424, + "matrix_hash": "b035031ca4fdf769", + "canonical_hash": "d032ff2d4364998f" + }, + { + "equation": "overflow_gate", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.5731, + 0.332, + 0.1713, + 0.1545, + -0.0612, + -0.2277, + -0.4045, + -0.5375 + ], + "laplacian_zero_count": 1, + "rank_estimate": 8, + "crossing_density": 0.14285714285714285, + "strand_entropy": 2.9999996117526866, + "spectral_gap": 0.241, + "matrix_hash": "0cdd94495ead7bf7", + "canonical_hash": "d762cd7369048546" + }, + { + "equation": "raw_cognitive_load", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 1.0067, + 0.25, + 0.1919, + 0.0345, + -0.1445, + -0.25, + -0.3378, + -0.7508 + ], + "laplacian_zero_count": 1, + "rank_estimate": 8, + "crossing_density": 0.26785714285714285, + "strand_entropy": 2.9999999978661074, + "spectral_gap": 0.7567, + "matrix_hash": "a67615298b60dca7", + "canonical_hash": "3674e0a7de950bbd" + }, + { + "equation": "residual_stress", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.8253, + 0.3081, + 0.0, + 0.0, + -0.0, + -0.25, + -0.3382, + -0.5451 + ], + "laplacian_zero_count": 2, + "rank_estimate": 5, + "crossing_density": 0.17857142857142858, + "strand_entropy": 2.807354429293209, + "spectral_gap": 0.5172, + "matrix_hash": "1ec9ce0dffd2518e", + "canonical_hash": "45faa11774fbd91c" + }, + { + "equation": "threshold", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.5536, + 0.25, + 0.25, + 0.0, + -0.0, + -0.1348, + -0.4188, + -0.5 + ], + "laplacian_zero_count": 2, + "rank_estimate": 6, + "crossing_density": 0.125, + "strand_entropy": 2.8073548912348407, + "spectral_gap": 0.3036, + "matrix_hash": "b34c3825d3e8a787", + "canonical_hash": "aa9fa673c27ca057" + }, + { + "equation": "total_protective_load", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.7484, + 0.3156, + 0.1645, + -0.0, + -0.1519, + -0.25, + -0.3397, + -0.4871 + ], + "laplacian_zero_count": 2, + "rank_estimate": 7, + "crossing_density": 0.16071428571428573, + "strand_entropy": 2.8073548769280863, + "spectral_gap": 0.4328, + "matrix_hash": "7c51705dadc8748b", + "canonical_hash": "1ab00574c5df0a37" + }, + { + "equation": "trauma_adjusted_emotional_barrier", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.816, + 0.2769, + 0.1948, + 0.0603, + -0.0977, + -0.2297, + -0.4471, + -0.5733 + ], + "laplacian_zero_count": 1, + "rank_estimate": 8, + "crossing_density": 0.19642857142857142, + "strand_entropy": 2.9999999818271634, + "spectral_gap": 0.5391, + "matrix_hash": "674fe9dfa12eca24", + "canonical_hash": "0bb5b2fb18c4bbf1" + }, + { + "equation": "trauma_adjusted_emotional_temperature", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.5339, + 0.5, + 0.1655, + 0.0, + -0.1655, + -0.25, + -0.25, + -0.5339 + ], + "laplacian_zero_count": 2, + "rank_estimate": 7, + "crossing_density": 0.14285714285714285, + "strand_entropy": 2.8320318356394223, + "spectral_gap": 0.0339, + "matrix_hash": "77bd49439e28de1b", + "canonical_hash": "9f0b661c87322593" + }, + { + "equation": "trauma_adjusted_offload_efficiency", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.6451, + 0.3786, + 0.1972, + -0.0, + -0.0, + -0.2692, + -0.4517, + -0.5 + ], + "laplacian_zero_count": 1, + "rank_estimate": 6, + "crossing_density": 0.16071428571428573, + "strand_entropy": 2.99999917639906, + "spectral_gap": 0.2666, + "matrix_hash": "601a5c7ef164d8c0", + "canonical_hash": "83a3885983c845ca" + }, + { + "equation": "trauma_adjusted_threshold", + "ground_truth": "CognitiveLoadField", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.6311, + 0.25, + 0.1981, + 0.0, + -0.0, + -0.1981, + -0.25, + -0.6311 + ], + "laplacian_zero_count": 2, + "rank_estimate": 6, + "crossing_density": 0.14285714285714285, + "strand_entropy": 2.8073548999723985, + "spectral_gap": 0.3811, + "matrix_hash": "b067e08c7a7a316c", + "canonical_hash": "221c3db7cbca41d9" + }, + { + "equation": "core_equations", + "ground_truth": "SignalShapedRouteCompiler", + "proxy_pred": "LogogramProjection", + "exact_pred": "CadForceProbeReceipt", + "zmp": 8, + "eigenvalues": [ + 0.6486, + 0.229, + 0.0, + 0.0, + 0.0, + -0.0, + -0.3471, + -0.5305 + ], + "laplacian_zero_count": 2, + "rank_estimate": 4, + "crossing_density": 0.125, + "strand_entropy": 2.807354901721629, + "spectral_gap": 0.4196, + "matrix_hash": "4c4a8d48547219a3", + "canonical_hash": "e6be0f1560c04c66" + }, + { + "equation": "field_mapping", + "ground_truth": "SignalShapedRouteCompiler", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.5713, + 0.3634, + 0.172, + 0.1094, + -0.1094, + -0.172, + -0.3634, + -0.5713 + ], + "laplacian_zero_count": 1, + "rank_estimate": 8, + "crossing_density": 0.14285714285714285, + "strand_entropy": 2.9997214687556726, + "spectral_gap": 0.208, + "matrix_hash": "991db9fa3a2754bc", + "canonical_hash": "e9cde7ce6f3b5db9" + }, + { + "equation": "source_domain", + "ground_truth": "SignalShapedRouteCompiler", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.8268, + 0.3888, + 0.2703, + -0.0624, + -0.1865, + -0.25, + -0.4487, + -0.5382 + ], + "laplacian_zero_count": 1, + "rank_estimate": 8, + "crossing_density": 0.21428571428571427, + "strand_entropy": 2.999999970061503, + "spectral_gap": 0.438, + "matrix_hash": "5bcad2600ca15cc4", + "canonical_hash": "a9e76493cae5b3c9" + }, + { + "equation": "target_domain", + "ground_truth": "SignalShapedRouteCompiler", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.9383, + 0.283, + 0.2005, + 0.0271, + -0.1387, + -0.3086, + -0.4398, + -0.5617 + ], + "laplacian_zero_count": 1, + "rank_estimate": 8, + "crossing_density": 0.23214285714285715, + "strand_entropy": 2.9999999974290796, + "spectral_gap": 0.6553, + "matrix_hash": "a213cc28bc6c52d8", + "canonical_hash": "24e1ce749a71b137" + }, + { + "equation": "heat_loss", + "ground_truth": "SignalShapedRouteCompiler", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.9877, + 0.3993, + 0.1822, + -0.0813, + -0.1442, + -0.25, + -0.45, + -0.6437 + ], + "laplacian_zero_count": 1, + "rank_estimate": 8, + "crossing_density": 0.26785714285714285, + "strand_entropy": 2.999999992757774, + "spectral_gap": 0.5883, + "matrix_hash": "d3c78960211d820b", + "canonical_hash": "fb8ae4632aab63da" + }, + { + "equation": "magnetic_projection", + "ground_truth": "SignalShapedRouteCompiler", + "proxy_pred": "LogogramProjection", + "exact_pred": "LogogramProjection", + "zmp": 8, + "eigenvalues": [ + 0.7388, + 0.3297, + 0.2331, + -0.0, + -0.1616, + -0.1858, + -0.3643, + -0.5898 + ], + "laplacian_zero_count": 1, + "rank_estimate": 7, + "crossing_density": 0.17857142857142858, + "strand_entropy": 2.9999999353526676, + "spectral_gap": 0.4091, + "matrix_hash": "c0222068f79da5aa", + "canonical_hash": "bbe3c982db3cfd07" + } + ] +} \ No newline at end of file