Download scripts/build_reward_graph_cache.py from Dhruv1000/tracegrpo: direct link, hf CLI and curl.
- Browser
- Download file 4.77 kB
-
https://huggingface.co/Dhruv1000/tracegrpo/resolve/main/scripts/build_reward_graph_cache.py
- Command line
-
hf download hf://Dhruv1000/tracegrpo/scripts/build_reward_graph_cache.py
-
curl -L -o build_reward_graph_cache.py https://huggingface.co/Dhruv1000/tracegrpo/resolve/main/scripts/build_reward_graph_cache.py
4.77 kB
| """Build compressed, per-sample DFG caches for chronological-train R3 scoring.""" | |
| from __future__ import annotations | |
| import argparse | |
| import gzip | |
| import json | |
| import signal | |
| from collections import Counter | |
| from multiprocessing import Pool | |
| from pathlib import Path | |
| import pandas as pd | |
| from src.rewards.checker import trace_validity | |
| from src.rewards.cache import cache_path | |
| from src.rewards.line_graph import build_line_graph | |
| ROW_TIMEOUT_S = 15 | |
| class _RowTimeout(Exception): | |
| pass | |
| def _alarm_handler(signum, frame): | |
| raise _RowTimeout() | |
| def _build_one(task: tuple[dict, dict, str]) -> dict: | |
| row, annotation, root_name = task | |
| sample_id = row["sample_id"] | |
| signal.signal(signal.SIGALRM, _alarm_handler) | |
| signal.alarm(ROW_TIMEOUT_S) | |
| try: | |
| graph = build_line_graph(row["func_vuln"]) | |
| sinks = set() | |
| for path in annotation["trace_gt"]: | |
| lines = [graph.node_lines[tuple(map(int, node.split("-", 1)))] for node in path] | |
| valid, _ = trace_validity(lines, graph, sink=lines[-1]) | |
| if not valid: | |
| return {"sample_id": sample_id, "status": "trace_mismatch"} | |
| sinks.add(lines[-1]) | |
| value = {"sample_id": sample_id, "annotation_backend": annotation["annotation_backend"], | |
| "sink_lines": sorted(sinks), "graph": graph.to_dict()} | |
| path = cache_path(Path(root_name), sample_id) | |
| tmp = path.with_suffix(".tmp") | |
| with gzip.open(tmp, "wt") as f: | |
| json.dump(value, f) | |
| tmp.replace(path) | |
| return {"sample_id": sample_id, "status": "cached", "n_paths": len(annotation["trace_gt"])} | |
| except _RowTimeout: | |
| return {"sample_id": sample_id, "status": "timeout"} | |
| except Exception as exc: | |
| return {"sample_id": sample_id, "status": "error", "error": f"{type(exc).__name__}: {exc}"} | |
| finally: | |
| signal.alarm(0) | |
| def main() -> None: | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--corpus", default="data/processed/corpus.parquet") | |
| parser.add_argument("--split", default="data/splits/chronological/train.json") | |
| parser.add_argument("--annotations", default="data/processed/annotations.jsonl") | |
| parser.add_argument("--out-dir", default="data/cpg_cache/reward_graphs") | |
| parser.add_argument("--report", default="results/phase5/reward_graph_cache.json") | |
| parser.add_argument("--limit", type=int, default=None) | |
| parser.add_argument("--workers", type=int, default=4) | |
| parser.add_argument("--seed", type=int, default=42) | |
| args = parser.parse_args() | |
| train_pairs = set(json.loads(Path(args.split).read_text())) | |
| annotations = {a["sample_id"]: a for line in Path(args.annotations).read_text().splitlines() | |
| if line.strip() and (a := json.loads(line)).get("trace_gt")} | |
| df = pd.read_parquet(args.corpus, columns=["sample_id", "pair_id", "target", "func_vuln"]) | |
| rows = [row for row in df.to_dict("records") if row["pair_id"] in train_pairs | |
| and int(row["target"]) == 1 and row["sample_id"] in annotations] | |
| if args.limit is not None: | |
| rows = rows[:args.limit] | |
| root = Path(args.out_dir) | |
| root.mkdir(parents=True, exist_ok=True) | |
| existing = sum(cache_path(root, row["sample_id"]).exists() for row in rows) | |
| tasks = [(row, annotations[row["sample_id"]], str(root)) for row in rows | |
| if not cache_path(root, row["sample_id"]).exists()] | |
| print(f"trace-covered rows for split {args.split}={len(rows)}, existing={existing}, pending={len(tasks)}", flush=True) | |
| counts: Counter = Counter({"already_cached": existing}) | |
| failures = [] | |
| with Pool(max(1, args.workers)) as pool: | |
| for i, result in enumerate(pool.imap_unordered(_build_one, tasks, chunksize=1), 1): | |
| counts[result["status"]] += 1 | |
| if result["status"] not in ("cached", "already_cached"): | |
| failures.append(result) | |
| if i % 500 == 0: | |
| print(f"processed {i}/{len(tasks)}; cached={counts['cached']}; errors={len(failures)}", flush=True) | |
| report = {"seed": args.seed, "split": args.split, "n_trace_covered_rows": len(rows), | |
| "counts": dict(counts), "n_cached_total": counts["already_cached"] + counts["cached"], | |
| "error_rate": len(failures) / len(rows) if rows else 0.0, | |
| "failures": failures[:50], "cache_dir": str(root)} | |
| report_path = Path(args.report) | |
| report_path.parent.mkdir(parents=True, exist_ok=True) | |
| report_path.write_text(json.dumps(report, indent=2) + "\n") | |
| print(json.dumps({k: v for k, v in report.items() if k != "failures"}, indent=2)) | |
| if report["error_rate"] > 0.02: | |
| raise ValueError("reward graph cache error rate exceeds 2%") | |
| if __name__ == "__main__": | |
| main() | |