Files
digital-drosophila/models/male-cns-lif/kcmbon-efficacy-v1/phase4m_performance.py
T

63 lines
5.4 KiB
Python

#!/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()