Files
digital-drosophila/models/male-cns-lif/wholebrain-plastic-interface-v1/comparison.py
T
2026-09-30 16:11:14 +08:00

604 lines
36 KiB
Python

"""Exact, read-only Phase 4AM output comparison; never a learning gate.
The sealed AJ comparator is loaded from verified bytes without modifying its
globals. Full-graph queue identities and refractory acceptance are additionally
reconstructed from actual events, including zero-weight edges. Only declared
performance fields may differ. New AM provenance is checked separately, not
mistaken for the inherited AJ implementation provenance.
"""
from __future__ import annotations
import argparse
import ast
import copy
from functools import lru_cache
import hashlib
import importlib.util
import json
import math
from pathlib import Path
import numpy as np
N = 166700
E = 25582938
K = 4064
CANDIDATES = 14232
STEPS = 1000
AJ_SHA = 'af1975ba5e5e700208c0a598d3cbe648a3cf10885c6231239b6e4b28f76d173b'
HELPER_SHA = '9010f070fba9b7c9cf2d86187ee2abc57f3ac606ebc08a42baad4f3922b51734'
CPP_SHA = '208a937d17bbb041b9bf6b25b89264ee566830b60609e58491b44583d8541f15'
CUDA_SHA = '669f166e18ce9a3f63fc723db8b0a8b74f7fa4757194b1bd65b4cf6008a7787e'
SEALED = {
'ag_gpu': '69f5d893f47dafff4ef7c3186c3df0b9014666b32c8f4e943b57016bec92b18a',
'ag_worker': '21ceec65fe42581dc55872010e0025d660e80358905f1cf4df1dadf341a3b291',
'ae_backend': '03758d07572f4b44d272059d9308fac534268ad201b57bdd77d69fee95e67278',
'af_queue': 'd72003d98f0f1624b12126eff50bd361c43d888f4a64d90ba9247a58976f1961',
}
ADAPTER_PINS = {
'apl-timing-lab-v1/gpu_timing.py': AJ_SHA,
'apl-timing-lab-v1/timing_queue.py': '54df6d4818e2a0c26e96fb0aaf5d9b71cd964981706da937562d48af79aca14e',
'author-apl-lab-v2/gpu_apl.py': SEALED['ag_gpu'],
'author-apl-lab-v2/legacy_apl_worker.py': SEALED['ag_worker'],
'author-runtime-lab-v1/gpu_backend.py': SEALED['ae_backend'],
'author-runtime-lab-v1/author_ext.cpp': CPP_SHA,
'author-runtime-lab-v1/author_ext.cu': CUDA_SHA,
'author-fullgraph-lab-v1/oracle_full.py': SEALED['af_queue'],
}
HOOK_CODE = '_phase4am_install_overlay(weights, graph, apl_out_mask, upload, out_dir, source_provenance)'
PERFORMANCE = frozenset(('wall_seconds', 'diagnostic_simulation_seconds', 'build_or_cache_seconds',
'peak_allocated_vram_bytes', 'peak_reserved_vram_bytes'))
BRIDGE = 'baseSI64 + (delta32 widened to float64) * float64(0.001); nonzero rows only'
SCHEDULE = 'before tick 0; after fast APL zeroing'
GRAPH_FIELDS = ('nodes', 'edge_id', 'src', 'dst', 'ptr', 'signed_raw', 'weights')
_WEIGHT_CACHE = {}
_BASE_RAW_CACHE = {}
def require(condition, message):
if not condition:
raise ValueError(message)
def _fingerprint(path):
path = Path(path).resolve()
stat = path.stat()
require(path.is_file(), 'Not a regular evidence file: ' + str(path))
return str(path), stat.st_dev, stat.st_ino, stat.st_size, stat.st_mtime_ns, stat.st_ctime_ns
def _digest_fingerprint(identity):
# Do not cache byte hashes by stat metadata. Same-size writes can share
# inode, mtime_ns and ctime_ns even on the actual server filesystem.
value = hashlib.sha256()
with Path(identity[0]).open('rb') as source:
for block in iter(lambda: source.read(8 << 20), b''):
value.update(block)
require(_fingerprint(identity[0]) == identity, 'Evidence changed while hashing')
return value.hexdigest()
def file_hash(path):
return _digest_fingerprint(_fingerprint(path))
def raw_hash(array):
return hashlib.sha256(np.ascontiguousarray(array).tobytes()).hexdigest()
@lru_cache(maxsize=2)
def _helper_from_bytes(identity):
require(file_hash(identity[0]) == HELPER_SHA, 'Sealed AJ comparator hash changed')
spec = importlib.util.spec_from_file_location('phase4am_readonly_sealed_aj_compare', identity[0])
module = importlib.util.module_from_spec(spec)
exec(compile(Path(identity[0]).read_bytes(), identity[0], 'exec'), module.__dict__)
return module
def _helper(used):
path = Path(__file__).resolve().parent.parent / 'apl-timing-lab-v1/compare_timing.py'
used.add(path)
sha = file_hash(path)
require(sha == HELPER_SHA, 'Sealed AJ comparator hash changed')
return _helper_from_bytes(_fingerprint(path) + (sha,))
def _json(path, used):
path = Path(path)
used.add(path)
def unique(pairs):
result = {}
for key, value in pairs:
require(key not in result, 'Duplicate JSON key: ' + key)
result[key] = value
return result
return json.loads(path.read_text(encoding='utf-8'), object_pairs_hook=unique,
parse_constant=lambda value: (_ for _ in ()).throw(ValueError('Nonfinite JSON: ' + value)))
def _npz(path, used):
path = Path(path)
used.add(path)
with np.load(path, allow_pickle=False) as data:
require(len(data.files) == len(set(data.files)), 'Duplicate NPZ key')
return {key: data[key].copy() for key in data.files}
@lru_cache(maxsize=2)
def _static_arrays(identities):
arrays = {Path(identity[0]).name: np.load(identity[0], mmap_mode='r', allow_pickle=False)
for identity in identities}
require(all(_fingerprint(identity[0]) == identity[:-1] and file_hash(identity[0]) == identity[-1]
for identity in identities), 'Static inputs changed while loading')
return arrays
def _load_case(case_dir, used):
spec = _json(case_dir / 'spec.json', used)
require(type(spec['steps']) is int and spec['steps'] == STEPS and spec['dt_ms'] == .1 and
spec['duration_ms'] == 100. and spec['record_history'] is False and
type(spec['apl_delay_steps']) is int and spec['apl_delay_steps'] == 18,
'Phase 4AM comparison requires the unchanged full 100-ms D18 case')
model = spec['model']
for key, value in (('synaptic_delay_ms', 1.8), ('refractory_ms', 2.2),
('w_syn_mv_per_anatomical_synapse', .275), ('resting_potential_mv', -52.),
('reset_potential_mv', -52.), ('threshold_mv', -45.)):
require(type(model[key]) in (int, float) and model[key] == value, 'Frozen model field changed: ' + key)
graph_dir, bundle_dir = Path(spec['graph_dir']), Path(spec['apl_bundle_dir'])
require(graph_dir.is_absolute() and bundle_dir.is_absolute(), 'Static input paths must be absolute')
paths = [graph_dir / (key + '.npy') for key in GRAPH_FIELDS]
paths += [bundle_dir / key for key in ('input_matrix.npy', 'output_matrix.npy', 'kc_globals.npy', 'apl_globals.npy')]
used.update(paths)
# Caches for parsed/derived immutable graph data are keyed by FRESH content
# SHA, not merely a filesystem timestamp. Re-read hashes on every call.
token = tuple(_fingerprint(p) + (file_hash(p),) for p in paths)
arrays = _static_arrays(token)
graph = {key: arrays[key + '.npy'] for key in GRAPH_FIELDS}
bundle = {key: arrays[key] for key in ('input_matrix.npy', 'output_matrix.npy', 'kc_globals.npy', 'apl_globals.npy')}
_validate_static(graph, bundle, token)
requests = _npz(case_dir / 'requests.npz', used)
require(set(requests) == {'tick', 'neuron_idx'}, 'Unexpected request arrays')
ticks, nodes = requests['tick'], requests['neuron_idx']
require(ticks.dtype == nodes.dtype == np.int32 and ticks.ndim == 1 and ticks.shape == nodes.shape and
np.all((ticks >= 0) & (ticks < STEPS)) and np.all((nodes >= 0) & (nodes < N)), 'Invalid sparse requests')
keys = ticks.astype(np.int64) * N + nodes
require(np.all(keys[1:] > keys[:-1]), 'Requests must be sorted unique; no silently deduplicated requests')
coeff_path = Path(spec['coefficients_path'])
require(coeff_path.is_absolute(), 'Coefficient path must be absolute')
coeff = _json(coeff_path, used)
for key in ('gd', 'c0', 'cg', 'cv', 'threshold', 'reset', 'seed_volt'):
require(type(coeff[key]) in (int, float) and math.isfinite(coeff[key]), 'Invalid author coefficient: ' + key)
require(coeff['threshold'] == model['threshold_mv'] * .001 and
coeff['reset'] == model['reset_potential_mv'] * .001 and coeff['seed_volt'] > coeff['threshold'],
'Author coefficient units/threshold mismatch')
input_hashes = {'spec.json': file_hash(case_dir / 'spec.json'),
'requests.npz': file_hash(case_dir / 'requests.npz'), 'base_weights.npy': file_hash(graph_dir / 'weights.npy'),
'coefficients.json': file_hash(coeff_path)}
if (case_dir / 'arrays.npz').exists():
used.add(case_dir / 'arrays.npz')
input_hashes['arrays.npz'] = file_hash(case_dir / 'arrays.npz')
return dict(spec=spec, graph=graph, bundle=bundle, token=token, input_hashes=input_hashes,
requested=list(zip(map(int, ticks), map(int, nodes))), graph_dir=graph_dir, bundle_dir=bundle_dir)
_VALIDATED_STATIC = set()
def _validate_static(graph, bundle, token):
if token in _VALIDATED_STATIC:
return
for key, dtype, shape in (('nodes', np.int32, (N,)), ('ptr', np.int64, (N + 1,)),
('edge_id', np.int64, (E,)), ('src', np.int32, (E,)), ('dst', np.int32, (E,)),
('signed_raw', np.int64, (E,)), ('weights', np.float64, (E,))):
require(graph[key].dtype == dtype and graph[key].shape == shape, 'Canonical graph schema changed: ' + key)
require(np.array_equal(graph['nodes'], np.arange(N, dtype=np.int32)), 'Global neuron identity changed')
ptr = graph['ptr']
require(ptr[0] == 0 and ptr[-1] == E and np.all(np.diff(ptr) >= 0), 'Invalid canonical CSR')
for start in range(0, E, 1 << 20):
stop = min(E, start + (1 << 20))
expected_ids = np.arange(start, stop, dtype=np.int64)
require(np.array_equal(graph['edge_id'][start:stop], expected_ids), 'Original edge identity changed')
require(np.array_equal(graph['src'][start:stop], np.searchsorted(ptr[1:], expected_ids, side='right')),
'CSR source order changed')
require(np.all((graph['dst'][start:stop] >= 0) & (graph['dst'][start:stop] < N)), 'Invalid edge targets')
original = graph['weights'][start:stop]
expected = graph['signed_raw'][start:stop] * (.275 * .001)
require(np.isfinite(original).all() and raw_hash(original) == raw_hash(expected), 'Original signed SI weights changed')
for name, dtype, shape in (('kc_globals.npy', np.int32, (K,)), ('apl_globals.npy', np.int32, (2,)),
('input_matrix.npy', np.float32, (2, N)), ('output_matrix.npy', np.float32, (2, K))):
a = bundle[name]
require(a.dtype == dtype and a.shape == shape and np.isfinite(a).all(), 'Invalid APL bundle ' + name)
if dtype == np.int32:
require(np.all((a >= 0) & (a < N)) and np.all(a[1:] > a[:-1]), 'Invalid APL/KC IDs')
else:
require(np.all(a >= 0), 'Negative normalized APL matrix')
require(not np.intersect1d(bundle['kc_globals.npy'], bundle['apl_globals.npy']).size, 'APL/KC overlap')
_VALIDATED_STATIC.add(token)
def _effective_hash(case, ids=None, delta=None):
"""Independent static bridge, cached per immutable graph/delta identity."""
key = (case['token'], None if ids is None else raw_hash(ids), None if delta is None else raw_hash(delta))
if key in _WEIGHT_CACHE:
return _WEIGHT_CACHE[key]
graph, apl = case['graph'], case['bundle']['apl_globals.npy']
baseline, effective = hashlib.sha256(), hashlib.sha256()
masked = 0
for start in range(0, len(graph['edge_id']), 1 << 20):
stop = min(len(graph['edge_id']), start + (1 << 20))
value = graph['weights'][start:stop].copy()
fast = np.isin(graph['src'][start:stop], apl)
masked += int(fast.sum())
value[fast] = np.float64(0)
baseline.update(value.astype('<f8', copy=False).tobytes())
if ids is not None:
lo, hi = np.searchsorted(ids, (start, stop))
ix, change = ids[lo:hi] - start, delta[lo:hi]
require(not fast[ix].any(), 'Plastic mask overlaps fast APL outputs')
nz = change != 0
value[ix[nz]] = np.add(value[ix[nz]], np.multiply(change[nz].astype(np.float64), np.float64(.001)))
require(np.isfinite(value[ix[nz]]).all() and np.all(value[ix[nz]] > 0), 'Static overlay changes candidate sign')
effective.update(value.astype('<f8', copy=False).tobytes())
result = (baseline.hexdigest(), effective.hexdigest(), masked)
_WEIGHT_CACHE[key] = result
return result
def _base_raw_hash(case):
key = case['token']
if key not in _BASE_RAW_CACHE:
h = hashlib.sha256()
values = case['graph']['weights']
for start in range(0, len(values), 1 << 20):
h.update(values[start:start + (1 << 20)].astype('<f8', copy=False).tobytes())
_BASE_RAW_CACHE[key] = h.hexdigest()
return _BASE_RAW_CACHE[key]
def _self_consistency(run, graph, requested, steps, helper, role, checks, differences):
"""Reconstruct every queue hash, request gate, and ref hash from events.
No edge weight, claimed queue digest, or stored PASS determines the result.
Expansion touches only the current delayed spike sources, not all edges.
"""
state, ptr, nodes = run['state'], graph['ptr'], graph['nodes']
n = len(nodes)
local = np.searchsorted(nodes, state['neuron_idx'])
require(np.all(local < n) and np.array_equal(nodes[local], state['neuron_idx']), 'Spike outside graph')
keys = state['step'].astype(np.int64) * N + state['neuron_idx']
require(np.all(keys[1:] > keys[:-1]), 'Duplicate or unordered actual events')
require(np.array_equal(np.bincount(local, minlength=n), state['counts']), 'Event counts do not match per-neuron counts')
require([(r['tick'], r['neuron_idx']) for r in run['requests']] == requested, 'Request input identity differs')
bounds = np.searchsorted(state['step'], np.arange(steps + 1))
rq_bounds = np.searchsorted([r['tick'] for r in run['requests']], np.arange(steps + 1))
last = np.full(n, -1_000_000_000, np.int64)
failures = {}
expected_due_total = expected_accepted_total = 0
for tick in range(steps):
fired = local[bounds[tick]:bounds[tick + 1]]
if np.any(tick - last[fired] < 22):
failures.setdefault('refractory_spike_violation', tick)
for row in run['requests'][rq_bounds[tick]:rq_bounds[tick + 1]]:
target = int(np.searchsorted(nodes, row['neuron_idx']))
require(target < n and nodes[target] == row['neuron_idx'], 'Request outside canonical graph')
accepted = tick - last[target] >= 22
if row['accepted'] != int(accepted) or row['rejected'] != int(not accepted):
failures.setdefault('request_refractory_gate', tick)
if bool(np.any(fired == target)) != bool(accepted):
failures.setdefault('request_actual_spike', tick)
last[fired] = tick
ref = np.maximum(22 - (tick - last), 0).astype(np.int32)
if helper.raw_hash(ref, '<i4') != run['hashes'][tick]['ref_sha256']:
failures.setdefault('all_tick_ref_hash', tick)
sources = local[bounds[tick - 18]:bounds[tick - 17]] if tick >= 18 else np.empty(0, np.int64)
pieces = [np.arange(ptr[source], ptr[source + 1], dtype=np.int64) for source in sources]
rows = np.concatenate(pieces) if pieces else np.empty(0, np.int64)
accepted = (ref[graph['dst'][rows]] == 0).astype(np.uint8)
expected = helper._queue_digest(tick, graph['edge_id'][rows], accepted)
expected_due_total += len(rows)
expected_accepted_total += int(accepted.sum())
if expected != run['queues'][tick]:
failures.setdefault('full_queue_edge_order_and_acceptance', tick)
if not np.array_equal(ref, state['ref']):
failures['final_ref_from_events'] = steps - 1
summary = run['summary']
if summary.get('arriving_edges') != expected_due_total or summary.get('accepted_arriving_edges') != expected_accepted_total:
failures['queue_summary_counts'] = 'mismatch'
checks[role + '.full_event_queue_request_accounting'] = not failures
if failures:
differences[role + '.independent_accounting'] = failures
def _overlay(run, case, used, zero_required=False):
folder = run['directory']
audit = _json(folder / 'overlay-audit.json', used)
require(audit['schema'] == 'phase4am-overlay-audit-v1' and audit['complete'] is True, 'Incomplete overlay audit')
source = audit['input']
inputs = {}
for label in ('overlay', 'manifest', 'mask', 'base_candidate'):
filename, sha = source[label + '_file'], source[label + '_sha256']
if filename is None:
require(label == 'base_candidate' and sha is None, 'Required overlay input absent')
inputs[label] = None
continue
path = Path(filename)
require(path.is_absolute(), 'Overlay provenance input must be absolute')
used.add(path)
require(file_hash(path) == sha, 'Overlay input hash mismatch: ' + label)
inputs[label] = path
manifest = _json(inputs['manifest'], used)
require(manifest['schema'] == 'phase4am-overlay-v1' and manifest['overlay_sha256'] == source['overlay_sha256'] and
manifest['mask_sha256'] == source['mask_sha256'] and Path(manifest['mask_path']).resolve() == inputs['mask'].resolve(),
'Overlay manifest linkage mismatch')
require(manifest['base_candidate_sha256'] == source['base_candidate_sha256'] and
(manifest['base_candidate_path'] is None if inputs['base_candidate'] is None else
Path(manifest['base_candidate_path']).resolve() == inputs['base_candidate'].resolve()), 'Candidate floor provenance mismatch')
require(Path(manifest['aj_model_dir']).resolve() == Path(__file__).resolve().parent.parent / 'apl-timing-lab-v1',
'Overlay is not bound to the sealed AJ model')
require(audit['copied_overlay_file'] == 'overlay-input.npz' and audit['copied_manifest_file'] == 'overlay-manifest.json',
'Unexpected copied overlay filenames')
for label, name in (('overlay', 'overlay-input.npz'), ('manifest', 'overlay-manifest.json')):
used.add(folder / name)
require(file_hash(folder / name) == source[label + '_sha256'], 'Copied input mismatch')
arrays = _npz(inputs['overlay'], used)
require(set(arrays) in ({'edge_id', 'plastic_delta_mv'}, {'edge_id', 'plastic_delta_mv', 'base_candidate_mv'}), 'Overlay NPZ schema changed')
ids, delta = arrays['edge_id'], arrays['plastic_delta_mv']
require(ids.dtype == np.int64 and ids.shape == (CANDIDATES,) and np.all(ids[1:] > ids[:-1]) and
np.all((ids >= 0) & (ids < len(case['graph']['edge_id']))), 'Invalid original plastic edge IDs')
mask = np.load(inputs['mask'], allow_pickle=False)
require(mask.dtype == np.int64 and mask.shape == ids.shape and np.array_equal(mask, ids), 'Frozen plastic mask identity mismatch')
require(delta.dtype == np.float32 and delta.shape == ids.shape and np.isfinite(delta).all() and np.all(delta <= 0), 'Invalid LTD delta')
audit_arrays = dict(arrays)
name = manifest['overlay_name']
require(name in ('zero', 'static25') and audit['overlay_name'] == name, 'Unknown overlay intervention')
if zero_required:
require(name == 'zero', 'Zero-regression cannot use a nonzero intervention')
if name == 'zero':
require(not np.any(delta != 0), 'Zero overlay contains nonzero delta')
else:
require(inputs['base_candidate'] is not None, 'Static25 requires its frozen float32 mV floor')
if inputs['base_candidate'] is not None:
base = np.load(inputs['base_candidate'], allow_pickle=False)
require(base.dtype == np.float32 and base.shape == ids.shape and np.isfinite(base).all() and np.all(base > 0), 'Invalid old float32 base')
floor = np.multiply(base, np.float32(-.25))
require(np.all(delta >= floor), 'Delta exceeds the existing depression bound')
if 'base_candidate_mv' in arrays:
require(arrays['base_candidate_mv'].dtype == np.float32 and raw_hash(arrays['base_candidate_mv']) == raw_hash(base), 'Embedded old base differs')
if name == 'static25':
require(raw_hash(delta) == raw_hash(floor), 'Static25 is not the declared old float32 floor')
audit_arrays['base_candidate_mv'] = base
else:
require('base_candidate_mv' not in arrays, 'Embedded unanchored old base is forbidden')
expected_arrays = {key: dict(dtype=str(value.dtype), shape=list(value.shape), raw_sha256=raw_hash(value))
for key, value in audit_arrays.items()}
require(audit['arrays'] == expected_arrays, 'Overlay array audit differs from actual bytes')
require(np.isin(case['graph']['src'][ids], case['bundle']['kc_globals.npy']).all() and
np.all(case['graph']['weights'][ids] > 0), 'Plastic edge source/sign changed')
baseline_sha, effective_sha, apl_count = _effective_hash(case, ids, delta)
graph = case['graph']
for key, value in {'candidate_count': CANDIDATES, 'nonzero_delta_count': int(np.count_nonzero(delta)),
'mapped_candidate_count': CANDIDATES, 'missing_candidate_count': 0,
'installed_nonzero_count': int(np.count_nonzero(delta)), 'delta_dtype': 'float32', 'delta_units': 'mV',
'state_dtype': 'float64', 'bridge': BRIDGE, 'installation_schedule': SCHEDULE,
'weights_updated_during_run': False, 'learning': False, 'zero_delta_no_write': True,
'frozen_mask_exact': True, 'bounds_pass': True, 'apl_overlap_count': 0,
'apl_fast_edges_zeroed': apl_count, 'base_graph_files_modified': False,
'untouched_weights_bitwise_preserved': True,
'base_weights_file_sha256': file_hash(case['graph_dir'] / 'weights.npy'),
'base_weights_raw_sha256': _base_raw_hash(case),
'effective_weights_raw_sha256': effective_sha, 'expected_effective_weights_raw_sha256': effective_sha}.items():
require(type(audit.get(key)) is type(value) and audit[key] == value, 'Overlay audit mismatch: ' + key)
require(run['summary']['effective_weights_raw_sha256'] == effective_sha, 'Installed effective-weight hash differs from independent bridge')
if zero_required:
require(effective_sha == baseline_sha, 'Zero overlay changed the baseline effective weights')
require(audit['inherited_aj_source_provenance'] == run['summary']['source_provenance'] and
audit['inherited_source_audit_scope'] == 'AJ source provenance only; AM overlay semantics are recorded separately' and
audit['static25_is_exact_fraction_of_float64_base_claimed'] is False and
audit['arrival_weight_sampling'] == 'arrival time; delta fixed for entire trial', 'Overlay semantic scope mismatch')
return audit
def _expected_ast(source_path, adapter_path):
"""Independently derive just the declared Expr insertion; no adapter import."""
tree = ast.parse(Path(source_path).read_bytes())
original = [node for node in tree.body if isinstance(node, ast.FunctionDef) and node.name == 'run_gpu']
require(len(original) == 1, 'Pinned AJ run_gpu not unique')
original = original[0]
adapted = copy.deepcopy(original)
tries = [node for node in adapted.body if isinstance(node, ast.Try)]
require(len(tries) == 1, 'Pinned run_gpu try not unique')
statements = tries[0].body
dump = lambda node: ast.dump(node, annotate_fields=True, include_attributes=False)
anchor = dump(ast.parse('effective_hash = hashlib.sha256()').body[0])
indices = [i for i, node in enumerate(statements) if dump(node) == anchor]
require(len(indices) == 1, 'Pinned hook anchor not unique')
index = indices[0]
previous = ast.parse('if bool(np.any(apl_out_mask)):\n weights[upload(apl_out_mask, torch.bool)] = 0.0').body[0]
require(index > 0 and dump(statements[index - 1]) == dump(previous), 'Hook is not immediately after APL zeroing')
statements.insert(index, ast.parse(HOOK_CODE).body[0])
loops = lambda node: [item for item in ast.walk(node) if isinstance(item, ast.For) and
isinstance(item.target, ast.Name) and item.target.id == 'tick']
before, after = loops(original), loops(adapted)
require(len(before) == len(after) == 1 and dump(before[0]) == dump(after[0]), 'Hook changed loop AST')
sha = lambda node: hashlib.sha256(dump(node).encode('utf-8')).hexdigest()
return dict(original_run_ast_sha256=sha(original), adapted_run_ast_sha256=sha(adapted),
loop_ast_sha256=sha(before[0]), entire_tick_loop_ast_identical=True, only_ast_change=HOOK_CODE,
inserted_ast_statements=1, hook_after_apl_zero_before_effective_hash=True,
derived_namespace_file=str(adapter_path.absolute()), original_module_file=str(Path(source_path).absolute()),
original_module_globals_patched=False, fake_original_file_used=False)
def _adapter_receipt(run, used):
folder = run['directory']
require(not (folder / 'adapter-error.json').exists(), 'Adapter recorded an error')
receipt = _json(folder / 'adapter-provenance.json', used)
require(receipt['schema'] == 'phase4am-adapter-provenance-v1' and receipt['complete'] is True,
'Completed inherited AJ summary is insufficient without completed AM receipt')
model = Path(__file__).resolve().parent
for label, filename in (('adapter', 'adapter.py'), ('overlay_module', 'overlay.py')):
path = Path(receipt[label + '_file'])
require(path.is_absolute() and path.resolve() == model / filename, 'Unexpected new wrapper source path')
used.add(path)
require(file_hash(path) == receipt[label + '_sha256'], 'Wrapper source hash changed')
source_rows = {}
for relative, sha in ADAPTER_PINS.items():
path = model.parent / relative
used.add(path)
require(file_hash(path) == sha, 'Frozen source changed: ' + relative)
source_rows[relative] = {'path': str(path.absolute()), 'sha256': sha}
require(receipt['sealed_sources'] == source_rows, 'Wrapper sealed-source inventory differs')
require(receipt['ast'] == _expected_ast(model.parent / 'apl-timing-lab-v1/gpu_timing.py', model / 'adapter.py'),
'Wrapper AST does not match independently reconstructed single pre-loop hook')
for key, value in dict(installation_calls=1, weights_updated_during_run=False, learning=False, apl_delay_steps=18,
original_module_globals_patched=False, delta_constant_for_entire_trial=True,
inherited_source_audit_describes_AM_semantics=False).items():
require(type(receipt.get(key)) is type(value) and receipt[key] == value, 'Wrapper receipt mismatch: ' + key)
require(receipt['scope'] == 'constant sparse overlay installation only; no within-trial plasticity or learning', 'Wrapper scope changed')
require(receipt['overlay_audit_sha256'] == file_hash(folder / 'overlay-audit.json') and
receipt['inherited_summary_sha256'] == file_hash(folder / 'summary.json') and
receipt['effective_weights_raw_sha256'] == run['summary']['effective_weights_raw_sha256'], 'Wrapper receipt not bound to output')
return {key: value for key, value in receipt.items() if key != 'inherited_summary_sha256'}
def _provenance(run, case, helper, used):
summary, component = run['summary'], run['component']
require(summary['input_sha256'] == case['input_hashes'], 'Actual case/weight/coefficient input hash differs')
require(summary['all_finite'] is True and summary['counts_match_sparse_events'] is True and
summary['learning'] is False and summary['state_units'] == 'SI volts', 'Invalid finite/state/learning declaration')
require(summary['sealed_ae_backend_sha256'] == SEALED['ae_backend'], 'Frozen backend changed')
build = summary['build']
require(build['sources'] == {'author_ext.cpp': CPP_SHA, 'author_ext.cu': CUDA_SHA} and
build['cuda_flags'] == ['-O2', '--fmad=false', '--ftz=false', '--prec-div=true', '--prec-sqrt=true'] and
build['arch_list'] == '8.9', 'Build arithmetic/source contract changed')
source = _json(run['directory'] / 'source-audit.json', used)
require(source == summary['source_provenance'] and source['sealed_source_sha256'] == SEALED and
source['candidate_sha256'] == AJ_SHA and source['module_globals_patched'] is False, 'Inherited AJ source audit changed')
diff = run['directory'] / 'sealed-gpu-source.diff'
used.add(diff)
require(file_hash(diff) == source['source_diff_sha256'], 'Inherited source diff hash mismatch')
bundle = case['bundle']
require(component['files_sha256'] == {name: file_hash(case['bundle_dir'] / name) for name in bundle} and
component['array_sha256'] == {name: raw_hash(value) for name, value in bundle.items()}, 'Actual APL bundle bytes changed')
require(component['ok'] is True and component['protocol'] == 'phase4ag-apl-v1' and
component['n_neurons'] == N and component['n_apl'] == 2 and component['n_kc'] == K and
component['dt_ms'] == .1, 'APL component dimensions/clock changed')
for key, value in (('decay_f32_bits', np.float32(math.exp(-.1 / 20.))), ('gain_f32_bits', np.float32(.01))):
require(component[key] == f'{int(value.view(np.uint32)):08x}', 'APL constant changed: ' + key)
env = component['environment']
require(env['cuda_matmul_allow_tf32'] is False and env['float32_matmul_precision'] == 'highest' and
env['precision_configuration_changed_by_component'] is False and '4090' in env['device_name'],
'GPU precision environment changed')
require(summary['apl_schedule'] == 'end, after threshold/reset and guarded arrivals, unconditional all KC' and
summary['apl_si_conversion'] == 'applied h32 -> float64 -> multiply float64 0.001 -> separate float64 subtraction',
'APL scheduling or SI conversion changed')
for key in PERFORMANCE:
require(type(summary[key]) in (int, float) and math.isfinite(summary[key]) and summary[key] >= 0, 'Invalid performance field: ' + key)
def _summary_pair(left, right, checks, differences):
a = {k: v for k, v in left.items() if k not in PERFORMANCE}
b = {k: v for k, v in right.items() if k not in PERFORMANCE}
checks['summary.nonperformance_exact'] = a == b
if a != b:
differences['summary'] = {key: {'left': a.get(key), 'right': b.get(key)}
for key in sorted(set(a) | set(b)) if a.get(key) != b.get(key) or (key in a) != (key in b)}
def compare_runs(case_dir, left, right, output_path=None, mode='zero'):
"""Zero-vs-sealed-AK or AM repeat equality; failures return a failed report.
``output_path=None`` is read-only. A provided report is created exclusively,
outside both run directories and the input case. Parent protocol must bind
the case and old manifests; this comparator reports exactly the bytes read.
"""
case_dir, left, right = (Path(p).resolve() for p in (case_dir, left, right))
if output_path is not None:
output_path = Path(output_path).resolve()
require(not output_path.exists(), 'Refusing existing comparison output')
require(not any(output_path.is_relative_to(p) for p in (case_dir, left, right)), 'Comparison must not write into input evidence')
checks, differences, used = {}, {}, {Path(__file__).resolve()}
report = dict(schema='phase4am-comparison-v1', mode=mode, case_dir=str(case_dir), left=str(left), right=str(right),
pass_all=False, learning_achieved=False, candidate_generated=False, tolerance=None,
scope='Exact declared trajectory and static overlay integrity; no physiological or learning PASS',
explicit_exclusions=sorted(PERFORMANCE), independent_cpu_wholebrain_oracle_claim=False)
try:
require(mode in ('zero', 'repeat', 'self'), 'Unknown comparison mode')
require((left == right) == (mode == 'self'), 'Only explicit single-run validation may use one directory')
helper = _helper(used)
case = _load_case(case_dir, used)
spec, graph = case['spec'], case['graph']
n, edges, steps = len(graph['nodes']), len(graph['edge_id']), spec['steps']
sides = ((left, 'left_gpu'),) if mode == 'self' else ((left, 'left_gpu'), (right, 'right_gpu'))
runs = [helper._load_run(path, role, n, edges, steps, False, spec['name'], 18, used, checks)
for path, role in sides]
for role, run in zip(('left_gpu', 'right_gpu'), runs):
_provenance(run, case, helper, used)
require(all(np.isfinite(a).all() for a in run['state'].values()), 'Nonfinite final state/events')
_self_consistency(run, graph, case['requested'], steps, helper, role, checks, differences)
helper._timing_sequence_checks(run['apl']['inhibition'], run['apl']['applied'], run['pending'], 18, role, checks, differences)
helper._timing_metadata_checks(run, role, 18, steps, checks)
require(not (run['directory'] / 'first-failure-state.npz').exists(), 'Recorded first numerical failure exists')
if mode != 'self':
helper._pair_outputs(*runs, checks, differences, n, steps, False)
require(runs[0]['timing'] == runs[1]['timing'], 'APL timing metadata differs')
_summary_pair(runs[0]['summary'], runs[1]['summary'], checks, differences)
baseline_sha, _, apl_count = _effective_hash(case)
for run in runs:
require(run['summary']['apl_fast_edges_zeroed'] == apl_count and run['summary']['kc_targets_present'] == K,
'Fast APL or KC coverage changed')
if mode == 'zero':
require(not (left / 'overlay-audit.json').exists(), 'Zero reference must be the unwrapped sealed AK run')
require(runs[0]['summary']['effective_weights_raw_sha256'] == baseline_sha, 'Sealed reference effective weights changed')
audit = _overlay(runs[1], case, used, zero_required=True)
_adapter_receipt(runs[1], used)
report['overlay_name'] = audit['overlay_name']
elif mode == 'repeat':
a, b = [_overlay(run, case, used) for run in runs]
require(a == b, 'Repeated overlay provenance/identity differs')
receipts = [_adapter_receipt(run, used) for run in runs]
require(receipts[0] == receipts[1], 'Repeated wrapper provenance differs beyond bound walltime-bearing summary')
report['overlay_name'] = a['overlay_name']
else:
report['overlay_name'] = _overlay(runs[0], case, used)['overlay_name']
_adapter_receipt(runs[0], used)
report['scope'] = 'Single-run event/queue/input/static-overlay self-consistency; no repeat, equivalence, physiology or learning PASS'
checks['input_overlay_and_source_provenance'] = True
report.update(nodes=n, edges=edges, steps=steps, apl_delay_steps=18,
full_queue_reconstructed=True, total_spikes=len(runs[0]['state']['step']))
except Exception as error:
report['error'] = {'type': type(error).__name__, 'message': str(error)}
manifest = []
try:
for path in sorted(used):
manifest.append(dict(path=str(path.resolve()), bytes=path.stat().st_size, sha256=file_hash(path)))
except Exception as error:
report['read_manifest_error'] = {'type': type(error).__name__, 'message': str(error)}
report.update(checks={key: bool(value) for key, value in checks.items()}, differences=differences,
read_manifest=manifest, failed_checks=sorted(key for key, value in checks.items() if not value))
report['pass_all'] = bool(checks) and all(checks.values()) and 'error' not in report and 'read_manifest_error' not in report
if output_path is not None:
output_path.parent.mkdir(parents=True, exist_ok=True)
with output_path.open('x', encoding='utf-8') as target:
json.dump(report, target, indent=2, sort_keys=True, allow_nan=False)
target.write('\n')
return report
def validate_run(case_dir, output_dir, output_path=None):
"""Single-run static-overlay integrity only; not numerical equivalence."""
return compare_runs(case_dir, output_dir, output_dir, output_path, mode='self')
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--case', required=True)
parser.add_argument('--left', required=True)
parser.add_argument('--right', required=True)
parser.add_argument('--output', required=True)
parser.add_argument('--mode', choices=('zero', 'repeat', 'self'), required=True)
args = parser.parse_args()
result = compare_runs(args.case, args.left, args.right, args.output, args.mode)
print(json.dumps({'pass_all': result['pass_all'], 'failed_checks': result['failed_checks'], 'error': result.get('error')}, allow_nan=False))
raise SystemExit(0 if result['pass_all'] else 1)