GNN4Colliders / validation /compare_step.py
ho22joshua's picture
Remove historical implementation from active tree
de46a3c
Raw History Blame Contribute Delete
3.14 kB
#!/usr/bin/env python3
"""Compare serialized training diagnostics."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
import numpy as np
def _compare(left: np.ndarray, right: np.ndarray, atol: float, rtol: float):
delta = np.abs(left.astype(np.float64) - right.astype(np.float64))
passed = np.isclose(left, right, atol=atol, rtol=rtol)
return {
"status": "PASS" if bool(np.all(passed)) else "FAIL",
"shape": list(left.shape),
"max_abs": float(delta.max()) if delta.size else 0.0,
"mean_abs": float(delta.mean()) if delta.size else 0.0,
"outside_tolerance": int((~passed).sum()),
"elements": int(delta.size),
"atol": atol,
"rtol": rtol,
}
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("reference", type=Path)
parser.add_argument("candidate", type=Path)
parser.add_argument("output", type=Path)
args = parser.parse_args()
with np.load(args.reference) as left, np.load(args.candidate) as right:
stages = {
"logits": _compare(left["logits"], right["logits"], 1e-5, 1e-5),
"loss": _compare(left["loss"], right["loss"], 1e-5, 1e-5),
"gradients": _compare(left["gradients"], right["gradients"], 1e-4, 1e-5),
"parameters_after_step": _compare(
left["parameters"], right["parameters"], 1e-5, 1e-5
),
}
if "history" in left.files and "history" in right.files:
stages["history"] = _compare(left["history"], right["history"], 1e-5, 1e-5)
if "exp_avg" in left.files and "exp_avg" in right.files:
stages["optimizer_exp_avg"] = _compare(
left["exp_avg"], right["exp_avg"], 1e-5, 1e-5
)
stages["optimizer_exp_avg_sq"] = _compare(
left["exp_avg_sq"], right["exp_avg_sq"], 1e-5, 1e-5
)
stages["optimizer_steps"] = _compare(
left["optimizer_steps"], right["optimizer_steps"], 0.0, 0.0
)
report = {"schema_version": 1, "stages": stages}
report["overall_status"] = (
"PASS"
if all(stage["status"] == "PASS" for stage in stages.values())
else "FAIL"
)
args.output.mkdir(parents=True, exist_ok=True)
(args.output / "training_step.json").write_text(
json.dumps(report, indent=2, sort_keys=True) + "\n"
)
lines = [
"# One-step training parity",
"",
f"Overall status: **{report['overall_status']}**",
"",
"| Stage | Status | Max abs | Outside tolerance |",
"|---|---|---:|---:|",
]
for name, stage in stages.items():
lines.append(
f"| {name} | {stage['status']} | {stage['max_abs']} | "
f"{stage['outside_tolerance']} |"
)
(args.output / "training_step.md").write_text("\n".join(lines) + "\n")
print(json.dumps({"overall_status": report["overall_status"]}, indent=2))
return 0 if report["overall_status"] == "PASS" else 1
if __name__ == "__main__":
raise SystemExit(main())