Validate isolated author LIF semantics without changing frozen models
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
"""Run fixed component arms and independent author oracle, preserving divergences."""
|
||||
import importlib.metadata
|
||||
import json
|
||||
from pathlib import Path
|
||||
import platform
|
||||
import sys
|
||||
import subprocess
|
||||
import time
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from context import CFG,ROOT,EXP,MODEL,AC,digest,save,check_prereg
|
||||
from math_lab import component,author_numpy
|
||||
from oracle_runner import run_brian,PACKAGE_PINS
|
||||
|
||||
|
||||
def save_result(where,result,nodes):
|
||||
where.mkdir(parents=True,exist_ok=False)
|
||||
pd.DataFrame(dict(step=result['step'],neuron_idx=result['neuron_idx'])).to_csv(where/'events.csv',index=False)
|
||||
np.savez_compressed(where/'state.npz',nodes=nodes,**{k:v for k,v in result.items() if k not in ('step','neuron_idx')})
|
||||
|
||||
|
||||
def compare(actual,expected,precision):
|
||||
event=bool(np.array_equal(actual['step'],expected['step']) and np.array_equal(actual['neuron_idx'],expected['neuron_idx']))
|
||||
a=np.asarray(actual['history'],np.float64);b=np.asarray(expected['history'],np.float64)
|
||||
assert a.shape==b.shape
|
||||
errs=np.abs(a[:,:2]-b[:,:2])
|
||||
# Describe numerical prefix separately from post-event-divergence trajectories.
|
||||
first=None
|
||||
for t in range(len(a)):
|
||||
aa=actual['neuron_idx'][actual['step']==t];bb=expected['neuron_idx'][expected['step']==t]
|
||||
if not np.array_equal(aa,bb):first=t;break
|
||||
prefix=errs[:first] if first is not None else errs
|
||||
tol=CFG['comparison'][precision+'_state_max_abs_tolerance_mv']
|
||||
return dict(exact_events=event,exact_counts=bool(np.array_equal(actual['counts'],expected['counts'])),
|
||||
ref_exact=bool(np.array_equal(a[:,2],b[:,2])),max_abs_mv=float(errs.max()),mean_abs_mv=float(errs.mean()),
|
||||
same_event_prefix_max_abs_mv=float(prefix.max()) if prefix.size else None,
|
||||
first_event_divergence_step=first,tolerance_mv=tol,
|
||||
pass_oracle=bool(event and errs.max()<=tol and np.array_equal(a[:,2],b[:,2])))
|
||||
|
||||
|
||||
def oracle_trial(name,nodes,edges,cfg,precision,seed,external=None):
|
||||
io=EXP/'oracle'/(name+'-'+precision)
|
||||
result=run_brian(EXP/'external-resources/model.py',io,nodes,edges,cfg,precision,seed,external)
|
||||
independent=author_numpy(nodes,edges,cfg,precision,seed,external)
|
||||
comparison=compare(result,independent,precision)
|
||||
save(io/'comparison.json',comparison)
|
||||
save_result(io/'brian-output',result,nodes);save_result(io/'independent-output',independent,nodes)
|
||||
assert np.isfinite(result['history']).all() and np.isfinite(independent['history']).all()
|
||||
return result,comparison
|
||||
|
||||
|
||||
def unit_fixtures():
|
||||
rows=[];nodes=np.array([-1],np.int32)
|
||||
# Brian2 2.5.1 calls max/min on connect arrays and rejects zero-length arrays.
|
||||
# An explicitly synthetic zero-weight self-edge is inert, not an anatomical edge.
|
||||
inert=pd.DataFrame(dict(edge_id=[-1],source_neuron_idx=[-1],target_neuron_idx=[-1],raw_count=[0]),dtype='int64')
|
||||
for precision in CFG['author_precisions']:
|
||||
# Synthetic fixture indices are negative and are not MaleCNS body IDs.
|
||||
c=dict(CFG,duration_ms=20.0)
|
||||
cases={}
|
||||
a=np.zeros((200,1));cases['zero']=(a,None,1.0)
|
||||
a=np.zeros((200,1));a[0,0]=1.;cases['weak_excitation']=(a,None,1.0)
|
||||
a=np.zeros((200,1));a[0,0]=-1.;cases['inhibition']=(a,None,1.0)
|
||||
a=np.zeros((200,1));a[[0,21,22,23],0]=.1;cases['refractory_boundary']=(a,-1,1.0)
|
||||
a=np.zeros((200,1));cases['equal_threshold']=(a,-1,0.0)
|
||||
a=np.zeros((200,1));cases['above_threshold']=(a,-1,1.0)
|
||||
a=np.zeros((200,1));a[0,0]=2000.;cases['nonzero_g_reset']=(a,None,1.0)
|
||||
for name,(arr,seed,margin) in cases.items():
|
||||
cfg=dict(c,seed_margin_mv=margin)
|
||||
r,cmp=oracle_trial('unit-'+name,nodes,inert,cfg,precision,seed,arr)
|
||||
h=r['history'];events=r['step'];checks={}
|
||||
if name=='zero':checks={'no_events':len(events)==0,'g_zero':bool(np.all(h[:,1]==0)),
|
||||
'rest_stationary':bool(np.max(np.abs(h[:,0]-CFG['model']['resting_potential_mv']))<1e-3)}
|
||||
elif name in ('weak_excitation','inhibition'):
|
||||
checks={'no_events':len(events)==0,'arrival_at_zero':bool(h[0,1,0]>0 if name=='weak_excitation' else h[0,1,0]<0),
|
||||
'integration_next_step':bool(h[1,0,0]>h[0,0,0] if name=='weak_excitation' else h[1,0,0]<h[0,0,0])}
|
||||
elif name=='refractory_boundary':checks={'one_seed':np.array_equal(events,[0]),'reject_through_21':bool(np.all(h[:22,1]==0)),
|
||||
'accept_at_22':bool(h[22,1,0]>0),'release_at_22':bool(h[21,2,0]==1 and h[22,2,0]==0),'reset_g_at_zero':h[0,1,0]==0}
|
||||
elif name=='equal_threshold':checks={'strict_threshold_no_event':len(events)==0}
|
||||
elif name=='nonzero_g_reset':checks={'natural_spike_at_1':np.array_equal(events,[1]),
|
||||
'nonzero_g_before_spike':bool(h[0,1,0]>1999),'zero_g_after_spike':bool(np.all(h[1:,1,0]==0))}
|
||||
else:checks={'single_seed':np.array_equal(events,[0]),'reset_g':bool(h[0,1,0]==0)}
|
||||
row=dict(fixture=name,precision=precision,checks={k:bool(v) for k,v in checks.items()},comparison=cmp)
|
||||
rows.append(row);save(EXP/'regression'/('unit-'+name+'-'+precision+'.json'),row)
|
||||
print(json.dumps(dict(unit=name,precision=precision,checks=all(checks.values()),oracle=cmp)),flush=True)
|
||||
delay_nodes=np.array([-2,-1],np.int32)
|
||||
weak_edge=pd.DataFrame(dict(edge_id=[-1],source_neuron_idx=[-2],target_neuron_idx=[-1],raw_count=[1]),dtype='int64')
|
||||
r,cmp=oracle_trial('unit-delay_18',delay_nodes,weak_edge,c,precision,-2)
|
||||
h=r['history'];checks=dict(one_seed=np.array_equal(r['step'],[0]),
|
||||
zero_before_arrival=bool(np.all(h[:18,1,1]==0)),arrival_at_18=bool(h[18,1,1]>0),
|
||||
first_voltage_effect_at_19=bool(h[19,0,1]>h[18,0,1]))
|
||||
row=dict(fixture='delay_18',precision=precision,checks=checks,comparison=cmp)
|
||||
rows.append(row);save(EXP/'regression'/('unit-delay_18-'+precision+'.json'),row)
|
||||
save(EXP/'regression/unit-fixtures.json',rows)
|
||||
# Unit oracle failures prevent real author-motif execution; preserve all failed traces.
|
||||
assert all(all(r['checks'].values()) and r['comparison']['pass_oracle'] for r in rows),'Author operator unit gate failed'
|
||||
|
||||
|
||||
def main():
|
||||
check_prereg();assert not (EXP/'audit/run-start.json').exists(),'Refusing overwritten run'
|
||||
assert all(importlib.metadata.version(n)==v for n,v in PACKAGE_PINS.items()),'Dependency-pin mismatch'
|
||||
save(EXP/'environment/installed-packages.json',json.loads(subprocess.check_output([sys.executable,'-m','pip','list','--format=json'],text=True)))
|
||||
save(EXP/'audit/run-start.json',dict(python=sys.version,platform=platform.platform(),
|
||||
packages={n:importlib.metadata.version(n) for n in ('brian2','numpy','pandas','scipy','pyarrow','sympy','cython')},
|
||||
codegen='numpy',learning=False,user_authorized_new_model_hypotheses=True))
|
||||
t0=time.perf_counter();rows=[];validations=[]
|
||||
for name in CFG['motifs']:
|
||||
edges=pd.read_csv(AC/'motifs'/f'{name}-edges.csv').sort_values('edge_id')
|
||||
old=np.load(AC/'motifs'/f'{name}-state.npz');nodes=old['nodes'];seed=None if name=='no_input' else CFG['seed_node']
|
||||
weights=edges.raw_count.to_numpy(np.float32)*np.float32(CFG['model']['w_syn_mv_per_anatomical_synapse'])
|
||||
for policy in CFG['component_policies']:
|
||||
r=component(nodes,edges,weights,CFG,policy,seed)
|
||||
assert np.isfinite(r['history']).all()
|
||||
if policy=='frozen':
|
||||
ev=pd.read_parquet(AC/'motifs'/f'{name}-events.parquet')
|
||||
eq={k:bool(np.array_equal(old[k],r[k])) for k in old.files if k!='nodes'}
|
||||
eq['events']=bool(np.array_equal(ev.step,r['step']) and np.array_equal(ev.neuron_idx,r['neuron_idx']))
|
||||
assert all(eq.values()),eq
|
||||
validations.append(dict(motif=name,frozen_vs_4AC_exact=eq))
|
||||
save_result(EXP/'components'/(name+'-'+policy),r,nodes)
|
||||
late=r['step']>=round(CFG['late_start_ms']/CFG['dt_ms'])
|
||||
for node in nodes:
|
||||
select=r['neuron_idx']==node
|
||||
rows.append(dict(motif=name,policy=policy,precision='float32',neuron_idx=int(node),spikes=int(select.sum()),
|
||||
late_spikes=int((select&late).sum()),last_ms=float(r['step'][select][-1]*CFG['dt_ms']) if select.any() else None))
|
||||
pd.DataFrame(rows).to_csv(EXP/'components/summary.csv',index=False)
|
||||
save(EXP/'regression/frozen-components.json',validations)
|
||||
unit_fixtures()
|
||||
from interface_audit import audit_author_poi
|
||||
audit_author_poi()
|
||||
ors=[]
|
||||
for name in CFG['motifs']:
|
||||
edges=pd.read_csv(AC/'motifs'/f'{name}-edges.csv').sort_values('edge_id')
|
||||
nodes=np.load(AC/'motifs'/f'{name}-state.npz')['nodes'];seed=None if name=='no_input' else CFG['seed_node'];by_precision={}
|
||||
for precision in CFG['author_precisions']:
|
||||
r,cmp=oracle_trial(name,nodes,edges,CFG,precision,seed);by_precision[precision]=r
|
||||
repeat=run_brian(EXP/'external-resources/model.py',EXP/'oracle'/(name+'-'+precision+'-repeat'),nodes,edges,CFG,precision,seed)
|
||||
exact_repeat={k:bool(np.array_equal(r[k],repeat[k])) for k in r}
|
||||
save_result(EXP/'oracle'/(name+'-'+precision+'-repeat')/'brian-output',repeat,nodes)
|
||||
save(EXP/'regression'/(name+'-'+precision+'-repeat.json'),exact_repeat)
|
||||
assert all(exact_repeat.values()),exact_repeat
|
||||
late=r['step']>=round(CFG['late_start_ms']/CFG['dt_ms'])
|
||||
for node in nodes:
|
||||
chosen=r['neuron_idx']==node
|
||||
rows.append(dict(motif=name,policy='author_complete',precision=precision,neuron_idx=int(node),
|
||||
spikes=int(chosen.sum()),late_spikes=int((chosen&late).sum()),last_ms=float(r['step'][chosen][-1]*CFG['dt_ms']) if chosen.any() else None))
|
||||
ors.append(dict(motif=name,precision=precision,comparison=cmp))
|
||||
print(json.dumps(dict(motif=name,precision=precision,events=len(r['step']),late=int(late.sum()),oracle=cmp)),flush=True)
|
||||
save(EXP/'regression'/(name+'-precision-cross-comparison.json'),compare(by_precision['float32'],by_precision['float64'],'float32'))
|
||||
pd.DataFrame(rows).to_csv(EXP/'audit/all-motif-results.csv',index=False)
|
||||
save(EXP/'regression/author-motifs.json',ors)
|
||||
save(EXP/'performance/summary.json',dict(wall_seconds=time.perf_counter()-t0,backend='CPU NumPy + Brian2',not_runtime_benchmark=True))
|
||||
save(EXP/'audit/run-complete.json',dict(all_author_oracle_comparisons_pass=all(x['comparison']['pass_oracle'] for x in ors),
|
||||
all_frozen_components_match_archive=True,learning_achieved=False,whole_brain_alternative_run=False,
|
||||
author_original_source_sha256=digest(EXP/'external-resources/model.py')))
|
||||
|
||||
|
||||
if __name__=='__main__':main()
|
||||
Reference in New Issue
Block a user