Download scripts/build_eval_set.py from NicolasT08/formal-mas: direct link, hf CLI and curl.
- Browser
- Download file 6.82 kB
-
https://huggingface.co/NicolasT08/formal-mas/resolve/main/scripts/build_eval_set.py
- Command line
-
hf download hf://NicolasT08/formal-mas/scripts/build_eval_set.py
-
curl -L -o build_eval_set.py https://huggingface.co/NicolasT08/formal-mas/resolve/main/scripts/build_eval_set.py
6.82 kB
| """Build data/eval.jsonl from the pinned sources and data/curation/criteria.yaml. | |
| bash scripts/fetch_eval_sources.sh # pinned miniF2F port and PutnamBench → data/raw/ | |
| python scripts/build_eval_set.py # draws, compiles with `sorry`, writes the set | |
| For each cell the problems are taken in seeded order; a problem that does not compile, or that the | |
| fidelity review discarded (criteria.yaml → discards), is logged in data/curation/discarded.json and | |
| replaced by the next one. No LLM runs here, and only Lean's compile-with-sorry check is used. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import sys | |
| from pathlib import Path | |
| import yaml | |
| ROOT = Path(__file__).resolve().parents[1] | |
| sys.path.insert(0, str(ROOT / "src")) | |
| from formal_mas import evalset as ev # noqa: E402 | |
| from formal_mas.lean.runner import LeanRunner, compiles # noqa: E402 | |
| from formal_mas.lean.statement import parse_theorem # noqa: E402 | |
| def load_pools(raw: Path, criteria: dict) -> dict[tuple[str, str], list[dict]]: | |
| labels = ev.flatten_labels(criteria["labels"]) | |
| seen = set(criteria["seen_in_repo"]) | |
| pools: dict[tuple[str, str], list[dict]] = {} | |
| for rec in ev.load_minif2f(raw / "minif2f.jsonl"): | |
| area = ev.area_of(rec["id"], labels) | |
| if area and rec["id"] not in seen: | |
| pools.setdefault((ev.difficulty_of(rec["id"]), area), []).append(rec) | |
| informal = json.loads((raw / "putnam" / "informal" / "putnam.json").read_text(encoding="utf-8")) | |
| for entry in informal: | |
| path = raw / "putnam" / "lean4" / "src" / f"{entry['problem_name']}.lean" | |
| if not path.exists(): | |
| continue | |
| rec = ev.putnam_record(entry["problem_name"], entry["tags"], entry["informal_statement"], | |
| path.read_text(encoding="utf-8")) | |
| if rec and ev.putnam_eligible(rec): | |
| pools.setdefault(("hard", "combinatorics"), []).append(rec) | |
| return pools | |
| def finalize(rec: dict, criteria: dict) -> dict: | |
| """Apply the only allowed edit and add provenance. Nothing else changes the statement.""" | |
| src = criteria["sources"][rec["origin"]] | |
| out = {"id": rec["id"], "source": src["name"], "source_commit": src["commit"], "source_id": rec["id"], | |
| "source_split": rec["source_split"], "license": src["license"], "informal": rec["informal"], | |
| "header": rec["header"]} | |
| if rec.get("tags"): | |
| out["source_tags"] = rec["tags"] | |
| converted = ev.convert_big_operators(rec["formal_statement"]) | |
| out["formal_statement"] = converted | |
| if converted != rec["formal_statement"]: | |
| out["formal_statement_original"] = rec["formal_statement"] | |
| out["converted"] = ev.CONVERSION_MARK | |
| out["statement"] = rec["header"] + converted | |
| return out | |
| def check_compiles(runner: LeanRunner, rec: dict) -> str | None: | |
| """None if the statement parses and compiles with `sorry`, else the reason it does not.""" | |
| try: | |
| thm = parse_theorem(rec["statement"], header=rec["header"]) | |
| except ValueError as exc: | |
| return f"parse_theorem: {exc}" | |
| result = runner.verify(f"{rec['header']}{rec['formal_statement'].rstrip()} sorry\n", thm.name) | |
| if compiles(result): | |
| return None | |
| if result.status != "finished": # a slow machine must not change the set | |
| raise SystemExit(f"{rec['id']}: Lean did not finish ({result.status}); nothing written") | |
| errors = " | ".join(m.text for m in result.errors) | |
| return f"does not compile with sorry ({result.status}): {errors[:300]}" | |
| def reserved_minimum(criteria: dict, pools: dict) -> dict[tuple[str, str], int]: | |
| reserved = set(criteria["reserved_dev"]) | |
| return {cell: n for cell, pool in pools.items() if (n := sum(r["id"] in reserved for r in pool))} | |
| def build(raw: Path, criteria: dict, runner: LeanRunner) -> tuple[list[dict], list[dict]]: | |
| seed = criteria["seed"] | |
| pools = load_pools(raw, criteria) | |
| manual = criteria["discards"] or {} | |
| ev.check_discards(manual) | |
| reserved = set(criteria["reserved_dev"]) | |
| dev_n = ev.dev_counts(criteria["grid"], criteria["total_dev"], seed, reserved_minimum(criteria, pools)) | |
| chosen: list[dict] = [] | |
| discarded: list[dict] = [] | |
| for difficulty, row in criteria["grid"].items(): | |
| for area, target in row.items(): | |
| pool = {r["id"]: r for r in pools.get((difficulty, area), [])} | |
| if not target: | |
| continue | |
| order = [i for i in criteria["reserved_dev"] if i in pool] | |
| order += ev.seeded_order([i for i in pool if i not in reserved], seed) | |
| cell: list[dict] = [] | |
| for problem_id in order: | |
| if len(cell) == target: | |
| break | |
| rec = finalize(pool[problem_id], criteria) | |
| if problem_id in manual: | |
| reason, stage = manual[problem_id], "fidelity_review" | |
| else: | |
| reason, stage = check_compiles(runner, rec), "compile_with_sorry" | |
| if reason: | |
| discarded.append({"id": problem_id, "difficulty": difficulty, "area": area, | |
| "stage": stage, "reason": reason}) | |
| continue | |
| cell.append({**rec, "area": area, "difficulty": difficulty}) | |
| if len(cell) < target: | |
| raise SystemExit(f"{difficulty}/{area}: only {len(cell)} of {target} problems available") | |
| splits = ev.assign_split([r["id"] for r in cell], dev_n[(difficulty, area)], reserved, seed) | |
| chosen += [{**r, "split": splits[r["id"]], "seed": seed} for r in cell] | |
| return chosen, discarded | |
| def main() -> int: | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("--raw", type=Path, default=ROOT / "data" / "raw") | |
| parser.add_argument("--criteria", type=Path, default=ROOT / "data" / "curation" / "criteria.yaml") | |
| parser.add_argument("--out", type=Path, default=ROOT / "data" / "eval.jsonl") | |
| args = parser.parse_args() | |
| criteria = yaml.safe_load(args.criteria.read_text(encoding="utf-8")) | |
| with LeanRunner() as runner: | |
| rows, discarded = build(args.raw, criteria, runner) | |
| lean_info = runner.run_info() | |
| args.out.write_text("".join(json.dumps(r, ensure_ascii=False) + "\n" for r in rows), encoding="utf-8") | |
| log = args.criteria.parent / "discarded.json" | |
| log.write_text(json.dumps(discarded, ensure_ascii=False, indent=1) + "\n", encoding="utf-8") | |
| info = args.criteria.parent / "build_info.json" | |
| info.write_text(json.dumps(lean_info, ensure_ascii=False, indent=1, default=str) + "\n", encoding="utf-8") | |
| json.dump({"problems": len(rows), "discarded": len(discarded)}, sys.stdout) | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |