#!/usr/bin/env python3 # /// script # requires-python = ">=3.10" # dependencies = [] # /// """ Build formal/SilverSight/PIST/Matrices250.lean from shared-data/rrc_pist_predictions_250_v1.json (or a local copy). Each 8×8 matrix is emitted as its own named `def` to avoid overwhelming Lean's kernel with a single large term. `findMatrix` is a `match` on the equation_id string that delegates to the individual def. Usage: python3 python/build_pist_matrices_250.py python3 python/build_pist_matrices_250.py \ --predictions /path/to/rrc_pist_predictions_250_v1.json \ --out-lean formal/SilverSight/PIST/Matrices250.lean """ from __future__ import annotations import argparse, hashlib, json, re, sys from pathlib import Path def lean_matrix_def_name(eid: str) -> str: """Sanitize equation_id to a valid Lean identifier.""" name = re.sub(r"[^a-zA-Z0-9_]", "_", eid) if name[0].isdigit(): name = "m_" + name return name def lean_array_int(xs: list[int]) -> str: return "#[" + ", ".join(str(x) for x in xs) + "]" def lean_matrix_rows(rows: list[list[int]]) -> str: inner = ",\n ".join(lean_array_int(r) for r in rows) return "#[\n " + inner + "\n ]" def main() -> int: parser = argparse.ArgumentParser( description="Generate SilverSight PIST Matrices250.lean" ) parser.add_argument( "--predictions", type=Path, default=Path("data/rrc_pist_predictions_250_v1.json"), help="Path to rrc_pist_predictions_250_v1.json", ) parser.add_argument( "--out-lean", type=Path, default=Path("formal/SilverSight/PIST/Matrices250.lean"), help="Output Lean module path", ) args = parser.parse_args() preds = json.loads(args.predictions.read_text()) raw = preds.get("predictions", []) print(f"Loaded {len(raw)} predictions from {args.predictions}", file=sys.stderr) raw_sorted = sorted(raw, key=lambda p: p.get("object_id", p.get("equation_id", ""))) defs: list[str] = [] find_cases: list[str] = [] for p in raw_sorted: eid = p.get("object_id") or p.get("equation_id", "") mat = p.get("matrix_8x8", []) if not eid or len(mat) != 8: print(f"WARNING: skipping malformed entry {p.get('equation_id', '?')}", file=sys.stderr) continue name = lean_matrix_def_name(eid) defs.append(f"def {name} : Array (Array Int) :=\n {lean_matrix_rows(mat)}") find_cases.append(f" | \"{eid}\" => some {name}") content_blob = json.dumps( {p.get("equation_id"): p.get("matrix_8x8") for p in raw_sorted}, sort_keys=True, separators=(",", ":"), ).encode("utf-8") content_hash = hashlib.sha256(content_blob).hexdigest() lines = [ "-- SilverSight.PIST.Matrices250 — AUTO-GENERATED by python/build_pist_matrices_250.py", "-- DO NOT EDIT BY HAND. Regenerate with:", "-- python3 python/build_pist_matrices_250.py", "--", "-- Source: rrc_pist_predictions_250_v1.json", "-- Content hash (SHA-256 canonical matrices): " + content_hash, "--", "-- This file is consumed by SilverSight.RRC.Q16_16Manifold at compile time", "-- to produce pistProxyLabel/pistExactLabel for each equation_id.", "", "namespace SilverSight.PIST.Matrices250", "", f"def pistMatrixDim : Nat := 8", "", *defs, "", "/-- Look up an 8×8 matrix by invariant equation_id. -/", "def findMatrix (eqId : String) : Option (Array (Array Int)) :=", " match eqId with", *find_cases, " | _ => none", "", "end SilverSight.PIST.Matrices250", "", ] args.out_lean.parent.mkdir(parents=True, exist_ok=True) args.out_lean.write_text("\n".join(lines)) print(f"Wrote {args.out_lean} ({len(defs)} entries)", file=sys.stderr) return 0 if __name__ == "__main__": sys.exit(main())