Files

88 lines
6.0 KiB
Python

#!/usr/bin/env python3
"""Compare external-only E with open-loop all-confirmed-DAN gate shadow."""
from __future__ import annotations
import argparse
import json
import time
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 ReplayEngine, compare_A_B, load_odor_inputs, replay_effect
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")
reproduction=json.loads((exp/"regression/event-trace-reproduction.json").read_text())
bad={int(seed):min(int(x["pairing"]) for x in reproduction["mismatched_trials"] if int(x["seed"])==int(seed))
for seed in ncfg["train_seeds"] if any(int(x["seed"])==int(seed) for x in reproduction["mismatched_trials"])}
v=Validator(cfg,basepath,exp);sim=OdorSimulator(v,cfg,exp)
comp=identity.compartment.astype(str).to_numpy()==str(ncfg["matched_compartment_verbatim"])
engine=ReplayEngine(sim,np.unique(identity.loc[comp,"target_neuron_idx"].to_numpy(np.int32)))
w=identity.anatomical_weight_mv.to_numpy(np.float32);old=Path(mcfg["experiment_root"])
rows=[];calls=[]
for seed,es in zip(ncfg["train_seeds"],ncfg["evaluation_seeds"]):
for pair in [20,50,60,75,100]:
with np.load(exp/"endogenous-dan"/f"E-all-DAN-shadow-seed-{seed}-pairings-{pair}.npz") as f:
states={"external-only":f["E_external_only"].copy(),"all-confirmed-DAN-shadow":f["E_all_DAN"].copy()}
for mechanism,E in states.items():
delta=w*(E-1);results={}
for odor in ("A","B"):
spikes=pd.read_parquet(old/"isolated-replay"/f"KC-spikes-ODOR_{odor}-seed-{es}.parquet")
zero=engine.run(spikes,np.zeros(len(E),np.float32),float(mcfg["protocol"]["duration_ms"]))
result=engine.run(spikes,delta,float(mcfg["protocol"]["duration_ms"]))
calls.append(result["wall_seconds"]);results[odor]=replay_effect(zero,result,sim.dt)
rows.append({"seed":seed,"eval_seed":es,"pairings":pair,"mechanism":mechanism,
"trace_exact_prefix":bool(pair < bad.get(int(seed),10**9)),
"A_specific_functional":compare_A_B(results["A"],results["B"]),
"A_MBON_spike_L1":results["A"]["MBON_spike_L1"],"B_MBON_spike_L1":results["B"]["MBON_spike_L1"],
"A_transmission_reduction_percent":results["A"]["transmitted_reduction_percent"],
"B_transmission_reduction_percent":results["B"]["transmitted_reduction_percent"]})
table=pd.DataFrame(rows);table.to_parquet(exp/"endogenous-dan/isolated-gate-comparison.parquet",index=False)
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]
whole=[];brain_calls=[]
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 (60,100):
with np.load(exp/"endogenous-dan"/f"E-all-DAN-shadow-seed-{seed}-pairings-{pair}.npz") as f:
states={"external-only":f["E_external_only"].copy(),"all-confirmed-DAN-shadow":f["E_all_DAN"].copy()}
for mechanism,E in states.items():
delta=w*(E-1)
for odor in ("A","B"):
post=sim.run_rates(*pn[odor],es,float(mcfg["protocol"]["duration_ms"]),delta=delta)
brain_calls.append(post["wall_seconds"]);e=effect(baseline[odor],post)
whole.append({"seed":seed,"eval_seed":es,"pairings":pair,"mechanism":mechanism,"odor":odor,
"trace_exact_prefix":bool(pair < bad.get(int(seed),10**9)),
"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"]})
wt=pd.DataFrame(whole);wt.to_parquet(exp/"endogenous-dan/wholebrain-gate-comparison.parquet",index=False)
iso=table.groupby(["mechanism","pairings"]).agg(A_specific_seeds=("A_specific_functional","sum"),A_spike_L1=("A_MBON_spike_L1","mean"),B_spike_L1=("B_MBON_spike_L1","mean")).reset_index()
pivot=wt.pivot(index=["mechanism","pairings","seed"],columns="odor",values="MBON_spike_L1").reset_index()
pivot["A_minus_B"]=pivot["A"]-pivot["B"]
wh=pivot.groupby(["mechanism","pairings"]).agg(A_minus_B_mean=("A_minus_B","mean"),A_specific_seeds=("A_minus_B",lambda x:int((x>0).sum()))).reset_index()
clean=table[table.trace_exact_prefix].groupby(["mechanism","pairings"]).agg(clean_seed_count=("seed","nunique"),A_specific_seeds=("A_specific_functional","sum"),
A_spike_L1=("A_MBON_spike_L1","mean"),B_spike_L1=("B_MBON_spike_L1","mean")).reset_index()
comp_summary={"isolated":iso.to_dict("records"),"wholebrain":wh.to_dict("records"),
"isolated_exact_prefix_only":clean.to_dict("records"),"nonexact_trials":reproduction["mismatched_trials"],
"all_DAN_branch_status":"counterfactual open-loop shadow gate, not frozen Phase 4M dynamics or physiological dopamine",
"isolated_replay_wall_seconds":float(sum(calls)),"wholebrain_wall_seconds":float(sum(brain_calls)),
"external_US_only_is_frozen_Phase4M_rule":True}
write_json(exp/"endogenous-dan/gate-specificity.json",comp_summary)
print(json.dumps(comp_summary),flush=True)
if __name__=="__main__":main()