"""Provenance block attached to every saved experimental result.""" from __future__ import annotations import datetime as _dt, json, os, platform, subprocess, sys from pathlib import Path def _sysctl(key: str) -> str: try: return subprocess.run(["sysctl", "-n", key], capture_output=True, text=True, timeout=5).stdout.strip() except Exception: # pragma: no cover return "" def host_info() -> dict: info = { "hostname": platform.node(), "os": f"{platform.system()} {platform.release()} ({platform.mac_ver()[0] or platform.version()})", "machine": platform.machine(), "python": sys.version.split()[0], } if platform.system() == "Darwin": info["cpu"] = _sysctl("machdep.cpu.brand_string") info["cores"] = int(_sysctl("hw.ncpu") or 0) mem = _sysctl("hw.memsize") info["ram_gb"] = round(int(mem) / 2**30, 1) if mem else None else: # pragma: no cover info["cpu"] = platform.processor() info["cores"] = os.cpu_count() return info def software_versions(mods=("numpy", "scipy", "sympy", "gmpy2", "z3", "pysat", "ecdsa", "coincurve")) -> dict: out = {} for m in mods: try: mod = __import__(m) v = getattr(mod, "__version__", None) if m == "z3": v = mod.get_version_string() if m == "gmpy2": v = mod.version() out[m] = v except Exception: out[m] = None return out def record(experiment: str, params: dict, measurements, *, seed=None, extra: dict | None = None, out: str | os.PathLike | None = None, started: float | None = None) -> dict: """Build (and optionally write) a result document with full provenance.""" doc = { "experiment": experiment, "timestamp_utc": _dt.datetime.now(_dt.timezone.utc).isoformat(timespec="seconds"), "command": " ".join(sys.argv), "cwd": os.getcwd(), "seed": seed, "host": host_info(), "software": software_versions(), "params": params, "measurements": measurements, } if started is not None: import time doc["elapsed_s"] = round(time.time() - started, 3) if extra: doc.update(extra) if out: p = Path(out) p.parent.mkdir(parents=True, exist_ok=True) p.write_text(json.dumps(doc, indent=1, default=str)) return doc