Files
digital-drosophila/models/male-cns-lif/mb-credit-transfer-audit-v1/run_audit.py
T
2026-09-30 13:58:39 +08:00

161 lines
9.5 KiB
Python

"""CPU-only read-only baseline and one predeclared conditional sensitivity."""
from concurrent.futures import ProcessPoolExecutor, as_completed
from functools import lru_cache
import time
import traceback
import numpy as np
from context import (ROOT, EXP, AK, CORE, COEFF, CFG, PINS, read, save, digest,
load_module, check, budget, bit_equal, write_npz)
from credit import replay_credit
from mbon_accounting import MBONAccounting
@lru_cache(maxsize=1)
def prepared():
core = load_module('sealed_ah_core_al', CORE, PINS[CORE.relative_to(ROOT).as_posix()])
with np.load(EXP / 'protocol/incoming.npz', allow_pickle=False) as f:
data = {k: f[k].copy() for k in f.files}
incoming = core.IncomingIndex(data['src_global'], data['dst_local'], data['edge_id'], data['weights'], len(data['targets']))
altered = data['weights'].copy()
altered[data['is_candidate']] *= np.float64(1.0 - CFG['conditional_depression_fraction'])
assert bit_equal(data['weights'][~data['is_candidate']], altered[~data['is_candidate']])
assert np.array_equal(np.sign(data['weights']), np.sign(altered))
depressed = core.IncomingIndex(data['src_global'], data['dst_local'], data['edge_id'], altered, len(data['targets']))
for array in data.values():
array.setflags(write=False)
return core, data, incoming, depressed, read(COEFF)
def compare_results(base, changed, base_history, changed_history):
event_equal = all(bit_equal(base[k], changed[k]) for k in ('step', 'neuron_idx'))
count_changes = base['counts'] != changed['counts']
latency_changes = base['first_spike'] != changed['first_spike']
return dict(event_sequence_changed=not event_equal,
spike_count_L1=int(np.abs(base['counts'].astype(np.int64) - changed['counts']).sum()),
neurons_with_count_change=int(count_changes.sum()), neurons_with_first_latency_change=int(latency_changes.sum()),
baseline_spikes=int(base['counts'].sum()), depressed_spikes=int(changed['counts'].sum()),
max_abs_V_difference_SI=float(np.max(np.abs(base_history['history_v'] - changed_history['history_v']))),
max_abs_g_difference_SI=float(np.max(np.abs(base_history['history_g'] - changed_history['history_g']))),
final_V_max_abs_difference_SI=float(np.max(np.abs(base['v'] - changed['v']))),
final_g_max_abs_difference_SI=float(np.max(np.abs(base['g'] - changed['g']))),
functional_count_or_first_latency_change=bool(count_changes.any() or latency_changes.any()),
result_scope='Conditional clamped-upstream replay only; all target threshold/reset/ref recalculated; not whole-brain or learning')
def audit_trial(row, stage):
start = time.perf_counter()
key = row['name'] + '-r' + str(row['repeat'])
out = EXP / 'trials' / key
assert stage in ('baseline', 'conditional25')
if stage == 'baseline':
out.mkdir(exist_ok=False)
else:
assert read(EXP / 'audit/baselines-complete.json')['pass_all'], 'All 144 baseline replays must pass first'
assert out.is_dir() and not (out / 'summary.json').exists()
case = AK / 'cases' / row['cohort'] / row['name']
trial = AK / 'gpu' / row['cohort'] / key
spec = read(case / 'spec.json')
assert spec['steps'] == CFG['steps'] and spec['dt_ms'] == CFG['dt_ms'] and spec['apl_delay_steps'] == 18
assert all(spec[k] == row[k] for k in ('name', 'seed', 'mode', 'odor_id'))
assert read(trial / 'summary.json')['complete'] and read(trial / 'summary.json')['pass_all']
with np.load(trial / 'state.npz', allow_pickle=False) as f:
state = {k: f[k].copy() for k in f.files}
assert set(state) == {'step', 'neuron_idx', 'counts', 'v', 'g', 'ref'}
with np.load(case / 'requests.npz', allow_pickle=False) as f:
requests = {k: f[k].copy() for k in f.files}
core, data, incoming, depressed, coeff = prepared()
targets, steps = data['targets'], spec['steps']
assert not np.isin(requests['neuron_idx'], targets).any(), 'Unexpected MBON injection: do not reinterpret it'
v0 = np.full(len(targets), spec['model']['resting_potential_mv'], np.float64) * np.float64(.001)
g0 = np.zeros(len(targets), np.float64)
events = dict(step=state['step'], neuron_idx=state['neuron_idx'])
if stage == 'baseline':
observer = MBONAccounting(incoming, data['groups'], tuple(CFG['groups']), coeff, steps)
result = core.replay(v0, g0, coeff, steps, targets, requests, events, incoming,
np.zeros((steps, len(targets)), np.float32), observer)
selected = np.isin(state['neuron_idx'], targets)
exact = {k: bit_equal(result[k], state[k][targets]) for k in ('counts', 'v', 'g', 'ref')}
exact.update({k: bit_equal(result[k], state[k][selected]) for k in ('step', 'neuron_idx')})
assert all(exact.values()), 'Baseline MBON divergence: ' + str(exact)
bt, be, bs = observer.finish()
write_npz(out / 'baseline-replay.npz', **result)
write_npz(out / 'baseline-target-accounting.npz', **bt)
pending = np.zeros(len(incoming.edge_id), np.int64)
for tick in range(steps - 18, steps):
due = incoming.due(state['neuron_idx'][state['step'] == tick])
pending[due] += 1
be['pending_after_window_count'] = pending
# Independent source-spike multiplicity covers due plus pending events.
source_counts = np.bincount(state['neuron_idx'], minlength=CFG['neurons'])
assert np.array_equal(be['due_count'] + pending, source_counts[incoming.src_global])
write_npz(out / 'baseline-edge-accounting.npz', **be)
save(out / 'baseline-accounting.json', bs)
trace = replay_credit(state['step'], state['neuron_idx'], data['kc_globals'],
steps=steps, gate_tick=CFG['virtual_gate_tick'], dt_ms=CFG['dt_ms'],
tau_ms=CFG['kc_trace_tau_ms'], cap=CFG['trace_cap'])
arrays = {k: value for k, value in trace.items() if isinstance(value, np.ndarray)}
write_npz(out / 'credit.npz', **arrays)
save(out / 'credit.json', {k: value for k, value in trace.items() if not isinstance(value, np.ndarray)})
report = dict(**row, pass_all=True, baseline_exact=exact, due_plus_pending_source_events_exact=True,
CPU_wall_seconds=time.perf_counter() - start)
save(out / 'baseline-summary.json', report)
return report
with np.load(out / 'baseline-replay.npz', allow_pickle=False) as f:
result = {k: f[k].copy() for k in f.files}
with np.load(out / 'baseline-target-accounting.npz', allow_pickle=False) as f:
bt = {k: f[k].copy() for k in f.files}
baseline_report = read(out / 'baseline-summary.json')
assert baseline_report['pass_all'] and all(baseline_report['baseline_exact'].values())
exact = baseline_report['baseline_exact']
d_observer = MBONAccounting(depressed, data['groups'], tuple(CFG['groups']), coeff, steps)
changed = core.replay(v0, g0, coeff, steps, targets, requests, events, depressed,
np.zeros((steps, len(targets)), np.float32), d_observer)
dt, de, ds = d_observer.finish()
write_npz(out / 'conditional25-replay.npz', **changed)
write_npz(out / 'conditional25-target-accounting.npz', **dt)
write_npz(out / 'conditional25-edge-accounting.npz', **de)
save(out / 'conditional25-accounting.json', ds)
sensitivity = compare_results(result, changed, bt, dt)
save(out / 'sensitivity.json', sensitivity)
report = dict(**row, pass_all=True, baseline_exact=exact, sensitivity=sensitivity,
credit_gate='Virtual tick900 post-update; no DAN injection or plastic update',
biological_duration_ms=spec['duration_ms'], CPU_wall_seconds=time.perf_counter() - start,
baseline_CPU_wall_seconds=baseline_report['CPU_wall_seconds'],
source_sha256={f: digest(trial / f) for f in ('state.npz', 'summary.json')},
conditional_weight_copy_only=True, training=False, candidate_generated=False)
save(out / 'summary.json', report)
save(out / 'output-hashes.json', [dict(path=p.name, bytes=p.stat().st_size, sha256=digest(p))
for p in sorted(out.iterdir()) if p.is_file()])
return report
def main():
reg = check()
assert not any((EXP / 'trials').iterdir()), 'No automatic resume/overwrite of diagnostic outputs'
complete = []
try:
for stage in ('baseline', 'conditional25'):
complete = []
with ProcessPoolExecutor(max_workers=CFG['workers']) as pool:
futures = {pool.submit(audit_trial, row, stage): row for row in reg['planned']}
for task in as_completed(futures):
result = task.result()
complete.append(dict(name=result['name'], repeat=result['repeat'], pass_all=True))
budget()
print(stage, len(complete), result['name'], result['repeat'],
round(result['CPU_wall_seconds'], 3), 'CPU s', flush=True)
assert len(complete) == 144
if stage == 'baseline':
save(EXP / 'audit/baselines-complete.json', dict(pass_all=True, trials=complete, primary=120, repeats=24))
except Exception as error:
save(EXP / 'audit/failure.json', dict(error=type(error).__name__, message=str(error),
traceback=traceback.format_exc(), stage=stage, completed=complete))
raise
assert len(complete) == 144
save(EXP / 'audit/replays-complete.json', dict(pass_all=True, trials=complete, primary=120, repeats=24,
meaning='All exact conditional baseline reconstructions; not an overall biological or learning PASS'))
if __name__ == '__main__':
main()