mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
- 24/24 processed, 0 errors (12 verified, 12 failed) - Average 2.0 steps per proof (max 5 steps) - 11 tactic families detected - Verified proofs: avg gap=2.50 vs Failed: avg gap=1.50 - proof_traces/*.trace.json + *.decomp.json stored per theorem
132 lines
5.8 KiB
Python
132 lines
5.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Multi-tactic canary batch for Tier 2 trace pipeline."""
|
|
import json
|
|
import os
|
|
import sys
|
|
import time
|
|
import subprocess
|
|
from pathlib import Path
|
|
from collections import Counter
|
|
|
|
from trace_canary_theorems import CANARY_THEOREMS
|
|
|
|
TRACES_DIR = os.path.join(os.path.dirname(__file__), "../..", "shared-data/proof_traces")
|
|
VECTORS_PATH = os.path.join(os.path.dirname(__file__), "../..", "shared-data/pist_trace_canary_vectors.jsonl")
|
|
REPORT_PATH = os.path.join(os.path.dirname(__file__), "../..", "shared-data/pist_trace_canary_report.json")
|
|
BRIDGE = os.path.join(os.path.dirname(__file__), "lean_trace_bridge.py")
|
|
DECOMPOSE = os.path.join(os.path.dirname(__file__), "pist_trace_decompose.py")
|
|
|
|
PROOF_SERVER_TOKEN = os.environ.get("PROOF_SERVER_TOKEN", "")
|
|
if not PROOF_SERVER_TOKEN:
|
|
tf = os.environ.get("PROOF_SERVER_TOKEN_FILE", os.path.expanduser("~/.config/ene/language-proof-server.token"))
|
|
try:
|
|
PROOF_SERVER_TOKEN = Path(tf).read_text().strip()
|
|
except (FileNotFoundError, OSError):
|
|
PROOF_SERVER_TOKEN = ""
|
|
|
|
|
|
def run_pipeline(theorem: dict) -> dict:
|
|
name = theorem["name"]
|
|
code = theorem["code"]
|
|
trace_path = os.path.join(TRACES_DIR, f"canary_{name}.trace.json")
|
|
decomp_path = os.path.join(TRACES_DIR, f"canary_{name}.decomp.json")
|
|
os.makedirs(TRACES_DIR, exist_ok=True)
|
|
|
|
t0 = time.time()
|
|
r1 = subprocess.run(
|
|
[sys.executable, BRIDGE, "--code", code, "--name", name, "--out", trace_path],
|
|
capture_output=True, text=True, timeout=120,
|
|
env={**os.environ, "PROOF_SERVER_TOKEN": PROOF_SERVER_TOKEN},
|
|
)
|
|
if r1.returncode != 0:
|
|
return {"error": f"bridge: {r1.stderr[:200]}", "name": name}
|
|
|
|
r2 = subprocess.run(
|
|
[sys.executable, DECOMPOSE, trace_path, "--out", decomp_path],
|
|
capture_output=True, text=True, timeout=30,
|
|
)
|
|
if r2.returncode != 0 or not os.path.exists(decomp_path):
|
|
return {"error": f"decomp: {r2.stderr[:200]}", "name": name}
|
|
|
|
with open(decomp_path) as f:
|
|
decomposition = json.load(f)
|
|
|
|
return {"name": name, "trace_file": trace_path, "wall_s": round(time.time() - t0, 2),
|
|
"decomposition": decomposition}
|
|
|
|
|
|
def report(results, errors):
|
|
good = [r for r in results if "error" not in r]
|
|
n = len(good)
|
|
print(f"\n{'='*60}\nTIER 2 TRACE CANARY REPORT\n{'='*60}", flush=True)
|
|
print(f"Total: {len(results)} ({n} ok, {len(errors)} errors)", flush=True)
|
|
|
|
steps = [len(r["decomposition"].get("flexure_joints", [])) for r in good]
|
|
if steps:
|
|
print(f"Steps: mean={sum(steps)/len(steps):.1f} max={max(steps)} min={min(steps)}", flush=True)
|
|
statuses = Counter(r["decomposition"]["trace_summary"]["status"] for r in good)
|
|
print(f"Status: {dict(statuses)}", flush=True)
|
|
|
|
families = Counter()
|
|
for r in good:
|
|
for f, c in r["decomposition"].get("tactic_family_distribution", {}).items():
|
|
families[f] += c
|
|
print(f"Families: {dict(families)}", flush=True)
|
|
|
|
u_mat = len(set(r["decomposition"]["spectral"]["n_states"] for r in good))
|
|
u_rank = len(set(r["decomposition"]["spectral"]["rank_estimate"] for r in good))
|
|
u_gap = len(set(round(r["decomposition"]["spectral"]["spectral_gap"], 4) for r in good))
|
|
print(f"Unique states: {u_mat}/{n} ranks: {u_rank} gaps: {u_gap}/{n}", flush=True)
|
|
|
|
v_gaps = [r for r in good if r["decomposition"]["trace_summary"]["status"] == "verified"]
|
|
f_gaps = [r for r in good if r["decomposition"]["trace_summary"]["status"] == "failed"]
|
|
for label, group in [("verified", v_gaps), ("failed", f_gaps)]:
|
|
if group:
|
|
gaps = [r["decomposition"]["spectral"]["spectral_gap"] for r in group]
|
|
print(f"{label} (n={len(group)}): gap={sum(gaps)/len(gaps):.4f} [{min(gaps):.4f},{max(gaps):.4f}]", flush=True)
|
|
|
|
with open(VECTORS_PATH, "w") as f:
|
|
for r in good:
|
|
row = {
|
|
"name": r["name"], "status": r["decomposition"]["trace_summary"]["status"],
|
|
"n_steps": len(r["decomposition"].get("flexure_joints", [])),
|
|
"n_states": r["decomposition"]["spectral"]["n_states"],
|
|
"spectral_gap": r["decomposition"]["spectral"]["spectral_gap"],
|
|
"rank_estimate": r["decomposition"]["spectral"]["rank_estimate"],
|
|
"density": r["decomposition"]["spectral"]["density"],
|
|
"feature_vector": r["decomposition"]["feature_vector"],
|
|
"tactic_family_distribution": r["decomposition"]["tactic_family_distribution"],
|
|
}
|
|
f.write(json.dumps(row) + "\n")
|
|
print(f"Vectors: {VECTORS_PATH}", flush=True)
|
|
|
|
with open(REPORT_PATH, "w") as f:
|
|
json.dump({"n": n, "n_errors": len(errors), "status_distribution": dict(statuses),
|
|
"tactic_family_distribution": dict(families),
|
|
"unique_spectral_gaps": u_gap, "unique_rank_estimates": u_rank}, f, indent=2)
|
|
print(f"Report: {REPORT_PATH}", flush=True)
|
|
|
|
|
|
def main():
|
|
theorems = CANARY_THEOREMS
|
|
print(f"Trace canary: {len(theorems)} theorems\n", flush=True)
|
|
results, errors = [], []
|
|
for i, theorem in enumerate(theorems):
|
|
name = theorem["name"]
|
|
print(f" [{i+1}/{len(theorems)}] {name:30s} ... ", end="", flush=True)
|
|
r = run_pipeline(theorem)
|
|
if "error" in r:
|
|
print(f"ERROR: {r['error'][:60]}", flush=True)
|
|
errors.append(r)
|
|
else:
|
|
st = r["decomposition"]["trace_summary"]["status"]
|
|
ns = len(r["decomposition"].get("flexure_joints", []))
|
|
gap = r["decomposition"]["spectral"]["spectral_gap"]
|
|
print(f"{st:10s} steps={ns:2d} gap={gap:.4f} rank={r['decomposition']['spectral']['rank_estimate']}", flush=True)
|
|
results.append(r)
|
|
report(results, errors)
|
|
return 0 if len(errors) < len(theorems) / 2 else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|