"""Event-resolved read-only diagnosis of frozen trials and existing 4Y hypotheses.""" from pathlib import Path import argparse,json,time import numpy as np import pandas as pd import torch,yaml import phase4p,phase4p_finalize,phase4s,phase4v,phase4w,phase4y from phase4p_revalidate import DeterministicAPLSim,door_inputs,make_drive from phase4r import save,digest,arrhash from phase4h_audit import verify_checksum from transmitter_roles_v1 import roles def checks(cfg): old=yaml.safe_load((Path(cfg['prior_model'])/'phase4y.yaml').read_text()) out=phase4y.checks(old) for key in ('prior_model','prior_root','event_experiment'): p=Path(cfg[key]);out['Z_input_'+key]=verify_checksum(p,p/'checksums.sha256') assert all(x['passed'] for x in out.values());return out class Observer(phase4w.Observer): def __init__(self,rt,watch,requests,targets): super().__init__(rt,watch) self.requests=requests;self.targets=np.asarray(targets,np.int32);self.request_rows=[] def force_step(self,*a): step=self.rt.step_index;ii=np.flatnonzero(self.requests[step]) if len(ii): tg=a[4][torch.as_tensor(ii,device=self.rt.device)] x=torch.stack((a[0][tg],self.rt.g[tg],a[1][tg].float(),a[2][tg].float())).cpu().numpy() self.ext.force_step(*a) if len(ii): after=a[2][tg].cpu().numpy() for j,k in enumerate(ii): node=int(self.targets[k]);status='preexisting' if x[3,j] else ('blocked' if x[2,j] else 'accepted') self.request_rows.append({'step':step,'neuron_idx':node,'status':status, 'pre_force_v':float(x[0,j]),'pre_force_g':float(x[1,j]),'pre_force_ref':int(x[2,j]), 'preexisting_spike':bool(x[3,j]),'actual_output_spike':bool(after[j])}) assert bool(after[j])==(status!='blocked') if status=='accepted':self.forced.append((step,node)) def role_codes(meta): x=roles(meta);out=x.role_group.to_numpy(object).copy() for cls,code in [('ALPN','ALPN'),('ALLN','ALLN'),('Kenyon_Cell','KC'),('MBON','MBON'),('DAN','DAN')]: out[meta.annotation_class.eq(cls).to_numpy()]=code out[meta.annotation_type.eq('DPM').to_numpy()]='DPM' out[meta.annotation_type.eq('APL').to_numpy()]='APL' return out def replay(arrival,overlay,requests,rt): f=np.float32;v=f(rt.rest);g=f(0);ref=0;events=[];states=[];accepted=[] for step,a in enumerate(arrival): g=f(f(g*rt.gd)+a);ref=max(ref-1,0) v=f(v+f(rt.alpha*f(g-f(v-rt.rest)))) if ref==0 else f(rt.resetv) fire=ref==0 and v>=rt.threshold if fire:v=f(rt.resetv);ref=rt.ref_steps states.append((v,g,float(fire))) if requests[step] and not fire and ref==0: v=f(rt.resetv);ref=rt.ref_steps;fire=True;accepted.append(step) if fire:events.append(step) g=f(np.float64(g)+overlay[step]) return np.asarray(events,np.int32),np.asarray(states,np.float32),v,g,np.asarray(accepted,np.int32) def summarize_drive(cfg,exp,name,sim,obs,result,edges,codes,request_frame): rt=sim.rt;state=np.asarray(obs.states);T=len(state);ev=obs.events() assert np.array_equal(np.bincount(ev.neuron_idx,minlength=sim.n),result['counts']) arrivals=ev.merge(edges,left_on='neuron_idx',right_on='source_neuron_idx').drop(columns='neuron_idx') arrivals=arrivals.rename(columns={'step':'emission_step'}) arrivals['arrival_step']=arrivals.emission_step+rt.delay_steps arrivals=arrivals[arrivals.arrival_step=round(cfg['late_start_ms']/rt.dt))), 'accepted_requests':len(rr[4]),'max_abs_v_diff':float(np.abs(rr[1][:,0].astype(float)-base[1][:,0]).max())}) for period,lo,hi in [('input_on',0,1200),('post_input',1200,T),('late',5000,T)]: aa=a[(a.arrival_step>=lo)&(a.arrival_step=5000)).sum()),'finite':r['finite'],'wall_seconds':r['wall_seconds']}) if oi==cfg['repeat_odor_index']: again=Observer(rt,watch,ff,targets);again.attach() try:rr=phase4v.observed(sim,targets,forced,targetkc,vc) finally:again.detach() repeat={'events':ev.equals(again.events()),'states':np.array_equal(obs.states,again.states),'requests':rq.equals(pd.DataFrame(again.request_rows))} assert all(repeat.values());regs[-1]['repeat_exact']=repeat save(exp/'regression/trials.json',regs) print(json.dumps({'trial_complete':name,'watched_neurons':len(watch),'incoming_edges':len(ids),'CPU_checks':len(reg)}),flush=True) requests.append(request_comparison(exp,oi,seed,reqpair['frozen_reference'],reqpair['DPM_fast_current_off'])) sim.install=original_install;original_install(None) assert arrhash(sim.base.cpu().numpy())==basehash and np.array_equal(rt.weights.cpu().numpy(),weights0) save(exp/'regression/restore.json',{'base_unchanged':True,'APL_overlay_restored':True,'new_weight_hypotheses':False}) assert phase4s.evidence_regression(cfg,exp,rt,sim) for folder,name,rows in [('audit','trials',trials),('replay','results',replays),('drive','edge-accounting',drives),('ancestry','event-supported-edges',paths),('input-audit','paired-summary',requests)]: pd.DataFrame(rows).to_parquet(exp/folder/f'{name}.parquet',index=False) save(exp/'ancestry/summary.json',pathsum);save(exp/'protocol/input-hashes.json',inputhash) save(exp/'audit/results.json',{'phase':'4Z','status':'COMPLETE-EVENT-RESOLVED-PN-AUDIT','trials':len(trials),'exact_repeat_trials':2, 'Y_reproductions':len(trials),'W_full_event_reproductions':6,'CPU_shadow_replay_checks':sum(len(x['CPU_shadow_replay']) for x in regs), 'reference_pass':True,'all_finite':True,'input_pairs':len(requests),'pairs_with_status_difference':sum(x['status_changed_requests']>0 for x in requests), 'full_brain_loop_necessity_proven':False,'biological_sign_proven':False,'weight_training':False,'candidate':False,'frozen_baseline_changed':False}) save(exp/'performance/summary.json',{'wall_seconds_excluding_build':time.perf_counter()-start,'peak_VRAM_bytes':torch.cuda.max_memory_allocated(), 'GPU':torch.cuda.get_device_name(0),'diagnostic_overhead_included':True}) def describe(cfg,exp): d=pd.read_parquet(exp/'drive/edge-accounting.parquet');rep=pd.read_parquet(exp/'replay/results.parquet') q=pd.read_parquet(exp/'input-audit/paired-summary.parquet');meta=pd.read_parquet(exp/'audit/incoming-source-metadata.parquet') late=d[d.period=='late'].copy();den=late.groupby(['trial','target_neuron_idx']).positive_arrival_sum.transform('sum') late['positive_fraction']=np.where(den>0,late.positive_arrival_sum/den,np.nan) late=late.merge(meta[['neuron_idx','body_id','annotation_type','annotation_class','neurotransmitter','nt_ground_truth']],left_on='source_neuron_idx',right_on='neuron_idx',how='left',validate='many_to_one').drop(columns='neuron_idx') late.to_parquet(exp/'drive/late-input-fractions.parquet',index=False) lines=['# Phase 4Z — PN feedback and input acceptance audit','', 'Diagnostic only. No training, no new sign hypothesis, no candidate. The two branches reproduce sealed Phase4Y reference and DPM-off conditions.', 'Target and three ALLNs were selected from prior data; this is mechanism exploration, not heldout validation.', 'All source fractions are positive arrival-weight accounting, not causal percentages or voltage attribution.','', '## Late positive inputs to KC 82312','', '| condition | source idx | body_id | type | mean fraction |','|---|---:|---:|---|---:|'] z=late[late.target_neuron_idx==cfg['target_KC']].copy();z['condition']=z.trial.str.split('-',n=2).str[-1] for (cond,idx,body,typ),g in z.groupby(['condition','source_neuron_idx','body_id','annotation_type'],dropna=False): if g.positive_arrival_sum.sum()>0:lines.append(f'| {cond} | {idx} | {body} | {typ} | {g.positive_fraction.mean():.6f} |') lines+=['','## Conditional single-cell replay','', 'Other recorded arrivals and measured APL remain fixed; original force requests and refractory logic retained. This does not test whole-brain loop necessity.', '| neuron_idx | condition | mode | late spike range |','|---:|---|---|---:|'] rep['condition']=rep.trial.str.split('-',n=2).str[-1] for (node,cond,mode),g in rep.groupby(['neuron_idx','condition','mode']): if mode=='base' or node==cfg['target_KC'] or (node in cfg['local_loop_nodes'] and mode=='omit_ALLN'): lines.append(f'| {node} | {cond} | {mode} | {g.late_spikes.min()}–{g.late_spikes.max()} |') lines+=['','## Input acceptance','', '| odor | requests | accepted reference | accepted off | status differences | first difference ms |','|---:|---:|---:|---:|---:|---:|'] for r in q.itertuples():lines.append(f'| {r.odor_index} | {r.scheduled_requests} | {r.reference_accepted} | {r.off_accepted} | {r.status_changed_requests} | {r.first_status_difference_ms} |') lines+=['','Identical requests do not establish matched delivered inputs. Refusal and preexisting spikes depend on endogenous state. No forcing clamp or altered refractory rule was introduced.', 'Ancestry edges require actual emission +18 steps <= downstream spike; greedy ranking is not a unique or sufficient causal path. Repeated neuron IDs indicate a time-supported recurrent motif, not proof it is the only sustaining loop.', 'Frozen LIF/APL, raw graph/counts, eligibility and masks unchanged. All detailed state/event checks are in regression/.'] (exp/'REPORT.md').write_text('\n'.join(lines)+'\n',encoding='utf-8') def seal(cfg,exp,model): assert not (exp/'checksums.sha256').exists(),'Already sealed' describe(cfg,exp) now=checks(cfg);hist=phase4p_finalize.integrity();pre=json.loads((exp/'protocol/preregistered.json').read_text()) ok=hist['all_static_checks_passed'] and now==json.loads((exp/'audit/preflight.json').read_text()) and all(digest(model/n)==h for n,h in pre['source_hashes'].items()) save(exp/'audit/frozen-integrity.json',{'pass':ok,'history':hist,'inputs':now});assert ok r=json.loads((exp/'audit/results.json').read_text());r['integrity_pass']=True;save(exp/'audit/results.json',r) for root in (model,exp): rows=[{'path':p.relative_to(root).as_posix(),'sha256':digest(p),'bytes':p.stat().st_size} for p in sorted(root.rglob('*')) if p.is_file() and p.name not in ('manifest.json','checksums.sha256')] save(root/'manifest.json',{'files':rows});rows.append({'path':'manifest.json','sha256':digest(root/'manifest.json')}) (root/'checksums.sha256').write_text(''.join(x['sha256']+' '+x['path']+'\n' for x in rows)) check=verify_checksum(root,root/'checksums.sha256');assert check['passed'];print(json.dumps(check),flush=True) if __name__=='__main__': p=argparse.ArgumentParser();p.add_argument('task',choices=['run','seal']);p.add_argument('--config',required=True);a=p.parse_args() cfg=yaml.safe_load(Path(a.config).read_text());(run if a.task=='run' else seal)(cfg,Path(cfg['experiment_root']),Path(cfg['model_root']))