#!/usr/bin/env python3 """Paired read-only 120-ms baseline versus efficacy-overlay runtime timing.""" from __future__ import annotations import argparse import json import shutil import subprocess import time from pathlib import Path import numpy as np import pandas as pd import torch import yaml from phase4h_audit import sha256, write_json from phase4i import Validator from phase4k import seal from phase4l import OdorSimulator, pn_drive from phase4m import frozen_checks, frozen_hashes, load_odor_inputs def main() -> None: ap = argparse.ArgumentParser(); ap.add_argument("--config", type=Path, required=True); args = ap.parse_args(); mcfg = yaml.safe_load(args.config.read_text()); bpath = Path(mcfg["frozen_phase4l_config"]); cfg = yaml.safe_load(bpath.read_text()); cfg["paths"]["phase4l_root"] = cfg["paths"]["experiment_root"]; cfg["paths"]["phase4l_model_root"] = str(bpath.parent) exp, model = Path(mcfg["experiment_root"]), Path(mcfg["model_root"]); summary_path = exp / "audit/final-summary.json"; summary = json.loads(summary_path.read_text()); before = frozen_hashes(cfg) panel, vectors, selection, mapping = load_odor_inputs(cfg); v = Validator(cfg, bpath, exp); sim = OdorSimulator(v, cfg, exp) oid = selection["ODOR_A"]["odor_id"]; targets, rates, _ = pn_drive(oid, vectors[oid], mapping, selection["glomeruli_order"], str(mcfg["protocol"]["input_mode"]), float(np.median(panel.mapped_response_sum)), cfg) duration = float(mcfg["protocol"]["duration_ms"]); zero = np.zeros(len(v.edge_ids), np.float32); records = [] sim.run_rates(targets, rates, 51999, duration, delta=zero) for ts, es in zip(map(int, mcfg["protocol"]["train_seeds"]), map(int, mcfg["protocol"]["evaluation_seeds"])): with np.load(exp / "efficacy-overlay" / f"E-seed-{ts}-pairings-100.npz") as state: E = state["E_bounded_multiplicative"].copy() delta = v.base_candidate.astype(np.float32) * (E - 1.0) for label, weights in (("baseline", zero), ("bounded_E100", delta)): t0 = time.perf_counter(); run = sim.run_rates(targets, rates, es, duration, delta=weights); total = time.perf_counter() - t0 records.append({"train_seed": ts, "eval_seed": es, "condition": label, "brain_loop_seconds": run["wall_seconds"], "total_call_seconds_including_overlay_install_and_PN_generation": total, "PN_event_generation_seconds": run["pn_generation_wall_seconds"], "APL_submit_seconds": run["overlay_cpu_submit_seconds"], "peak_VRAM_bytes": run["peak_vram_bytes"], "finite": run["finite"]}) frame = pd.DataFrame(records); frame.to_parquet(exp / "performance/paired-overlay-runtime.parquet", index=False); base = frame[frame.condition == "baseline"].sort_values("train_seed"); post = frame[frame.condition == "bounded_E100"].sort_values("train_seed") loop_overhead = post.brain_loop_seconds.to_numpy() - base.brain_loop_seconds.to_numpy(); total_overhead = post.total_call_seconds_including_overlay_install_and_PN_generation.to_numpy() - base.total_call_seconds_including_overlay_install_and_PN_generation.to_numpy() report = {"biological_duration_ms": duration, "trial": "frozen ODOR_A 120 ms, paired evaluation seed, no dopamine or learning", "baseline_loop_seconds_mean": float(base.brain_loop_seconds.mean()), "bounded_E100_loop_seconds_mean": float(post.brain_loop_seconds.mean()), "loop_overhead_seconds_mean": float(loop_overhead.mean()), "loop_overhead_percent_mean": float(100 * np.mean(loop_overhead / base.brain_loop_seconds.to_numpy())), "baseline_total_call_seconds_mean": float(base.total_call_seconds_including_overlay_install_and_PN_generation.mean()), "bounded_E100_total_call_seconds_mean": float(post.total_call_seconds_including_overlay_install_and_PN_generation.mean()), "total_call_overhead_seconds_mean": float(total_overhead.mean()), "mean_peak_VRAM_bytes": float(frame.peak_VRAM_bytes.mean()), "five_paired_seeds": True, "numeric_results_not_reused_for_gates": True, "all_finite": bool(frame.finite.all())} write_json(exp / "performance/paired-overlay-summary.json", report); summary["performance"]["paired_overlay_runtime"] = report write_json(exp / "performance/summary.json", summary["performance"]); write_json(summary_path, summary) version = json.loads((model / "VERSION.json").read_text()); version["summary_sha256"] = sha256(summary_path); write_json(model / "VERSION.json", version) shutil.copy2(Path(__file__).resolve(), exp / "audit/phase4m_performance.py"); shutil.copy2(Path(__file__).resolve(), model / "phase4m_performance.py") if frozen_hashes(cfg) != before or not frozen_checks(cfg)["all_passed"]: raise RuntimeError("frozen integrity failed after performance audit") seal(model, model / "MANIFEST.json", model / "checksums.sha256"); seal(exp, exp / "manifests/manifest.json", exp / "manifests/checksums.sha256") for root, checksum in ((model, "checksums.sha256"), (exp, "manifests/checksums.sha256")): p = subprocess.run(["sha256sum", "-c", checksum], cwd=root, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, text=True) if p.returncode: raise RuntimeError(f"Phase 4M self checksum failed for {root}: {p.stderr[-1000:]}") print(json.dumps({"performance": report, "model_and_experiment_SHA256": "PASS"}, indent=2)) if __name__ == "__main__": main()