85 lines
5.2 KiB
Python
85 lines
5.2 KiB
Python
"""Independent file-level exact gate; no reliance on a stored backend PASS."""
|
|
import argparse
|
|
import csv
|
|
import hashlib
|
|
import json
|
|
import numpy as np
|
|
from context import EXP,CFG,check,save,digest
|
|
|
|
def jsonl(path):
|
|
with path.open() as f: return [json.loads(line) for line in f if line.strip()]
|
|
|
|
def bit_equal(a,b):
|
|
return a.shape==b.shape and a.dtype==b.dtype and np.array_equal(np.ascontiguousarray(a).view(np.uint8),np.ascontiguousarray(b).view(np.uint8))
|
|
|
|
def evaluate(name):
|
|
spec=json.loads((EXP/'cases'/name/'spec.json').read_text());steps=spec['steps'];n=CFG['neurons']
|
|
cpu=EXP/'oracle'/name;gpu=EXP/'gpu'/name;checks={};details={}
|
|
arrays={key:np.load(cpu/filename,mmap_mode='r') for key,filename in (
|
|
('v','v-history.float64.npy'),('g','g-history.float64.npy'),('ref','ref-history.int32.npy'))}
|
|
expected={'v':np.dtype('float64'),'g':np.dtype('float64'),'ref':np.dtype('int32')}
|
|
checks['history_shapes_dtypes']=all(x.shape==(steps,n) and x.dtype==expected[k] for k,x in arrays.items())
|
|
assert checks['history_shapes_dtypes']
|
|
hashes=jsonl(gpu/'tick-state-hashes.jsonl')
|
|
checks['gpu_state_hash_coverage']=len(hashes)==steps and [x['tick'] for x in hashes]==list(range(steps))
|
|
assert checks['gpu_state_hash_coverage']
|
|
failures=[];finite=True
|
|
for tick,row in enumerate(hashes):
|
|
for key,value in arrays.items():
|
|
a=np.ascontiguousarray(value[tick]);sha=hashlib.sha256(a.tobytes()).hexdigest()
|
|
if sha!=row[key+'_sha256']:failures.append(dict(tick=tick,field=key))
|
|
if key!='ref' and not np.isfinite(a).all():finite=False
|
|
checks['all_tick_raw_state_hashes_exact']=not failures;checks['all_oracle_states_finite']=finite
|
|
details['first_hash_mismatches']=failures[:10]
|
|
with np.load(cpu/'state.npz',allow_pickle=False) as a,np.load(gpu/'state.npz',allow_pickle=False) as b:
|
|
for key in ('step','neuron_idx','counts','v','g','ref'):
|
|
checks['exact_'+key]=bit_equal(a[key],b[key])
|
|
checks['event_clock_range']=bool(np.all((a['step']>=0)&(a['step']<steps)))
|
|
checks['event_neuron_range']=bool(np.all((a['neuron_idx']>=0)&(a['neuron_idx']<n)))
|
|
checks['event_sort']=np.array_equal(np.lexsort((a['neuron_idx'],a['step'])),np.arange(len(a['step'])))
|
|
checks['event_unique']=len(np.unique(a['step'].astype(np.int64)*n+a['neuron_idx']))==len(a['step'])
|
|
checks['cpu_event_counts']=np.array_equal(np.bincount(a['neuron_idx'],minlength=n),a['counts'])
|
|
checks['gpu_event_counts']=np.array_equal(np.bincount(b['neuron_idx'],minlength=n),b['counts'])
|
|
for key in arrays:checks['cpu_final_'+key]=bit_equal(arrays[key][-1],a[key])
|
|
details.update(events=len(a['step']),active_neurons=int(np.count_nonzero(a['counts'])),
|
|
max_per_neuron_count=int(a['counts'].max()),
|
|
mean_rate_hz=float(len(a['step'])/(n*spec['duration_ms']/1000)),
|
|
first_event_step=None if not len(a['step']) else int(a['step'][0]))
|
|
ptr=np.load(EXP/'graph/ptr.npy',mmap_mode='r');degrees=np.diff(ptr)
|
|
expected_arrivals=np.zeros(steps,np.int64);due=a['step'].astype(np.int64)+18
|
|
valid=due<steps
|
|
np.add.at(expected_arrivals,due[valid],degrees[a['neuron_idx'][valid]])
|
|
cq,gq=jsonl(cpu/'queue.jsonl'),jsonl(gpu/'queue.jsonl')
|
|
checks['queue_coverage']=len(cq)==len(gq)==steps and [r['tick'] for r in cq]==list(range(steps))
|
|
checks['queue_original_order_exact']=cq==gq
|
|
checks['all_presynaptic_edges_due']=np.array_equal(expected_arrivals,np.asarray([r['count'] for r in cq],np.int64))
|
|
with (cpu/'requests.csv').open(newline='') as f:cr=list(csv.DictReader(f))
|
|
with (gpu/'requests.csv').open(newline='') as f:gr=list(csv.DictReader(f))
|
|
checks['request_rows_exact']=cr==gr
|
|
checks['request_count']=len(cr)==len(spec['seed_nodes'])
|
|
checks['actual_requests_match_protocol']=cr==[
|
|
dict(tick='0',neuron_idx=str(node),attempted='1',accepted='1',rejected='0')
|
|
for node in sorted(spec['seed_nodes'])]
|
|
details['request_rows']=cr
|
|
if cq:
|
|
details['queue_fields']=list(cq[0])
|
|
details['arrival_count']=sum(r['count'] for r in cq)
|
|
details['accepted_arrival_count']=sum(r['accepted_count'] for r in cq)
|
|
summary=json.loads((gpu/'summary.json').read_text())
|
|
checks['backend_summary_pass']=summary['pass_all'] is True
|
|
oracle_summary=json.loads((cpu/'summary.json').read_text())
|
|
checks['oracle_complete']=oracle_summary['complete'] is True and oracle_summary['steps']==steps and oracle_summary['nodes']==n and oracle_summary['edges']==CFG['edges']
|
|
checks={k:bool(v) for k,v in checks.items()}
|
|
files=[cpu/'state.npz',gpu/'state.npz',cpu/'queue.jsonl',gpu/'queue.jsonl',gpu/'tick-state-hashes.jsonl']
|
|
return dict(case=name,pass_all=all(checks.values()),checks=checks,details=details,
|
|
evidence_sha256={str(p.relative_to(EXP)):digest(p) for p in files})
|
|
|
|
def main():
|
|
ap=argparse.ArgumentParser();ap.add_argument('case');a=ap.parse_args();check()
|
|
assert a.case in [t['name'] for t in CFG['trials']]
|
|
result=evaluate(a.case);save(EXP/'audit'/('completed-'+a.case+'.json'),result)
|
|
print(json.dumps(result,ensure_ascii=False),flush=True)
|
|
if not result['pass_all']:raise SystemExit(1)
|
|
|
|
if __name__=='__main__':main()
|