63 lines
5.4 KiB
Python
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()
|