"""Literature-informed panel coverage test; frozen brain and encoder, no learning.""" from pathlib import Path import argparse import json import time import numpy as np import pandas as pd import torch import yaml import phase4p import phase4p_finalize import phase4s import phase4t from phase4p_revalidate import DeterministicAPLSim,door_inputs,make_drive from phase4l import load_external from phase4r import save,digest,arrhash,curves from phase4q import last_spike_trace,v2_trace from phase4h_audit import verify_checksum def checks(cfg): tc=yaml.safe_load((Path(cfg['prior_model'])/'phase4t.yaml').read_text()) out=phase4t.checks(tc) for key in ('prior_root','prior_model'): root=Path(cfg[key]);out[key]=verify_checksum(root,root/'checksums.sha256') assert all(x['passed'] for x in out.values());return out def prepare(cfg,exp): oc,old,oldvec,selection,mapping=door_inputs() _,response,odors,provenance=load_external(oc,exp) assert 'InChIKey' in odors.columns and 'Name' in odors.columns usable=mapping[mapping.mapping_confidence.isin(['exact','alias-normalized'])&mapping.neuron_idx.notna()] cg=usable[['odor_channel','glomerulus']].drop_duplicates();gloms=selection['glomeruli_order'];gi={g:i for i,g in enumerate(gloms)} assert sorted(cg.glomerulus.unique())==gloms names=odors.dropna(subset=['InChIKey']).drop_duplicates('InChIKey').set_index('InChIKey')['Name'].to_dict() vectors={};rows=[] for key,row in response.iterrows(): if str(key)=='SFR':continue name=names.get(key) if pd.isna(name) or not str(name).strip():continue arr=np.zeros(len(gloms),np.float32) for _,q in cg.iterrows(): value=row.get(q.odor_channel) if pd.notna(value):arr[gi[q.glomerulus]]=max(arr[gi[q.glomerulus]],max(float(value),0.)) allsum=float(np.maximum(np.nan_to_num(row.to_numpy(np.float64),nan=0.),0.).sum()) mapped=float(arr.sum());strong=int(np.count_nonzero(arr>=float(oc['odor_encoder']['strong_response_threshold']))) if mapped<=0 or strong=.9)].copy() pool=pool.sort_values(['mapped_response_sum','odor_id'],kind='stable').drop_duplicates('odor_name').reset_index(drop=True) n=int(cfg['panel']['novel_count']);assert len(pool)>=n,'Not enough independent physiology odors' bins=np.array_split(np.arange(len(pool)),n);selected=[int(b[(len(b)-1)//2]) for b in bins] novel=pool.iloc[selected].copy();novel['split']='new_odor_validation' cal=old[old.split=='calibration'].drop(columns=['formal_label']).copy();cal['split']='calibration';assert len(cal)==6 panel=pd.concat([cal,novel],ignore_index=True) formal={selection['ODOR_A']['odor_id'],selection['ODOR_B']['odor_id']} assert not set(panel.odor_id)&formal and not set(novel.odor_id)&set(old.odor_id) pool.to_parquet(exp/'protocol/eligible-new-odor-pool.parquet',index=False) panel.to_parquet(exp/'protocol/frozen-panel.parquet',index=False) np.savez_compressed(exp/'protocol/frozen-vectors.npz',glomeruli=np.asarray(gloms),**{oid:vectors[oid] for oid in panel.odor_id}) save(exp/'regression/frozen-encoder-exact.json',{'pass':True,'rows':tests}) save(exp/'protocol/panel-selection.json',{'eligible_new_odors':len(pool),'rank_bins':[list(map(int,b)) for b in bins], 'selected_ranks':selected,'new_odor_ID_overlap_old12':0,'input_response_only':True,'runtime_responses_used':False, 'source':provenance,'frozen_mapping_sha256':digest(Path(oc['paths']['phase4l_root'])/'receptor-mapping/olfactory-mapping.parquet')}) return oc,old,panel,vectors,selection,mapping def run(cfg,exp,model): for d in ('protocol','external-resources','calibration','validation','events','regression','audit','performance'):(exp/d).mkdir(parents=True,exist_ok=True) assert not (exp/'protocol/preregistered.json').exists(),'Refusing overwrite' before=checks(cfg);save(exp/'audit/preflight.json',before) oc,old,panel,vectors,selection,mapping=prepare(cfg,exp) cal=panel[panel.split=='calibration'];nc=len(cal) save(exp/'protocol/preregistered.json',{'config':cfg,'panel':panel.to_dict('records'), 'sources':{p.name:digest(p) for p in model.glob('phase4u*') if p.is_file()}, 'created_before_simulation':True,'old12_excluded_from_new_test':True,'no_new_dynamic_results_seen':True, 'method':'Expanded calibration coverage; fixed Phase4T grid, score and gate. Not posthoc retest of Phase4T failures.'}) print(json.dumps({'panel':panel[['odor_id','odor_name','split','mapped_response_sum']].to_dict('records')}),flush=True) pcfg=yaml.safe_load((Path(cfg['runtime_root'])/'phase4p.yaml').read_text()) rt=phase4p.CanonicalRuntime(pcfg,Path(cfg['runtime_root']),exp);sim=DeterministicAPLSim(rt) lookup=np.full(sim.n,-1,np.int32);lookup[sim.kc]=np.arange(len(sim.kc)) identity=sim.identity;assert len(identity)==14232 mask=identity.compartment.eq(cfg['target_compartment']).to_numpy();loc=lookup[identity.source_neuron_idx.to_numpy(np.int32)] multi=np.bincount(loc[mask],minlength=len(sim.kc)).astype(np.float32) dan=sim.dan_targets(cfg['matched_DAN'],cfg['target_compartment']);duration=cfg['duration_ms'];nsteps=round(duration/sim.dt) records=[];repeats=[];live=[];times=[];start=time.perf_counter();torch.cuda.reset_peak_memory_stats() def trial(oi,seed,split,ws,us): odor=panel.iloc[oi];targets,rates=make_drive(oc,old,vectors,selection,mapping,odor.odor_id,'physiological') actual=cfg['seed_namespace_offset']+oi*cfg['odor_seed_offset']+seed r=sim.run(targets,rates,actual,duration,collect_events=True);assert r['finite'] phase4s.persist_run(exp,f'{split}-{oi}-{seed}',r) cs=curves(r,lookup,multi,ws,sim.dt,nsteps) for wi,w in enumerate(ws): den=float(cs[wi].mean()) for u in us: m=float(cs[wi][round(u/sim.dt)]) records.append({'split':split,'odor_id':odor.odor_id,'odor_name':odor.odor_name,'odor_index':oi,'seed':seed,'actual_seed':actual, 'window_ms':w,'US_ms':u,'matched':m,'random_expectation':den,'ratio':m/den if den else 0.}) s=r['kc_event_step'];nodes=r['kc_event_node'];target=multi[lookup[nodes]]>0;ts=s[target] times.append({'split':split,'odor_index':oi,'seed':seed,'KC_active':int(np.count_nonzero(r['kc_counts'])), 'target_active_KC':int(np.unique(nodes[target]).size),'target_events':len(ts), 'first_target_ms':float(ts.min()*sim.dt) if len(ts) else None, 'median_target_ms':float(np.median(ts)*sim.dt) if len(ts) else None}) return r,targets,rates,actual for oi in range(nc): for si,seed in enumerate(cfg['calibration_seeds']): r,t,rates,actual=trial(oi,seed,'calibration',cfg['windows_ms'],cfg['candidate_US_ms']) if si==0: rr=sim.run(t,rates,actual,duration,collect_events=True);assert rr['finite'] repeats.append({'split':'calibration','odor_index':oi,'seed':seed, 'events_exact':arrhash(r['kc_event_step'],r['kc_event_node'])==arrhash(rr['kc_event_step'],rr['kc_event_node']), 'states_exact':arrhash(r['final_v'],r['final_g'])==arrhash(rr['final_v'],rr['final_g'])}) print(json.dumps({'stage':'calibration','odor_index':oi,'seeds':len(cfg['calibration_seeds'])}),flush=True) grid=[phase4t.gate(records,cfg,w,u) for w in cfg['windows_ms'] for u in cfg['candidate_US_ms']] good=[x for x in grid if x['pass']];chosen=sorted(good,key=lambda x:(-x['score'],x['window_ms'],x['US_ms']))[0] if good else None pd.DataFrame(records).to_parquet(exp/'calibration/grid.parquet',index=False) save(exp/'calibration/selection.json',{'grid':grid,'chosen':chosen,'saved_before_validation':True}) print(json.dumps({'selected':chosen}),flush=True) summary={'phase':'4U','calibration':chosen,'seed_validation':None,'new_odor_validation':None,'timing_gate_pass':False, 'weight_updates':0,'memory_claim':False,'calibration_odors':nc,'new_test_odors':len(panel)-nc,'formal_AB_used':False} if chosen: w=chosen['window_ms'];u=chosen['US_ms'];us=round(u/sim.dt);phase4t.controls(w,u,sim.dt,exp/'regression') for oi in range(len(panel)): split='seed_validation' if oi