Files
digital-drosophila/models/male-cns-lif/phase4n_control_whole.py
T

57 lines
3.2 KiB
Python

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