#!/usr/bin/env python3 """Held-out whole-brain readout for independent Phase 4N control E overlays.""" from __future__ import annotations import argparse import json from pathlib import Path import numpy as np import pandas as pd from phase4h_audit import write_json from phase4i import Validator from phase4j import effect from phase4l import OdorSimulator, pn_drive from phase4m import load_odor_inputs from phase4n import load_config def main(): ap=argparse.ArgumentParser();ap.add_argument("--config",type=Path,required=True);args=ap.parse_args() ncfg,mcfg,cfg,basepath=load_config(args.config);exp=Path(ncfg["experiment_root"]) identity=pd.read_parquet(exp/"edge-identity/frozen-edge-identity.parquet") v=Validator(cfg,basepath,exp);sim=OdorSimulator(v,cfg,exp) w=identity.anatomical_weight_mv.to_numpy(np.float32) panel,vectors,selection,mapping=load_odor_inputs(cfg);gloms=selection["glomeruli_order"];ms=float(np.median(panel.mapped_response_sum)) pn={} for odor in ("A","B"): oid=selection[f"ODOR_{odor}"]["odor_id"] pn[odor]=pn_drive(oid,vectors[oid],mapping,gloms,str(mcfg["protocol"]["input_mode"]),ms,cfg)[:2] rows=[];walls=[];conditions=("B+matched-US","A+random-US-timing","A+unrelated-compartment-US") for seed,es in zip(ncfg["train_seeds"],ncfg["evaluation_seeds"]): baseline={odor:sim.run_rates(*pn[odor],es,float(mcfg["protocol"]["duration_ms"])) for odor in pn} for pair in (50,100): for condition in conditions: with np.load(exp/"controls"/f"E-{condition}-seed-{seed}-pairings-{pair}.npz") as f:E=f["E"].copy() delta=w*(E-1) for odor in ("A","B"): post=sim.run_rates(*pn[odor],es,float(mcfg["protocol"]["duration_ms"]),delta=delta) e=effect(baseline[odor],post);walls.append(post["wall_seconds"]) rows.append({"condition":condition,"train_seed":seed,"eval_seed":es,"pairings":pair,"odor":odor, "MBON_spike_L1":e["MBON_spike_l1"],"downstream_spike_L1":e["downstream_spike_l1"], "KC_spike_L1":int(np.abs(post["kc_counts"]-baseline[odor]["kc_counts"]).sum()), "finite":bool(post["finite"]),"wall_seconds":post["wall_seconds"]}) print(json.dumps({"seed":seed,"completed_control_wholebrain":True}),flush=True) table=pd.DataFrame(rows);table.to_parquet(exp/"controls/wholebrain-heldout-control-replay.parquet",index=False) pivot=table.pivot(index=["condition","pairings","train_seed"],columns="odor",values="MBON_spike_L1").reset_index() pivot["A_minus_B"]=pivot["A"]-pivot["B"] summary=pivot.groupby(["condition","pairings"]).agg(A_minus_B_mean=("A_minus_B","mean"),A_specific_seeds=("A_minus_B",lambda x:int((x>0).sum()))).reset_index() write_json(exp/"controls/wholebrain-summary.json",{"rows":len(table),"conditions":summary.to_dict("records"), "total_brain_wall_seconds":float(sum(walls)),"frozen_E1_reference_reused_per_evaluation_seed":True, "A_D0_and_frozen_E1_effect_are_exactly_zero_by_E_identity":True}) print(json.dumps(summary.to_dict("records")),flush=True) if __name__=="__main__":main()