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