159 lines
10 KiB
Python
159 lines
10 KiB
Python
"""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()
|