diff --git a/models/male-cns-lif/conditioning-execution-v1/PROTOCOL.md b/models/male-cns-lif/conditioning-execution-v1/PROTOCOL.md new file mode 100644 index 0000000..28196df --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/PROTOCOL.md @@ -0,0 +1,178 @@ +# Conditioning execution branch — bounded preparation protocol + +Status: DRAFT, NOT REGISTERED, NO GPU EXECUTION AUTHORITY YET. + +This is a new independent model branch authorized by the user. All previously +registered sources, raw data, frozen brains and experiment artifacts remain +read-only. Existing scientific failures remain failures. The completed AP +interface test does not establish functional learning. + +## Scope and the next scientific decision + +Consolidate updated-weight/same-state continuation, fixed functional gates and +measured complete-process resource costs in this package. Then register and run +the already proposed normal acquisition and all six controls, for all eight +cells and five independent training replicates. Do not keep replacing functional +learning experiments with unbounded engineering milestones. + +No learning-rate, threshold, gain, transmitter, anatomical weight, input-rate, +odor, mask, APL calibration or eligibility-v2 changes are permitted by this +package. Any future biological dynamics hypothesis requires a separately named +model/version and a pre-outcome hypothesis; permission for a new branch does not +reclassify a changed model as an exact optimization of the old one. + +No games, decoder, RL, visual stimulation, web services or 3D are included. + +## Exact boundary semantics + +- A fresh, sealed neural epoch and its actual original E postprocess identify + the one permitted update. Recompute original U from the actual sealed spike + events in a separate CPU interpreter, without executing the brain or E again. +- Install original base plus the full after-delta once, using the frozen + float32-mV to float64-SI bridge. Never add the after-delta to old effective + weights. Keep the original 14,232 existing candidate edges and fixed floor. +- Preserve voltage, existing g, refractory state, pending spike ring, APL state, + complete APL pending amplitudes and absolute tick. Counts/events are explicitly + segment-local in the new recording, not falsely lifetime counts. +- Already accumulated g is not recomputed or rescaled. Pending presynaptic spike + flags use current weights when they arrive, exactly as the frozen runtime + does. Already queued APL amplitudes are preserved. The delays remain 18 steps; + the spike ring has 19 slots and the APL ring has 18. +- Later idle continuations retain the same after-overlay. A probe can fork a + checkpoint but cannot become an idle ancestor. No analytical time advance. +- New checkpoints use their own explicit updated-boundary schema. They do not + claim the original C fixed-weight boundary rule was satisfied. + +The independent oracle re-executes the exact fresh parent prefix, never calls +restore, verifies live prefix state/events against that parent, changes only +the authorized weights once and executes the unchanged reference timestep loop. +Its explicit within-run weight switch is diagnostic, not a production backend. +The one added boundary hook is independently removed to verify exact AST +rollback. Its honest `within_trial_weight_updates=true` admission is not passed +off as an unchanged-weight checkpoint. + +## Bounded validation inventory + +Use sealed AP reference epoch-001 parents for D1, D0 and frozen. New outputs never +go into AP. Compare D1 restored reference/light 100 ms suffixes to a fresh +220 ms continuous oracle. Compare D0/frozen new reference/light suffixes to +original C fixed-weight continuation. Compare all events and final state/queues +exactly; compare per-tick hashes, request acceptance, CSR queue and APL traces +where actually recorded. Do not claim light records unseen intermediate states. + +AP D1 epoch-001 has 765 spikes in its final 18 ticks, but none from its 79 +changed-edge sources. Thus it has useful real pending queues but cannot alone +prove updated-weight handling on a pending changed candidate edge. + +Add one deterministic mechanical diagnostic parent: 120 ms fresh dynamics with +no requests until tick 1199, then one requested spike in the minimum canonical +KC source ID among actual y5B'2a candidates, together with the four verified +PAM15 DAN IDs. Run original U with US at tick 1199, D=1 and the unchanged rule. +This is a test fixture stimulus, not a new odor or a learning-success trial. +No seed search or parameter adjustment follows missing coverage. Require a +real changed existing candidate edge, a real source spike crossing the boundary, +and an accepted timed arrival. Nonempty queues alone are insufficient. Compare +its restored reference/light suffixes to the independent continuous oracle. + +Also run actual no-input 1 s then 9 s idle dynamics from AP D1, preserving one +lineage. At 0, 1 and 10 s fork A/B no-US/no-update 120 ms probes. The two saved +evaluation inputs use the original first block's distinct evaluation streams +from master 20260930, the original L physiological rate tables and [0,100 ms) +CS window; no sampler is run until registration. Repeated probes are not new +training seeds and never feed back to the idle chain. + +The bounded engineering suite does not run acquisition or claim retention of +a learned association. A failure is preserved with partial outputs; no silent +retry, replacement seed or post-result trial selection is allowed. + +Two fixed measurement calls avoid extrapolating incomplete or mismatched costs. +Re-execute AP D1 epoch-001's original saved natural input from a fresh state and +its recorded before-overlay, using light recording followed by original E and +the independent CPU validator. Compare its events, final state and actual delta +to AP. Separately measure one fresh-state/no-US A evaluation probe with the +original zero overlay. Retention-continuation probes are not substituted for +normal fresh-state probe costs. These measurements are engineering diagnostics, +not additional acquisition or a seed search. + +The complete proposed inventory is 23 new brain calls / 125,200 ticks, seven +CPU update/validation children and one GPU sampler process (two uniform batches): +31 child processes. Compilation caches may evolve and are inventoried separately; +previous scientific outputs remain immutable. All future checkpoints/hashes are +derived from actual completed children, not fabricated during registration. + +## Functional gates fixed before acquisition + +`learning_gates.py` retains the frozen normal/delayed predicates, requires five +independent training replicates, checks full 63-bit stream identities and keeps +evaluation streams separate. A majority means at least three replicates each +satisfy the entire conjunction, not different seeds passing different clauses. + +Two explicit additional operationalizations are fixed before outcomes: + +1. Reversal and real-time retention use nonMBONDownstream for the downstream + functional gate, while reporting the entire legacyDownstream union as well. + MBON-local change cannot be counted twice as downstream transfer. +2. The legacy H mean-control predicate is called separately for the PAM15 and + PPL103 control sources in each (input mode, reinforced cue) group. Both must + pass that separate audit. No control pooling or favorable-source selection. + +Reversal keeps the original conditioned-minus-neutral orientation and original +pre baseline, with strict nonzero sign crossing and no-reversal continuation +remaining on its original side. Retention requires the same replicate to pass +both 1 s and 10 s against time-matched frozen controls. Delta change alone is +never a functional learning gate. See the pinned original PROTOCOL_DRAFT and +EVALUATION_POLICY_DRAFT for the complete unchanged input/assay inventory. + +Pure gate functions authenticate neither disk/process evidence nor anatomy; +their results explicitly say conditional predicates, not scientific PASS. +The eventual coordinator must bind completed real trajectories and input, +population, seed, delta and checkpoint receipts before making a learning claim. + +## Resource accounting and stop policy + +Proposed bounded validation cap: 4 GiB of new outputs, at least 10 GiB free +reserve, 1,800 seconds per child and 7,200 seconds total. Confirm measured free +space and commit the registration before execution. Use only the admitted +RTX 4090, check all three ComfyUI queues and do not stop or evict services. + +Measure parent wall time from before spawn through actual exit and independent +result validation, including input hashing, CUDA startup/compilation, graph load, +CPU update verification, lossless compression and I/O. Worker/core timing is a +separate field. No extrapolation may label GPU-only time as complete latency. + +The proposed normal suite has 30,000 training/control epochs plus 3,920 probes: +33,920 brain calls and 4,070.4 biological seconds. The full proposal has 68,000 +epochs, 9,440 probes and 80 actual 10 s idle lineages, totaling 11,892.8 biological +seconds before engineering validation. These are inventory arithmetic, not wall +time or disk measurements. Predeclare a finite normal-run resource budget using +measured complete outputs, a stated reserve and explicit uncertainty about +learning-dependent activity. No unbounded automatic launch is authorized here. + +If normal acquisition fails the registered all-cell gate, preserve and seal the +actual failure; delayed/reversal/retention are NOT-RUN. Do not tune to rescue it. +If it passes, run the already registered follow-up matrix. Frozen integrity and +required numerical regressions remain independent gates, not learning evidence. + +## Provenance + +Before registration require every new source to match its recorded Git commit, +snapshot the protocol and sources, and pin all old inputs by SHA-256. Record +actual child exits; worker self-receipts do not prove their own process exited. +All output files receive SHA-256 and an exact manifest at sealing. No old source +or runtime global is patched. Full experimental arrays remain server-side; +source/evidence Git synchronization must not be described as a bulk-data backup. + +`audit_execution.py` reads actual recorded process exits and full output hashes; +`prepare_execution.py integrity` runs in a separate CPU process after execution. +`finalize_execution.py` requires externally supplied registration, numerical, +budget and after-integrity report hashes and creates an exclusive experiment +seal. The entire model directory is not itself claimed sealed: every registered +source is bound to its committed blob and sealed source snapshot instead. + +`budget_report.py` may return insufficient information rather than invent future +large-suite overhead. A few measured cycles cannot establish the cost of a +growing full-directory hash policy, unknown learning-dependent activity or final +integrity work. No normal-suite resource assumptions or execution authorization +are implicitly supplied by this finite engineering registration. Real measured +cycle cost and conservative accounting bounds must still be reported even when +a credible reserved complete-suite projection is unavailable. diff --git a/models/male-cns-lif/conditioning-execution-v1/audit_execution.py b/models/male-cns-lif/conditioning-execution-v1/audit_execution.py new file mode 100644 index 0000000..c5c8318 --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/audit_execution.py @@ -0,0 +1,491 @@ +"""Read-only, process-bound finite engineering audit; never a learning gate. + +Only the optional final JSON is written, exclusively. The recorded successful +parent-process exits are authenticated file evidence, not re-observed exits. +Old integrity/sealing is deliberately a separate process. No E, U or Torch is +imported here; all checkpoint loading shares updated_continuation.C.R. +""" +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import sys +from types import ModuleType + +import numpy as np + +import boundary_compare as B +import boundary_plan as PLAN +import execution_authority as A +import isolated_execution as W +import updated_continuation as D +from prepare_execution import EXP, MODEL, LAB, save + +N, EDGES = 166700, 25582938 +require, read, digest = A.require, A.read, A.digest + + +def canonical(value): + return json.dumps(value, sort_keys=True, separators=(',', ':'), ensure_ascii=False, allow_nan=False) + + +def same_json(left, right): + return canonical(left) == canonical(right) + + +def verify_pins(pins): + require(type(pins) is dict and pins, 'Nonempty input authority required') + for path, sha in pins.items(): + require(A.pin(path)['sha256'] == sha, 'Pinned file changed: ' + str(path)) + + +def tree(directory): + root = Path(directory) + require(root.is_dir(), 'Missing output tree') + result = {} + for p in sorted(root.rglob('*')): + require(not any(q.is_symlink() or (hasattr(q, 'is_junction') and q.is_junction()) + for q in (p, *p.parents)), 'Linked evidence forbidden') + if p.is_file(): + result[p.relative_to(root).as_posix()] = dict(sha256=digest(p), bytes=p.stat().st_size) + else: + require(p.is_dir(), 'Nonregular evidence forbidden') + require(result, 'Empty output tree') + return result + + +def _helpers(): + raw = W.AP.read_bytes() + require(hashlib.sha256(raw).hexdigest() == W.AP_SHA, 'Frozen worker helper changed') + module = ModuleType('_conditioning_execution_audit_pure_worker_helpers') + module.__file__ = str(W.AP) + exec(compile(raw, str(W.AP), 'exec'), module.__dict__) + return module + + +def expected_processes(plan): + calls = {row['call_id']: row for row in plan['calls']} + require(len(calls) == len(plan['calls']), 'Duplicate call') + kinds = {name: ('brain' if row['runner'] in ('runtime_light', 'fresh_probe') else + 'oracle' if row['runner'] == 'boundary_oracle' else + 'unchanged' if row['runner'] == 'runtime_continuation' else 'updated') + for name, row in calls.items()} + for arm in ('D1', 'D0', 'frozen'): + kinds['AP-' + arm + '--cpu-verify'] = 'validate_cpu' + for name in ('mechanical--fresh-parent', 'performance--natural-light-fresh'): + kinds[name + '--original-E'] = 'update' + kinds[name + '--cpu-verify'] = 'validate_cpu' + kinds['evaluation-inputs'] = 'sample' + require(len(kinds) == plan['counts']['total_children'], 'Planned child inventory differs') + return kinds + + +def _closure(reg, reg_sha): + return {**reg['source_sha256'], **reg['input_sha256'], **reg['prepared_file_sha256'], + str(EXP / 'protocol/preregistered.json'): reg_sha} + + +def verify_process(name, kind, reg, reg_sha, process_path, helper): + """Recheck actual bytes; never accept worker pass flags as a seal.""" + paths = {key: EXP / f'{sub}/{name}{suffix}' for key, sub, suffix in ( + ('job', 'jobs', '.json'), ('worker', 'receipts', '.worker.json'), + ('process', 'receipts', '.process.json'), ('stdout', 'logs', '.stdout.log'), + ('stderr', 'logs', '.stderr.log'))} + require(Path(process_path) == paths['process'], 'Wrong fixed process record path') + job, receipt, process = (read(paths[k]) for k in ('job', 'worker', 'process')) + ap = kind in ('brain', 'update') + script = W.AP if ap else MODEL / 'isolated_execution.py' + worker_sha = W.AP_SHA if ap else reg['source_sha256'][str(script)] + schema = 'phase4ap-isolated-worker-job-v1' if ap else W.SCHEMA + require(job['schema'] == schema and job['job_id'] == name and job['kind'] == kind and + job['registration_sha256'] == reg_sha and job['worker_sha256'] == worker_sha, + 'Actual job identity differs') + require(process['schema'] == 'conditioning-execution-process-v1' and process['complete'] is True and + process['exit_code'] == 0 and type(process['exit_code']) is int and process['timed_out'] is False and + process['name'] == name and process['kind'] == kind and process['registration_sha256'] == reg_sha, + 'Missing successful recorded process exit') + job_sha = digest(paths['job']) + require(process['job_sha256'] == job_sha and process['worker_receipt_sha256'] == digest(paths['worker']) and + all(process[k + '_sha256'] == digest(paths[k]) for k in ('stdout', 'stderr')), 'Process file chain changed') + argv = process['argv'] + require(type(argv) is list and len(argv) == (8 if ap else 7) and type(argv[0]) is str and bool(argv[0]), + 'Recorded process command is incomplete') + require(argv[1:] == ['-B', str(script), *([kind] if ap else []), '--job', str(paths['job']), + '--job-sha256', job_sha], 'Recorded command differs from pinned worker/job') + expected_schema = 'phase4ap-isolated-worker-receipt-v1' if ap else 'conditioning-isolated-execution-receipt-v1' + require(receipt['schema'] == expected_schema and receipt['complete'] is True and + all(receipt[k] == job[k] for k in ('job_id', 'kind', 'registration_sha256', 'worker_sha256')) and + receipt['job_sha256'] == job_sha and receipt['process_exit_independently_observed'] is False, + 'Worker receipt identity/scope differs') + closure = _closure(reg, reg_sha) + require(all(job['input_sha256'].get(p) == s for p, s in closure.items()), 'Registered input closure omitted') + inputs = dict(closure) + if kind == 'brain': + roles = ('runtime_light', 'runtime_continuation') + case = Path(job['case_dir']) + extra = [case / 'spec.json', case / 'requests.npz', Path(job['overlay_file']), Path(job['overlay_manifest'])] + inputs.update({str(p): digest(p) for p in extra}) + else: + authority = Path(job['authority_file']) + require(digest(authority) == job['authority_sha256'], 'Worker authority changed') + inputs[str(authority)] = job['authority_sha256'] + value = read(authority) + if kind == 'sample': + require(authority == EXP / 'protocol/preregistered.json' and job['authority_sha256'] == reg_sha, + 'Sampler is not bound to actual registration') + else: + inputs.update(A.files_from_authority(value)) + if kind == 'validate_cpu': + update = Path(job['update_dir']) + require({p.name for p in update.iterdir()} == set(D.UPDATE_FILES), 'Update must contain exact four files') + inputs.update({str(p): digest(p) for p in update.iterdir()}) + require(job['chain_sha256'] == digest(update / 'chain-receipt.json'), 'CPU chain pin differs') + if kind == 'oracle': + case = Path(job['oracle_case']) + for filename, key in (('spec.json', 'oracle_spec_sha256'), ('requests.npz', 'oracle_requests_sha256')): + require(digest(case / filename) == job[key], 'Live oracle case pin differs') + inputs[str(case / filename)] = job[key] + roles = ('conditioning_epoch',) if kind == 'update' else () + require(job['input_sha256'] == inputs, 'Actual job has missing/undeclared input authority') + pins = {**inputs, str(paths['job']): job_sha, str(script): worker_sha} + if ap: + expected_sources = {n: dict(path=str(LAB / (n + '.py')), sha256=helper.SOURCE_PINS[n]) for n in roles} + require(job['sources'] == expected_sources and receipt['source_files'] == expected_sources, 'Frozen process source differs') + pins.update({v['path']: v['sha256'] for v in expected_sources.values()}) + else: + require(set(job['source_sha256']) == W.SOURCES[kind], 'New worker source inventory differs') + for filename, sha in job['source_sha256'].items(): + require(reg['source_sha256'][str(MODEL / filename)] == sha, 'New source not registered') + pins[str(MODEL / filename)] = sha + pins[str(W.AP)] = W.AP_SHA + require(receipt['verified_input_sha256' if ap else 'input_sha256'] == pins, 'Worker did not check the complete actual closure') + verify_pins(pins) + if kind == 'unchanged': + W.bind_unchanged_authority(helper, job, dict(authority=Path(job['authority_file']), + checkpoint=Path(job['checkpoint_dir']), case=Path(job['case_dir']), + overlay_file=Path(job['overlay_file']), overlay_manifest=Path(job['overlay_manifest'])), + {Path(p): sha for p, sha in pins.items()}) + expected_output = EXP / ('updates' if kind == 'update' else 'runs') / name.removesuffix('--original-E') + require(Path(job['output_dir']) == expected_output and receipt['output_dir'] == str(expected_output) and + job['receipt_file'] == str(paths['worker']), 'Output or receipt escaped fixed inventory') + outputs = tree(expected_output) + hashes = {k: v['sha256'] for k, v in outputs.items()} + require(hashes == receipt['output_files_sha256'] and process['output_bytes'] == sum(v['bytes'] for v in outputs.values()) and + process['output_inventory_sha256'] == hashlib.sha256(json.dumps(hashes, sort_keys=True).encode()).hexdigest(), + 'Full output inventory changed') + attempt = Path(str(paths['worker']) + '.attempt.json') + require(digest(attempt) == receipt['attempt_sha256'], 'Attempt record changed') + av = read(attempt) + require(av['complete'] is False and av['job_sha256'] == job_sha and av['no_automatic_retry'] is True, + 'Attempt record does not bind actual invocation') + require(receipt.get('scientific_learning_pass_claimed', receipt.get('scientific_gate_pass_claimed')) is False, + 'Worker claims a scientific gate') + for key in ('child_wall_seconds', 'end_to_end_seconds', 'validation_wall_seconds'): + require(type(process[key]) in (float, int) and np.isfinite(process[key]) and process[key] >= 0, 'Invalid recorded timing') + evidence = {str(p): digest(p) for p in (*paths.values(), attempt)} + return dict(name=name, kind=kind, paths=paths, job=job, receipt=receipt, process=process, + output=expected_output, output_inventory=outputs, evidence_sha256=evidence, input_pins=pins) + + +def _npz(path): + with np.load(path, allow_pickle=False) as archive: + return {name: archive[name].copy() for name in archive.files} + + +def _pair(left, right): + B._complete(left); B._complete(right) + checks, observed = B.C._pair_checks(left, right) + exact = all(checks.values()) + return dict(numerical_exact=exact, pass_all=exact, checks=checks, + failed_checks=[k for k, v in checks.items() if not v], observed=observed, + light_intermediate_states_observed=False, learning_pass_claimed=False) + + +def _parent(reg, complete, role): + record = complete['parents'][role] + if role in reg['parent_records']: + original = reg['parent_records'][role] + require(all(same_json(record[k], v) for k, v in original.items()), 'Imported parent declaration changed') + return record + + +def _update_check(parent, verified, cpu_name): + """Bind actual CPU recomputation process to original E four-file evidence.""" + cpu = verified[cpu_name] + require(cpu['kind'] == 'validate_cpu', 'Missing independent CPU update verification process') + directory = Path(parent['update_dir']); hashes = {n: digest(directory / n) for n in D.UPDATE_FILES} + require(set(p.name for p in directory.iterdir()) == set(D.UPDATE_FILES), 'Unexpected update inventory') + require(parent['after_overlay'] == dict(overlay_file=str(directory / 'delta-after.npz'), + overlay_manifest=str(directory / 'overlay-manifest.json')), 'After snapshot paths leave actual E update') + authority = parent['epoch_authority']; epoch = read(authority['path']) + require(A.pin(authority['path']) == authority and cpu['job']['authority_file'] == authority['path'] and + cpu['job']['authority_sha256'] == authority['sha256'] and Path(cpu['job']['update_dir']) == directory and + cpu['job']['chain_sha256'] == hashes['chain-receipt.json'], 'CPU verification points to another update') + chain, update = (read(directory / name) for name in ('chain-receipt.json', 'update-receipt.json')) + report = read(cpu['output'] / 'validation.json') + require(same_json(report, cpu['receipt']['result']) and report['schema'] == 'conditioning-update-cpu-validation-v1' and + report['complete'] is True and report['epoch_authority_sha256'] == authority['sha256'] and + report['parent_checkpoint_manifest_sha256'] == parent['checkpoint_sha256'] and + report['update_files_sha256'] == hashes and report['frozen_U_verify_receipt_recomputed'] is True and + report['actual_sealed_events_used'] is True and report['full_candidate_mapping_verified'] is True and + report['brain_executed'] is False and report['learning_pass_claimed'] is False and + report['frozen_U_source_sha256'] == D.SOURCE_PINS['conditioning_update_source'] and + report['validator_source'] == A.pin(MODEL / 'validate_update_cpu.py'), 'CPU recomputation receipt mismatch') + require(chain.get('schema') == 'phase4an-epoch-postprocess-chain-v1' and chain['complete'] is True and + all(chain[k] is False for k in ('weights_installed', 'brain_executed', 'learning_pass_claimed', 'candidate_generated')) and + chain['authority_sha256'] == authority['sha256'] and chain['source_neural_checkpoint_dir'] == parent['directory'] and + chain['source_neural_checkpoint_manifest_sha256'] == parent['checkpoint_sha256'] and + chain['output_files_sha256'] == {k: v for k, v in hashes.items() if k != 'chain-receipt.json'} and + chain['update_receipt_payload_sha256'] == report['update_receipt_payload_sha256'] == update['payload_sha256'], + 'Original E chain no longer binds the same update') + before = _npz(parent['before_overlay']['overlay_file']); after = _npz(parent['after_overlay']['overlay_file']) + require(set(before) == set(after) == {'edge_id', 'plastic_delta_mv'} and + B.bit_equal(before['edge_id'], after['edge_id']), 'Overlay identity changed') + require(report['before_overlay_sha256'] == digest(parent['before_overlay']['overlay_file']) and + report['before_manifest_sha256'] == digest(parent['before_overlay']['overlay_manifest']) and + report['after_overlay_sha256'] == hashes['delta-after.npz'] and + report['after_manifest_sha256'] == hashes['overlay-manifest.json'] and + report['before_delta_raw_sha256'] == B._raw(before['plastic_delta_mv']) and + report['after_delta_raw_sha256'] == B._raw(after['plastic_delta_mv']), 'Actual delta differs from CPU verification') + require(update['payload_sha256'] == hashlib.sha256(canonical({k: v for k, v in update.items() if k != 'payload_sha256'}).encode()).hexdigest(), + 'Update receipt payload changed') + require(epoch['checkpoint_dir'] == parent['directory'] and epoch['checkpoint_manifest_sha256'] == parent['checkpoint_sha256'], + 'Update is not from actual neural parent') + return dict(pass_all=True, CPU_process=cpu_name, update_files_sha256=hashes, + before_delta_raw_sha256=report['before_delta_raw_sha256'], after_delta_raw_sha256=report['after_delta_raw_sha256'], + U_receipt_payload_sha256=update['payload_sha256'], U_recomputed_in_this_audit=False, + U_recomputation_from_authenticated_separate_CPU_process=True) + + +def outcome(checks, pending): + """Numerical failure and missing nonvacuous coverage are not equivalent.""" + require(type(checks) is dict and bool(checks) and all(type(v) is bool for v in checks.values()), + 'Explicit nonempty numerical/lineage checks required') + require(set(pending) == {'D1', 'mechanical'}, 'Both predetermined pending-edge audits required') + exact = all(checks.values()) + coverage = pending['mechanical']['accepted_changed_edge_witness_verified'] is True + status = 'FAIL-NUMERICAL-OR-LINEAGE' if not exact else ('PASS-EXACT-UPDATED-BOUNDARY-ENGINEERING-NOT-LEARNING' + if coverage else 'INCOMPLETE-CHANGED-EDGE-COVERAGE') + return exact, coverage, status + + +def _pending(parent, child, parent_descriptor): + epoch = read(parent_descriptor['epoch_authority']['path']); spec = read(epoch['files']['spec']['path']) + graph = Path(spec['graph_dir']); apl_dir = Path(spec['apl_bundle_dir']); arrays = {} + try: + for name in ('ptr', 'src', 'dst', 'edge_id', 'weights'): + arrays[name] = np.load(graph / (name + '.npy'), mmap_mode='r', allow_pickle=False) + apl = np.load(apl_dir / 'apl_globals.npy', allow_pickle=False) + before = _npz(parent_descriptor['before_overlay']['overlay_file']) + after = _npz(parent_descriptor['after_overlay']['overlay_file']) + require(B.bit_equal(before['edge_id'], after['edge_id']), 'Changed witness candidate mask differs') + paths = [graph / (n + '.npy') for n in arrays] + [apl_dir / 'apl_globals.npy', + Path(parent_descriptor['before_overlay']['overlay_file']), Path(parent_descriptor['after_overlay']['overlay_file'])] + return B.audit_changed_pending(parent, child, ptr=arrays['ptr'], src=arrays['src'], dst=arrays['dst'], + edge_id=arrays['edge_id'], base_weights=arrays['weights'], apl_mask=np.isin(arrays['src'], apl), + candidate_edge_id=before['edge_id'], delta_before=before['plastic_delta_mv'], + delta_after=after['plastic_delta_mv'], input_pins={str(p): digest(p) for p in paths}) + finally: + for value in arrays.values(): + value._mmap.close() + + +def _lineage(reg, complete, verified, records): + """Actual advancing idle chain plus non-feedback probes, not memory success.""" + checks = {}; probe_rows = [] + base = reg['parent_records'][reg['plan']['imported_parents']['D1']['role']] + delta = {k: digest(v) for k, v in base['after_overlay'].items()} + sampler = verified['evaluation-inputs']; sr = read(sampler['output'] / 'sampler-receipt.json') + require(same_json(sr, sampler['receipt']['result']) and sr['complete'] is True and sr['registration_sha256'] == sampler['job']['registration_sha256'] and + sr['uniform_batch_calls'] == 2 and sr['US_added'] is False and sr['brain_executed'] is False, + 'Sampler receipt changed or wrong scope') + for cue in ('A', 'B'): + sample = sampler['output'] / (cue + '-requests.npz') + rows = [r for r in sr['rows'] if r['cue'] == cue] + require(len(rows) == 1 and rows[0]['saved_requests_sha256'] == digest(sample), 'Sampler cue binding differs') + req = _npz(sample); rates = _npz(EXP / f'inputs/static/{cue}-rates.npz') + require(set(req) == {'tick', 'neuron_idx'} and req['tick'].dtype == req['neuron_idx'].dtype == np.int32 and + req['tick'].shape == req['neuron_idx'].shape and np.all((req['tick'] >= 0) & (req['tick'] < 1000)) and + np.isin(req['neuron_idx'], rates['targets']).all(), 'Probe contains non-CS or out-of-window input') + require(rows[0]['stream']['seed'] == reg['plan']['evaluation_streams'][cue]['seed'] and + rows[0]['stream']['semantic_key'] == reg['plan']['evaluation_streams'][cue]['key'] and + rows[0]['stream']['split'] == 'evaluation', 'Probe seed semantic binding differs') + for label in ('t0', 't1', 't10'): + name = f'D1--probe--{label}--{cue}'; call = next(c for c in reg['plan']['calls'] if c['call_id'] == name) + job = verified[name]['job']; auth = read(job['authority_file']) + case = Path(auth['case']['requests']['path']) + parent = _parent(reg, complete, call['parent']) + checks[name + '.input_bytes'] = digest(case) == digest(sample) + checks[name + '.parent_clock'] = auth['parent'] == dict(directory=parent['directory'], checkpoint_manifest_sha256=parent['checkpoint_sha256']) and auth['global_tick_offset'] == call['global_tick_offset'] + checks[name + '.fixed_delta'] = auth['overlay_after']['file']['sha256'] == delta['overlay_file'] and auth['overlay_after']['manifest']['sha256'] == delta['overlay_manifest'] + checks[name + '.no_update_US_backflow'] = call['US'] is call['plastic_update'] is call['feedback_to_idle'] is False and not records[name]['summary']['within_trial_weight_updates'] + probe_rows.append(dict(call_id=name, global_tick_offset=records[name]['global_tick_offset'], + actual_CS_requests=len(req['tick']), actual_US_requests=0, inputs_sha256=digest(case), + complete_response_metrics_or_retention_gate=False)) + for name in ('D1--idle--1s', 'D1--idle--9s'): + call = next(c for c in reg['plan']['calls'] if c['call_id'] == name) + auth = read(verified[name]['job']['authority_file']); parent = _parent(reg, complete, call['parent']) + req = _npz(auth['case']['requests']['path']) + checks[name + '.actual_duration'] = records[name]['steps'] == call['steps'] + checks[name + '.parent_clock'] = auth['parent'] == dict(directory=parent['directory'], checkpoint_manifest_sha256=parent['checkpoint_sha256']) and auth['global_tick_offset'] == call['global_tick_offset'] + checks[name + '.empty_input'] = len(req['tick']) == len(req['neuron_idx']) == 0 + checks[name + '.fixed_delta'] = auth['overlay_after']['file']['sha256'] == delta['overlay_file'] and auth['overlay_after']['manifest']['sha256'] == delta['overlay_manifest'] + checks['no_probe_feeds_other_segment'] = all('--probe--' not in str(c.get('parent')) for c in reg['plan']['calls']) + checks['ten_seconds_actually_advanced'] = records['D1--idle--1s']['steps'] + records['D1--idle--9s']['steps'] == 100000 and records['D1--idle--9s']['summary']['global_next_tick'] == 101200 + perf = verified['performance--evaluation-A-light-fresh']['job'] + checks['normal_probe_performance_actual_A_bytes'] = digest(Path(perf['case_dir']) / 'requests.npz') == digest(sampler['output'] / 'A-requests.npz') + checks['normal_probe_performance_zero_overlay'] = perf['overlay_file'] == reg['initial_overlay']['overlay_file'] and perf['overlay_manifest'] == reg['initial_overlay']['overlay_manifest'] + return dict(pass_all=all(checks.values()), checks=checks, failed_checks=[k for k, v in checks.items() if not v], + probes=probe_rows, biological_retention_demonstrated=False, learning_pass_claimed=False, + light_per_tick_identity_claimed=False, one_and_nine_second_same_state_lineage_verified=all(checks.values())) + + +def audit(reg_sha, *, save_report=True): + require(sys.dont_write_bytecode and 'torch' not in sys.modules and 'conditioning_epoch' not in sys.modules, + 'Run a fresh pure CPU Python -B audit process') + regfile = EXP / 'protocol/preregistered.json' + require(digest(regfile) == reg_sha, 'External registration SHA changed') + reg = read(regfile); PLAN.validate_plan(reg['plan']) + require(reg['schema'] == 'conditioning-execution-registration-v1' and reg['engineering_only'] is True and + reg['scientific_acquisition_authorized'] is False, 'Not an engineering-only authority') + pins = _closure(reg, reg_sha); verify_pins(pins) + complete_path = EXP / 'audit/execution-complete.json'; complete_sha = digest(complete_path); complete = read(complete_path) + calls = reg['plan']['calls']; expected = expected_processes(reg['plan']) + require(complete['complete'] is True and complete['registration_sha256'] == reg_sha and + complete['completed_calls'] == [c['call_id'] for c in calls] and set(complete['processes']) == set(expected) and + complete['scientific_learning_pass_claimed'] is complete['candidate_generated'] is False, + 'Execution inventory incomplete or scientifically overclaimed') + helper = _helpers(); verified = {name: verify_process(name, kind, reg, reg_sha, complete['processes'][name], helper) + for name, kind in expected.items()} + records = {}; imports = {}; checks = {}; updates = {}; comparisons = {}; pending = {}; used_pins = dict(pins) + for item in verified.values(): + for path, sha in {**item['input_pins'], **item['evidence_sha256']}.items(): + require(path not in used_pins or used_pins[path] == sha, 'Conflicting actual process authority') + used_pins[path] = sha + try: + for arm, descriptor in reg['plan']['imported_parents'].items(): + parent = _parent(reg, complete, descriptor['role']) + require(str(Path(parent['directory']) / 'checkpoint-manifest.json') == descriptor['checkpoint_manifest'] and + parent['checkpoint_sha256'] == descriptor['sha256'], 'Imported authority differs') + D.C.load_checkpoint(parent['directory'], descriptor['sha256']) + imports[arm] = B.load_record(parent['directory'], kind='parent') + updates['AP-' + arm] = _update_check(parent, verified, 'AP-' + arm + '--cpu-verify') + for call in calls: + name = call['call_id']; run = verified[name]; directory = run['output']; receipt = run['receipt'] + case = EXP / 'cases' / call['case_group'] + expected_spec = read(reg['case_templates'][call['case_group']]) + if call['parent'] is not None: + p = _parent(reg, complete, call['parent']) + expected_spec.update(parent_checkpoint_manifest_sha256=p['checkpoint_sha256'], + global_tick_offset=call['global_tick_offset']) + require(same_json(read(case / 'spec.json'), expected_spec), 'Executed case drifted from registered template') + if run['kind'] in ('brain', 'unchanged'): + require(Path(run['job']['case_dir']) == case, 'Wrong registered runtime case') + elif run['kind'] == 'oracle': + require(Path(run['job']['oracle_case']) == case, 'Wrong registered oracle case') + else: + auth = read(run['job']['authority_file']) + require(auth['case'] == dict(spec=A.pin(case / 'spec.json'), requests=A.pin(case / 'requests.npz')) and + auth['boundary_id'] == name and auth['registration_sha256'] == reg_sha and + auth['diagnostic_mode'] == call['diagnostic_mode'] and auth['input_policy'] == call['input_policy'] and + auth['transition'] == call['transition'] and auth['parent'] == dict(directory=p['directory'], + checkpoint_manifest_sha256=p['checkpoint_sha256']), 'Boundary authority differs from actual registered call') + if call['runner'] == 'boundary_oracle': + manifest = read(directory / 'oracle-manifest.json') + actual = {k: v['sha256'] for k, v in tree(directory).items() if k != 'oracle-manifest.json'} + require(manifest == dict(schema='conditioning-live-oracle-files-v1', complete=True, files_sha256=actual), + 'Live oracle exact manifest differs') + kind = 'oracle' + else: + checkpoint_sha = receipt['result']['checkpoint_manifest_sha256'] + D.load_checkpoint(directory, checkpoint_sha) + kind = 'updated' if call['runner'] == 'updated_continuation' else 'parent' + record = B.load_record(directory, kind=kind); records[name] = record; B._complete(record) + require(record['steps'] == call['steps'] and record['global_tick_offset'] == call['global_tick_offset'] and + record['summary']['nodes'] == N and record['summary']['edges'] == EDGES and + record['summary']['diagnostic_mode'] == call['diagnostic_mode'], 'Actual graph/clock/mode differs from registered call') + if call['runner'] == 'updated_continuation': + declared = _parent(reg, complete, name) + require(declared['directory'] == str(directory) and declared['checkpoint_sha256'] == checkpoint_sha, + 'Saved parent registry differs from actual checkpoint') + helper._verify_precision(receipt['environment'], record['summary']['apl_component']['environment']) + cycle = read(EXP / 'receipts' / (name + '.cycle.json')) + used_pins[str(EXP / 'receipts' / (name + '.cycle.json'))] = digest(EXP / 'receipts' / (name + '.cycle.json')) + require(cycle['complete'] is True and same_json(cycle['call'], call) and cycle['source_registration_sha256'] == reg_sha, + 'Complete-cycle descriptor mismatch') + for relative, evidence in cycle['output_inventory'].items(): + path = EXP / relative + require(path.is_relative_to(EXP) and '..' not in Path(relative).parts and + evidence == dict(sha256=digest(path), bytes=path.stat().st_size), 'Cycle immutable output changed') + for name in ('mechanical--fresh-parent', 'performance--natural-light-fresh'): + parent = _parent(reg, complete, name) + require(parent['directory'] == str(verified[name]['output']) and parent['checkpoint_sha256'] == records[name]['manifest_sha256'], + 'New E source parent identity differs') + updates[name] = _update_check(parent, verified, name + '--cpu-verify') + worker = verified[name + '--original-E'] + require(worker['output'] == Path(parent['update_dir']) and + worker['receipt']['result']['chain_receipt_sha256'] == digest(worker['output'] / 'chain-receipt.json'), + 'Original E actual process output differs') + for arm in ('D1', 'mechanical'): + oracle = records[arm + '--live-oracle'] + parent = imports['D1'] if arm == 'D1' else records['mechanical--fresh-parent'] + descriptor = _parent(reg, complete, reg['plan']['imported_parents']['D1']['role'] if arm == 'D1' else 'mechanical--fresh-parent') + original_authority = Path(verified[arm + '--updated--reference']['job']['authority_file']) + auth = read(original_authority) + for mode in ('reference', 'light'): + name = arm + '--updated--' + mode; authority = Path(verified[name]['job']['authority_file']) + comparisons[arm + '-live-vs-' + mode] = B.compare_boundary(oracle, records[name], parent=parent, + expected_before_sha256=auth['expected_effective_weight_before_sha256'], + expected_after_sha256=auth['expected_effective_weight_after_sha256'], + paired_authorities=(original_authority.read_bytes(), authority.read_bytes())) + pending[arm] = _pending(parent, records[arm + '--updated--reference'], descriptor) + for arm in ('D0', 'frozen'): + for mode in ('reference', 'light'): + comparisons[arm + '-old-C-vs-' + mode] = _pair(records[arm + '--old-C--reference'], records[arm + '--updated--' + mode]) + performance = 'performance--natural-light-fresh' + comparisons['performance-natural-vs-AP'] = _pair(imports['D1'], records[performance]) + old = reg['parent_records'][reg['plan']['imported_parents']['D1']['role']] + new = complete['parents'][performance] + old_delta, new_delta = (_npz(x['after_overlay']['overlay_file']) for x in (old, new)) + checks['natural_fullcycle_after_delta_exact'] = set(old_delta) == set(new_delta) and all(B.bit_equal(v, new_delta[k]) for k, v in old_delta.items()) + checks['natural_fullcycle_U_receipt_exact'] = same_json(read(Path(old['update_dir']) / 'update-receipt.json'), read(Path(new['update_dir']) / 'update-receipt.json')) + lineage = _lineage(reg, complete, verified, records) + checks.update({name: result['numerical_exact'] for name, result in comparisons.items()}) + checks['D1_conditional_queue_exact'] = pending['D1']['conditional_ref_request_queue_exact'] and pending['D1']['reference_queue_and_ref_coverage_complete'] + checks['mechanical_conditional_queue_exact'] = pending['mechanical']['conditional_ref_request_queue_exact'] and pending['mechanical']['reference_queue_and_ref_coverage_complete'] + checks['actual_idle_and_probe_lineage'] = lineage['pass_all'] + exact, coverage, status = outcome(checks, pending) + require(len(records) == 23 and sum(r['steps'] for r in records.values()) == 125200, 'Actual full-call/tick inventory differs') + verify_pins(used_pins) + for name, item in verified.items(): + require(tree(item['output']) == item['output_inventory'], 'Audited outputs changed') + require(digest(complete_path) == complete_sha, 'Execution receipt changed') + result = dict(schema='conditioning-execution-numerical-audit-v1', complete=True, status=status, + pass_all=exact and coverage, numerical_exact=exact, registration_sha256=reg_sha, + execution_complete_sha256=digest(complete_path), checks=checks, failed_checks=[k for k, v in checks.items() if not v], + comparisons=comparisons, updates=updates, pending_edge_audits=pending, idle_probe_lineage=lineage, + actual_new_brain_calls=len(records), actual_new_brain_ticks=sum(r['steps'] for r in records.values()), + actual_processes=len(verified), actual_CPU_processes=sum(r['kind'] in ('update', 'validate_cpu') for r in verified.values()), + actual_GPU_sampler_processes=sum(r['kind'] == 'sample' for r in verified.values()), all_actual_runs_finite=True, + process_evidence={name: dict(kind=v['kind'], process_sha256=digest(v['paths']['process']), worker_sha256=digest(v['paths']['worker']), + job_sha256=digest(v['paths']['job']), output_files=len(v['output_inventory']), recorded_exit_code=0) for name, v in verified.items()}, + observed_scope='Sparse events, complete final state/queues; full reference-only histories; authenticated recorded process evidence', + light_intermediate_states_observed=False, AP_D1_changed_edge_coverage_is_not_required=True, + historical_integrity_after_checked_here=False, sealed_here=False, brain_executed_here=False, + U_recomputed_here=False, learning_pass_claimed=False, full_retention_experiment=False, candidate_generated=False) + if save_report: + save(EXP / 'audit/numerical-comparison.json', result) + return result + finally: + for record in [*records.values(), *imports.values()]: + B.close_record(record) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--registration-sha256', required=True) + args = parser.parse_args() + result = audit(args.registration_sha256) + print(json.dumps({k: result[k] for k in ('complete', 'status', 'pass_all', 'actual_new_brain_calls', 'actual_processes')}), flush=True) diff --git a/models/male-cns-lif/conditioning-execution-v1/boundary_compare.py b/models/male-cns-lif/conditioning-execution-v1/boundary_compare.py new file mode 100644 index 0000000..dd6c2f0 --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/boundary_compare.py @@ -0,0 +1,371 @@ +"""Pure CPU comparison of live-boundary and saved/restored diagnostics. + +Array APIs require caller-authenticated records and immutable original graph / +delta arrays. They do NOT authenticate registrations, process receipts or disk +seals. load_record reads existing bytes without writing; its scope tags are not +authority. No GPU, learning, membrane replay or physiological inference. +""" +from __future__ import annotations + +import hashlib +import json +from pathlib import Path +from types import ModuleType + +import numpy as np + + +PINS = { + 'engineering_compare.py': '5a2403f11bc08d65a9d242ac2cf9686b850e0c3680d65659f785e9b6b50c0238', + 'engineering_queue_audit.py': '6ddaec52f2a0b5d0c528d51230d372fe2344160aef5eccea6a95b6c5e61a574e', +} +AUTHORITY_FIELDS = {'schema', 'boundary_id', 'registration_sha256', 'transition', 'diagnostic_mode', 'input_policy', + 'case', 'parent', 'global_tick_offset', 'overlay_before', 'overlay_after', + 'expected_effective_weight_before_sha256', 'expected_effective_weight_after_sha256', 'update', + 'cpu_validation', 'source_files', 'frozen_inputs'} + + +def _digest(path): + h = hashlib.sha256() + with Path(path).open('rb') as stream: + for block in iter(lambda: stream.read(8 << 20), b''): + h.update(block) + return h.hexdigest() + + +def _load(filename, expected): + path = Path(__file__).resolve().parent.parent / 'olfactory-conditioning-lab-v1' / filename + raw = path.read_bytes() + if hashlib.sha256(raw).hexdigest() != expected: + raise ValueError('Frozen pure helper source differs: ' + filename) + module = ModuleType('conditioning_boundary_' + filename[:-3]) + module.__file__ = str(path) + exec(compile(raw, str(path), 'exec'), module.__dict__) + return module + + +C = _load('engineering_compare.py', PINS['engineering_compare.py']) +require, bit_equal = C.require, C.bit_equal + + +def _raw(a): + h = hashlib.sha256() + for start in range(0, len(a), 1 << 18): + h.update(np.ascontiguousarray(a[start:start+(1 << 18)], dtype=a.dtype.newbyteorder('<')).tobytes()) + return h.hexdigest() + + +def load_record(directory, *, kind): + """Read actual inherited schemas; never disguise oracle as fixed-weight. + + No external SHA pin is accepted or inferred here. The caller independently + verifies the complete oracle/new/old checkpoint seal before this reader. + """ + require(kind in ('oracle', 'updated', 'parent'), 'Explicit record kind required') + record = C.load_run(directory) + try: + summary = record['summary'] + if kind == 'oracle': + require(summary.get('diagnostic_live_boundary') is True and summary.get('within_trial_weight_updates') is True and + summary.get('production_backend') is False and summary.get('restored_from_checkpoint') is False, + 'Not a declared live-boundary diagnostic') + record['boundary_snapshot'] = C._npz(Path(directory)/'boundary-snapshot.npz', + ('v','g','ref','counts','spike_ring','apl_state','apl_pending')) + record['boundary_report'] = C._json(Path(directory)/'boundary-comparison.json') + record['oracle_provenance'] = C._json(Path(directory)/'oracle-provenance.json') + else: + require(summary.get('within_trial_weight_updates') is False, 'Checkpoint segment updated during run') + if kind == 'updated': + require(summary.get('updated_boundary_schema') == 'conditioning-updated-boundary-v1' and + summary.get('checkpoint_continuation_used') is True, 'Not an updated checkpoint segment') + record['manifest_sha256'] = _digest(Path(directory)/'checkpoint-manifest.json') + record['read_scope'] = kind + return record + except BaseException: + C.close_run(record) + raise + + +def close_record(record): + C.close_run(record) + + +def _checks(): + checks = {} + def check(name, okay): + checks[name] = bool(okay) + return checks, check + + +def _complete(record): + C.validate_run(record) + s=record.get('summary',{}) + require(s.get('complete') is True and s.get('pass_all') is True and s.get('all_finite') is True and + type(s.get('steps')) is int and s['steps']==record['steps']==s.get('completed_ticks') and + type(s.get('nodes')) is int and s['nodes']==len(record['state']['v']) and + s.get('precision')=='float64' and s.get('apl_precision')=='float32' and s.get('apl_delay_steps')==18, + 'Record summary is incomplete or has wrong numerical scope') + + +def _canonical(value): + return json.dumps(value,sort_keys=True,separators=(',',':'),ensure_ascii=False,allow_nan=False).encode('utf-8') + + +def _paired_authorities(pair, oracle_sha, child_sha, child_mode): + """Bind actual raw JSON bytes and compare canonical content, not self hashes. + + File SHA and canonical SHA are intentionally distinct. Only diagnostic_mode + and boundary_id may differ; every other byte of canonical JSON must agree. + """ + require(type(pair) in (tuple,list) and len(pair)==2 and all(type(raw)is bytes for raw in pair), + 'paired_authorities must be the two original raw JSON byte strings') + def unique(items): + result={} + for key,value in items: + require(key not in result,'Duplicate authority JSON key: '+key);result[key]=value + return result + values=[json.loads(raw.decode('utf-8'),object_pairs_hook=unique, + parse_constant=lambda value:(_ for _ in ()).throw(ValueError('Nonfinite authority JSON'))) for raw in pair] + for value in values: + require(type(value)is dict and set(value)==AUTHORITY_FIELDS and value.get('schema')=='conditioning-updated-boundary-v1', + 'Paired authority must use the exact boundary schema') + require(type(value['boundary_id'])is str and bool(value['boundary_id']) and value['boundary_id'].strip()==value['boundary_id'], + 'Invalid paired boundary ID') + hashes=[hashlib.sha256(raw).hexdigest() for raw in pair] + allowed={'diagnostic_mode','boundary_id'} + normalized=[{k:v for k,v in value.items() if k not in allowed} for value in values] + differences=[key for key in sorted(AUTHORITY_FIELDS-allowed) if _canonical(values[0][key])!=_canonical(values[1][key])] + checks=dict(actual_raw_file_SHA_bound=hashes==[oracle_sha,child_sha], + explicit_modes=values[0]['diagnostic_mode']=='reference' and values[1]['diagnostic_mode']==child_mode, + only_two_allowed_fields_differ=_canonical(normalized[0])==_canonical(normalized[1])) + return dict(pass_all=all(checks.values()),checks=checks,raw_sha256=hashes, + canonical_sha256=[hashlib.sha256(_canonical(value)).hexdigest() for value in values], + common_content_canonical_sha256=[hashlib.sha256(_canonical(value)).hexdigest() for value in normalized], + allowed_differences=sorted(allowed),disallowed_different_fields=differences, + actual_disk_or_registration_authority_verified_here=False) + + +def compare_boundary(oracle, continuation, *, parent, expected_before_sha256, expected_after_sha256, paired_authorities=None): + """Compare full oracle prefix/suffix against an authenticated parent/child. + + Both reference and light children are supported. A light child's absent + intermediate traces/hashes are explicitly unobserved, never called exact. + No nonvacuity gate is invented here; changed-edge coverage is separate. + """ + for record in (oracle, continuation, parent): _complete(record) + require(C._sha(expected_before_sha256) and C._sha(expected_after_sha256), 'External expected weight identities required') + require(C._sha(parent.get('manifest_sha256')), 'Caller-authenticated parent manifest identity required') + require(parent['global_tick_offset'] == oracle['global_tick_offset'] == 0, 'Need original fresh parent/live oracle') + cut, steps = parent['steps'], continuation['steps'] + require(continuation['global_tick_offset'] == cut and oracle['steps'] == cut+steps, 'Wrong boundary/duration') + n = len(parent['state']['v']) + require(len(oracle['state']['v']) == len(continuation['state']['v']) == n, 'Population differs') + require(oracle.get('traces') and oracle.get('state_hashes') is not None and oracle.get('queues') is not None, + 'Live oracle must retain full reference diagnostics') + os, cs, ps = oracle['summary'], continuation['summary'], parent['summary'] + require(cs.get('diagnostic_mode') in ('reference','light'), 'Child recording mode is undeclared') + reference = cs['diagnostic_mode'] == 'reference' + if reference: + require(continuation.get('traces') and continuation.get('state_hashes') is not None and continuation.get('queues') is not None, + 'Reference child diagnostics missing') + else: + require(not continuation.get('traces') and continuation.get('state_hashes') is None and continuation.get('queues') is None, + 'Light scope must not manufacture intermediate histories') + if ps.get('diagnostic_mode')=='reference': + require(parent.get('traces') and parent.get('state_hashes') is not None and parent.get('queues') is not None, + 'Reference parent diagnostics missing') + checks, check = _checks() + check('oracle.explicit_live_scope', os.get('diagnostic_mode') == 'reference' and + os.get('diagnostic_live_boundary') is True and os.get('within_trial_weight_updates') is True and + os.get('explicit_weight_switches') == 1 and type(os.get('explicit_weight_switches')) is int and + os.get('boundary_tick') == cut and os.get('restored_from_checkpoint') is False and os.get('production_backend') is False) + check('child.explicit_updated_scope', cs.get('within_trial_weight_updates') is False and + cs.get('checkpoint_continuation_used') is True and cs.get('updated_boundary_schema') == 'conditioning-updated-boundary-v1' and + cs.get('parent_checkpoint_manifest_sha256') == parent['manifest_sha256'] and + cs.get('global_next_tick') == cut+steps and cs.get('boundary_transition') == 'epoch_update') + check('weights.before', ps.get('effective_weights_raw_sha256') == os.get('initial_effective_weights_raw_sha256') == expected_before_sha256) + check('weights.after', cs.get('effective_weights_raw_sha256') == os.get('effective_weights_raw_sha256') == expected_after_sha256) + ot, on = oracle['state']['step'], oracle['state']['neuron_idx'] + split = int(np.searchsorted(ot,cut)) + check('prefix.events.tick',bit_equal(ot[:split],parent['state']['step'])) + check('prefix.events.neuron_idx',bit_equal(on[:split],parent['state']['neuron_idx'])) + check('prefix.counts',np.array_equal(np.bincount(on[:split],minlength=n),parent['state']['counts'])) + check('suffix.events.absolute_tick',np.array_equal(continuation['state']['step'].astype(np.int64)+cut,ot[split:].astype(np.int64))) + check('suffix.events.neuron_idx',bit_equal(continuation['state']['neuron_idx'],on[split:])) + check('suffix.segment_counts',np.array_equal(continuation['state']['counts'],np.bincount(on[split:],minlength=n))) + check('prefix_plus_suffix.total_counts',np.array_equal(parent['state']['counts'].astype(np.int64)+continuation['state']['counts'],oracle['state']['counts'])) + for name in ('v','g','ref'):check('final.'+name,bit_equal(continuation['state'][name],oracle['state'][name])) + check('final.phase_normalized_spike_ring',bit_equal(C.normalized_ring(continuation['terminal']),C.normalized_ring(oracle['terminal']))) + for name in ('apl_pending','apl_state'):check('final.'+name,bit_equal(continuation['terminal'][name],oracle['terminal'][name])) + snapshot = oracle.get('boundary_snapshot') + require(type(snapshot) is dict and set(snapshot) == {'v','g','ref','counts','spike_ring','apl_state','apl_pending'}, 'Missing live boundary snapshot') + for name in ('v','g','ref','counts'):check('boundary.live_parent.'+name,bit_equal(snapshot[name],parent['state'][name])) + for name in ('spike_ring','apl_state','apl_pending'):check('boundary.live_parent.'+name,bit_equal(snapshot[name],parent['terminal'][name])) + report, provenance = oracle.get('boundary_report'), oracle.get('oracle_provenance') + require(type(report) is dict and type(provenance) is dict, 'Missing explicit oracle provenance/report') + paired=None + authority_matches=(C._sha(cs.get('boundary_authority_sha256')) and + provenance.get('authority_sha256')==cs['boundary_authority_sha256']) + if paired_authorities is not None: + paired=_paired_authorities(paired_authorities,provenance.get('authority_sha256'), + cs.get('boundary_authority_sha256'),cs['diagnostic_mode']) + authority_matches=paired['pass_all'] + check('oracle.boundary_metadata',report.get('complete') is True and report.get('boundary_tick') == cut and + report.get('parent_checkpoint_manifest_sha256') == parent['manifest_sha256'] and report.get('restore_called') is False and + report.get('initial_state_replaced') is False and report.get('queues_replaced') is False and report.get('existing_weights_replaced_once') is True and + report.get('before_effective_sha256') == expected_before_sha256 and report.get('after_effective_sha256') == expected_after_sha256) + check('oracle.authority_matches_child',provenance.get('schema') == 'conditioning-live-boundary-oracle-v1' and + provenance.get('complete') is True and provenance.get('restore_called') is False and + provenance.get('prefix_sparse_events_exact') is True and provenance.get('learning_pass_claimed') is False and + authority_matches) + for label,record,start,stop in (('prefix',parent,0,cut),('suffix',continuation,cut,cut+steps)): + req=oracle['requests'];lo,hi=np.searchsorted(req[:,0],(start,stop)) + actual=record['requests'].copy();actual[:,0]+=start + check(label+'.requests_all_five_fields',bit_equal(actual,req[lo:hi])) + if record.get('traces'): + for name in C.TRACE_FILES:check(label+'.trace.'+name,bit_equal(record['traces'][name],oracle['traces'][name][start:stop])) + if record.get('state_hashes') is not None: + check(label+'.state_hashes',all(all(row[key]==oracle['state_hashes'][start+t][key] for key in C.HASH_FIELDS) + for t,row in enumerate(record['state_hashes']))) + if record.get('queues') is not None: + check(label+'.queue_order_acceptance',all(all(row[key]==oracle['queues'][start+t][key] for key in C.QUEUE_FIELDS) + for t,row in enumerate(record['queues']))) + consume=min(steps,18) + if reference:check('boundary.applied_parent_APL',bit_equal(continuation['traces']['applied'][:consume],parent['terminal']['apl_pending'][:consume])) + if steps<18:check('boundary.unconsumed_parent_APL',bit_equal(continuation['terminal']['apl_pending'][:18-steps],parent['terminal']['apl_pending'][steps:])) + if steps<19:check('boundary.unwritten_parent_spike_slots',bit_equal(continuation['terminal']['spike_ring'][steps:],C.normalized_ring(parent['terminal'])[steps:])) + exact=all(checks.values()) + return dict(schema='conditioning-live-vs-restored-comparison-v1',pass_all=exact,numerical_exact=exact, + status='EXACT-RECORDED-BOUNDARY-SCOPE-NOT-LEARNING' if exact else 'FAIL-BOUNDARY-NUMERICAL',checks=checks, + failed_checks=[k for k,v in checks.items() if not v],boundary_tick=cut,suffix_steps=steps,neurons=n, + child_mode=cs['diagnostic_mode'],suffix_reference_diagnostics_compared=reference, + paired_authority_audit=paired, + light_intermediate_states_observed=False,light_per_tick_identity_claimed=False, + queue_joint_hash_compared=False,queue_joint_hash_exclusion='Local tick headers differ; independent audit checks each actual header', + changed_candidate_pending_arrival_coverage_verified=False,external_file_authority_verified_here=False, + independent_membrane_replay_performed=False,learning_pass_claimed=False,candidate_generated=False) + + +def _effective_hash(base, apl, rows, values): + h=hashlib.sha256() + for start in range(0,len(base),1<<18): + stop=min(start+(1<<18),len(base));a=np.array(base[start:stop],copy=True) + a[apl[start:stop]]=np.float64(0) + lo,hi=np.searchsorted(rows,(start,stop));a[rows[lo:hi]-start]=values[lo:hi] + h.update(np.ascontiguousarray(a,dtype='0 and + np.all(ids>=0) and np.all(ids[1:]>ids[:-1]),'Candidate IDs must preserve canonical sorted unique order') + for name,a in (('before',before),('after',after)): + require(isinstance(a,np.ndarray) and a.dtype==np.float32 and a.shape==ids.shape and + np.isfinite(a).all() and np.all(a<=0),'Invalid f32 mV delta '+name) + require(np.all(after<=before),'This diagnostic admits only existing depression, not potentiation') + rows=np.searchsorted(edge_id,ids) + require(np.all(rows0),'Candidate overlaps APL or has nonpositive original weight') + values=[] + for delta in (before,after): + a=base[rows].copy();nz=delta!=np.float32(0) + a[nz]=np.add(a[nz],np.multiply(delta[nz].astype(np.float64),np.float64(.001))) + require(np.isfinite(a).all() and np.all(a>0),'Effective candidate weight changed sign/nonfinite') + values.append(a) + return n,e,rows,values[0],values[1] + + +def audit_changed_pending(parent,child,*,ptr,src,dst,edge_id,base_weights,apl_mask, + candidate_edge_id,delta_before,delta_after,input_pins): + """Conditional ref/request/CSR audit plus a changed-edge crossing witness. + + Post-threshold ref=22 precedes arrivals; requests use prior ref<=1. Every + real edge remains in source/CSR order, including rejected/zero weights. + Witnesses require an actual numeric effective-weight change and accepted + arrival emitted before cut and arriving within the child. This is not a + unique-cause claim and does not independently predict membrane spikes. + """ + require(type(input_pins)is dict and input_pins and all(type(k)is str and C._sha(v) for k,v in input_pins.items()),'Caller input anchors required') + Q=_load('engineering_queue_audit.py',PINS['engineering_queue_audit.py']) + for record in (parent,child):_complete(record) + n,e,rows,before,after=_candidates(ptr,src,dst,edge_id,base_weights,apl_mask,candidate_edge_id,delta_before,delta_after,Q) + require(parent['global_tick_offset']==0 and child['global_tick_offset']==parent['steps'],'Fresh parent / exact child boundary required') + require(len(parent['state']['v'])==len(child['state']['v'])==n,'Record/graph population mismatch') + require(all(type(record['summary'].get('edges')) is int and record['summary']['edges']==e for record in (parent,child)), + 'Record/graph edge count differs') + cut,total=parent['steps'],parent['steps']+child['steps'] + require(total<=np.iinfo(np.int32).max,'Diagnostic clock exceeds exact int32 event range') + before_sha=_effective_hash(base_weights,apl_mask,rows,before);after_sha=_effective_hash(base_weights,apl_mask,rows,after) + require(parent['summary'].get('effective_weights_raw_sha256')==before_sha and + child['summary'].get('effective_weights_raw_sha256')==after_sha,'Delta/base arithmetic mismatches actual runtime effective SHA') + changed=(before.view(np.uint64)!=after.view(np.uint64)) & (before!=after) + changed_rows=rows[changed];changed_ids=candidate_edge_id[changed] + effective=base_weights.copy();effective[apl_mask]=np.float64(0);effective[rows]=after + tick=np.concatenate((parent['state']['step'],child['state']['step']+np.int32(cut))) + node=np.concatenate((parent['state']['neuron_idx'],child['state']['neuron_idx'])) + request=np.concatenate((parent['requests'],child['requests']+np.array([cut,0,0,0,0],np.int64))) + ref=np.zeros(n,np.int32);differences=[];difference_count=0 + def check(ok,field,t): + nonlocal difference_count + if not ok: + difference_count+=1 + if len(differences)<60:differences.append(dict(field=field,global_tick=t)) + coverage=dict(changed_candidate_crossing_arrivals=0,accepted_changed_candidate_crossing_arrivals=0, + rejected_changed_candidate_crossing_arrivals=0,first_witness=None) + full=all(record.get('queues') is not None and record.get('state_hashes') is not None for record in (parent,child)) + for t in range(total): + record,local=(parent,t) if t=18 else node[:0] + qrows,_,_=Q._queue(t,local,sources,ref,ptr,dst,edge_id,effective,[]) + expected_queue=qrows[0] if tlen(differences),coverage=coverage,neurons=n,edges=e,boundary_tick=cut, + changed_delta_bit_count=int(np.count_nonzero(delta_before.view(np.uint32)!=delta_after.view(np.uint32))), + changed_effective_numeric_count=int(changed.sum()),before_effective_sha256=before_sha,after_effective_sha256=after_sha, + supplied_input_pins=dict(input_pins),array_raw_sha256={key:_raw(a) for key,a in dict(ptr=ptr,src=src,dst=dst,edge_id=edge_id, + base_weights=base_weights,apl_mask=apl_mask,candidate_edge_id=candidate_edge_id,delta_before=delta_before,delta_after=delta_after).items()}, + delay_steps=18,refractory_steps=22,actual_membrane_dynamics_independently_replayed=False, + observed_spikes_condition_the_audit=True,external_file_authority_verified_here=False,learning_pass_claimed=False) diff --git a/models/male-cns-lif/conditioning-execution-v1/boundary_oracle.py b/models/male-cns-lif/conditioning-execution-v1/boundary_oracle.py new file mode 100644 index 0000000..60517dc --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/boundary_oracle.py @@ -0,0 +1,219 @@ +"""Diagnostic live-boundary oracle, NOT a production/learned brain backend. + +Re-execute a fresh parent's exact inputs/old overlay, without calling restore. +At the declared epoch boundary compare live state to the sealed parent, replace +only existing candidate weights with base+delta-after, and continue the original +reference loop. One whitelisted hook is independently removable from its AST. +The diagnostic has an explicit within-run boundary switch; old C must NOT load +its output as an unchanged-weight checkpoint. +""" +from __future__ import annotations + +import ast +import copy +import hashlib +import importlib.util +import json +from pathlib import Path +import sys + +import numpy as np + +LAB=Path(__file__).resolve().parent.parent/'olfactory-conditioning-lab-v1' +C_SHA='0b8e8e0a6a23a079dc62a88355ce355d53b3ae22f6665519a8f6aef2dcfc5aba' +HOOK="if tick == _oracle.boundary_tick:\n _oracle.switch(tick, weights, v, g, ref, counts, ring, apl_ring, apl, out_dir)" + + +def require(ok,message): + if not ok:raise ValueError(message) + + +def digest(path): + h=hashlib.sha256() + with Path(path).open('rb') as stream: + for b in iter(lambda:stream.read(8<<20),b''):h.update(b) + return h.hexdigest() + + +def load(name,path,sha): + raw=Path(path).read_bytes() + require(hashlib.sha256(raw).hexdigest()==sha and name not in sys.modules,'Unverified oracle source/module binding') + spec=importlib.util.spec_from_file_location(name,path);module=importlib.util.module_from_spec(spec) + sys.modules[name]=module;exec(compile(raw,str(path),'exec'),module.__dict__) + return module + + +def frozen(): + return load('_conditioning_live_oracle_C',LAB/'runtime_continuation.py',C_SHA) + + +def derive_oracle(c,source_bytes): + tree,inherited=c.R.derive_ast(source_bytes,'reference');before=copy.deepcopy(tree) + loops=[n for n in ast.walk(tree) if isinstance(n,ast.For) and isinstance(n.target,ast.Name) and n.target.id=='tick'] + require(len(loops)==1,'Unique frozen timestep loop required') + loops[0].body.insert(0,ast.parse(HOOK).body[0]);tree=ast.fix_missing_locations(tree) + rollback=copy.deepcopy(tree) + matches=[n for n in ast.walk(rollback) if isinstance(n,ast.For) and isinstance(n.target,ast.Name) and n.target.id=='tick'] + require(ast.unparse(matches[0].body[0])==HOOK,'Oracle hook changed') + del matches[0].body[0] + require(c.R._dump(rollback)==c.R._dump(before),'Unlisted oracle mathematical AST edit') + return tree,dict(base=inherited,explicit_boundary_hook=HOOK,full_AST_rollback_identical=True, + frozen_within_epoch_math_unchanged=True,restore_called=False, + diagnostic_live_weight_switch=True,production_backend=False, + ast_sha256=hashlib.sha256(c.R._dump(tree).encode()).hexdigest()) + + +def expected_candidate_values(base_si,edge_ids,after_delta): + """Base+f32-mV delta with the frozen separate f64 multiply/add contract.""" + require(base_si.dtype==np.float64 and base_si.ndim==1 and edge_ids.dtype==np.int64 and edge_ids.ndim==1 and + after_delta.dtype==np.float32 and after_delta.shape==edge_ids.shape,'Wrong candidate bridge dtype/shape') + require(np.all((edge_ids>=0)&(edge_idsedge_ids[:-1]),'Invalid existing edge IDs') + require(np.isfinite(after_delta).all() and np.all(after_delta<=0),'Invalid depression delta') + result=base_si[edge_ids].copy();nonzero=after_delta!=np.float32(0) + result[nonzero]=np.add(result[nonzero],np.multiply(after_delta[nonzero].astype(np.float64),np.float64(.001))) + require(np.isfinite(result).all() and np.all(result>0),'Candidate sign changed') + return result + + +def boundary_arrays(parent,tick,v,g,ref,counts,ring,apl_ring,apl_state): + require(type(tick)is int and tick==parent['steps']==parent['global_next_tick'] and parent['fresh'] is True, + 'Oracle needs the exact fresh-parent boundary') + arrays=dict(v=v,g=g,ref=ref,counts=counts,spike_ring=ring,apl_state=apl_state, + apl_pending=np.concatenate((apl_ring[tick%18:],apl_ring[:tick%18]),axis=0)) + expected={**{k:parent['state'][k] for k in ('v','g','ref','counts')}, + **{k:parent['terminal'][k] for k in ('spike_ring','apl_state','apl_pending')}} + checks={k:(a.dtype==expected[k].dtype and a.shape==expected[k].shape and a.tobytes()==expected[k].tobytes()) + for k,a in arrays.items()} + require(all(checks.values()),'Live prefix differs from sealed parent: '+str([k for k,v in checks.items() if not v])) + return arrays,checks + + +def runtime_class(c): + r=c.R + class Oracle(r._Runtime): + def __init__(self,*args,boundary,**kwargs): + super().__init__(*args,**kwargs);self.boundary=boundary;self.parent=boundary['parent'] + self.boundary_tick=self.parent['steps'];self.switches=0 + + def tensor_sha(self,value): + h=hashlib.sha256() + for start in range(0,value.numel(),1<<20):h.update(value[start:start+(1<<20)].cpu().numpy().astype('= 0) and np.all(edge < 25582938) and np.all(edge[1:] > edge[:-1]), 'Canonical edge order required') + for key in ('source_neuron_idx', 'target_neuron_idx'): + _require(np.all((candidate_map[key] >= 0) & (candidate_map[key] < N)), 'Neuron index out of range') + _require(isinstance(pam15_ids, np.ndarray) and pam15_ids.dtype == np.int32 and pam15_ids.shape == (4,) + and np.all((pam15_ids >= 0) & (pam15_ids < N)) and np.all(pam15_ids[1:] > pam15_ids[:-1]), + 'Four canonical sorted unique int32 PAM15 indices required') + _require(type(identity_binding) is dict and set(identity_binding) == {'candidate_map_sha256', 'pam15_membership_sha256'} + and all(type(v) is str and len(v) == 64 and all(c in '0123456789abcdef' for c in v) + for v in identity_binding.values()), 'External identity digest descriptors required') + sources = candidate_map['source_neuron_idx'][candidate_map['target_compartment_verbatim'] == COMPARTMENT] + _require(len(sources) > 0, 'No exact y5B\'2a candidate rows') + selected = int(sources.min()) + _require(selected not in pam15_ids, 'KC/DAN identity overlap') + node = np.sort(np.concatenate((np.array([selected], dtype=np.int32), pam15_ids))) + audit = dict(selected_KC_source_neuron_idx=selected, PAM15_neuron_idx=pam15_ids.tolist(), tick=1199, + request_count=5, compartment=COMPARTMENT, identity_binding=deepcopy(identity_binding), + selection='minimum canonical source_neuron_idx among exact compartment candidate rows', + input_arrays={k: dict(dtype=a.dtype.str, shape=list(a.shape), raw_c_order_sha256= + hashlib.sha256(a.tobytes(order='C')).hexdigest()) for k, a in candidate_map.items()}, + external_identity_verified_here=False, olfactory_input=False, sampled=False, accepted_spikes_verified=False, + before_overlay_role='AP.initial_overlay', inherited_learned_delta=False, scientific_learning_claimed=False) + return dict(tick=np.full(5, 1199, dtype=np.int32), neuron_idx=node), audit diff --git a/models/male-cns-lif/conditioning-execution-v1/boundary_sampler.py b/models/male-cns-lif/conditioning-execution-v1/boundary_sampler.py new file mode 100644 index 0000000..b27cbdf --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/boundary_sampler.py @@ -0,0 +1,75 @@ +"""Exactly two registered evaluation-input batches; no brain, US or learning.""" +from __future__ import annotations + +import hashlib +import json +from pathlib import Path +import sys +import time + +import numpy as np + +import boundary_plan as PLAN +import execution_authority as A + +MODEL=Path(__file__).resolve().parent +LAB=MODEL.parent/'olfactory-conditioning-lab-v1' +PINS={ + 'conditioning_inputs.py':'19382e066e450140bb653a3661f980ffe7cc62f412020ef57df0b6e6b3cbb8ba', + 'conditioning_preflight_inputs.py':'846d2141b472f18b1bb728eed4aa33876896d4b33dece364787c81204f62995f', + 'evaluation_plan.py':'991c813ff3322b230947f302b12b04ec382c3b632817f94b698101e4b17efa7d', + 'trial_plan.py':'6fed3f494d075339a7e5b001d43b2598640b189e038952fbd4d41200bacac587', + 'seed_registry.py':'0da033b132095ec3087b5d9f64e53c2e391fa50ab0b7653b31296eeb84799b7e', +} + + +def save(path,value): + with Path(path).open('x',encoding='utf-8') as f:json.dump(value,f,sort_keys=True,indent=2,allow_nan=False) + + +def run(authority_file,authority_sha,out_dir): + A.require(sys.dont_write_bytecode and A.digest(authority_file)==authority_sha,'Use -B with external registration pin') + reg=A.read(authority_file) + A.require(reg['schema']=='conditioning-execution-registration-v1','Wrong input generation authority') + PLAN.validate_plan(reg['plan']) + for path,sha in {**reg['source_sha256'],**reg['input_sha256'],**reg['prepared_file_sha256']}.items(): + A.require(A.pin(path)['sha256']==sha,'Sampler source/input drift') + for name,sha in PINS.items(): + A.require(A.digest(LAB/name)==sha and reg['input_sha256'].get(str(LAB/name))==sha,'Unpinned frozen input dependency: '+name) + A.require(not any(name in sys.modules for name in ('conditioning_inputs','conditioning_preflight_inputs','evaluation_plan')), + 'Sampler needs fresh scientific imports') + sys.path.insert(0,str(LAB)) + import conditioning_preflight_inputs as I + import conditioning_inputs as CI + import evaluation_plan as EP + rates=I.load_rate_tables('/data/digital-drosophila') + full=reg['original_full_plan'];A.require(A.digest(full['path'])==full['sha256'],'Full seed/trial plan changed') + streams=EP._streams(A.read(full['path'])) + exp=Path(authority_file).parent.parent;out=Path(out_dir) + A.require(not out.exists() and out.parent.is_dir() and out.is_relative_to(exp),'Exclusive new sampler output required') + out.mkdir();rows=[] + for cue in ('A','B'): + declared=reg['plan']['evaluation_streams'][cue];actual=streams[(PLAN.BLOCK,cue)] + for field in ('seed','seed_bits','stream_id','derivation_sha256'): + A.require(declared[field]==actual[field],'Evaluation semantic stream differs') + A.require(declared['key']==actual['semantic_key'] and actual['split']=='evaluation','Evaluation stream provenance differs') + table=rates['tables'][cue] + with np.load(exp/f'inputs/static/{cue}-rates.npz',allow_pickle=False) as saved: + A.require(all(saved[k].dtype==table[k].dtype and saved[k].shape==table[k].shape and + saved[k].tobytes()==table[k].tobytes() for k in ('targets','rates_hz')),'Registered original PN mapping changed') + started=time.perf_counter() + result=CI.generate_cs_requests(table['targets'],table['rates_hz'],seed=declared['seed'],steps=1200, + cs_start_tick=0,cs_stop_tick=1000,device='cuda:0',max_uniform_draws=1200*len(table['targets'])) + with (out/f'{cue}-requests.npz').open('xb') as f:np.savez_compressed(f,**result['requests']) + save(out/f'{cue}-sampler.json',result['audit']) + rows.append(dict(cue=cue,stream=actual,requests=len(result['requests']['tick']),US_requests=0, + uniform_draws=1200*len(table['targets']),wall_seconds=time.perf_counter()-started, + saved_requests_sha256=A.digest(out/f'{cue}-requests.npz'))) + pins={p.name:A.digest(p) for p in out.iterdir() if p.is_file()} + summary=dict(schema='conditioning-boundary-sampler-v1',complete=True,registration_sha256=authority_sha, + uniform_batch_calls=2,uniform_draws=sum(r['uniform_draws'] for r in rows),rows=rows,files_sha256=pins, + brain_executed=False,learning_executed=False,US_added=False,original_rate_mapping_preserved=True, + inputs_sampled_once=True,planned_retention_reuse_checkpoints_seconds=[0,1,10], + reuse_executed_here=False,seed_search=False,learning_success_claimed=False) + save(out/'sampler-receipt.json',summary) + return summary diff --git a/models/male-cns-lif/conditioning-execution-v1/budget_report.py b/models/male-cns-lif/conditioning-execution-v1/budget_report.py new file mode 100644 index 0000000..d9dbdea --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/budget_report.py @@ -0,0 +1,277 @@ +"""Read-only actual boundary-cost accounting; never authorizes normal training. + +Only the two registered fresh/light performance cycles are cost samples. Their +next serial cycle start is a measured conservative tail-completion upper bound, +not an invented postvalidation duration. All current compiled-cache bytes are +charged to EACH sample (deliberate overcount); cache reuse is never free by fiat. +No normal directory-hashing/finalization policy or reserves are registered yet, +so the strict full-suite forecast remains INSUFFICIENT-MEASUREMENTS. This report +does not independently re-observe historical OS exits or certify learning. +""" +from __future__ import annotations + +from copy import deepcopy +import hashlib +import json +import math +from pathlib import Path + +import execution_authority as A +import execution_budget as B +import execution_workflow as F + + +SCHEMA = 'conditioning-actual-budget-report-v1' +TRAIN = 'performance--natural-light-fresh' +PROBE = 'performance--evaluation-A-light-fresh' +SELECTED = {TRAIN: [('brain',TRAIN),('original_E',TRAIN+'--original-E'),('cpu_validator',TRAIN+'--cpu-verify')], + PROBE: [('brain',PROBE)]} +POLICY = 'measured-fresh-light-1200-fullgraph-with-original-E-and-CPU-verification-for-training' +require = A.require + + +def _canonical(value): + return json.dumps(value,sort_keys=True,separators=(',', ':'),ensure_ascii=False,allow_nan=False) + + +def _sha(value): + return hashlib.sha256(_canonical(value).encode()).hexdigest() + + +def _same(a,b): + return _canonical(a)==_canonical(b) + + +def _number(value): + return type(value) in (int,float) and math.isfinite(value) and value>=0 + + +class Evidence: + def __init__(self): + self.pins={} + self.derived={} + + def pin(self,path,expected=None): + if not Path(path).exists(): + raise FileNotFoundError(2,'Missing measurement evidence',str(path)) + entry=A.pin(path);p=entry['path'];sha=entry['sha256'] + require(expected is None or sha==expected,'Evidence bytes changed: '+p) + require(p not in self.pins or self.pins[p]==sha,'Conflicting evidence pin: '+p) + self.pins[p]=sha + return dict(receipt_id=p,sha256=sha) + + def read(self,path,expected=None): + self.pin(path,expected) + return A.read(path) + + def inventory(self,directory): + result=F.tree(directory) + for name,row in result.items():self.pin(Path(directory)/name,row['sha256']) + return result + + def derived_binding(self,name,value): + key='derived:'+name + self.derived[key]=deepcopy(value) + return dict(receipt_id=key,sha256=_sha(value)) + + def authenticated(self): + return {**self.pins,**{key:_sha(value) for key,value in self.derived.items()}} + + +def _parent_process(name,reg,reg_sha,evidence): + exp=F.EXP;process_path=exp/'receipts'/f'{name}.process.json' + process=evidence.read(process_path) + require(process['schema']=='conditioning-execution-process-v1' and process['complete'] is True and + process['name']==name and process['registration_sha256']==reg_sha and + type(process['exit_code']) is int and process['exit_code']==0 and process['timed_out'] is False, + 'Unsuccessful/unbound parent process: '+name) + jobpath=exp/'jobs'/f'{name}.json';job=evidence.read(jobpath,process['job_sha256']) + workerpath=exp/'receipts'/f'{name}.worker.json' + worker=evidence.read(workerpath,process['worker_receipt_sha256']) + ap=job['kind'] in ('brain','update');script=F.AP_WORKER if ap else F.MODEL/'isolated_execution.py' + schema='phase4ap-isolated-worker-receipt-v1' if ap else 'conditioning-isolated-execution-receipt-v1' + worker_sha=F.W.AP_SHA if ap else reg['source_sha256'][str(script)] + require(worker['schema']==schema and worker['complete'] is True and job['worker_sha256']==worker_sha and + all(worker[k]==job[k] for k in ('job_id','kind','registration_sha256','worker_sha256')) and + job['job_id']==name and job['kind']==process['kind'] and job['registration_sha256']==reg_sha and + worker['job_sha256']==process['job_sha256'] and job['receipt_file']==str(workerpath), + 'Worker/job/parent receipt identity differs') + expected={**job['input_sha256'],str(jobpath):process['job_sha256'],str(script):worker_sha} + if ap:expected.update({v['path']:v['sha256'] for v in job['sources'].values()}) + else: + expected[str(F.AP_WORKER)]=F.W.AP_SHA + expected.update({str(F.MODEL/n):s for n,s in job['source_sha256'].items()}) + require(expected.get(str(exp/'protocol/preregistered.json'))==reg_sha and + worker['verified_input_sha256' if ap else 'input_sha256']==expected,'Actual input/source closure differs') + for path,sha in expected.items():evidence.pin(path,sha) + command=process['argv'] + require(type(command) is list and len(command)>3 and command[1:3]==['-B',str(script)] and + command[3:]==(['brain' if job['kind']=='brain' else 'update'] if ap else [])+ + ['--job',str(jobpath),'--job-sha256',process['job_sha256']], 'Observed child command differs') + for side in ('stdout','stderr'):evidence.pin(exp/'logs'/f'{name}.{side}.log',process[side+'_sha256']) + evidence.pin(str(workerpath)+'.attempt.json',worker['attempt_sha256']) + output=Path(job['output_dir']) + require(output.is_relative_to(exp) and worker['output_dir']==str(output),'Wrong child output root') + inventory=evidence.inventory(output);hashes={k:v['sha256'] for k,v in inventory.items()} + require(inventory and hashes==worker['output_files_sha256'] and + process['output_bytes']==sum(v['bytes'] for v in inventory.values()) and + process['output_inventory_sha256']==hashlib.sha256(json.dumps(hashes,sort_keys=True).encode()).hexdigest(), + 'Actual complete child output inventory differs') + start,exit_,end=(process[k] for k in ('spawn_monotonic','exit_monotonic','validation_end_monotonic')) + require(all(_number(v) for v in (start,exit_,end)) and start=0)&(maskmask[:-1]) and + np.array_equal(ids[mask],mask) and not fast[mask].any() and np.isfinite(delta).all() and np.all(delta<=0), + 'Original candidate overlay contract differs') + nz=delta!=np.float32(0) + rows=mask[nz] + weights[rows]=np.add(weights[rows],np.multiply(delta[nz].astype(np.float64),np.float64(.001))) + require(np.isfinite(weights).all() and np.all(weights[mask]>0),'Effective candidate sign/nonfinite failure') + return hashlib.sha256(weights.astype('= 0 + + +def _text(value): + return type(value) is str and bool(value) and value.strip() == value + + +def _binding(value, authenticated): + _require(type(value) is dict and set(value) == {'receipt_id','sha256'} and + _text(value['receipt_id']) and _sha(value['sha256']) and + authenticated.get(value['receipt_id']) == value['sha256'], 'Unauthenticated receipt descriptor') + + +def _measure(row, authenticated, used): + _require(type(row) is dict and set(row) == CYCLE_FIELDS and row['schema'] == SCHEMA and + row['complete'] is True, 'Explicit complete authoritative cycle required; no worker-slot adapter') + _require(_text(row['cycle_id']) and _text(row['clock_id']) and _sha(row['context_sha256']), 'Missing cycle identity/context/clock') + _binding(row['authority'], authenticated) + _require(row['kind'] in ('training','probe','state_only_idle') and + row['diagnostic_mode'] in ('light','reference'), 'Unsupported cycle kind/mode') + _require(type(row['steps']) is int and row['steps'] > 0 and type(row['neurons']) is int and + row['neurons'] == N and type(row['edges']) is int and row['edges'] == E and + type(row['dt_ms']) in (int,float) and row['dt_ms'] == .1, 'Full frozen graph/clock required') + _require(all(_number(row[k]) for k in ('started_s','validated_s')) and + row['validated_s'] > row['started_s'], 'Invalid whole-cycle timing') + events = row['events'] + roles = ['brain','original_E','cpu_validator'] if row['kind'] == 'training' else ['brain'] + _require(type(events) is list and [e.get('role') for e in events if type(e) is dict] == roles, + 'Training requires brain + original E + CPU validator; probes/idle must not update') + previous, totals = row['started_s'], dict(core_seconds=0.,runtime_seconds=0.,child_wall_seconds=0.) + measured = {key: True for key in totals} + receipt_ids = [row['authority']['receipt_id']] + for event in events: + _require(type(event) is dict and set(event) == EVENT_FIELDS and _text(event['event_id']) and + ('event',event['event_id']) not in used and event['receipt_kind'] == 'parent-process' and + type(event['exit_code']) is int and event['exit_code'] == 0, 'Actual successful unique parent process required') + used.add(('event',event['event_id'])) + _binding(event['receipt'], authenticated) + receipt_ids.append(event['receipt']['receipt_id']) + _require(all(_number(event[k]) for k in ('spawn_s','exit_s','validated_s')) and + previous <= event['spawn_s'] < event['exit_s'] <= event['validated_s'] <= row['validated_s'], + 'Event clock ordering/whole-cycle binding failed') + previous = event['validated_s'] + child = event['exit_s'] - event['spawn_s'] + _require(_number(event['child_wall_seconds']) and math.isclose(event['child_wall_seconds'],child,rel_tol=1e-12), + 'Child wall must be actual parent spawn-to-exit time') + upper = child + for key in ('runtime_seconds','core_seconds'): + value = event[key] + if value is not None: + _require(_number(value) and value <= upper, 'Core/runtime/child timing layers inconsistent') + upper = value + for key in totals: + if event[key] is None: + measured[key] = False + else: + totals[key] += event[key] + outputs = row['outputs'] + _require(type(outputs) is dict and set(outputs) == {'inventory','scope','complete','total_bytes'} and + outputs['scope'] == OUTPUT_SCOPE and outputs['complete'] is True and + type(outputs['total_bytes']) is int and outputs['total_bytes'] > 0, 'Actual complete cycle output inventory required') + _binding(outputs['inventory'], authenticated) + receipt_ids.append(outputs['inventory']['receipt_id']) + _require(len(set(receipt_ids)) == len(receipt_ids), 'Cycle/event/output receipts must have distinct identities') + _require(not any(('receipt',key) in used for key in receipt_ids), 'Receipt reused across purported independent cycles') + used.update(('receipt',key) for key in receipt_ids) + return dict(cycle_id=row['cycle_id'], kind=row['kind'], diagnostic_mode=row['diagnostic_mode'], + steps=row['steps'], context_sha256=row['context_sha256'], + timing={**{k: totals[k] if measured[k] else None for k in totals}, + 'end_to_end_seconds':row['validated_s']-row['started_s']}, output_bytes=outputs['total_bytes']) + + +def estimate_normal_suite(cycles, *, authenticated_receipts, target_context_sha256, reserves, limits=None): + """Return proposal or INSUFFICIENT-MEASUREMENTS, never permission to execute. + + reserves requires cycle_multiplier>=1 plus all RESERVE_ROLES, each with + wall_seconds/output_bytes/basis. Explicit zero is permitted with a stated + basis (e.g. already included); none are silently assumed zero. limits, when + supplied, must specify wall_seconds and output_bytes caps for this proposal. + Only matched-context light complete 1200-tick cycles enter extrapolation. + """ + _require(type(cycles) is list and type(authenticated_receipts) is dict and + all(_text(k) and _sha(v) for k,v in authenticated_receipts.items()) and + _sha(target_context_sha256), 'Explicit authenticated receipt/context descriptors required') + _require(type(reserves) is dict and set(reserves) == RESERVE_ROLES | {'cycle_multiplier'} and + _number(reserves['cycle_multiplier']) and reserves['cycle_multiplier'] >= 1, 'Explicit complete reserves required') + for role in RESERVE_ROLES: + reserve = reserves[role] + _require(type(reserve) is dict and set(reserve) == {'wall_seconds','output_bytes','basis'} and + _number(reserve['wall_seconds']) and type(reserve['output_bytes']) is int and + reserve['output_bytes'] >= 0 and _text(reserve['basis']), 'Invalid explicit reserve: '+role) + if limits is not None: + _require(type(limits) is dict and set(limits) == {'wall_seconds','output_bytes'} and + _number(limits['wall_seconds']) and limits['wall_seconds'] > 0 and + type(limits['output_bytes']) is int and limits['output_bytes'] > 0, 'Invalid resource caps') + used = set() + measured = [_measure(row,authenticated_receipts,used) for row in cycles] + _require(len({r['cycle_id'] for r in measured}) == len(measured), 'Duplicate cycles') + eligible = {kind:[r for r in measured if r['kind'] == kind and r['diagnostic_mode'] == 'light' and + r['steps'] == 1200 and r['context_sha256'] == target_context_sha256] for kind in ('training','probe')} + missing = [kind+' complete matched-context light 1200-tick cycle' for kind,rows in eligible.items() if not rows] + result = dict(status='INSUFFICIENT-MEASUREMENTS' if missing else 'ESTIMATED-NOT-AUTHORIZED', + inventory=deepcopy(INVENTORY), measured_cycles=measured, missing=missing, + eligible_cycle_ids={kind:[r['cycle_id'] for r in rows] for kind,rows in eligible.items()}, + symbolic_wall='cycle_multiplier * (30000 * max(train_end_to_end) + 3920 * max(probe_end_to_end)) + explicit_reserves', + symbolic_storage='cycle_multiplier * (30000 * max(train_full_bytes) + 3920 * max(probe_full_bytes)) + explicit_reserves', + estimated_wall_seconds=None, estimated_output_bytes=None, caps=deepcopy(limits), caps_pass=None, + reserves=deepcopy(reserves), basis='observed maxima; no fitted throughput, amortization or deletion assumptions', + limitations=['caller authenticates receipt bytes, event identity, complete inventories and execution context', + 'reference and state-only idle are diagnostics only, never light training performance', + 'observed maxima cannot bound unobserved learning-dependent activity or contention', + 'retains every measured cycle output; no assumed cleanup/deduplication/compression gain', + 'reserve coverage and sufficiency remain caller responsibilities; no guarantee'], + engineering_scope='measurement/interface evidence only, not learning PASS', + external_identity_verified_here=False, scientific_learning_claimed=False, execution_authorized=False, + on_limit='STOP; never_reduce_inventory_or_shorten_trials') + if missing: + return result + count = {'training':30000,'probe':3920} + wall = sum(count[k]*max(r['timing']['end_to_end_seconds'] for r in rows) for k,rows in eligible.items()) + size = sum(count[k]*max(r['output_bytes'] for r in rows) for k,rows in eligible.items()) + result['estimated_wall_seconds'] = math.ceil(reserves['cycle_multiplier']*wall + sum(reserves[k]['wall_seconds'] for k in RESERVE_ROLES)) + result['estimated_output_bytes'] = math.ceil(reserves['cycle_multiplier']*size + sum(reserves[k]['output_bytes'] for k in RESERVE_ROLES)) + if limits is not None: + result['caps_pass'] = result['estimated_wall_seconds'] <= limits['wall_seconds'] and result['estimated_output_bytes'] <= limits['output_bytes'] + if not result['caps_pass']: + result['status'] = 'STOP-BUDGET-EXCEEDED' + return result diff --git a/models/male-cns-lif/conditioning-execution-v1/execution_workflow.py b/models/male-cns-lif/conditioning-execution-v1/execution_workflow.py new file mode 100644 index 0000000..1a341d8 --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/execution_workflow.py @@ -0,0 +1,320 @@ +"""Finite, process-isolated boundary experiment; NOT the conditioning runner. + +Requires external preregistration SHA and exclusive outputs. No automatic retry. +The old AP worker is invoked as an executable only, never imported as a neural +module into this coordinator. Every child exit and output inventory is checked. +""" +from __future__ import annotations + +import argparse +import copy +from datetime import datetime, timezone +import hashlib +import json +import os +from pathlib import Path +import shutil +import subprocess +import sys +import time +import traceback + +import numpy as np + +import boundary_plan as PLAN +import execution_authority as A +import isolated_execution as W +from prepare_execution import ROOT, MODEL, EXP, AP, LAB, save, save_npz, npz, verify, copy_exclusive + +require,read,digest=A.require,A.read,A.digest +AP_WORKER=MODEL.parent/'conditioning-interface-v2/isolated_workers.py' + + +def registration(expected): + file=EXP/'protocol/preregistered.json' + require(digest(file)==expected,'External registration SHA changed') + reg=read(file) + require(reg['schema']=='conditioning-execution-registration-v1' and reg['engineering_only'] is True and + reg['scientific_acquisition_authorized'] is False,'Wrong experiment authority') + PLAN.validate_plan(reg['plan']) + verify({**reg['source_sha256'],**reg['input_sha256'],**reg['prepared_file_sha256']}) + return reg + + +def tree(directory,*,exclude_build=False): + result={} + for p in sorted(Path(directory).rglob('*')): + require(not p.is_symlink(),'Linked output forbidden') + if exclude_build and p.relative_to(directory).parts[0]=='extension-build':continue + if p.is_file():result[p.relative_to(directory).as_posix()]=dict(sha256=digest(p),bytes=p.stat().st_size) + else:require(p.is_dir(),'Nonregular output forbidden') + return result + + +class Workflow: + def __init__(self,sha): + self.started=time.perf_counter();self.sha=sha;self.reg=registration(sha) + self.deadline=self.started+self.reg['budget']['workflow_timeout_seconds'] + self.parents=copy.deepcopy(self.reg['parent_records']);self.jobs={};self.boundaries={} + self.completed=[];self.cycles=[];self.sampler=None + self.h=W.ap_helpers() + + def admission(self): + require(time.perf_counter()= + budget['minimum_free_reserve_bytes']+max(0,budget['output_cap_bytes']-used),'Finite disk cap/reserve exceeded') + + def allpins(self): + return {**self.reg['source_sha256'],**self.reg['input_sha256'],**self.reg['prepared_file_sha256'], + str(EXP/'protocol/preregistered.json'):self.sha} + + def launch(self,name,job,*,ap=False): + self.admission() + loc={n:EXP/f'{sub}/{name}{suffix}' for n,sub,suffix in + (('job','jobs','.json'),('receipt','receipts','.worker.json'),('process','receipts','.process.json'), + ('stdout','logs','.stdout.log'),('stderr','logs','.stderr.log'))} + require(not any(p.exists() for p in loc.values()),'Exclusive process record exists; no retry') + require(job['receipt_file']==str(loc['receipt']),'Receipt location differs') + save(loc['job'],job);jobsha=digest(loc['job']) + script=AP_WORKER if ap else MODEL/'isolated_execution.py' + command=[sys.executable,'-B',str(script)] + if ap:command.append(job['kind']) + command+=['--job',str(loc['job']),'--job-sha256',jobsha] + start=time.perf_counter();started_utc=datetime.now(timezone.utc).isoformat() + timeout=min(self.reg['budget']['child_timeout_seconds'],self.deadline-start) + require(timeout>0,'No remaining workflow wall allowance') + timed_out=False;exit_code=None + with loc['stdout'].open('xb') as out,loc['stderr'].open('xb') as err: + try:exit_code=subprocess.run(command,cwd=MODEL,stdout=out,stderr=err,timeout=timeout).returncode + except subprocess.TimeoutExpired:timed_out=True + exited=time.perf_counter() + base=dict(schema='conditioning-execution-process-v1',name=name,kind=job['kind'],registration_sha256=self.sha, + argv=command,job_sha256=jobsha,exit_code=exit_code,timed_out=timed_out,started_utc=started_utc, + spawn_monotonic=start,exit_monotonic=exited,child_wall_seconds=exited-start, + stdout_sha256=digest(loc['stdout']),stderr_sha256=digest(loc['stderr'])) + try: + require(exit_code==0 and not timed_out and loc['receipt'].is_file(),'Child failed; preserve partial outputs') + receipt=read(loc['receipt']);expected='phase4ap-isolated-worker-receipt-v1' if ap else 'conditioning-isolated-execution-receipt-v1' + require(receipt['schema']==expected and receipt['complete'] is True and receipt['job_sha256']==jobsha and + receipt['registration_sha256']==self.sha and receipt['kind']==job['kind'] and + receipt['job_id']==job['job_id'] and receipt['worker_sha256']==job['worker_sha256'], + 'Real worker receipt identity differs') + expectedpins={**job['input_sha256'],str(loc['job']):jobsha,str(script):job['worker_sha256']} + if not ap: + expectedpins[str(AP_WORKER)]=W.AP_SHA + expectedpins.update({str(MODEL/n):s for n,s in job['source_sha256'].items()}) + else:expectedpins.update({v['path']:v['sha256'] for v in job['sources'].values()}) + require(receipt['verified_input_sha256' if ap else 'input_sha256']==expectedpins, + 'Child did not verify exact external source/input closure') + actual={k:v['sha256'] for k,v in tree(Path(job['output_dir'])).items()} + require(actual and actual==receipt['output_files_sha256'] and receipt['output_dir']==job['output_dir'], + 'Actual output inventory differs from successful worker') + require(digest(str(loc['receipt'])+'.attempt.json')==receipt['attempt_sha256'],'Attempt record changed') + verify(expectedpins);self.admission() + end=time.perf_counter() + record=dict(base,complete=True,worker_receipt_sha256=digest(loc['receipt']), + validation_end_monotonic=end,end_to_end_seconds=end-start,validation_wall_seconds=end-exited, + output_bytes=sum(v['bytes'] for v in tree(Path(job['output_dir'])).values()), + output_inventory_sha256=hashlib.sha256(json.dumps(actual,sort_keys=True).encode()).hexdigest(), + timing_scope='before child spawn through actual exit and independent parent file verification') + save(loc['process'],record) + except BaseException as exc: + if not loc['process'].exists():save(loc['process'],dict(base,complete=False,error_type=type(exc).__name__,error=str(exc))) + raise + self.jobs[name]=dict(loc={k:str(v) for k,v in loc.items()},job=job,process=record,receipt=receipt) + print(json.dumps(dict(child=name,complete=True,end_to_end_seconds=record['end_to_end_seconds']),sort_keys=True),flush=True) + return receipt + + def new_job(self,name,kind,authority,*,output=None,**extra): + auth=read(authority);pins=self.allpins();pins[str(authority)]=digest(authority) + if kind=='sample':pass + else:pins.update(A.files_from_authority(auth)) + return dict(schema=W.SCHEMA,job_id=name,kind=kind,registration_sha256=self.sha, + worker_sha256=self.reg['source_sha256'][str(MODEL/'isolated_execution.py')], + source_sha256={n:self.reg['source_sha256'][str(MODEL/n)] for n in W.SOURCES[kind]},input_sha256=pins, + output_dir=str(output or EXP/'runs'/name),receipt_file=str(EXP/'receipts'/f'{name}.worker.json'), + budget={k:self.reg['budget'][k] for k in ('root','output_cap_bytes','minimum_free_reserve_bytes')}, + authority_file=str(authority),authority_sha256=digest(authority),**extra) + + def ap_job(self,name,kind,*,case=None,overlay=None,authority=None,output): + pins=self.allpins() + job=dict(schema='phase4ap-isolated-worker-job-v1',job_id=name,kind=kind,registration_sha256=self.sha, + worker_sha256=W.AP_SHA,output_dir=str(output),receipt_file=str(EXP/'receipts'/f'{name}.worker.json')) + if kind=='brain': + for path in (case/'spec.json',case/'requests.npz',overlay['overlay_file'],overlay['overlay_manifest']):pins[str(path)]=digest(path) + job.update(case_dir=str(case),build_root=str(EXP/'extension-build'),**overlay,diagnostic_mode='light' if name.startswith('performance') else 'reference', + admission=dict(gpu_uuid=self.h.GPU,comfy_queue_urls=list(self.h.COMFY_URLS)), + sources={n:dict(path=str(LAB/(n+'.py')),sha256=self.h.SOURCE_PINS[n]) for n in ('runtime_light','runtime_continuation')}) + else: + value=read(authority);pins.update(A.files_from_authority(value));pins[str(authority)]=digest(authority) + job.update(authority_file=str(authority),authority_sha256=digest(authority), + sources={'conditioning_epoch':dict(path=str(LAB/'conditioning_epoch.py'),sha256=self.h.SOURCE_PINS['conditioning_epoch'])}) + job['input_sha256']=pins + return job + + def cpu_verify(self,key,parent): + name=key+'--cpu-verify';authority=Path(parent['epoch_authority']['path']);update=Path(parent['update_dir']) + job=self.new_job(name,'validate_cpu',authority,update_dir=str(update),chain_sha256=digest(update/'chain-receipt.json')) + # Original epoch authority doesn't include its future update files. + job['input_sha256'].update({str(p):digest(p) for p in update.iterdir()}) + self.launch(name,job) + parent['cpu_validation']=str(Path(job['output_dir'])/'validation.json') + + def materialize(self,call,parent=None): + group=call['case_group'];case=EXP/'cases'/group + spec=read(self.reg['case_templates'][group]) + if parent is not None: + spec.update(parent_checkpoint_manifest_sha256=parent['checkpoint_sha256'],global_tick_offset=call['global_tick_offset']) + if call['runner']=='runtime_light' and call['call_id'].startswith('performance'): + request=Path(self.parents[self.reg['plan']['imported_parents']['D1']['role']]['epoch_authority']['path']) + source=Path(read(request)['files']['requests']['path']);arrays=None + elif call['runner']=='runtime_light':source=EXP/'inputs/static/mechanical-requests.npz';arrays=None + elif call['runner']=='boundary_oracle': + pp=self.parents[call['prefix_replay_parent']] + arrays=npz(read(pp['epoch_authority']['path'])['files']['requests']['path']);source=None + elif call['input_policy']=='externally_pinned_CS_probe': + require(self.sampler is not None,'Evaluation input not actually sampled') + source=Path(self.sampler['output_dir'])/f"{call['cue']}-requests.npz";arrays=None + else:source=None;arrays=dict(tick=np.empty(0,np.int32),neuron_idx=np.empty(0,np.int32)) + if case.exists(): + require(read(case/'spec.json')==spec,'Shared case metadata differs') + if source:require(digest(case/'requests.npz')==digest(source),'Shared saved inputs differ') + else:require(all(npz(case/'requests.npz')[k].tobytes()==v.tobytes() for k,v in arrays.items()),'Shared request arrays differ') + else: + case.mkdir();save(case/'spec.json',spec) + if source:copy_exclusive(source,case/'requests.npz') + else:save_npz(case/'requests.npz',arrays) + return case + + def sources(self,epoch): + files=copy.deepcopy(epoch['files']) + roles=('runtime_continuation_source','runtime_light_source','conditioning_update_source', + 'eligibility_bridge_source','phase4j_source','am_overlay_source') + result={r:files[r] for r in roles} + for role,name in (('conditioning_epoch_source','conditioning_epoch.py'), + ('conditioning_preflight_compare_source','conditioning_preflight_compare.py'), + ('engineering_compare_source','engineering_compare.py')):result[role]=A.pin(LAB/name) + for role,name in (('updated_continuation_source','updated_continuation.py'),('cpu_validator_source','validate_update_cpu.py')): + result[role]=A.pin(MODEL/name) + return result + + def boundary(self,call,case,parent): + transition=call['transition'];epoch=read(parent['epoch_authority']['path']) + before=parent['before_overlay'] if transition=='epoch_update' else parent['after_overlay'] + path=EXP/'authorities'/f"{call['call_id']}.json" + value=A.boundary_authority(boundary_id=call['call_id'],registration_sha256=self.sha,case=case,parent=parent['directory'], + transition=transition,input_policy=call['input_policy'],mode=call['diagnostic_mode'], + before_overlay=before,after_overlay=parent['after_overlay'],source_files=self.sources(epoch),frozen_inputs=epoch['frozen_inputs'], + epoch=parent['epoch_authority']['path'] if transition=='epoch_update' else None, + update_dir=parent['update_dir'] if transition=='epoch_update' else None, + cpu_receipt=parent['cpu_validation'] if transition=='epoch_update' else None) + save(path,value);self.boundaries[call['call_id']]=path + return path + + def fresh(self,call): + name=call['call_id'];case=self.materialize(call) + performance=name.startswith('performance') + prior=self.parents[self.reg['plan']['imported_parents']['D1']['role']] + overlay=prior['before_overlay'] if performance else self.reg['initial_overlay'] + output=EXP/'runs'/name + result=self.launch(name,self.ap_job(name,'brain',case=case,overlay=overlay,output=output),ap=True) + auth=EXP/'authorities'/f'{name}.epoch.json' + value=A.epoch_authority(read(prior['epoch_authority']['path']),case=case,checkpoint=output,overlay=overlay, + epoch_id=name,us_tick=900 if performance else 1199) + save(auth,value);update=EXP/'updates'/name + self.launch(name+'--original-E',self.ap_job(name+'--original-E','update',authority=auth,output=update),ap=True) + parent=dict(directory=str(output),checkpoint_sha256=result['result']['checkpoint_manifest_sha256'], + epoch_authority=A.pin(auth),update_dir=str(update),before_overlay=overlay, + after_overlay=dict(overlay_file=str(update/'delta-after.npz'),overlay_manifest=str(update/'overlay-manifest.json'))) + self.cpu_verify(name,parent);self.parents[name]=parent + return result + + def execute_call(self,call): + name=call['call_id'];runner=call['runner'] + if runner=='runtime_light':return self.fresh(call) + if runner=='fresh_probe': + require(call.get('US') is False and call.get('plastic_update') is False and + call.get('feedback_to_idle') is False and call['parent'] is None and + call['overlay_after_role']=='AP.initial_overlay','Fresh probe scope differs') + case=self.materialize(call) + output=EXP/'runs'/name + result=self.launch(name,self.ap_job(name,'brain',case=case,overlay=self.reg['initial_overlay'],output=output),ap=True) + return result + if runner=='boundary_oracle': + ref='mechanical--updated--reference' if name.startswith('mechanical') else 'D1--updated--reference' + authority=self.boundaries[ref];case=self.materialize(call) + job=self.new_job(name,'oracle',authority,build_root=str(EXP/'extension-build'),oracle_case=str(case), + oracle_spec_sha256=digest(case/'spec.json'),oracle_requests_sha256=digest(case/'requests.npz')) + job['input_sha256'].update({str(case/n):digest(case/n) for n in ('spec.json','requests.npz')}) + else: + parent=self.parents[call['parent']];case=self.materialize(call,parent) + if runner=='runtime_continuation': + authority=self.boundaries[name.split('--')[0]+'--updated--reference'] + job=self.new_job(name,'unchanged',authority,case_dir=str(case),build_root=str(EXP/'extension-build'), + diagnostic_mode='reference',checkpoint_dir=parent['directory'],checkpoint_sha256=parent['checkpoint_sha256'],**parent['before_overlay']) + else: + if call['input_policy']=='externally_pinned_CS_probe': + require(call.get('US') is False and call.get('plastic_update') is False and call.get('feedback_to_idle') is False, + 'Probe cannot request US, updates, or feed idle') + authority=self.boundary(call,case,parent) + job=self.new_job(name,'updated',authority,build_root=str(EXP/'extension-build'),diagnostic_mode=call['diagnostic_mode']) + result=self.launch(name,job) + if runner=='updated_continuation': + parent=self.parents[call['parent']] + self.parents[name]={**copy.deepcopy(parent), 'directory':job['output_dir'], + 'checkpoint_sha256':result['result']['checkpoint_manifest_sha256'], + 'before_overlay':parent['after_overlay']} + return result + + def run(self): + attempt=EXP/'audit/execution-attempt.json' + require(not attempt.exists(),'Workflow already attempted; no automatic retry') + save(attempt,dict(registration_sha256=self.sha,started_utc=datetime.now(timezone.utc).isoformat(), + pid=os.getpid(),monotonic_origin=self.started,no_automatic_retry=True)) + for arm,descriptor in self.reg['plan']['imported_parents'].items():self.cpu_verify('AP-'+arm,self.parents[descriptor['role']]) + for call in self.reg['plan']['calls']: + if call['input_policy']=='externally_pinned_CS_probe' and self.sampler is None: + job=self.new_job('evaluation-inputs','sample',EXP/'protocol/preregistered.json') + self.sampler=self.launch('evaluation-inputs',job) + started=time.perf_counter();before=tree(EXP,exclude_build=True) + build_before=tree(EXP/'extension-build') if (EXP/'extension-build').is_dir() else {} + self.execute_call(call) + after=tree(EXP,exclude_build=True) + build_after=tree(EXP/'extension-build') if (EXP/'extension-build').is_dir() else {} + end=time.perf_counter() + self.admission() + require(all(k in after and after[k]==v for k,v in before.items()),'Earlier output was modified') + created={k:v for k,v in after.items() if k not in before} + record=dict(call=call,complete=True,start_monotonic=started,end_monotonic=end,wall_seconds=end-started, + output_inventory=created,output_bytes=sum(v['bytes'] for v in created.values()), + mutable_build_inventory_before=build_before,mutable_build_inventory_after=build_after, + mutable_build_bytes_before=sum(v['bytes'] for v in build_before.values()), + mutable_build_bytes_after=sum(v['bytes'] for v in build_after.values()), + inventory_scope='all new immutable artifacts; separate actual mutable build before/after inventory; shared prior setup excluded', + source_registration_sha256=self.sha,learning_pass_claimed=False) + save(EXP/'receipts'/f"{call['call_id']}.cycle.json",record) + self.cycles.append(record);self.completed.append(call['call_id']) + self.admission() + require(len(self.completed)==self.reg['plan']['counts']['new_brain_calls'] and + len(self.jobs)==self.reg['plan']['counts']['total_children'],'Actual finite call inventory differs') + self.admission() + save(EXP/'audit/execution-complete.json',dict(complete=True,registration_sha256=self.sha, + completed_calls=self.completed,processes={k:v['loc']['process'] for k,v in self.jobs.items()}, + boundary_authorities={k:str(v) for k,v in self.boundaries.items()},parents=self.parents, + wall_seconds=time.perf_counter()-self.started,numerical_comparison_complete=False, + scientific_learning_pass_claimed=False,candidate_generated=False)) + + +if __name__=='__main__': + parser=argparse.ArgumentParser(description=__doc__) + parser.add_argument('--registration-sha256',required=True) + args=parser.parse_args() + try:Workflow(args.registration_sha256).run() + except BaseException as exc: + stop=EXP/'audit/stopped.json' + if stop.parent.is_dir() and not stop.exists():save(stop,dict(complete=False,error_type=type(exc).__name__,error=str(exc), + registration_sha256=args.registration_sha256,retry_forbidden=True,traceback=traceback.format_exc())) + raise diff --git a/models/male-cns-lif/conditioning-execution-v1/finalize_execution.py b/models/male-cns-lif/conditioning-execution-v1/finalize_execution.py new file mode 100644 index 0000000..ef55f49 --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/finalize_execution.py @@ -0,0 +1,124 @@ +"""Exclusive finite engineering seal; never creates a learned candidate. + +Run only after independent numerical/budget reports and a NEW integrity process. +Report hashes are external arguments. Historical regression evidence is preserved, +not relabeled as a newly executed five-trial numerical regression. +""" +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import subprocess +import sys +from types import ModuleType + +import execution_authority as A +from prepare_execution import ROOT, EXP, MODEL, save, verify + +HELPER = ROOT/'models/male-cns-lif/pn-recruitment-audit-v1/publication.py' +HELPER_SHA = '7a79c499098b74163e7bb16adabcbc54e7069dafa204d0b99c7f4c03d79f8016' +require, read, digest = A.require, A.read, A.digest + + +def helper(): + raw = HELPER.read_bytes() + require(hashlib.sha256(raw).hexdigest() == HELPER_SHA, 'Frozen seal verifier changed') + module = ModuleType('_execution_seal_pure_verifier') + module.__file__ = str(HELPER) + exec(compile(raw, str(HELPER), 'exec'), module.__dict__) + return module + + +def seal(directory): + directory = Path(directory) + require(directory.is_absolute() and directory.is_dir(), 'Exact real seal root required') + require(not any((directory/n).exists() for n in ('manifest.json', 'checksums.sha256')), 'Never replace seal controls') + paths = sorted(directory.rglob('*')) + require(all(not p.is_symlink() and not (hasattr(p, 'is_junction') and p.is_junction()) and + '__pycache__' not in p.parts and (p.is_file() or p.is_dir()) for p in (directory, *paths)), + 'Unsafe seal payload') + files = [dict(path=p.relative_to(directory).as_posix(), bytes=p.stat().st_size, sha256=digest(p)) + for p in paths if p.is_file()] + require(files, 'Empty seal forbidden') + save(directory/'manifest.json', dict(root=str(directory), files=files)) + with (directory/'checksums.sha256').open('x', encoding='utf-8', newline='\n') as out: + for row in files: + out.write(row['sha256']+' '+row['path']+'\n') + out.write(digest(directory/'manifest.json')+' manifest.json\n') + return helper().verify_seal(directory) + + +def finalize(reg_sha, numerical_sha, budget_sha, integrity_sha): + require(sys.dont_write_bytecode and 'torch' not in sys.modules, 'Fresh CPU -B process required') + require(not any((EXP/n).exists() for n in ('audit/final-status.json', 'manifest.json', 'checksums.sha256', 'audit/stopped.json')), + 'Stopped, finalized or partly sealed workflow requires a separate explicit failure audit') + reg_path = EXP/'protocol/preregistered.json' + require(digest(reg_path) == reg_sha, 'External registration changed') + reg = read(reg_path) + require(reg['schema'] == 'conditioning-execution-registration-v1' and reg['engineering_only'] is True and + reg['scientific_acquisition_authorized'] is False, 'Wrong scope') + verify({**reg['source_sha256'], **reg['input_sha256'], **reg['prepared_file_sha256']}) + reports = {} + for key, relative, sha in ( + ('numerical', 'audit/numerical-comparison.json', numerical_sha), + ('budget', 'audit/budget-report.json', budget_sha), + ('integrity', 'regression/integrity-after.json', integrity_sha)): + path = EXP/relative + require(A.pin(path)['sha256'] == sha, 'External '+key+' report changed') + reports[key] = read(path) + numerical, budget, integrity = (reports[n] for n in ('numerical', 'budget', 'integrity')) + require(numerical['schema'] == 'conditioning-execution-numerical-audit-v1' and numerical['complete'] is True and + numerical['registration_sha256'] == reg_sha and numerical['actual_new_brain_calls'] == 23 and + numerical['actual_processes'] == 31 and numerical['actual_new_brain_ticks'] == 125200 and + numerical['learning_pass_claimed'] is numerical['candidate_generated'] is False, 'Wrong numerical coverage/scope') + status = numerical['status'] + require(status in ('PASS-EXACT-UPDATED-BOUNDARY-ENGINEERING-NOT-LEARNING', + 'FAIL-NUMERICAL-OR-LINEAGE', 'INCOMPLETE-CHANGED-EDGE-COVERAGE'), 'Unknown engineering outcome') + require(numerical['pass_all'] == status.startswith('PASS-'), 'Inconsistent engineering gate') + require(budget['registration_sha256'] == reg_sha and budget['learning_pass_claimed'] is False, + 'Wrong budget authority or scientific overclaim') + require(integrity['complete'] is True and integrity['AP']['pass_all'] is True and + integrity['old_experiments_reexecuted'] is integrity['old_files_modified'] is False and + integrity['five_frozen_reference_trials_rerun_here'] is integrity['scientific_learning_pass_claimed'] is False, + 'Historical integrity/scope not verified') + # Recheck the actual file-bound evidence at sealing, not only old PASS flags. + import audit_execution + import budget_report + require(audit_execution.same_json(audit_execution.audit(reg_sha, save_report=False), numerical), + 'Numerical evidence changed after the independent audit') + require(audit_execution.same_json(budget_report.report(reg_sha, save_report=False), budget), + 'Budget evidence changed after the independent audit') + # Historical integrity has a different frozen runtime import owner; isolate it. + seal_integrity = EXP/'regression/integrity-at-seal.json' + require(not seal_integrity.exists(), 'No automatic retry of partly completed finalization') + command = [sys.executable, '-B', str(MODEL/'prepare_execution.py'), 'integrity', '--output', str(seal_integrity)] + result = subprocess.run(command, cwd=MODEL, capture_output=True, text=True, timeout=1800) + require(result.returncode == 0 and seal_integrity.is_file(), + 'Independent integrity-at-seal process failed: '+result.stderr[-2000:]) + require(audit_execution.same_json(read(seal_integrity), integrity), 'Frozen integrity changed before sealing') + final = dict(schema='conditioning-execution-final-v1', status=status, registration_sha256=reg_sha, + numerical_sha256=numerical_sha, budget_sha256=budget_sha, integrity_after_sha256=integrity_sha, + integrity_at_seal_sha256=digest(seal_integrity), numerical_and_budget_recomputed_at_seal=True, + actual_new_brain_calls=23, actual_new_brain_ticks=125200, actual_execution_child_processes=31, + historical_integrity_after_pass=True, historical_five_trial_regression='AM sealed evidence preserved; not rerun here', + scientific_acquisition_executed=False, scientific_acquisition_authorized=False, + candidate_generated=False, learning_pass_claimed=False, + source_code_commit=reg['source_code_commit'], entire_model_directory_sealed=False, + source_scope='All registered source bytes and committed snapshot frozen by this experiment seal', + bulk_data_git_backup_claimed=False) + save(EXP/'audit/final-status.json', final) + checked = seal(EXP) + return dict(status=status, manifest_sha256=checked['manifest_sha256'], checksums_sha256=checked['checksums_sha256'], + final_sha256=digest(EXP/'audit/final-status.json'), payload_files=checked['payload_file_count'], + payload_bytes=checked['payload_bytes'], learning_pass_claimed=False) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + for name in ('registration', 'numerical', 'budget', 'integrity'): + parser.add_argument('--'+name+'-sha256', required=True) + args = parser.parse_args() + print(json.dumps(finalize(args.registration_sha256, args.numerical_sha256, args.budget_sha256, + args.integrity_sha256), sort_keys=True), flush=True) diff --git a/models/male-cns-lif/conditioning-execution-v1/isolated_execution.py b/models/male-cns-lif/conditioning-execution-v1/isolated_execution.py new file mode 100644 index 0000000..bd33575 --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/isolated_execution.py @@ -0,0 +1,240 @@ +"""Fresh exec workers for the NEW boundary-validation branch, not learning. + +The unchanged AP worker supplies read-only path/hash/environment helpers. +One worker imports exactly one new scientific entry point. The process parent +must authenticate registration, record actual exit status and independently +check the result. A worker receipt cannot prove its own future successful exit. +""" +from __future__ import annotations + +import argparse +from datetime import datetime, timezone +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import shutil +import sys +import time + +AP_SHA = 'e47651a8ea99d7c44115107ff6b5dc71312183609ae11c891db65bb21ed61e30' +MODEL = Path(__file__).resolve().parent +AP = MODEL.parent / 'conditioning-interface-v2' / 'isolated_workers.py' +SCHEMA = 'conditioning-isolated-execution-job-v1' +C_PATH = MODEL.parent/'olfactory-conditioning-lab-v1/runtime_continuation.py' +C_SHA = '0b8e8e0a6a23a079dc62a88355ce355d53b3ae22f6665519a8f6aef2dcfc5aba' +COMMON = {'schema', 'job_id', 'kind', 'registration_sha256', 'worker_sha256', + 'source_sha256', 'input_sha256', 'output_dir', 'receipt_file', 'budget'} +FIELDS = { + 'updated': {'authority_file', 'authority_sha256', 'build_root', 'diagnostic_mode'}, + 'oracle': {'authority_file', 'authority_sha256', 'build_root', 'oracle_case', + 'oracle_spec_sha256', 'oracle_requests_sha256'}, + 'validate_cpu': {'authority_file', 'authority_sha256', 'update_dir', 'chain_sha256'}, + 'unchanged': {'authority_file', 'authority_sha256', 'case_dir', 'build_root', 'diagnostic_mode', + 'overlay_file', 'overlay_manifest', 'checkpoint_dir', 'checkpoint_sha256'}, + 'sample': {'authority_file', 'authority_sha256'}, +} +SOURCES = { + 'updated': {'updated_continuation.py'}, + 'oracle': {'updated_continuation.py', 'boundary_oracle.py'}, + 'validate_cpu': {'validate_update_cpu.py'}, + 'unchanged': set(), + 'sample': {'boundary_sampler.py','boundary_plan.py'}, +} + + +def ap_helpers(): + raw = AP.read_bytes() + if hashlib.sha256(raw).hexdigest() != AP_SHA: + raise ValueError('Frozen AP helper SHA differs') + name = '_conditioning_execution_AP_helpers' + if name in sys.modules: + raise ValueError('Fresh exec helper binding required') + spec = importlib.util.spec_from_file_location(name, AP) + module = importlib.util.module_from_spec(spec) + sys.modules[name] = module + exec(compile(raw, str(AP), 'exec'), module.__dict__) + return module + + +def validate_job(h, job_file, expected_sha): + job_file = h.absolute_path(str(job_file), kind='file') + h.require(h.valid_sha(expected_sha) and h.digest(job_file) == expected_sha, 'External job pin differs') + job = h.read_json(job_file) + kind = job.get('kind') + h.require(kind in FIELDS and set(job) == COMMON | FIELDS[kind] and job['schema'] == SCHEMA, + 'Wrong job schema/fields') + h.require(type(job['job_id']) is str and job['job_id'] and + all(c.isascii() and (c.isalnum() or c in '-_') for c in job['job_id']), 'Unsafe job ID') + h.require(h.valid_sha(job['registration_sha256']) and job['worker_sha256'] == h.digest(__file__), + 'Missing registration/worker binding') + h.require(type(job['source_sha256']) is dict and set(job['source_sha256']) == SOURCES[kind] and + type(job['input_sha256']) is dict and job['input_sha256'], 'Wrong source/input inventory') + pins = {job_file: expected_sha, Path(__file__).resolve(): job['worker_sha256'], AP: AP_SHA} + for path, sha in job['input_sha256'].items(): + h._put(pins, h.absolute_path(path, kind='file'), sha) + for name, sha in job['source_sha256'].items(): + h._put(pins, h.absolute_path(str(MODEL / name), kind='file'), sha) + authority = h.absolute_path(job['authority_file'], kind='file') + h._required(pins, authority, job['authority_sha256']) + if kind=='sample': + h.require(job['authority_sha256']==job['registration_sha256'],'Sampler registration and authority differ') + registered=h.read_json(authority) + h.require(registered.get('schema')=='conditioning-execution-registration-v1','Wrong sampler registration') + for field in ('source_sha256','input_sha256','prepared_file_sha256'): + for path,sha in registered[field].items():h._required(pins,path,sha) + output, receipt = (h.absolute_path(job[name]) for name in ('output_dir', 'receipt_file')) + attempt = Path(str(receipt) + '.attempt.json') + h.require(output.parent.is_dir() and receipt.parent.is_dir() and + not any(p.exists() for p in (output, receipt, attempt)), 'Output/receipt exists; no automatic retry') + protected = {p.parent for p in pins} + h.require(not any(output.is_relative_to(p) or receipt.is_relative_to(p) for p in protected) and + not receipt.is_relative_to(output) and not output.is_relative_to(receipt.parent), + 'Output/receipt overlaps protected input or each other') + paths = dict(job=job_file, authority=authority, output=output, receipt=receipt, attempt=attempt) + if kind in ('updated', 'oracle', 'unchanged'): + build = h.absolute_path(job['build_root']) + h.require(not any(build.is_relative_to(p) for p in protected) and not build.is_relative_to(output) and + not output.is_relative_to(build), 'Build overlaps protected data') + paths['build'] = build + if kind in ('updated', 'unchanged'): + h.require(job['diagnostic_mode'] in ('light', 'reference'), 'Unknown diagnostic mode') + if kind == 'unchanged': + h._required(pins, C_PATH, C_SHA) + paths['case'] = h.absolute_path(job['case_dir'], kind='dir') + paths['checkpoint'] = h.absolute_path(job['checkpoint_dir'], kind='dir') + h._required(pins, paths['checkpoint']/'checkpoint-manifest.json', job['checkpoint_sha256']) + for role in ('overlay_file', 'overlay_manifest'): + paths[role] = h._required(pins, job[role]) + for name in ('spec.json', 'requests.npz'): h._required(pins, paths['case']/name) + h.require(not output.is_relative_to(paths['checkpoint']) and not paths['build'].is_relative_to(paths['checkpoint']), + 'Execution overlaps frozen parent') + elif kind == 'oracle': + paths['case'] = h.absolute_path(job['oracle_case'], kind='dir') + for name, key in (('spec.json', 'oracle_spec_sha256'), ('requests.npz', 'oracle_requests_sha256')): + h._required(pins, paths['case'] / name, job[key]) + elif kind == 'validate_cpu': + paths['update'] = h.absolute_path(job['update_dir'], kind='dir') + h._required(pins, paths['update'] / 'chain-receipt.json', job['chain_sha256']) + h.require(not output.is_relative_to(paths['update']), 'CPU receipt overlaps source update') + budget = job['budget'] + h.require(type(budget) is dict and set(budget) == {'root', 'output_cap_bytes', 'minimum_free_reserve_bytes'}, + 'Explicit bounded resource plan required') + paths['budget'] = h.absolute_path(budget['root'], kind='dir') + h.require(paths['budget'].name.startswith('phase4') and output.is_relative_to(paths['budget']) and + receipt.is_relative_to(paths['budget']) and + (kind in ('validate_cpu','sample') or paths['build'].is_relative_to(paths['budget'])), 'Execution leaves assigned experiment') + for name in ('output_cap_bytes', 'minimum_free_reserve_bytes'): + h.require(type(budget[name]) is int and budget[name] > 0, 'Positive resource limit required') + h.verify_pins(pins) + return job, paths, pins + + +def budget_check(h, job, paths): + used = 0 + for path in paths['budget'].rglob('*'): + h.require(not path.is_symlink(), 'Linked output forbidden') + if path.is_file(): + used += path.stat().st_size + else: + h.require(path.is_dir(), 'Nonregular output forbidden') + cap = job['budget']['output_cap_bytes'] + free = shutil.disk_usage(paths['budget']).free + h.require(used <= cap and free >= job['budget']['minimum_free_reserve_bytes'] + max(0, cap - used), + 'Resource budget exceeded; do not change protocol or retry') + return dict(output_bytes=used, free_bytes=free, output_cap_bytes=cap) + + +def bind_unchanged_authority(h, job, paths, pins): + admitted=h.read_json(paths['authority']) + h.require(admitted.get('schema')=='conditioning-updated-boundary-v1' and admitted.get('transition')=='epoch_update' and + admitted['diagnostic_mode']==job['diagnostic_mode'] and admitted['registration_sha256']==job['registration_sha256'] and + admitted['expected_effective_weight_before_sha256']==admitted['expected_effective_weight_after_sha256'], + 'Old C comparison requires its unchanged-weight registered authority') + h.require(admitted['parent']==dict(directory=str(paths['checkpoint']),checkpoint_manifest_sha256=job['checkpoint_sha256']), + 'Old C job parent differs from authority') + for pair,role in ((admitted['case']['spec'],'spec.json'),(admitted['case']['requests'],'requests.npz')): + h.require(pair['path']==str(paths['case']/role) and pins[paths['case']/role]==pair['sha256'], + 'Old C case differs from authority') + for role,key in (('file','overlay_file'),('manifest','overlay_manifest')): + pair=admitted['overlay_before'][role] + h.require(pair['path']==str(paths[key]) and pins[paths[key]]==pair['sha256'], + 'Old C must use actual before snapshot, not equal after data') + def visit(value): + if type(value)is dict: + if set(value)=={'path','sha256'}:h._required(pins,value['path'],value['sha256']) + else: + for key,item in value.items(): + if key=='frozen_inputs': + for path,sha in item.items():h._required(pins,path,sha) + else:visit(item) + elif type(value)is list: + for item in value:visit(item) + visit(admitted) + return admitted + + +def execute(job_file, expected_sha): + started = time.perf_counter() + h = ap_helpers() + h.require(sys.dont_write_bytecode, 'Use Python -B') + job, paths, pins = validate_job(h, job_file, expected_sha) + kind = job['kind'] + h._clean_process('update' if kind == 'validate_cpu' else 'brain') + before = budget_check(h, job, paths) + h.save_json(paths['attempt'], dict(schema='conditioning-execution-attempt-v1', complete=False, + job_sha256=expected_sha, pid=os.getpid(), started_utc=datetime.now(timezone.utc).isoformat(), + no_automatic_retry=True)) + admission = None if kind == 'validate_cpu' else h._admit_brain() + if kind == 'unchanged': + module = h._load_source('_conditioning_execution_entry', C_PATH, C_SHA) + bind_unchanged_authority(h, job, paths, pins) + result = module.run_continued(paths['case'], paths['output'], paths['build'], paths['overlay_file'], + paths['overlay_manifest'], paths['checkpoint'], mode=job['diagnostic_mode']) + checked = module.load_checkpoint(paths['output'], result['checkpoint_manifest_sha256']) + h.require(checked['summary'] == {k:v for k,v in result.items() if k!='checkpoint_manifest_sha256'}, + 'Original C returned checkpoint differs') + else: + entry = {'updated': 'updated_continuation.py', 'oracle': 'boundary_oracle.py', + 'validate_cpu': 'validate_update_cpu.py', 'sample': 'boundary_sampler.py'}[kind] + module = h._load_source('_conditioning_execution_entry', MODEL / entry, job['source_sha256'][entry]) + if kind == 'updated': + result = module.run(paths['authority'], job['authority_sha256'], paths['output'], paths['build'], + mode=job['diagnostic_mode']) + elif kind == 'oracle': + result = module.run(paths['authority'], job['authority_sha256'], job['source_sha256']['updated_continuation.py'], + paths['case'], job['oracle_spec_sha256'], job['oracle_requests_sha256'], paths['output'], paths['build']) + elif kind == 'validate_cpu': + paths['output'].mkdir() + result = module.validate(paths['authority'], job['authority_sha256'], paths['update'], + job['chain_sha256'], paths['output'] / 'validation.json') + elif kind == 'sample': + result = module.run(paths['authority'], job['authority_sha256'], paths['output']) + environment = h._environment('update' if kind == 'validate_cpu' else 'brain') + if kind not in ('validate_cpu','sample'): + h._verify_precision(environment, result['apl_component']['environment']) + outputs = h.tree_hashes(paths['output']) + h.verify_pins(pins) + after = budget_check(h, job, paths) + h.require(outputs == h.tree_hashes(paths['output']), 'Outputs changed during verification') + receipt = dict(schema='conditioning-isolated-execution-receipt-v1', complete=True, + job_id=job['job_id'], kind=kind, registration_sha256=job['registration_sha256'], job_sha256=expected_sha, + worker_sha256=job['worker_sha256'], attempt_sha256=h.digest(paths['attempt']), + input_sha256={str(p): sha for p, sha in pins.items()}, output_files_sha256=outputs, + output_dir=str(paths['output']), result=result, environment=environment, admission=admission, + resource_before=before, resource_after=after, wall_seconds=time.perf_counter()-started, + wall_scope='entry through verification; parent separately measures spawn/exit/parent validation', + process_exit_independently_observed=False, registration_authority_independently_proven=False, + scientific_learning_pass_claimed=False, candidate_generated=False, old_source_globals_patched=False) + h.save_json(paths['receipt'], receipt) + return receipt + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--job', required=True) + parser.add_argument('--job-sha256', required=True) + args = parser.parse_args() + receipt = execute(args.job, args.job_sha256) + print(json.dumps(dict(complete=True, job_id=receipt['job_id'], kind=receipt['kind'])), flush=True) diff --git a/models/male-cns-lif/conditioning-execution-v1/learning_gates.py b/models/male-cns-lif/conditioning-execution-v1/learning_gates.py new file mode 100644 index 0000000..3f4ad42 --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/learning_gates.py @@ -0,0 +1,562 @@ +"""Pure, pre-registration conditioning gates; never an execution authority. + +The numeric rules come from PROTOCOL_DRAFT sections 6-8 and the fixed +EVALUATION_POLICY_DRAFT. Two explicit pre-outcome operationalizations require +the caller's policy descriptor: reversal/retention downstream means the exact +nonMBONDownstream set, with legacyDownstream also reported; the old H mean +predicate is evaluated twice per (mode, original cue), using each DAN branch's +own complete control vectors, and both results must pass that separate audit. +Neither choice is described as the original H data layout. + +All evaluation functions are CPU/pure: descriptors and raw arrays are checked, +but disk receipts, anatomical identities, seed derivation, actual execution, +registration authority and full numerical/integrity audits remain EXTERNAL. +A supplied SHA or Boolean is not proof of any of those facts. Consequently no +return value claims scientific learning, execution approval or a candidate. +The only file read is the pinned Python dependency at module import time. +""" +from __future__ import annotations + +import hashlib +import json +from collections.abc import Mapping +from pathlib import Path +import types + +import numpy as np + + +METRICS_SHA256 = '47319e21db3c8b030b1bafbf0703cd86fb588d23bb29436accbacdba73d08c3c' +_path = Path(__file__).resolve().parent.parent / 'olfactory-conditioning-lab-v1' / 'metrics.py' +_source = _path.read_bytes() +if hashlib.sha256(_source).hexdigest() != METRICS_SHA256: + raise ValueError('Frozen metrics source SHA256 differs') +M = types.ModuleType('conditioning_execution_frozen_metrics') +M.__file__ = str(_path) +exec(compile(_source, str(_path), 'exec'), M.__dict__) +del _source + +SCHEMA = 'conditioning-learning-gates-v1' +REGISTRY_SCHEMA = 'conditioning-gate-registry-descriptor-v1' +AUTHORITY_SCHEMA = 'conditioning-data-authority-descriptor-v1' +PAIR_SCHEMA = 'conditioning-probe-pair-descriptor-v1' +DOWNSTREAM_POLICY = 'nonMBONDownstream-pre-outcome-operationalization-v1' +H_MAPPING = 'each-DAN-control-source-separately-both-pass-v1' +ASSAYS = ('normal', 'delayed', 'reversal', 'retention') +DAN_COMPARTMENTS = {'PAM15': "y5B'2a", 'PPL103': "y2a'1"} +CARRY = ('v', 'g', 'ref', 'spike_ring', 'apl_state', 'apl_pending', 'absolute_tick', 'delta', 'relevant_rng_state') +EXTERNAL = ('registration/source authority', 'anatomical membership', 'full seed derivation and alias inventory', + 'actual input/receipt files and completed trajectories', 'continuous state/queue/RNG restoration', + 'full finite/integrity/resource audits') + + +def require(ok, message): + if not ok: + raise ValueError(message) + + +def _keys(value, fields, label): + require(isinstance(value, Mapping) and set(value) == set(fields), 'Wrong ' + label + ' fields') + + +def _sha(value, label): + require(M._sha(value), 'Invalid SHA256: ' + label) + return value + + +def _token(value, label): + return M._token(value, label) + + +def descriptor_sha256(value): + """Canonical descriptor hash; this does NOT establish external authority.""" + return hashlib.sha256(json.dumps(value, sort_keys=True, separators=(',', ':'), + ensure_ascii=False, allow_nan=False).encode('utf-8')).hexdigest() + + +def _scope(): + return dict(external_authority_verified=False, scientific_learning_pass_claimed=False, + candidate_generated=False, execution_authorized=False, data_backed_claimed=False, + external_requirements=list(EXTERNAL), frozen_metrics_source_sha256=METRICS_SHA256) + + +def validate_registry(registry, *, expected_sha256): + """Check all 40 declared blocks and actual 63-bit seeds, never just labels. + +training_sources lists independent training/design source streams, excluding +counterfactual aliases. The external registrar must prove that this list is +complete and agrees with the frozen full trial/seed inventory. Evaluation is +one original A/B input pair per block, reused within that block across assays. +""" + _keys(registry, ('schema', 'protocol_sha256', 'population_binding_sha256', + 'nodes_raw_sha256', 'population_membership_sha256', 'blocks'), 'registry') + require(registry['schema'] == REGISTRY_SCHEMA, 'Wrong registry schema') + require(descriptor_sha256(registry) == _sha(expected_sha256, 'external registry'), 'Registry descriptor changed') + for field in ('protocol_sha256', 'population_binding_sha256', 'nodes_raw_sha256', 'population_membership_sha256'): + _sha(registry[field], field) + require(isinstance(registry['blocks'], Mapping) and len(registry['blocks']) == 40, 'Exactly 40 registered blocks required') + identities, streams, seeds = set(), set(), set() + for block_id, block in registry['blocks'].items(): + _token(block_id, 'block_id') + _keys(block, ('mode', 'dan_type', 'compartment_verbatim', 'conditioned_cue', 'replicate', + 'training_sources', 'evaluation_sources', 'evaluation_input_sha256'), 'block') + require(block['mode'] in M.MODES and block['dan_type'] in DAN_COMPARTMENTS and + block['compartment_verbatim'] == DAN_COMPARTMENTS[block['dan_type']] and + block['conditioned_cue'] in M.CUES and type(block['replicate']) is int and 1 <= block['replicate'] <= 5, + 'Unknown cell/compartment/replicate') + identity = (block['mode'], block['dan_type'], block['conditioned_cue'], block['replicate']) + require(identity not in identities, 'Duplicate cell/replicate') + identities.add(identity) + require(type(block['training_sources']) is list and block['training_sources'], 'Missing independent training/design sources') + _keys(block['evaluation_sources'], M.CUES, 'evaluation sources') + _keys(block['evaluation_input_sha256'], M.CUES, 'evaluation input hashes') + for cue in M.CUES: + _sha(block['evaluation_input_sha256'][cue], 'evaluation input') + entries = [(x, ('training', 'design')) for x in block['training_sources']] + entries += [(block['evaluation_sources'][cue], ('evaluation',)) for cue in M.CUES] + for source, allowed in entries: + _keys(source, ('stream_id', 'seed', 'split'), 'independent source') + _token(source['stream_id'], 'stream_id') + require(source['split'] in allowed and type(source['seed']) is int and 0 <= source['seed'] < 2**63, + 'Wrong source split or full63bit seed') + require(source['stream_id'] not in streams and source['seed'] not in seeds, + 'Independent source ID/seed collision, including training/evaluation overlap') + streams.add(source['stream_id']); seeds.add(source['seed']) + require(len(identities) == 40, 'Incomplete eight-cell/five-replicate inventory') + return dict(schema=SCHEMA, registry_sha256=expected_sha256, blocks=40, + independent_declared_streams=len(streams), **_scope()) + + +def _context(registry, authority, nodes=None, populations=None): + _keys(authority, ('schema', 'registry_sha256', 'verification_receipt_sha256', + 'execution_inventory_sha256', 'population_binding_sha256'), 'external data authority descriptor') + require(authority['schema'] == AUTHORITY_SCHEMA, 'Wrong data authority schema') + for field in ('registry_sha256', 'verification_receipt_sha256', 'execution_inventory_sha256', 'population_binding_sha256'): + _sha(authority[field], field) + validate_registry(registry, expected_sha256=authority['registry_sha256']) + require(authority['population_binding_sha256'] == registry['population_binding_sha256'], 'Population authority binding differs') + if nodes is not None: + membership = M.validate_populations(nodes, populations) + require(M._hash(nodes) == registry['nodes_raw_sha256'] and + membership['membership_sha256'] == registry['population_membership_sha256'], 'Raw population binding differs') + + +def _cell_rows(rows, registry, mode, dan_type, conditioned_cue): + require(mode in M.MODES and dan_type in DAN_COMPARTMENTS and conditioned_cue in M.CUES, 'Unknown cell') + require(type(rows) is list and len(rows) == 5, 'Exactly five independent training rows required') + expected = {key for key, value in registry['blocks'].items() + if (value['mode'], value['dan_type'], value['conditioned_cue']) == (mode, dan_type, conditioned_cue)} + require(len(expected) == 5 and {row['replicate_id'] for row in rows} == expected, 'Missing/duplicate/wrong-cell training rows') + for row in rows: + block = registry['blocks'][row['replicate_id']] + require(row['replicate_unit'] == 'independent_training', 'Evaluation draws are not training replicates') + require(row['training_stream_ids'] == [x['stream_id'] for x in block['training_sources']], 'Training source lineage differs') + require(row['evaluation_stream_ids'] == [block['evaluation_sources'][cue]['stream_id'] for cue in M.CUES], + 'Both registered evaluation inputs required; no extra evaluation replication') + return expected + + +def _pair(pair, block_id, registry, nodes, populations, used, *, policy, delta=None, checkpoint=None): + fields = ('schema', 'block_id', 'snapshot_id', 'receipt_sha256', 'probe_receipt_sha256', + 'evaluation_input_sha256', 'state_raw_sha256', 'states', 'state_policy', 'source_checkpoint_sha256', + 'delta_sha256', 'probe_steps', 'cs_start_tick', 'cs_stop_tick_exclusive', 'US_requested', + 'weight_updates_enabled', 'feedback_to_training_or_idle') + _keys(pair, fields, 'probe pair') + require(pair['schema'] == PAIR_SCHEMA and pair['block_id'] == block_id and pair['state_policy'] == policy, + 'Probe identity/state policy differs') + _token(pair['snapshot_id'], 'snapshot_id') + for field in ('receipt_sha256', 'source_checkpoint_sha256', 'delta_sha256'): + _sha(pair[field], field) + require(type(pair['probe_steps']) is int and pair['probe_steps'] == 1200 and + type(pair['cs_start_tick']) is int and pair['cs_start_tick'] == 0 and + type(pair['cs_stop_tick_exclusive']) is int and pair['cs_stop_tick_exclusive'] == 1000 and + pair['US_requested'] is False and pair['weight_updates_enabled'] is False and + pair['feedback_to_training_or_idle'] is False, 'Probe timing/US/update/feedback differs') + require(delta is None or pair['delta_sha256'] == delta, 'Probe delta source differs') + require(checkpoint is None or pair['source_checkpoint_sha256'] == checkpoint, 'Probe checkpoint source differs') + for field in ('probe_receipt_sha256', 'evaluation_input_sha256', 'state_raw_sha256', 'states'): + _keys(pair[field], M.CUES, field) + require(pair['evaluation_input_sha256'] == registry['blocks'][block_id]['evaluation_input_sha256'], + 'Probe must use the exact registered evaluation inputs') + for cue in M.CUES: + _sha(pair['probe_receipt_sha256'][cue], 'probe receipt') + checked = M.validate_state(pair['states'][cue], nodes=nodes, steps=1200) + require(pair['state_raw_sha256'][cue] == checked['hashes'], 'Probe raw arrays differ from descriptor hashes') + # Re-referencing one baseline is permitted, but a new snapshot cannot claim + # that it executed a probe by reusing another snapshot's receipt. + fingerprint = descriptor_sha256({key: value for key, value in pair.items() if key != 'states'}) + identity = pair['snapshot_id'] + require(identity not in used['snapshots'] or used['snapshots'][identity] == fingerprint, 'Snapshot identity reused with changed data') + for receipt in (pair['receipt_sha256'], *pair['probe_receipt_sha256'].values()): + require(receipt not in used['receipts'] or used['receipts'][receipt] == identity, 'Probe execution receipt reused across snapshots') + used['receipts'][receipt] = identity + require(len({pair['receipt_sha256'], *pair['probe_receipt_sha256'].values()}) == 3, 'A/B probes require distinct actual receipts') + used['snapshots'][identity] = fingerprint + return pair['states'] + + +def _arm_execution(value, *, assay, arm, used): + _keys(value, ('receipt_sha256', 'recording_verified', 'complete', 'completed_epochs', + 'reinforcement_opportunities', 'terminal_pairings', 'epoch_duration_ms', 'cs_duration_ms', 'us_tick', + 'initial_delta_sha256', 'terminal_delta_sha256', 'terminal_checkpoint_sha256', + 'neural_state_policy', 'weight_rule', 'reinforced_cue'), 'training execution descriptor') + for key in ('receipt_sha256', 'initial_delta_sha256', 'terminal_delta_sha256', 'terminal_checkpoint_sha256'): + _sha(value[key], key) + require(value['receipt_sha256'] not in used, 'Training receipt reused across arms/replicates') + used.add(value['receipt_sha256']) + require(value['complete'] is True and value['recording_verified'] is True, + 'Caller must supply a completed, externally verified execution descriptor') + for field, expected in (('completed_epochs', 150 if arm == 'unpaired' else 100), + ('reinforcement_opportunities', 0 if arm == 'CS-only' else 50), ('terminal_pairings', 50), + ('us_tick', 1500 if assay == 'delayed' else 900)): + require(type(value[field]) is int and value[field] == expected, 'Wrong terminal inventory: ' + field) + require(value['epoch_duration_ms'] == (180. if assay == 'delayed' else 120.) and value['cs_duration_ms'] == 100. and + value['neural_state_policy'] == 'fresh_epoch_delta_only' and value['weight_rule'] == 'frozen_temporal_eligibility_v2', + 'Frozen training timing/state/update policy differs') + + +def _report(assay, rows, registry, authority, mode, dan_type, conditioned_cue): + rows = sorted(rows, key=lambda row: row['replicate_id']) + count = sum(row['conjunction_pass'] for row in rows) + return dict(schema=SCHEMA, status='CONDITIONAL-PREDICATES-PASS' if count >= 3 else 'CONDITIONAL-PREDICATES-FAIL', + assay=assay, mode=mode, dan_type=dan_type, conditioned_cue=conditioned_cue, + predicate_pass=count >= 3, successful_same_replicate_conjunctions=count, required_majority=3, + training_replicates=5, replicates=rows, registry_sha256=authority['registry_sha256'], + external_data_authority_descriptor_sha256=descriptor_sha256(authority), + downstream_policy=DOWNSTREAM_POLICY, policy_scope='pre-outcome additional operationalization', **_scope()) + + +def evaluate_acquisition(replicates, *, assay, mode, dan_type, conditioned_cue, registry, authority): + """Reuse the pinned normal/delayed gate, preserving every six-control rule. + +replicates has the original frozen metrics schema. recording_verified is the +external caller's descriptor assertion, NOT a verification done by this call. +""" + _context(registry, authority) + _cell_rows(replicates, registry, mode, dan_type, conditioned_cue) + registered = {row['replicate_id']: row['training_stream_ids'] for row in replicates} + for row in replicates: + for arm in row['arms'].values(): + for cue in M.CUES: + summary = arm['metrics']['cues'][cue] + require(summary['nodes_raw_sha256'] == registry['nodes_raw_sha256'] and + summary['membership']['membership_sha256'] == registry['population_membership_sha256'], 'Population metric binding differs') + frozen = M.evaluate_acquisition(replicates, assay=assay, mode=mode, dan_type=dan_type, + conditioned_cue=conditioned_cue, registered_training_streams=registered) + result = _report(assay, frozen['replicates'], registry, authority, mode, dan_type, conditioned_cue) + result.update(frozen_acquisition_result=frozen, acquisition_controls=list(M.CONTROLS), + downstream_policy='unchanged acquisition requires both legacyDownstream and nonMBONDownstream positive gains', + acquisition_metric_inputs=json.loads(json.dumps(replicates, allow_nan=False)), + legacy_control_MBON_gains={name: {row['replicate_id']: row['arms'][name]['metrics']['separation']['MBON']['gain'] + for row in replicates} for name in ('D=0', 'random reward', 'frozen')}) + return result + + +def evaluate_reversal(replicates, *, mode, dan_type, conditioned_cue, registry, authority, + nodes, populations, downstream_policy): + """Original pre/orientation, strict nonzero sign crossing, fixed 50 count.""" + require(downstream_policy == DOWNSTREAM_POLICY, 'Explicit preregistered downstream operationalization required') + _context(registry, authority, nodes, populations) + _cell_rows(replicates, registry, mode, dan_type, conditioned_cue) + reports, execution_used = [], set() + used = dict(snapshots={}, receipts={}) + neutral = next(cue for cue in M.CUES if cue != conditioned_cue) + for row in replicates: + _keys(row, ('replicate_id', 'replicate_unit', 'training_stream_ids', 'evaluation_stream_ids', + 'pre', 'acquisition', 'reversal', 'no_reversal', 'execution'), 'reversal row') + block_id = row['replicate_id'] + _keys(row['execution'], ('acquisition', 'reversal', 'no_reversal'), 'reversal executions') + for arm, execution in row['execution'].items(): + _arm_execution(execution, assay='normal', arm=arm, used=execution_used) + require(execution['reinforced_cue'] == (neutral if arm == 'reversal' else conditioned_cue), 'Reversal cue assignment differs') + acq = row['execution']['acquisition'] + for arm in ('reversal', 'no_reversal'): + require(row['execution'][arm]['initial_delta_sha256'] == acq['terminal_delta_sha256'], 'Reversal must fork exact acquisition delta') + pre = _pair(row['pre'], block_id, registry, nodes, populations, used, policy='common_frozen_reset', delta=acq['initial_delta_sha256']) + metrics, states = {}, {} + for arm in ('acquisition', 'reversal', 'no_reversal'): + execution = row['execution'][arm] + states[arm] = _pair(row[arm], block_id, registry, nodes, populations, used, policy='common_frozen_reset', + delta=execution['terminal_delta_sha256'], checkpoint=execution['terminal_checkpoint_sha256']) + metrics[arm] = M.arm_metrics(pre, states[arm], conditioned_cue=conditioned_cue, nodes=nodes, populations=populations) + changes = {cue: M.functional_summary(states['acquisition'][cue], states['reversal'][cue], + nodes=nodes, populations=populations, steps=1200) for cue in M.CUES} + s = {arm: metric['rawS'] for arm, metric in metrics.items()} + expected = (s['acquisition'] > 0 and s['no_reversal'] > 0 and s['reversal'] < 0) if dan_type == 'PAM15' else ( + s['acquisition'] < 0 and s['no_reversal'] < 0 and s['reversal'] > 0) + mbon, down = (changes[neutral]['populations'][name] for name in ('MBON', 'nonMBONDownstream')) + predicates = dict(strict_three_signed_relations=bool(expected), + former_neutral_MBON_count_or_defined_first_latency=bool(mbon['spike_count_l1'] > 0 or mbon['defined_first_latency_changed']), + former_neutral_nonMBONDownstream_event_or_count=bool(down['events_changed'] or down['spike_count_l1'] > 0)) + reports.append(dict(replicate_id=block_id, rawS=s, predicates=predicates, conjunction_pass=all(predicates.values()), + metrics=metrics, acquisition_to_reversal_changes_by_cue=changes, + legacy_style_sign_inequality_on_registered_S=bool(np.sign(s['acquisition']) != np.sign(s['reversal'])), + exact_historical_H_readout_equivalence_claimed=False)) + return _report('reversal', reports, registry, authority, mode, dan_type, conditioned_cue) + + +def _idle(branch, used, *, arm, conditioned_cue): + _keys(branch, ('source_checkpoint_sha256', 'delta_sha256', 'idle_lineage_id', 'idle_receipt_sha256', + 'total_idle_ticks', 'no_input', 'updates_disabled', 'reset_state', 'analytic_time_advance', + 'one_second_is_prefix_of_ten_second', 'checkpoints', 'source_execution'), 'idle branch') + _arm_execution(branch['source_execution'], assay='normal', arm=arm, used=used) + source = branch['source_execution'] + require(source['terminal_checkpoint_sha256'] == branch['source_checkpoint_sha256'] and + source['terminal_delta_sha256'] == branch['delta_sha256'] and source['reinforced_cue'] == conditioned_cue, + 'Idle source must bind the complete terminal normal paired/frozen execution') + require(arm != 'frozen' or source['initial_delta_sha256'] == source['terminal_delta_sha256'], + 'Frozen training cannot change delta') + for field in ('source_checkpoint_sha256', 'delta_sha256', 'idle_receipt_sha256'): + _sha(branch[field], field) + _token(branch['idle_lineage_id'], 'idle lineage') + require(branch['idle_receipt_sha256'] not in used and branch['idle_lineage_id'] not in used, 'Idle branch/receipt reused') + used.update((branch['idle_receipt_sha256'], branch['idle_lineage_id'])) + require(type(branch['total_idle_ticks']) is int and branch['total_idle_ticks'] == 100000 and + branch['no_input'] is True and branch['updates_disabled'] is True and branch['reset_state'] is False and + branch['analytic_time_advance'] is False and branch['one_second_is_prefix_of_ten_second'] is True, + 'Retention must be actual continuous 10-second idle with a 1-second prefix') + _keys(branch['checkpoints'], ('0', '1', '10'), 'idle checkpoints') + previous, start = None, None + for second in (0, 1, 10): + checkpoint = branch['checkpoints'][str(second)] + _keys(checkpoint, ('checkpoint_sha256', 'parent_checkpoint_sha256', 'elapsed_ticks', + 'absolute_tick', 'delta_sha256', 'carry_sha256'), 'idle checkpoint') + for field in ('checkpoint_sha256', 'parent_checkpoint_sha256', 'delta_sha256'): + _sha(checkpoint[field], field) + require(type(checkpoint['elapsed_ticks']) is int and checkpoint['elapsed_ticks'] == second * 10000 and + type(checkpoint['absolute_tick']) is int and checkpoint['absolute_tick'] >= 0, + 'Retention clock differs') + start = checkpoint['absolute_tick'] if second == 0 else start + require(checkpoint['absolute_tick'] == start + second * 10000 and checkpoint['delta_sha256'] == branch['delta_sha256'], + 'Absolute clock or idle delta changed') + require(checkpoint['parent_checkpoint_sha256'] == (branch['source_checkpoint_sha256'] if previous is None else previous), + 'Idle checkpoint chain does not preserve the same 1-to-10-second trajectory') + _keys(checkpoint['carry_sha256'], CARRY, 'complete carried-state hashes') + for field, value in checkpoint['carry_sha256'].items(): + _sha(value, field) + previous = checkpoint['checkpoint_sha256'] + + +def evaluate_retention(replicates, *, mode, dan_type, conditioned_cue, registry, authority, + nodes, populations, downstream_policy): + """One conjunction per training replicate at BOTH 1 and 10 real seconds.""" + require(downstream_policy == DOWNSTREAM_POLICY, 'Explicit preregistered downstream operationalization required') + _context(registry, authority, nodes, populations) + _cell_rows(replicates, registry, mode, dan_type, conditioned_cue) + reports, idle_used = [], set() + used = dict(snapshots={}, receipts={}) + for row in replicates: + _keys(row, ('replicate_id', 'replicate_unit', 'training_stream_ids', 'evaluation_stream_ids', + 'pre', 'checkpoints', 'execution'), 'retention row') + _keys(row['pre'], ('trained', 'frozen'), 'retention pre') + _keys(row['execution'], ('trained', 'frozen'), 'retention branches') + _keys(row['checkpoints'], ('0', '1', '10'), 'retention time inventory') + block_id = row['replicate_id'] + pre = {} + for branch in ('trained', 'frozen'): + execution = row['execution'][branch] + _idle(execution, idle_used, arm='paired' if branch == 'trained' else 'frozen', conditioned_cue=conditioned_cue) + pre[branch] = _pair(row['pre'][branch], block_id, registry, nodes, populations, used, policy='common_frozen_reset', + delta=execution['source_execution']['initial_delta_sha256']) + times, complete = {}, [] + for second in (0, 1, 10): + pairs = row['checkpoints'][str(second)] + _keys(pairs, ('trained', 'frozen'), 'time-matched trained/frozen probes') + states, metrics = {}, {} + for branch in ('trained', 'frozen'): + execution = row['execution'][branch] + checkpoint = execution['checkpoints'][str(second)] + states[branch] = _pair(pairs[branch], block_id, registry, nodes, populations, used, + policy='continuous_checkpoint', delta=execution['delta_sha256'], checkpoint=checkpoint['checkpoint_sha256']) + metrics[branch] = M.arm_metrics(pre[branch], states[branch], conditioned_cue=conditioned_cue, nodes=nodes, populations=populations) + differences = {cue: M.functional_summary(states['frozen'][cue], states['trained'][cue], + nodes=nodes, populations=populations, steps=1200) for cue in M.CUES} + trained, frozen = (metrics[branch]['rawS'] for branch in ('trained', 'frozen')) + mbon, down = (differences[conditioned_cue]['populations'][name] for name in ('MBON', 'nonMBONDownstream')) + predicates = dict(raw_signed_direction=bool(trained > 0 if dan_type == 'PAM15' else trained < 0), + beyond_time_matched_frozen=bool(trained > frozen if dan_type == 'PAM15' else trained < frozen), + conditioned_MBON_count_or_defined_first_latency=bool(mbon['spike_count_l1'] > 0 or mbon['defined_first_latency_changed']), + conditioned_nonMBONDownstream_event_or_count=bool(down['events_changed'] or down['spike_count_l1'] > 0)) + times[str(second)] = dict(rawS_trained=trained, rawS_frozen=frozen, rawS_trained_minus_frozen=trained - frozen, + metrics=metrics, frozen_to_trained_differences_by_cue=differences, predicates=predicates, + contributes_to_gate=second in (1, 10), conjunction_pass=all(predicates.values())) + if second in (1, 10): + complete.append(all(predicates.values())) + reports.append(dict(replicate_id=block_id, times=times, conjunction_pass=all(complete), + legacy_style_last_MBON_separation_exceeds_pre=bool(times['10']['metrics']['trained']['separation']['MBON']['gain'] > 0), + legacy_reset_retention_equivalence_claimed=False)) + return _report('retention', reports, registry, authority, mode, dan_type, conditioned_cue) + + +def _checked_difference(summary, before, after, cue): + """Rebind reported differences to raw vectors in their two source metrics. + +The event-change bit comes from raw-event comparison in evaluate_*; a summary +does not contain the event files. It remains externally bound evidence, never +an independent disk/event verification performed by this cohort function. +""" + left, right = before['cues'][cue], after['cues'][cue] + require(summary['membership'] == left['membership'] == right['membership'] and + summary['nodes_raw_sha256'] == left['nodes_raw_sha256'] == right['nodes_raw_sha256'] and + summary['pre_state_raw_sha256'] == left['post_state_raw_sha256'] and + summary['post_state_raw_sha256'] == right['post_state_raw_sha256'], 'Difference source binding changed') + for name in M.POPULATIONS: + value, a, b = summary['populations'][name], left['populations'][name], right['populations'][name] + for field in ('counts', 'first_latency_ms_per_neuron', 'final_v', 'final_g', 'final_ref'): + require(value['pre_' + field] == a['post_' + field] and value['post_' + field] == b['post_' + field], + 'Difference population vectors changed') + count = sum(abs(x - y) for x, y in zip(value['pre_counts'], value['post_counts'])) + first = [] + for phase in ('pre', 'post'): + defined = [x for x in value[phase + '_first_latency_ms_per_neuron'] if x is not None] + first.append(min(defined) if defined else None) + changed = first[0] is not None and first[1] is not None and first[0] != first[1] + require(type(value['spike_count_l1']) is int and value['spike_count_l1'] == count and + value['defined_first_latency_changed'] is changed and type(value['events_changed']) is bool, + 'Difference predicate contradicts its raw count/defined-latency vectors') + require(count == 0 or value['events_changed'], 'Changed counts require changed events') + same_events = all(summary['pre_state_raw_sha256'][key] == summary['post_state_raw_sha256'][key] + for key in ('step', 'neuron_idx')) + require(not same_events or not value['events_changed'], 'Identical complete event arrays cannot change population events') + return summary['populations'] + + +def _validate_followup_report(row, assay, dan, cue): + neutral = next(x for x in M.CUES if x != cue) + if assay == 'reversal': + _keys(row['metrics'], ('acquisition', 'reversal', 'no_reversal'), 'reversal report metrics') + metrics = {name: M._bound_metric(value, cue) for name, value in row['metrics'].items()} + for odor in M.CUES: + require(len({tuple(sorted(metric['cues'][odor]['pre_state_raw_sha256'].items())) for metric in metrics.values()}) == 1, + 'Reversal reports changed original pre baseline') + s = {name: value['rawS'] for name, value in metrics.items()} + require(row['rawS'] == s, 'Reversal raw signed result differs from source metrics') + _keys(row['acquisition_to_reversal_changes_by_cue'], M.CUES, 'reversal cue changes') + groups = {odor: _checked_difference(row['acquisition_to_reversal_changes_by_cue'][odor], + metrics['acquisition'], metrics['reversal'], odor) for odor in M.CUES} + mbon, down = (groups[neutral][name] for name in ('MBON', 'nonMBONDownstream')) + signed = (s['acquisition'] > 0 and s['no_reversal'] > 0 and s['reversal'] < 0) if dan == 'PAM15' else ( + s['acquisition'] < 0 and s['no_reversal'] < 0 and s['reversal'] > 0) + predicates = dict(strict_three_signed_relations=bool(signed), + former_neutral_MBON_count_or_defined_first_latency=bool(mbon['spike_count_l1'] > 0 or mbon['defined_first_latency_changed']), + former_neutral_nonMBONDownstream_event_or_count=bool(down['events_changed'] or down['spike_count_l1'] > 0)) + require(row['predicates'] == predicates and all(type(x) is bool for x in row['predicates'].values()), + 'Reversal predicates differ from source metrics') + return all(predicates.values()) + _keys(row['times'], ('0', '1', '10'), 'retention report times') + baselines, conjunctions = {}, [] + for second in ('0', '1', '10'): + value = row['times'][second] + _keys(value['metrics'], ('trained', 'frozen'), 'retention report metrics') + metrics = {name: M._bound_metric(metric, cue) for name, metric in value['metrics'].items()} + for branch, metric in metrics.items(): + baseline = {odor: metric['cues'][odor]['pre_state_raw_sha256'] for odor in M.CUES} + require(branch not in baselines or baselines[branch] == baseline, 'Retention changed original pre baseline across time') + baselines[branch] = baseline + trained, frozen = (metrics[branch]['rawS'] for branch in ('trained', 'frozen')) + require(value['rawS_trained'] == trained and value['rawS_frozen'] == frozen and + value['rawS_trained_minus_frozen'] == trained - frozen, 'Retention signed contrast differs from source metrics') + _keys(value['frozen_to_trained_differences_by_cue'], M.CUES, 'retention cue differences') + groups = {odor: _checked_difference(value['frozen_to_trained_differences_by_cue'][odor], + metrics['frozen'], metrics['trained'], odor) for odor in M.CUES} + mbon, down = (groups[cue][name] for name in ('MBON', 'nonMBONDownstream')) + predicates = dict(raw_signed_direction=bool(trained > 0 if dan == 'PAM15' else trained < 0), + beyond_time_matched_frozen=bool(trained > frozen if dan == 'PAM15' else trained < frozen), + conditioned_MBON_count_or_defined_first_latency=bool(mbon['spike_count_l1'] > 0 or mbon['defined_first_latency_changed']), + conditioned_nonMBONDownstream_event_or_count=bool(down['events_changed'] or down['spike_count_l1'] > 0)) + require(value['predicates'] == predicates and all(type(x) is bool for x in value['predicates'].values()) and + value['conjunction_pass'] is all(predicates.values()) and value['contributes_to_gate'] is (second != '0'), + 'Retention predicates differ from source metrics') + if second != '0': + conjunctions.append(all(predicates.values())) + return all(conjunctions) + + +def summarize_cohort(reports, *, registry, authority, required_assays): + """Exact eight-cell inventory; each assay keeps its own same-replicate 3/5. + +Use ('normal',) for the staged acquisition decision or ASSAYS for the full +functional table. The function does not silently intersect successful seeds +across assays, pool cells, or incorporate legacy H/integrity gates. Those are +separately registered coordinator decisions. Missing assays remain NOT-RUN. +""" + _context(registry, authority) + require(tuple(required_assays) in (('normal',), ASSAYS), 'Only staged normal or full fixed assay scope supported') + expected = {(m, d, c, a) for m in M.MODES for d in M.DAN_TYPES for c in M.CUES for a in required_assays} + actual, table = set(), [] + for report in reports: + require(report.get('schema') == SCHEMA and report.get('registry_sha256') == authority['registry_sha256'] and + report.get('external_data_authority_descriptor_sha256') == descriptor_sha256(authority), 'Report authority binding differs') + identity = (report['mode'], report['dan_type'], report['conditioned_cue'], report['assay']) + require(identity in expected and identity not in actual, 'Duplicate/unexpected cohort cell/assay') + expected_ids = {key for key, value in registry['blocks'].items() + if (value['mode'], value['dan_type'], value['conditioned_cue']) == identity[:3]} + rows = report['replicates'] + require(len(rows) == 5 and {row['replicate_id'] for row in rows} == expected_ids and + all(type(row['conjunction_pass']) is bool for row in rows), 'Cohort replicate coverage differs') + if report['assay'] in ('normal', 'delayed'): + inputs = report.get('acquisition_metric_inputs') + require(isinstance(inputs, list), 'Missing original normal/delayed metric inputs') + rebuilt = evaluate_acquisition(inputs, assay=identity[3], mode=identity[0], dan_type=identity[1], + conditioned_cue=identity[2], registry=registry, authority=authority) + require(rebuilt == report, + 'Normal/delayed report differs from frozen gate reconstruction') + else: + for row in rows: + require(report['downstream_policy'] == DOWNSTREAM_POLICY, 'Follow-up downstream policy changed') + conjunction = _validate_followup_report(row, report['assay'], report['dan_type'], report['conditioned_cue']) + require(row['conjunction_pass'] is conjunction, 'Replicate conjunction differs from its predicates') + count = sum(row['conjunction_pass'] for row in rows) + require(report['successful_same_replicate_conjunctions'] == count and report['predicate_pass'] is (count >= 3), + 'Cohort summary disagrees with replicate conjunctions') + require(all(report.get(key) is False for key in ('external_authority_verified', 'scientific_learning_pass_claimed', + 'candidate_generated', 'execution_authorized', 'data_backed_claimed')), 'Conditional report contains an unsupported authority claim') + actual.add(identity) + table.append(dict(mode=identity[0], dan_type=identity[1], conditioned_cue=identity[2], assay=identity[3], + status='CONDITIONAL-PASS' if count >= 3 else 'CONDITIONAL-FAIL', successful_replicates=count)) + for identity in sorted(expected - actual): + table.append(dict(mode=identity[0], dan_type=identity[1], conditioned_cue=identity[2], assay=identity[3], status='NOT-RUN', successful_replicates=None)) + complete = actual == expected + return dict(schema=SCHEMA, status='COMPLETE' if complete else 'NOT-RUN', required_assays=list(required_assays), + complete_inventory=complete, all_functional_predicates_pass=(all(row['status'] == 'CONDITIONAL-PASS' for row in table) if complete else None), + table=sorted(table, key=lambda row: (row['mode'], row['dan_type'], row['conditioned_cue'], row['assay'])), + legacy_H_and_integrity_gates_included=False, **_scope()) + + +def legacy_h_cohort_audit(normal_reports, *, registry, authority, declared_mapping): + """Four groups, two frozen H calls each; no branch/control pooling. + +Both stricter control-source mapping and both_pass aggregate are new explicit +pre-outcome operationalizations, NOT a reproduced complete original H gate. +""" + if declared_mapping is None: + return dict(schema=SCHEMA, status='NOT-TESTABLE', both_pass=None, + reason='Missing explicit pre-outcome control-source mapping', **_scope()) + require(declared_mapping == H_MAPPING, 'Unknown declared H control-source mapping') + inventory = summarize_cohort(normal_reports, registry=registry, authority=authority, required_assays=('normal',)) + if not inventory['complete_inventory']: + return dict(schema=SCHEMA, status='NOT-RUN', both_pass=None, reason='Complete eight normal cells required', **_scope()) + lookup = {(r['mode'], r['dan_type'], r['conditioned_cue']): r for r in normal_reports} + groups = [] + for mode in M.MODES: + for cue in M.CUES: + reports = {dan: lookup[(mode, dan, cue)] for dan in M.DAN_TYPES} + outcomes = {} + for source in M.DAN_TYPES: + # The normal report must retain all raw arm metrics; gains are + # taken from their validated source payload, not a selected mean. + gains = {} + for dan, label in (('PAM15', 'appetitive-conditioning'), ('PPL103', 'aversive-conditioning')): + gains[label] = [row['population_separation_gains']['MBON'] for row in reports[dan]['replicates']] + controls = reports[source].get('legacy_control_MBON_gains') + require(isinstance(controls, Mapping) and set(controls) == {'D=0', 'random reward', 'frozen'}, + 'Normal report lacks complete branch-bound legacy control gains') + for name, label in (('D=0', 'd-zero-control'), ('random reward', 'random-reward-control'), ('frozen', 'frozen-brain-control')): + require(set(controls[name]) == {row['replicate_id'] for row in reports[source]['replicates']}, 'Legacy control replicate mapping differs') + gains[label] = [controls[name][row['replicate_id']] for row in reports[source]['replicates']] + outcomes[source] = M.legacy_h_mean_control_audit(gains) + groups.append(dict(mode=mode, conditioned_cue=cue, control_sources=outcomes, + both_pass=all(value['predicate_pass'] for value in outcomes.values()))) + return dict(schema=SCHEMA, status='CONDITIONAL-AUDIT', declared_mapping=declared_mapping, groups=groups, + both_pass=all(group['both_pass'] for group in groups), original_H_data_layout_reproduced=False, + new_stricter_control_source_mapping=True, **_scope()) diff --git a/models/male-cns-lif/conditioning-execution-v1/prepare_execution.py b/models/male-cns-lif/conditioning-execution-v1/prepare_execution.py new file mode 100644 index 0000000..9ba8e3f --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/prepare_execution.py @@ -0,0 +1,178 @@ +"""Exclusive preregistration for a finite NEW execution-boundary experiment. + +No sampler, brain, update or historical experiment is executed by preparation. +Frozen checksums are read. Future outputs are derived later from explicit rules, +never given fabricated preregistered hashes. Real learning is NOT authorized. +""" +from __future__ import annotations + +import argparse +import hashlib +import importlib.util +import json +from pathlib import Path +import shutil +import subprocess +import sys + +import numpy as np + +import boundary_plan as PLAN +import execution_authority as A + +ROOT=Path('/data/digital-drosophila') +MODEL=Path(__file__).resolve().parent +EXP=ROOT/'experiments/phase4aq-conditioning-execution' +AP=ROOT/'experiments/phase4ap-conditioning-interface-v2' +AO=ROOT/'experiments/phase4ao-conditioning-interface' +LAB=MODEL.parent/'olfactory-conditioning-lab-v1' +require,read,digest=A.require,A.read,A.digest + + +def save(path,value): + with Path(path).open('x',encoding='utf-8') as f: + json.dump(value,f,sort_keys=True,indent=2,allow_nan=False,ensure_ascii=False);f.write('\n') + + +def copy_exclusive(source,target): + with Path(source).open('rb') as inp,Path(target).open('xb') as out:shutil.copyfileobj(inp,out) + require(digest(source)==digest(target),'Exclusive source copy differs') + + +def npz(path): + with np.load(path,allow_pickle=False) as f:return {n:f[n].copy() for n in f.files} + + +def save_npz(path,values): + with Path(path).open('xb') as f:np.savez_compressed(f,**values) + + +def verify(pins): + for path,sha in pins.items():require(A.pin(path)['sha256']==sha,'Pinned input changed: '+str(path)) + + +def old_context(): + pins={str(AP/k):v for k,v in PLAN.AP_ANCHORS.items()} + pins.update({str(MODEL.parent/k):v for k,v in PLAN.SOURCE_PINS.items()}) + verify(pins) + apreg=read(AP/'protocol/preregistered.json') + verify({**apreg['input_sha256'],**apreg['source_sha256'],**apreg['prepared_file_sha256']}) + path=MODEL.parent/'conditioning-interface-v2/coordinator.py' + raw=path.read_bytes();name='_conditioning_execution_prior_integrity' + require(name not in sys.modules,'Fresh preparation/integrity process required') + spec=importlib.util.spec_from_file_location(name,path);module=importlib.util.module_from_spec(spec) + sys.modules[name]=module;exec(compile(raw,str(path),'exec'),module.__dict__) + return module,apreg,pins + + +def integrity(): + old,apreg,pins=old_context() + earlier=old.previous_integrity();current=old.helper().verify_seal(AP) + require(read(AP/'audit/final-status.json')['status']=='PASS-EXACT-PROCESS-ISOLATED-DELTA-INTERFACE-NOT-LEARNING', + 'AP interface gate changed') + return dict(complete=True,AP=current,prior=earlier,anchors=pins, + old_experiments_reexecuted=False,old_files_modified=False, + five_frozen_reference_trials_rerun_here=False, + historical_reference_evidence_scope='Preserved AM numerical regression evidence; checksums now reverified', + scientific_learning_pass_claimed=False) + + +def source_inventory(): + entries=list(MODEL.iterdir()) + require(entries and all(p.is_file() and not p.is_symlink() and p.suffix in ('.py','.md') for p in entries), + 'Source directory may contain only explicit unlinked Python/Markdown source files') + return {str(p):digest(p) for p in sorted(entries)} + + +def prepare(): + require(sys.dont_write_bytecode and EXP.is_dir() and not any(EXP.iterdir()),'Use -B and exact new empty experiment directory') + old,apreg,anchors=old_context() + before=dict(complete=True,AP=old.helper().verify_seal(AP),prior=old.previous_integrity(), + five_frozen_reference_trials_rerun_here=False,old_files_modified=False) + plan=PLAN.build_plan();PLAN.validate_plan(plan) + source=source_inventory();commit=subprocess.check_output(['git','-C',str(ROOT),'rev-parse','HEAD']).decode().strip() + require(len(commit)==40,'Missing actual code commit') + for path,sha in source.items(): + raw=subprocess.check_output(['git','-C',str(ROOT),'show',commit+':'+Path(path).relative_to(ROOT).as_posix()]) + require(hashlib.sha256(raw).hexdigest()==sha,'Source not equal to committed blob: '+path) + budget=dict(root=str(EXP),output_cap_bytes=4*1024**3,minimum_free_reserve_bytes=10*1024**3, + child_timeout_seconds=1800,workflow_timeout_seconds=7200) + require(shutil.disk_usage(EXP).free>=budget['output_cap_bytes']+budget['minimum_free_reserve_bytes'], + 'Insufficient space for finite validation budget') + pins={**anchors,**apreg['input_sha256'],**apreg['source_sha256'],**apreg['prepared_file_sha256']} + parents={} + for arm,descriptor in plan['imported_parents'].items(): + directory=Path(descriptor['checkpoint_manifest']).parent + record=old.C.load_checkpoint(directory,descriptor['sha256']) + require(record['fresh'] and record['steps']==1200,'Imported parent is not the declared fresh epoch') + authority=AP/'jobs'/f'{arm}--reference--epoch-001.authority.json' + update=AP/'updates'/f'{arm}--reference--epoch-001' + ea=read(authority) + require(ea['checkpoint_dir']==str(directory) and ea['checkpoint_manifest_sha256']==descriptor['sha256'], + 'Original update authority is not bound to selected parent') + for path in (directory/'checkpoint-manifest.json',authority,*update.iterdir()):pins[str(path)]=digest(path) + parents[descriptor['role']]=dict(directory=str(directory),checkpoint_sha256=descriptor['sha256'], + epoch_authority=A.pin(authority),update_dir=str(update),update_sha256={p.name:digest(p) for p in update.iterdir()}, + before_overlay=dict(overlay_file=ea['files']['overlay_before']['path'],overlay_manifest=ea['files']['overlay_manifest_before']['path']), + after_overlay=dict(overlay_file=str(update/'delta-after.npz'),overlay_manifest=str(update/'overlay-manifest.json'))) + zero=apreg['initial_overlay'] + require(A.pin(zero['overlay_file'])==plan['initial_overlay']['overlay_file'] and + A.pin(zero['overlay_manifest'])==plan['initial_overlay']['overlay_manifest'],'Initial zero reference differs') + require(not np.any(npz(zero['overlay_file'])['plastic_delta_mv'].view(np.uint32)),'Initial delta is not positive zero') + template=read(AP/'cases/D1--reference--epoch-001/spec.json') + for name in ('protocol/source-snapshot','protocol/case-templates','inputs/static','cases','runs','updates', + 'jobs','authorities','receipts','logs','audit','regression'):(EXP/name).mkdir(parents=True) + for path in source:copy_exclusive(path,EXP/'protocol/source-snapshot'/Path(path).name) + save(EXP/'regression/integrity-before.json',before) + templates={} + for call in plan['calls']: + group=call.get('case_group',call['call_id']) + spec=PLAN.derive_case_spec(template,call['call_id'],budget_root=str(EXP)) + if group in templates:require(spec==read(templates[group]),'Shared comparison case templates differ') + else: + dest=EXP/'protocol/case-templates'/f'{group}.json';save(dest,spec);templates[group]=str(dest) + ea=read(parents[plan['imported_parents']['D1']['role']]['epoch_authority']['path']) + candidate=npz(ea['files']['candidate_map']['path']) + population_file=AO/'inputs/static/populations.npz' + require(str(population_file) in pins,'Original anatomical population inventory not pinned by AP') + populations=npz(population_file);dan=populations['PAM15'] + requests,selection=PLAN.mechanical_requests(candidate,dan,identity_binding=dict( + candidate_map_sha256=ea['files']['candidate_map']['sha256'], + pam15_membership_sha256=hashlib.sha256(dan.tobytes()).hexdigest())) + require(np.isin(candidate['source_neuron_idx'],populations['Kenyon_Cell']).all(),'Candidate source outside original KC population') + save_npz(EXP/'inputs/static/mechanical-requests.npz',requests) + save(EXP/'inputs/static/mechanical-selection.json',selection) + # Original physiological PN mapping is recomputed without stochastic sampling. + import conditioning_preflight_inputs as I + rates=I.load_rate_tables(ROOT) + for cue,table in rates['tables'].items(): + existing=npz(AO/f'inputs/static/{cue}-rates.npz') + require(all(table[k].dtype==existing[k].dtype and table[k].shape==existing[k].shape and + table[k].tobytes()==existing[k].tobytes() for k in ('targets','rates_hz')), + 'Prior physiological PN mapping differs') + copy_exclusive(AO/f'inputs/static/{cue}-rates.npz',EXP/f'inputs/static/{cue}-rates.npz') + save(EXP/'inputs/static/rate-audit.json',rates['audit']) + fullplan=AO/'protocol/full-trial-plan.json' + require(str(fullplan) in pins,'Original full seed/trial plan not pinned') + prepared={str(p):digest(p) for sub in ('protocol','inputs/static','regression') for p in (EXP/sub).rglob('*') if p.is_file()} + reg=dict(schema='conditioning-execution-registration-v1',engineering_only=True,scientific_acquisition_authorized=False, + scope='bounded updated-state boundary, fixed functional-gate code, complete-process budget measurement', + plan=plan,source_sha256=source,source_code_commit=commit,input_sha256=pins,prepared_file_sha256=prepared, + parent_records=parents,initial_overlay=zero,case_templates=templates,budget=budget,original_full_plan=A.pin(fullplan), + old_source_changes=False,old_experiments_reexecuted=False,brain_or_sampler_run_during_preparation=False, + future_hashes_fabricated=False,learning_success_claimed=False, + failure_policy='stop and preserve partials; never automatic retry, tuning, seed search, or trial truncation') + verify({**pins,**source,**prepared}) + save(EXP/'protocol/preregistered.json',reg) + return digest(EXP/'protocol/preregistered.json') + + +if __name__=='__main__': + parser=argparse.ArgumentParser(description=__doc__) + parser.add_argument('mode',choices=('prepare','integrity')) + parser.add_argument('--output') + args=parser.parse_args() + if args.mode=='prepare':print('REGISTRATION_SHA256',prepare(),flush=True) + else: + require(args.output and not Path(args.output).exists(),'Exclusive integrity report required') + save(args.output,integrity());print('INTEGRITY_SHA256',digest(args.output),flush=True) diff --git a/models/male-cns-lif/conditioning-execution-v1/test_audit_execution.py b/models/male-cns-lif/conditioning-execution-v1/test_audit_execution.py new file mode 100644 index 0000000..d1592ee --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/test_audit_execution.py @@ -0,0 +1,299 @@ +"""Synthetic file-chain/dispatch tests; never a brain or historical audit.""" +from __future__ import annotations + +import copy +import hashlib +import json +from pathlib import Path +import tempfile +from contextlib import ExitStack +from types import SimpleNamespace +import unittest +from unittest.mock import patch + +import numpy as np + +import audit_execution as T + + +def write(path, value): + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(value, sort_keys=True), encoding='utf-8') + + +def npz(path, **values): + with path.open('wb') as stream: + np.savez(stream, **values) + + +class PlanTests(unittest.TestCase): + def test_exact_fixed_process_budget_and_kinds(self): + plan = T.PLAN.build_plan(); values = T.expected_processes(plan) + self.assertEqual(len(values), 31) + self.assertEqual(sum(k in ('updated', 'oracle', 'unchanged', 'brain') for k in values.values()), 23) + self.assertEqual(sum(k in ('update', 'validate_cpu') for k in values.values()), 7) + self.assertEqual(list(values.values()).count('sample'), 1) + self.assertEqual(sum(c['steps'] for c in plan['calls']), 125200) + + def test_duplicate_call_and_wrong_budget_rejected(self): + plan = T.PLAN.build_plan(); plan['calls'].append(copy.deepcopy(plan['calls'][0])) + with self.assertRaisesRegex(ValueError, 'Duplicate'): T.expected_processes(plan) + plan = T.PLAN.build_plan(); plan['counts']['total_children'] += 1 + with self.assertRaisesRegex(ValueError, 'inventory'): T.expected_processes(plan) + + def test_D1_zero_coverage_does_not_deny_mechanical_witness(self): + pending = {'D1': {'accepted_changed_edge_witness_verified': False}, + 'mechanical': {'accepted_changed_edge_witness_verified': True}} + exact, coverage, status = T.outcome({'all_numeric': True}, pending) + self.assertTrue(exact and coverage) + self.assertEqual(status, 'PASS-EXACT-UPDATED-BOUNDARY-ENGINEERING-NOT-LEARNING') + pending['mechanical']['accepted_changed_edge_witness_verified'] = False + self.assertEqual(T.outcome({'all_numeric': True}, pending)[2], 'INCOMPLETE-CHANGED-EDGE-COVERAGE') + self.assertEqual(T.outcome({'all_numeric': False}, pending)[2], 'FAIL-NUMERICAL-OR-LINEAGE') + with self.assertRaises(ValueError): T.outcome({}, pending) + with self.assertRaises(ValueError): T.outcome({'impostor_bool': 1}, pending) + + +class ProcessTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory(); self.exp = Path(self.temp.name).resolve() / 'phase4aq-synthetic' + self.patch = patch.object(T, 'EXP', self.exp); self.patch.start() + self.name = 'performance--natural-light-fresh'; self.kind = 'brain' + self.regfile = self.exp / 'protocol/preregistered.json'; write(self.regfile, {'synthetic': True}) + self.reg_sha = T.digest(self.regfile) + self.h = T._helpers() + self.reg = dict(source_sha256={str(T.MODEL / 'isolated_execution.py'): T.digest(T.MODEL / 'isolated_execution.py')}, + input_sha256={}, prepared_file_sha256={}) + case = self.exp / 'cases' / self.name; write(case / 'spec.json', {'synthetic': True}) + (case / 'requests.npz').write_bytes(b'not-scientific-requests') + overlay = self.exp / 'inputs/before.npz'; overlay.parent.mkdir(); overlay.write_bytes(b'not-scientific-overlay') + om = self.exp / 'inputs/before.json'; write(om, {}) + self.output = self.exp / 'runs' / self.name; write(self.output / 'summary.json', {'synthetic': True}) + self.jobpath = self.exp / 'jobs' / (self.name + '.json') + self.workerpath = self.exp / 'receipts' / (self.name + '.worker.json') + self.processpath = self.exp / 'receipts' / (self.name + '.process.json') + self.stdout = self.exp / 'logs' / (self.name + '.stdout.log'); self.stdout.parent.mkdir() + self.stdout.write_bytes(b'fixture'); self.stderr = self.stdout.with_name(self.name + '.stderr.log'); self.stderr.write_bytes(b'') + self.sources = {n: dict(path=str(T.LAB / (n + '.py')), sha256=self.h.SOURCE_PINS[n]) + for n in ('runtime_light', 'runtime_continuation')} + inputs = T._closure(self.reg, self.reg_sha) + inputs.update({str(p): T.digest(p) for p in (case / 'spec.json', case / 'requests.npz', overlay, om)}) + self.job = dict(schema='phase4ap-isolated-worker-job-v1', job_id=self.name, kind='brain', registration_sha256=self.reg_sha, + worker_sha256=T.W.AP_SHA, case_dir=str(case), overlay_file=str(overlay), overlay_manifest=str(om), + output_dir=str(self.output), receipt_file=str(self.workerpath), input_sha256=inputs, sources=self.sources) + self.seal_records() + + def tearDown(self): + self.patch.stop(); self.temp.cleanup() + + def seal_records(self): + write(self.jobpath, self.job); job_sha = T.digest(self.jobpath) + attempt = Path(str(self.workerpath) + '.attempt.json') + write(attempt, dict(complete=False, job_sha256=job_sha, no_automatic_retry=True)) + hashes = {k: v['sha256'] for k, v in T.tree(self.output).items()} + pins = {**self.job['input_sha256'], str(self.jobpath): job_sha, str(T.W.AP): T.W.AP_SHA, + **{v['path']: v['sha256'] for v in self.sources.values()}} + receipt = dict(schema='phase4ap-isolated-worker-receipt-v1', complete=True, job_id=self.name, kind='brain', + registration_sha256=self.reg_sha, worker_sha256=T.W.AP_SHA, job_sha256=job_sha, + process_exit_independently_observed=False, scientific_gate_pass_claimed=False, source_files=self.sources, + verified_input_sha256=pins, output_dir=str(self.output), output_files_sha256=hashes, + attempt_sha256=T.digest(attempt), result={'synthetic': True}) + write(self.workerpath, receipt) + process = dict(schema='conditioning-execution-process-v1', name=self.name, kind='brain', registration_sha256=self.reg_sha, + complete=True, exit_code=0, timed_out=False, job_sha256=job_sha, worker_receipt_sha256=T.digest(self.workerpath), + stdout_sha256=T.digest(self.stdout), stderr_sha256=T.digest(self.stderr), + argv=['/synthetic/python', '-B', str(T.W.AP), 'brain', '--job', str(self.jobpath), '--job-sha256', job_sha], + output_bytes=sum(v['bytes'] for v in T.tree(self.output).values()), + output_inventory_sha256=hashlib.sha256(json.dumps(hashes, sort_keys=True).encode()).hexdigest(), + child_wall_seconds=.1, end_to_end_seconds=.2, validation_wall_seconds=.1) + write(self.processpath, process) + + def verify(self): + return T.verify_process(self.name, self.kind, self.reg, self.reg_sha, self.processpath, self.h) + + def test_actual_file_chain_not_working_memory_flags(self): + result = self.verify() + self.assertEqual(result['kind'], 'brain'); self.assertEqual(result['output'], self.output) + + def test_nonzero_exit_and_bool_exit_rejected(self): + original = T.read(self.processpath) + for value in (1, False, None): + changed = copy.deepcopy(original); changed['exit_code'] = value; write(self.processpath, changed) + with self.subTest(value=value), self.assertRaisesRegex(ValueError, 'recorded process exit'): self.verify() + + def test_log_mutation_and_unknown_output_rejected(self): + self.stderr.write_bytes(b'changed') + with self.assertRaisesRegex(ValueError, 'file chain'): self.verify() + self.stderr.write_bytes(b''); (self.output / 'unreported.json').write_text('{}') + with self.assertRaisesRegex(ValueError, 'inventory'): self.verify() + + def test_missing_registration_source_closure_even_rehashed_rejected(self): + del self.job['input_sha256'][str(T.MODEL / 'isolated_execution.py')]; self.seal_records() + with self.assertRaisesRegex(ValueError, 'closure omitted'): self.verify() + + def test_extra_self_authorized_input_even_rehashed_rejected(self): + extra = self.exp / 'arbitrary.txt'; extra.write_text('extra') + self.job['input_sha256'][str(extra)] = T.digest(extra); self.seal_records() + with self.assertRaisesRegex(ValueError, 'undeclared input'): self.verify() + + def test_wrong_command_and_process_path_rejected(self): + p = T.read(self.processpath); p['argv'][2] = '/unregistered/worker.py'; write(self.processpath, p) + with self.assertRaisesRegex(ValueError, 'command differs'): self.verify() + with self.assertRaisesRegex(ValueError, 'fixed process record'): + T.verify_process(self.name, self.kind, self.reg, self.reg_sha, self.stdout, self.h) + + def test_changed_source_snapshot_and_attempt_rejected(self): + worker = T.read(self.workerpath); worker['attempt_sha256'] = '0' * 64; write(self.workerpath, worker) + p = T.read(self.processpath); p['worker_receipt_sha256'] = T.digest(self.workerpath); write(self.processpath, p) + with self.assertRaisesRegex(ValueError, 'Attempt record changed'): self.verify() + + +class ComparisonTests(unittest.TestCase): + def test_light_missing_perstep_is_reported_not_fabricated(self): + state = {k: np.zeros(3, np.int32) for k in T.B.C.STATE_KEYS} + terminal = {k: np.zeros(3, np.int32) for k in ('spike_ring', 'apl_state', 'apl_pending', 'next_tick')} + left = dict(steps=1, global_tick_offset=0, state=state, terminal=terminal, requests=np.empty((0, 5), np.int64), + summary={'effective_weights_raw_sha256': '1'*64}, traces={}, state_hashes=None, queues=None) + right = copy.deepcopy(left) + with patch.object(T.B, '_complete'): + report = T._pair(left, right) + self.assertTrue(report['numerical_exact']); self.assertFalse(any(report['observed'].values())) + right['state']['v'][0] = 1 + report = T._pair(left, right) + self.assertFalse(report['numerical_exact']) + self.assertIn('pair.state.v', report['failed_checks']) + + def test_imported_parent_fields_cannot_be_rebound(self): + reg = {'parent_records': {'p': {'directory': '/original', 'checkpoint_sha256': '1'*64}}} + complete = {'parents': {'p': {**reg['parent_records']['p'], 'cpu_validation': '/new/check.json'}}} + self.assertEqual(T._parent(reg, complete, 'p')['directory'], '/original') + complete['parents']['p']['directory'] = '/other' + with self.assertRaisesRegex(ValueError, 'Imported parent'): T._parent(reg, complete, 'p') + + def test_output_tree_nonfiles_and_pins_are_actual_bytes(self): + with tempfile.TemporaryDirectory() as name: + p = Path(name).resolve() / 'payload'; p.write_bytes(b'abc') + T.verify_pins({str(p): T.digest(p)}) + with self.assertRaisesRegex(ValueError, 'Pinned file changed'): T.verify_pins({str(p): '0'*64}) + + +class OrchestrationTests(unittest.TestCase): + """Dispatch fixture: deliberately mocked physics/loaders, no scientific PASS.""" + def fixture(self, exp): + plan = T.PLAN.build_plan(); parents = {}; imports = {}; records = {}; verified = {}; templates = {} + delta = dict(edge_id=np.array([0], np.int64), plastic_delta_mv=np.array([0], np.float32)) + def parent(directory, name): + directory.mkdir(parents=True, exist_ok=True) + write(directory / 'summary.json', {'synthetic': True}) + write(directory / 'checkpoint-manifest.json', {'synthetic': True}) + update = exp / 'updates' / name; update.mkdir(parents=True, exist_ok=True) + npz(update / 'delta-after.npz', **delta); write(update / 'update-receipt.json', {'same': 'synthetic-U'}) + write(update / 'overlay-manifest.json', {'synthetic': True}) + auth = exp / 'authorities' / (name + '.epoch.json'); write(auth, {'synthetic': True}) + return dict(directory=str(directory), checkpoint_sha256=T.digest(directory / 'checkpoint-manifest.json'), + epoch_authority=T.A.pin(auth), update_dir=str(update), + after_overlay=dict(overlay_file=str(update / 'delta-after.npz'), overlay_manifest=str(update / 'overlay-manifest.json'))) + for arm, desc in plan['imported_parents'].items(): + p = parent(exp / 'old-AP' / arm, 'old-' + arm) + desc['checkpoint_manifest'] = str(Path(p['directory']) / 'checkpoint-manifest.json') + desc['sha256'] = p['checkpoint_sha256']; parents[desc['role']] = p; imports[arm] = p + registered_parents = copy.deepcopy(parents) + for call in plan['calls']: + name = call['call_id']; out = exp / 'runs' / name + if call['runner'] != 'boundary_oracle': + p = parent(out, name); parents[name] = p + else: + out.mkdir(parents=True); write(out / 'summary.json', {'synthetic': True}) + write(out / 'oracle-manifest.json', dict(schema='conditioning-live-oracle-files-v1', complete=True, + files_sha256={'summary.json': T.digest(out / 'summary.json')})) + group = call['case_group']; template = exp / 'protocol/case-templates' / (group + '.json') + spec = {'synthetic': group, 'steps': call['steps']} + if group not in templates: write(template, spec); templates[group] = str(template) + if call['parent'] is not None: + spec.update(parent_checkpoint_manifest_sha256=parents[call['parent']]['checkpoint_sha256'], + global_tick_offset=call['global_tick_offset']) + case = exp / 'cases' / group; write(case / 'spec.json', spec) + npz(case / 'requests.npz', tick=np.empty(0, np.int32), neuron_idx=np.empty(0, np.int32)) + record = dict(source=str(out), steps=call['steps'], global_tick_offset=call['global_tick_offset'], + summary=dict(nodes=1, edges=1, diagnostic_mode=call['diagnostic_mode'], apl_component={'environment': {}}), + state={'v': np.array([0.], np.float64)}) + if call['runner'] != 'boundary_oracle': record['manifest_sha256'] = parents[name]['checkpoint_sha256'] + records[str(out)] = record + cycle = dict(complete=True, call=call, source_registration_sha256='placeholder', output_inventory={}) + write(exp / 'receipts' / (name + '.cycle.json'), cycle) + for arm, p in imports.items(): + records[p['directory']] = dict(source=p['directory'], summary={}, manifest_sha256=p['checkpoint_sha256']) + reg = dict(schema='conditioning-execution-registration-v1', engineering_only=True, scientific_acquisition_authorized=False, + plan=plan, source_sha256={}, input_sha256={}, prepared_file_sha256={}, parent_records=registered_parents, + case_templates=templates) + regfile = exp / 'protocol/preregistered.json'; write(regfile, reg); sha = T.digest(regfile) + kinds = T.expected_processes(plan) + for name, kind in kinds.items(): + out = exp / ('updates' if kind == 'update' else 'runs') / name.removesuffix('--original-E') + out.mkdir(parents=True, exist_ok=True) + if not any(out.iterdir()): write(out / 'payload.json', {'synthetic': True}) + job = {'kind': kind} + call = next((c for c in plan['calls'] if c['call_id'] == name), None) + if call is not None: + case = exp / 'cases' / call['case_group'] + if kind in ('brain', 'unchanged'): job['case_dir'] = str(case) + elif kind == 'oracle': job['oracle_case'] = str(case) + else: + p = parents[call['parent']] + auth = dict(case=dict(spec=T.A.pin(case / 'spec.json'), requests=T.A.pin(case / 'requests.npz')), + boundary_id=name, registration_sha256=sha, diagnostic_mode=call['diagnostic_mode'], + input_policy=call['input_policy'], transition=call['transition'], + parent=dict(directory=p['directory'], checkpoint_manifest_sha256=p['checkpoint_sha256']), + expected_effective_weight_before_sha256='a'*64, expected_effective_weight_after_sha256='b'*64) + path = exp / 'authorities' / (name + '.json'); write(path, auth); job['authority_file'] = str(path) + cyclefile = exp / 'receipts' / (name + '.cycle.json'); cycle = T.read(cyclefile) + cycle['source_registration_sha256'] = sha; write(cyclefile, cycle) + result = {} + if kind in ('brain', 'unchanged', 'updated'): result['checkpoint_manifest_sha256'] = parents[name]['checkpoint_sha256'] + if kind == 'update': + write(out / 'chain-receipt.json', {'synthetic': True}); result['chain_receipt_sha256'] = T.digest(out / 'chain-receipt.json') + paths = {key: exp / folder / (name + suffix) for key, folder, suffix in + (('process', 'receipts', '.process.json'), ('worker', 'receipts', '.worker.json'), ('job', 'jobs', '.json'))} + for p in paths.values(): write(p, {'synthetic': name}) + verified[name] = dict(name=name, kind=kind, job=job, paths=paths, output=out, + output_inventory=T.tree(out), receipt={'result': result, 'environment': {}}, input_pins={}, + evidence_sha256={str(p): T.digest(p) for p in paths.values()}) + complete = dict(complete=True, registration_sha256=sha, completed_calls=[c['call_id'] for c in plan['calls']], + processes={k: str(v['paths']['process']) for k, v in verified.items()}, parents=parents, + scientific_learning_pass_claimed=False, candidate_generated=False) + write(exp / 'audit/execution-complete.json', complete) + return sha, records, verified + + def test_all_predetermined_calls_compared_and_numeric_failure_not_hidden(self): + with tempfile.TemporaryDirectory() as name, ExitStack() as stack: + exp = Path(name).resolve() / 'phase4aq-mock'; sha, records, verified = self.fixture(exp) + stack.enter_context(patch.object(T, 'EXP', exp)) + stack.enter_context(patch.object(T, 'N', 1)); stack.enter_context(patch.object(T, 'EDGES', 1)) + stack.enter_context(patch.object(T.PLAN, 'validate_plan')) + stack.enter_context(patch.object(T, '_helpers', return_value=SimpleNamespace(_verify_precision=lambda *a: None))) + verifier = stack.enter_context(patch.object(T, 'verify_process', side_effect=lambda n, *args: verified[n])) + stack.enter_context(patch.object(T.D, 'load_checkpoint')); stack.enter_context(patch.object(T.D.C, 'load_checkpoint')) + stack.enter_context(patch.object(T.B, 'load_record', side_effect=lambda d, **kw: records[str(d)])) + stack.enter_context(patch.object(T.B, '_complete')); stack.enter_context(patch.object(T.B, 'close_record')) + stack.enter_context(patch.object(T, '_update_check', return_value={'pass_all': True})) + live = stack.enter_context(patch.object(T.B, 'compare_boundary', return_value={'numerical_exact': True})) + pair = stack.enter_context(patch.object(T, '_pair', return_value={'numerical_exact': True})) + pending = dict(conditional_ref_request_queue_exact=True, reference_queue_and_ref_coverage_complete=True, + accepted_changed_edge_witness_verified=True) + stack.enter_context(patch.object(T, '_pending', return_value=pending)) + stack.enter_context(patch.object(T, '_lineage', return_value={'pass_all': True})) + report = T.audit(sha, save_report=False) + self.assertTrue(report['pass_all']); self.assertEqual(report['actual_new_brain_calls'], 23) + self.assertEqual(report['actual_new_brain_ticks'], 125200); self.assertEqual(report['actual_processes'], 31) + self.assertEqual(verifier.call_count, 31); self.assertEqual(live.call_count, 4); self.assertEqual(pair.call_count, 5) + self.assertFalse(report['brain_executed_here']); self.assertFalse(report['learning_pass_claimed']) + self.assertFalse((exp / 'audit/numerical-comparison.json').exists()) + pair.return_value = {'numerical_exact': False} + report = T.audit(sha) + self.assertEqual(report['status'], 'FAIL-NUMERICAL-OR-LINEAGE'); self.assertFalse(report['pass_all']) + self.assertTrue((exp / 'audit/numerical-comparison.json').is_file()) + with self.assertRaises(FileExistsError): T.audit(sha) + + +if __name__ == '__main__': + unittest.main() diff --git a/models/male-cns-lif/conditioning-execution-v1/test_boundary_compare.py b/models/male-cns-lif/conditioning-execution-v1/test_boundary_compare.py new file mode 100644 index 0000000..4303e96 --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/test_boundary_compare.py @@ -0,0 +1,281 @@ +"""Tiny synthetic arrays / temporary read fixtures; no brain or GPU execution.""" +import copy +import csv +import json +from pathlib import Path +import tempfile +import unittest + +import numpy as np + +import boundary_compare as m + + +Q=m._load('engineering_queue_audit.py',m.PINS['engineering_queue_audit.py']) + + +def raw(a):return __import__('hashlib').sha256(np.ascontiguousarray(a).tobytes()).hexdigest() + + +def save(path,value):path.write_text(json.dumps(value,allow_nan=False),encoding='utf-8') + + +class Fixture: + def __init__(self,cut=20,total=61,reject=False,delta=None): + self.cut,self.total=cut,total + self.graph=dict(ptr=np.array([0,2,4,5,6],np.int64),src=np.array([0,0,1,1,2,3],np.int32), + dst=np.array([1,2,2,3,3,0],np.int32),edge_id=np.arange(6,dtype=np.int64), + base_weights=np.full(6,.000275,np.float64),apl_mask=np.zeros(6,np.bool_), + candidate_edge_id=np.array([0],np.int64),delta_before=np.array([0.],np.float32), + delta_after=np.array([-.00275 if delta is None else delta],np.float32)) + g=self.graph + _,_,rows,before,after=m._candidates(g['ptr'],g['src'],g['dst'],g['edge_id'],g['base_weights'],g['apl_mask'], + g['candidate_edge_id'],g['delta_before'],g['delta_after'],Q) + self.before_sha=m._effective_hash(g['base_weights'],g['apl_mask'],rows,before) + self.after_sha=m._effective_hash(g['base_weights'],g['apl_mask'],rows,after) + self.effective=g['base_weights'].copy();self.effective[rows]=after + pairs=[(0,3),(cut-2,2),(cut-1,0),(cut+23,0),(total-1,2)] + if reject:pairs.append((cut+17,1)) + pairs=sorted(pairs);self.tick=np.array([t for t,n in pairs],np.int32);self.node=np.array([n for t,n in pairs],np.int32) + self.requests=np.column_stack((self.tick,self.node,np.ones(len(pairs),np.int32),np.ones(len(pairs),np.int32),np.zeros(len(pairs),np.int32))).astype(np.int64) + refs=[];ref=np.zeros(4,np.int32);self.queues=[] + for t in range(total): + ref=np.maximum(ref-1,0).astype(np.int32);ref[self.node[self.tick==t]]=22;refs.append(ref.copy()) + sources=self.node[self.tick==t-18] if t>=18 else self.node[:0] + qr,_,_=Q._queue(t,t if t=start)&(self.tick=start)&(self.requests[:,0]0, + global_tick_offset=start,global_next_tick=stop) + return dict(steps=stop-start,global_tick_offset=start,state=state, + terminal=dict(spike_ring=ring,apl_state=self.traces['state'][stop-1].copy(),apl_pending=pending,next_tick=np.asarray(stop-start,np.int64)), + requests=req,summary=summary,traces={k:v[start:stop].copy() for k,v in self.traces.items()},state_hashes=hashes,queues=queues, + terminal_origin='recorded_terminal_queues') + + def compare(self,paired_authorities=None): + return m.compare_boundary(self.oracle,self.child,parent=self.parent, + expected_before_sha256=self.before_sha,expected_after_sha256=self.after_sha,paired_authorities=paired_authorities) + + def audit(self): + return m.audit_changed_pending(self.parent,self.child,**self.graph,input_pins={'synthetic-input-authority':'a'*64}) + + +class ComparisonTests(unittest.TestCase): + def test_exact_reference_suffix_and_different_raw_ring_phase(self): + f=Fixture();result=f.compare() + self.assertTrue(result['numerical_exact'],result['failed_checks']) + self.assertTrue(result['suffix_reference_diagnostics_compared']) + self.assertFalse(np.array_equal(f.child['terminal']['spike_ring'],f.oracle['terminal']['spike_ring'])) + self.assertFalse(result['learning_pass_claimed']) + self.assertFalse(result['changed_candidate_pending_arrival_coverage_verified']) + + def test_light_reports_only_observed_scope(self): + f=Fixture();f.child.update(traces={},state_hashes=None,queues=None);f.child['summary']['diagnostic_mode']='light' + result=f.compare();self.assertTrue(result['numerical_exact']);self.assertFalse(result['suffix_reference_diagnostics_compared']) + self.assertFalse(result['light_per_tick_identity_claimed']) + self.assertNotIn('suffix.state_hashes',result['checks']) + + def test_signed_zero_final_change_is_detected(self): + f=Fixture();f.child['state']['g'][0]=0.;f.oracle['state']['g'][0]=-0. + f.child['state_hashes'][-1]['g_sha256']=raw(f.child['state']['g']) + f.oracle['state_hashes'][-1]['g_sha256']=raw(f.oracle['state']['g']) + result=f.compare();self.assertFalse(result['pass_all']);self.assertIn('final.g',result['failed_checks']) + + def test_tick_shift_ref_and_event_counts_are_not_interchangeable(self): + f=Fixture();old=int(f.child['state']['step'][0]);f.child['state']['step'][0]-=1 + f.child['terminal']['spike_ring'][old%19,0]=0 + f.child['terminal']['spike_ring'][(old-1)%19,0]=1 + result=f.compare();self.assertFalse(result['numerical_exact']);self.assertIn('suffix.events.absolute_tick',result['failed_checks']) + + def test_reference_missing_history_is_rejected_not_false_pass(self): + f=Fixture();f.child['state_hashes']=None + with self.assertRaisesRegex(ValueError,'diagnostics missing'):f.compare() + + def test_queue_joint_header_exclusion_is_only_comparison_not_audit(self): + f=Fixture();f.child['queues'][17]['joint_sha256']='1'*64 + self.assertTrue(f.compare()['pass_all']) + self.assertFalse(f.audit()['pass_all']) + + def test_wrong_live_prefix_parent_cannot_pass_suffix_only(self): + f=Fixture();f.oracle['boundary_snapshot']['g'][0]+=1e-12 + result=f.compare();self.assertFalse(result['pass_all']);self.assertIn('boundary.live_parent.g',result['failed_checks']) + + def test_original_authority_and_weight_scope_are_checked(self): + f=Fixture();f.oracle['oracle_provenance']['authority_sha256']='a'*64 + self.assertFalse(f.compare()['pass_all']) + + def pair(self,f,changed=None): + original={key:{'synthetic':1} for key in m.AUTHORITY_FIELDS} + original.update(schema='conditioning-updated-boundary-v1',boundary_id='reference-boundary',diagnostic_mode='reference') + other=copy.deepcopy(original);other.update(boundary_id='light-boundary',diagnostic_mode='light') + if changed:other.update(changed) + pair=(json.dumps(original,sort_keys=True,indent=2).encode(),json.dumps(other,sort_keys=True,indent=2).encode()) + f.oracle['oracle_provenance']['authority_sha256']=__import__('hashlib').sha256(pair[0]).hexdigest() + f.child['summary']['boundary_authority_sha256']=__import__('hashlib').sha256(pair[1]).hexdigest() + f.child.update(traces={},state_hashes=None,queues=None);f.child['summary']['diagnostic_mode']='light' + return pair + + def test_separately_pinned_ref_light_authorities_allow_only_mode_and_id(self): + f=Fixture();pair=self.pair(f) + self.assertFalse(f.compare()['pass_all']) + result=f.compare(pair);self.assertTrue(result['pass_all'],result['failed_checks']) + audit=result['paired_authority_audit'];self.assertTrue(audit['checks']['actual_raw_file_SHA_bound']) + self.assertNotEqual(audit['raw_sha256'][0],audit['canonical_sha256'][0]) + self.assertEqual(audit['common_content_canonical_sha256'][0],audit['common_content_canonical_sha256'][1]) + + def test_paired_authority_cannot_change_parent_after_requests_or_registration(self): + for key in ('parent','overlay_after','case','registration_sha256','cpu_validation','source_files','frozen_inputs'): + with self.subTest(key=key): + f=Fixture();pair=self.pair(f,{key:{'different':True}}) + result=f.compare(pair);self.assertFalse(result['pass_all']) + self.assertIn(key,result['paired_authority_audit']['disallowed_different_fields']) + + def test_paired_authority_raw_sha_extra_field_and_duplicate_key_are_rejected(self): + f=Fixture();pair=self.pair(f) + self.assertFalse(f.compare((pair[0]+b' ',pair[1]))['pass_all']) + extra=json.loads(pair[1]);extra['unapproved']=True + with self.assertRaisesRegex(ValueError,'exact boundary schema'):f.compare((pair[0],json.dumps(extra).encode())) + duplicate=pair[1][:-1]+b',"boundary_id":"duplicate"}' + with self.assertRaisesRegex(ValueError,'Duplicate authority'):f.compare((pair[0],duplicate)) + f=Fixture();f.child['summary']['effective_weights_raw_sha256']='a'*64 + self.assertFalse(f.compare()['pass_all']) + + +class CoverageTests(unittest.TestCase): + def test_actual_changed_accepted_crossing_witness(self): + f=Fixture();result=f.audit();self.assertTrue(result['pass_all'],result['differences']) + w=result['coverage']['first_witness'];self.assertEqual(w['global_edge_id'],0) + self.assertEqual(w['source_spike_global_tick'],19);self.assertEqual(w['arrival_global_tick'],37) + self.assertEqual(w['arrival_child_local_tick'],17);self.assertEqual(w['post_threshold_target_ref'],0) + self.assertNotEqual(w['before_weight_f64_le_hex'],w['after_weight_f64_le_hex']) + + def test_fixed18_delay_across_all_nearby_ring_phases(self): + for cut in (17,18,19,20,21): + with self.subTest(cut=cut): + result=Fixture(cut=cut,total=cut+41).audit() + self.assertTrue(result['pass_all'],result['differences']) + self.assertEqual(result['coverage']['first_witness']['arrival_child_local_tick'],17) + + def test_changed_candidate_without_pending_source_is_incomplete(self): + f=Fixture();g=f.graph;g['candidate_edge_id'][0]=2 # source1 never spikes in this fixture + _,_,rows,_,after=m._candidates(g['ptr'],g['src'],g['dst'],g['edge_id'],g['base_weights'],g['apl_mask'], + g['candidate_edge_id'],g['delta_before'],g['delta_after'],Q) + f.child['summary']['effective_weights_raw_sha256']=m._effective_hash(g['base_weights'],g['apl_mask'],rows,after) + result=f.audit();self.assertTrue(result['conditional_ref_request_queue_exact']) + self.assertEqual(result['changed_effective_numeric_count'],1) + self.assertIsNone(result['coverage']['first_witness']);self.assertFalse(result['pass_all']) + + def test_target_spike_same_arrival_tick_rejects_changed_edge(self): + f=Fixture(reject=True);result=f.audit() + self.assertTrue(result['conditional_ref_request_queue_exact']);self.assertFalse(result['pass_all']) + self.assertEqual(result['coverage']['rejected_changed_candidate_crossing_arrivals'],1) + self.assertIsNone(result['coverage']['first_witness']) + + def test_zero_delta_and_signed_zero_delta_are_not_changed_edge_coverage(self): + for delta in (np.float32(0.),np.float32(-0.)): + f=Fixture(delta=delta);result=f.audit() + self.assertFalse(result['pass_all']);self.assertEqual(result['changed_effective_numeric_count'],0) + self.assertEqual(result['status'],'INCOMPLETE-CHANGED-EDGE-COVERAGE') + self.assertEqual(result['changed_delta_bit_count'],1) + + def test_wrong_delta_rejected_against_effective_runtime_hash(self): + f=Fixture();f.graph['delta_after'][0]=np.float32(-.003) + with self.assertRaisesRegex(ValueError,'effective SHA'):f.audit() + + def test_source_array_shuffle_and_queue_order_shuffle_rejected(self): + f=Fixture();f.graph['src'][0],f.graph['src'][2]=f.graph['src'][2],f.graph['src'][0] + with self.assertRaisesRegex(ValueError,'CSR ordering'):f.audit() + f=Fixture();f.child['queues'][17]['ordered_edge_sha256']='f'*64 + result=f.audit();self.assertFalse(result['pass_all']);self.assertGreater(result['difference_count'],0) + + def test_wrong_delay_by_one_tick_changes_queue_and_acceptance(self): + f=Fixture();f.child['queues'][17],f.child['queues'][18]=f.child['queues'][18],f.child['queues'][17] + for t,row in enumerate(f.child['queues']):row['tick']=t + self.assertFalse(f.audit()['pass_all']) + + def test_reference_absence_is_incomplete_even_with_computed_witness(self): + f=Fixture();f.child.update(traces={},state_hashes=None,queues=None) + result=f.audit();self.assertTrue(result['conditional_ref_request_queue_exact']) + self.assertIsNotNone(result['coverage']['first_witness']);self.assertFalse(result['accepted_changed_edge_witness_verified']) + + def test_duplicate_or_missing_candidate_and_nonfinite_reject(self): + f=Fixture();f.graph['candidate_edge_id'][0]=99 + with self.assertRaisesRegex(ValueError,'Candidate missing'):f.audit() + f=Fixture();f.graph['delta_after'][0]=np.nan + with self.assertRaisesRegex(ValueError,'delta after'):f.audit() + + def test_request_acceptance_reconstructed_not_just_copied(self): + f=Fixture();f.child['requests'][0,3:]=[0,1] + result=f.audit();self.assertFalse(result['pass_all']) + self.assertTrue(any(row['field']=='request.acceptance' for row in result['differences'])) + + def test_incomplete_summary_is_not_an_array_only_pass(self): + f=Fixture();f.child['summary']['complete']=False + with self.assertRaisesRegex(ValueError,'summary is incomplete'):f.audit() + + +class LoaderTests(unittest.TestCase): + def test_actual_frozen_loader_accepts_declared_oracle_without_fake_schema(self): + f=Fixture() + with tempfile.TemporaryDirectory() as tmp: + root=Path(tmp);r=f.oracle + save(root/'summary.json',r['summary']);np.savez_compressed(root/'state.npz',**r['state']) + np.savez_compressed(root/'terminal-queues.npz',**r['terminal']) + np.save(root/'apl-pending-tail.float32.npy',r['terminal']['apl_pending']) + save(root/'apl-timing-audit.json',{'final_apl_state_u32_bits':r['terminal']['apl_state'].view(np.uint32).tolist()}) + for name,filename in m.C.TRACE_FILES.items():np.save(root/filename,r['traces'][name]) + for name,rows in (('queue.jsonl',r['queues']),('tick-state-hashes.jsonl',r['state_hashes'])): + (root/name).write_text(''.join(json.dumps(row)+'\n' for row in rows),encoding='utf-8') + with (root/'requests.csv').open('w',newline='') as stream: + writer=csv.writer(stream);writer.writerow(m.C.REQUEST_FIELDS);writer.writerows(r['requests']) + np.savez_compressed(root/'boundary-snapshot.npz',**r['boundary_snapshot']) + save(root/'boundary-comparison.json',r['boundary_report']);save(root/'oracle-provenance.json',r['oracle_provenance']) + loaded=m.load_record(root,kind='oracle') + try: + self.assertTrue(loaded['summary']['within_trial_weight_updates']) + self.assertFalse(loaded['summary']['production_backend']) + result=m.compare_boundary(loaded,f.child,parent=f.parent, + expected_before_sha256=f.before_sha,expected_after_sha256=f.after_sha) + self.assertTrue(result['pass_all']) + finally:m.close_record(loaded) + + +if __name__=='__main__':unittest.main() diff --git a/models/male-cns-lif/conditioning-execution-v1/test_boundary_oracle.py b/models/male-cns-lif/conditioning-execution-v1/test_boundary_oracle.py new file mode 100644 index 0000000..306c24c --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/test_boundary_oracle.py @@ -0,0 +1,331 @@ +"""Independent CPU tests of the new diagnostic oracle, not GPU evidence. + +AST tests consume the real pinned AJ/C/R source. Tiny tensor/helper doubles +exercise switch/installation code without claiming a fullgraph simulation, +executing any CUDA kernel, or restoring state as an equivalence oracle. +""" +import ast +import copy +import hashlib +from pathlib import Path +import sys +import tempfile +from types import SimpleNamespace +import unittest +from unittest.mock import patch +import uuid + +import numpy as np + +import boundary_oracle as b + + +ROOT = Path(__file__).resolve().parent.parent +AJ = ROOT / 'apl-timing-lab-v1' / 'gpu_timing.py' + + +def sha(value): + return hashlib.sha256(value).hexdigest() + + +def bits(value): + return np.asarray(value).tobytes() + + +def fixture(tick=41): + """Raw global-phase rings; the live oracle must NOT rotate spike slots.""" + v = np.array([-.051, -.047], np.float64) + g = np.array([-0., .00013], np.float64) + ref, counts = np.array([22, 3], np.int32), np.array([5, 2], np.int32) + ring = np.zeros((19, 2), np.uint8) + ring[(tick - 1) % 19, 0] = 1 + ring[(tick - 18) % 19, 1] = 1 + # Distinct samples across every row expose off-by-one and 18/19 confusion. + apl_ring = np.arange(36, dtype=np.float32).reshape(18, 2) / np.float32(16) + apl_ring[0, 0] = np.float32(-0.) + apl_state = np.array([.5, -0.], np.float32) + parent = dict(steps=tick, global_next_tick=tick, fresh=True, manifest_sha256='a' * 64, + state={key: value.copy() for key, value in zip(('v', 'g', 'ref', 'counts'), (v, g, ref, counts))}, + terminal=dict(spike_ring=ring.copy(), apl_state=apl_state.copy(), + apl_pending=np.concatenate((apl_ring[tick % 18:], apl_ring[:tick % 18])), next_tick=np.asarray(tick, np.int64))) + return parent, [v, g, ref, counts, ring, apl_ring, apl_state] + + +class Tensor: + """NumPy-only tensor double, including assignment accounting.""" + def __init__(self, value): + self.a = np.array(value, copy=True) + self.assignments = [] + + def cpu(self): return self + def numpy(self): return self.a + def numel(self): return self.a.size + + def __getitem__(self, key): + return Tensor(self.a[key.a if isinstance(key, Tensor) else key]) + + def __setitem__(self, key, value): + index = key.a if isinstance(key, Tensor) else key + self.assignments.append(np.array(index, copy=True)) + self.a[index] = value.a if isinstance(value, Tensor) else value + + def __add__(self, other): return Tensor(self.a + (other.a if isinstance(other, Tensor) else other)) + def __mul__(self, other): return Tensor(self.a * other) + + +class CanonicalEdgeIdentity: + """No actual fullgraph arrays: only the private test's admission double.""" + def __len__(self): return 25582938 + def __getitem__(self, key): return np.asarray(key, dtype=np.int64).copy() + + +class TinyHelper: + @staticmethod + def map_installation(base, edges, apl_mask, plan): + nz = plan['plastic_delta_mv'] != np.float32(0) + rows = plan['edge_id'][nz] + # Independent frozen bridge expression; do not invoke oracle helper. + expected = np.add(base[rows], np.multiply(plan['plastic_delta_mv'][nz].astype(np.float64), np.float64(.001))) + return dict(rows=rows, expected_si=expected, missing_candidate_count=0) + + @staticmethod + def expected_hashes(base, apl_mask, mapping): + result = base.copy() + result[mapping['rows']] = mapping['expected_si'] + return sha(bits(base)), sha(bits(result)) + + @staticmethod + def bit_equal(a, c): + return a.dtype == c.dtype and a.shape == c.shape and a.tobytes() == c.tobytes() + + +class BoundaryOracleTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + # Use the actual pinned dependency, with no Torch/CUDA import. + cls.c = b.frozen() + cls.source = AJ.read_bytes() + + def test_real_AJ_hook_rollback_and_entire_original_functions_identical(self): + source_before = sha(self.source) + derived, audit = b.derive_oracle(self.c, self.source) + old_reference, original_audit = self.c.R.derive_ast(self.source, 'reference') + loops = [n for n in ast.walk(derived) if isinstance(n, ast.For) and isinstance(n.target, ast.Name) and n.target.id == 'tick'] + self.assertEqual(len(loops), 1) + self.assertEqual(ast.unparse(loops[0].body[0]), b.HOOK) + hookless = copy.deepcopy(derived) + loop = next(n for n in ast.walk(hookless) if isinstance(n, ast.For) and isinstance(n.target, ast.Name) and n.target.id == 'tick') + del loop.body[0] + self.assertEqual(self.c.R._dump(hookless), self.c.R._dump(old_reference)) + # Independently roll back the inherited R edits as well. This reaches + # the ACTUAL frozen AJ functions, not merely the adapter's own receipt. + loader, run = hookless.body + admission = [n for n in ast.walk(loader) if isinstance(n, ast.Compare) and ast.unparse(n) == "0 < steps <= spec['max_steps']"] + self.assertEqual(len(admission), 1) + admission[0].comparators[-1] = ast.Constant(value=1000) + body = next(n for n in run.body if isinstance(n, ast.Try)).body + for text in (self.c.R._INSTALL, self.c.R._FINISH): + indices = [i for i, n in enumerate(body) if ast.unparse(n) == text] + self.assertEqual(len(indices), 1) + del body[indices[0]] + originals = ast.parse(self.source) + original_module = ast.Module(body=[next(n for n in originals.body if isinstance(n, ast.FunctionDef) and n.name == name) + for name in ('load_inputs', 'run_gpu')], type_ignores=[]) + self.assertEqual(self.c.R._dump(hookless), self.c.R._dump(original_module)) + self.assertEqual(audit['base'], original_audit) + self.assertTrue(audit['full_AST_rollback_identical']) + self.assertFalse(audit['restore_called']) + self.assertFalse(audit['production_backend']) + self.assertEqual(sha(self.source), source_before) + compile(derived, '', 'exec') + + def test_oracle_has_one_hook_before_all_frozen_tick_operations_and_no_restore(self): + tree, _ = b.derive_oracle(self.c, self.source) + loop = next(n for n in ast.walk(tree) if isinstance(n, ast.For) and isinstance(n.target, ast.Name) and n.target.id == 'tick') + self.assertEqual(ast.unparse(loop.body[1]), 'requests.zero_()') + calls = [ast.unparse(n.func) for n in ast.walk(tree) if isinstance(n, ast.Call)] + self.assertEqual(calls.count('_oracle.switch'), 1) + self.assertFalse(any('restore' in name for name in calls)) + self.assertFalse(any(isinstance(n, ast.Name) and n.id == '_continuation' for n in ast.walk(tree))) + oracle_class = b.runtime_class(self.c) + self.assertIs(oracle_class.__bases__[0], self.c.R._Runtime) + self.assertFalse(issubclass(oracle_class, self.c.ContinuedRuntime)) + self.assertFalse(hasattr(oracle_class, 'restore')) + self.assertIn('due = ring[(tick - 18) % 19]', ast.unparse(tree)) + self.assertIn('slot = tick % apl_delay', ast.unparse(tree)) + + def test_real_source_pin_rejects_even_whitespace_drift(self): + for changed in (self.source + b'\n', self.source.replace(b'tick >= 18', b'tick >= 17')): + with self.assertRaisesRegex(ValueError, 'AJ source pin'): + b.derive_oracle(self.c, changed) + + def test_frozen_C_pin_and_existing_module_binding_refuse_before_execution(self): + with tempfile.TemporaryDirectory() as tmp: + path = Path(tmp) / 'tiny.py' + path.write_text('VALUE = 17\n', encoding='utf-8') + name = 'boundary_oracle_cpu_fixture_' + uuid.uuid4().hex + with self.assertRaisesRegex(ValueError, 'Unverified'): + b.load(name, path, '0' * 64) + self.assertNotIn(name, sys.modules) + try: + module = b.load(name, path, b.digest(path)) + self.assertEqual(module.VALUE, 17) + with self.assertRaisesRegex(ValueError, 'Unverified'): + b.load(name, path, b.digest(path)) + self.assertIs(sys.modules[name], module) + finally: + sys.modules.pop(name, None) # Only this test-created tiny module. + frozen_path = Path(tmp) / 'runtime_continuation.py' + frozen_path.write_text('raise AssertionError("must not execute")\n', encoding='utf-8') + with patch.object(b, 'LAB', Path(tmp)), self.assertRaisesRegex(ValueError, 'Unverified'): + b.frozen() + + def test_expected_bridge_is_original_base_plus_widened_after_delta_not_old_effective(self): + base = np.array([.000275, .000550, -.00011, .0], np.float64) + mask = np.array([0, 1], np.int64) + before = np.array([-.00275, -.013125], np.float32) + after = np.array([-.0055, -.02125], np.float32) + old_effective = base[mask] + before.astype(np.float64) * np.float64(.001) + expected = np.add(base[mask], np.multiply(after.astype(np.float64), np.float64(.001))) + actual = b.expected_candidate_values(base, mask, after) + self.assertEqual(bits(actual), bits(expected)) + self.assertNotEqual(bits(actual), bits(old_effective + after.astype(np.float64) * np.float64(.001))) + self.assertNotEqual(bits(actual), bits(base[mask] + (after * np.float32(.001)).astype(np.float64))) + zero = np.array([0., -0.], np.float32) + self.assertEqual(bits(b.expected_candidate_values(base, mask, zero)), bits(base[mask])) + self.assertEqual(bits(base), bits(np.array([.000275, .000550, -.00011, .0], np.float64))) + + def test_candidate_bridge_rejects_dtype_bounds_order_sign_and_nonfinite(self): + base, mask, delta = np.array([.01, .02], np.float64), np.array([0, 1], np.int64), np.array([-.001, -.002], np.float32) + cases = [(base.astype(np.float32), mask, delta), (base, mask.astype(np.int32), delta), + (base, mask, delta.astype(np.float64)), (base, mask[::-1].copy(), delta), + (base, np.array([0, 0], np.int64), delta), (base, np.array([-1, 1], np.int64), delta), + (base, np.array([0, 2], np.int64), delta), (base, mask, np.array([np.nan, 0.], np.float32)), + (base, mask, np.array([np.inf, 0.], np.float32)), (base, mask, np.array([.01, 0.], np.float32)), + (base, mask, np.array([-20., 0.], np.float32)), (np.array([np.inf, .02], np.float64), mask, delta)] + for args in cases: + with self.subTest(args=[(a.dtype.str, a.tolist()) for a in args]), self.assertRaises(ValueError): + b.expected_candidate_values(*args) + + def test_live_raw_boundary_checks_18_and_19_phases_without_rotating_spike_ring(self): + for tick in (1, 5, 17, 18, 19, 20, 35, 36, 37, 38, 41, 1200): + parent, arrays = fixture(tick) + before = [bits(value) for value in arrays] + actual, checks = b.boundary_arrays(parent, tick, *arrays) + self.assertTrue(all(checks.values())) + self.assertEqual(bits(actual['spike_ring']), bits(arrays[4])) + order = np.array([(tick + i) % 18 for i in range(18)]) + self.assertEqual(bits(actual['apl_pending']), bits(arrays[5][order])) + self.assertEqual(before, [bits(value) for value in arrays]) + if tick % 19: + wrong = list(arrays); wrong[4] = self.c.rotate_spike_ring(arrays[4], tick) + with self.subTest(tick=tick), self.assertRaisesRegex(ValueError, 'spike_ring'): + b.boundary_arrays(parent, tick, *wrong) + + def test_boundary_rejects_rawbit_dtype_queue_clock_or_counts_corruption(self): + for variant in ('negative_zero_g', 'negative_zero_apl', 'g_dtype', 'v_shape', 'counts', 'spike', 'apl_phase', 'clock', 'continued', 'booltick'): + parent, arrays = fixture(41) + tick = 41 + if variant == 'negative_zero_g': arrays[1][0] = 0. + if variant == 'negative_zero_apl': arrays[6][1] = np.float32(0.) + if variant == 'g_dtype': arrays[1] = arrays[1].astype(np.float32) + if variant == 'v_shape': arrays[0] = arrays[0][:-1] + if variant == 'counts': arrays[3][0] += 1 + if variant == 'spike': arrays[4][2, 0] ^= 1 + if variant == 'apl_phase': arrays[5] = np.roll(arrays[5], 1, axis=0) + if variant == 'clock': parent['global_next_tick'] += 1 + if variant == 'continued': parent['fresh'] = False + if variant == 'booltick': tick = True + with self.subTest(variant=variant), self.assertRaises(ValueError): b.boundary_arrays(parent, tick, *arrays) + + def runtime_fixture(self, out): + parent, arrays = fixture(41) + base = np.array([.000275, .000550, -.00011, .0], np.float64) + mask = np.array([0, 1], np.int64) + old = dict(edge_id=mask, plastic_delta_mv=np.array([-.00275, -.013125], np.float32)) + new = dict(edge_id=mask, plastic_delta_mv=np.array([-.0055, -.02125], np.float32)) + helper = TinyHelper() + apl_mask = SimpleNamespace(sum=lambda: 5845) + prior = helper.map_installation(base, None, apl_mask, old) + following = helper.map_installation(base, None, apl_mask, new) + before_sha = helper.expected_hashes(base, apl_mask, prior)[1] + after_sha = helper.expected_hashes(base, apl_mask, following)[1] + parent['summary'] = {'effective_weights_raw_sha256': before_sha} + saved = {} + # Only these diagnostic I/O and GPU-observer dependencies are doubles; + # the actual Oracle install/switch implementation is executed below. + r = SimpleNamespace(_Runtime=self.c.R._Runtime, FiniteObserver=lambda *_: object(), + _copy_input=lambda *_: None, digest=lambda p: sha(str(p).encode()), + _save=lambda p, value: saved.__setitem__(Path(p).name, value)) + cls = b.runtime_class(SimpleNamespace(R=r)) + boundary = dict(parent=parent, after_plan=new, + value=dict(expected_effective_weight_before_sha256=before_sha, expected_effective_weight_after_sha256=after_sha), + paths={key: out / key for key in ('before_overlay', 'before_manifest', 'after_overlay', 'after_manifest')}) + obj = cls('reference', {'max_steps': 60, 'steps': 60}, helper, old, {}, {}, + SimpleNamespace(int64=np.int64, float64=np.float64), out / 'old.npz', out / 'old.json', boundary=boundary) + weights = Tensor(base) + graph = dict(nodes=range(166700), edge_id=CanonicalEdgeIdentity(), weights=base) + upload = lambda value, dtype: Tensor(np.asarray(value, dtype=dtype)) + args = [Tensor(value) for value in arrays[:-1]] + [SimpleNamespace(state=Tensor(arrays[-1]))] + return obj, weights, graph, apl_mask, upload, args, saved, base, new + + def test_actual_install_then_one_switch_replaces_base_plus_after_and_never_state_or_queues(self): + with tempfile.TemporaryDirectory() as tmp: + out = Path(tmp) + obj, weights, graph, mask, upload, args, saved, base, new = self.runtime_fixture(out) + obj.install(weights, graph, mask, upload, out, {}, 'CPU-double') + before = weights.a.copy() + state_before = [bits(x.a) for x in args[:-1]] + [bits(args[-1].state.a)] + obj.switch(41, weights, *args, out) + expected = base.copy() + expected[new['edge_id']] = np.add(base[new['edge_id']], np.multiply(new['plastic_delta_mv'].astype(np.float64), np.float64(.001))) + self.assertEqual(bits(weights.a), bits(expected)) + wrong = before.copy(); wrong[new['edge_id']] += new['plastic_delta_mv'].astype(np.float64) * np.float64(.001) + self.assertNotEqual(bits(weights.a), bits(wrong)) + self.assertEqual(bits(weights.a[2:]), bits(before[2:])) + self.assertEqual(obj.switches, 1) + self.assertEqual(obj.installations, 1) + self.assertEqual(state_before, [bits(x.a) for x in args[:-1]] + [bits(args[-1].state.a)]) + self.assertFalse(saved['boundary-comparison.json']['restore_called']) + self.assertFalse(saved['boundary-comparison.json']['queues_replaced']) + self.assertEqual(len(weights.assignments), 2) # One old-overlay install, one replacement. + with np.load(out / 'boundary-snapshot.npz', allow_pickle=False) as recorded: + self.assertEqual(bits(recorded['g']), state_before[1]) + with self.assertRaisesRegex(ValueError, 'repeated'): obj.switch(41, weights, *args, out) + with self.assertRaisesRegex(ValueError, 'one intact'): obj.install(weights, graph, mask, upload, out, {}, 'CPU-double') + + def test_switch_refuses_wrong_time_or_preexisting_weight_state_drift(self): + for variant in ('before_install', 'wrong_tick', 'weight_drift', 'state_drift', 'after_hash'): + with tempfile.TemporaryDirectory() as tmp: + out = Path(tmp) + obj, weights, graph, mask, upload, args, _, _, _ = self.runtime_fixture(out) + obj.install(weights, graph, mask, upload, out, {}, 'CPU-double') + tick = 41 + if variant == 'before_install': obj.installations = 0 + if variant == 'wrong_tick': tick = 42 + if variant == 'weight_drift': weights.a[3] = .0001 + if variant == 'state_drift': args[1].a[0] = 0. + if variant == 'after_hash': obj.after_sha = 'f' * 64 + with self.subTest(variant=variant), self.assertRaises(ValueError): obj.switch(tick, weights, *args, out) + self.assertEqual(obj.switches, 0) + self.assertFalse((out / 'boundary-snapshot.npz').exists()) + + def test_finish_requires_the_boundary_and_summary_discloses_diagnostic_scope(self): + cls = b.runtime_class(self.c) + obj = object.__new__(cls) + obj.switches = 0 + with self.assertRaisesRegex(ValueError, 'did not execute'): obj.finish() + obj.mode = 'reference'; obj.spec = dict(max_steps=100, steps=100) + obj.result = {'first_failure_tick': -1}; obj.boundary_tick = 41; obj.switches = 1 + obj.after_sha = 'a' * 64; obj.before_sha = 'b' * 64 + result = obj.summary({'complete': True, 'effective_weights_raw_sha256': 'b' * 64, 'learning': False}) + self.assertTrue(result['within_trial_weight_updates']) + self.assertFalse(result['production_backend']) + self.assertFalse(result['restored_from_checkpoint']) + self.assertFalse(result['learning_pass_claimed']) + self.assertEqual(result['effective_weights_raw_sha256'], 'a' * 64) + self.assertEqual(result['initial_effective_weights_raw_sha256'], 'b' * 64) + + +if __name__ == '__main__': + unittest.main() diff --git a/models/male-cns-lif/conditioning-execution-v1/test_boundary_plan.py b/models/male-cns-lif/conditioning-execution-v1/test_boundary_plan.py new file mode 100644 index 0000000..0d321f7 --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/test_boundary_plan.py @@ -0,0 +1,284 @@ +"""CPU fixtures and frozen-source checks; never sample or run a brain.""" +from copy import deepcopy +import hashlib +import importlib.util +import json +from pathlib import Path +import sys +import unittest +from unittest.mock import patch + +import numpy as np +import boundary_plan as B + + +SOURCE = Path(__file__).resolve().parent.parent +EVIDENCE = SOURCE.parent / 'reports/phase4ap-publication/evidence' +if not EVIDENCE.is_dir(): + EVIDENCE = SOURCE.parents[1] / 'registry/phase4ap-conditioning-interface-v2/evidence' +OUTPUT_ROOT = '/data/digital-drosophila/experiments/boundary-plan-fixture' + + +def load_source(name, path): + spec = importlib.util.spec_from_file_location(name, path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +class BoundaryPlanTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.template = json.loads((EVIDENCE / 'cases/D1--reference--epoch-001/spec.json').read_text()) + cls.registry = load_source('boundary_test_seed_registry', SOURCE/'olfactory-conditioning-lab-v1/seed_registry.py') + with patch.dict(sys.modules, {'seed_registry': cls.registry}): + cls.trial_plan = load_source('boundary_test_trial_plan', SOURCE/'olfactory-conditioning-lab-v1/trial_plan.py') + + def setUp(self): + self.plan = B.build_plan() + self.calls = {c['call_id']: c for c in self.plan['calls']} + + def spec(self, call): + return B.derive_case_spec(self.template, call, budget_root=OUTPUT_ROOT) + + def test_source_protocol_and_template_pins_match_actual_local_bytes(self): + for path, pin in B.SOURCE_PINS.items(): + self.assertEqual(hashlib.sha256((SOURCE/path).read_bytes()).hexdigest(), pin, path) + for path, pin in B.AP_ANCHORS.items(): + self.assertEqual(hashlib.sha256((EVIDENCE/path).read_bytes()).hexdigest(), pin, path) + for arm, pin in B.PARENT_PINS.items(): + raw = (EVIDENCE/f'runs/{arm}--reference--epoch-001/checkpoint-manifest.json').read_bytes() + self.assertEqual(hashlib.sha256(raw).hexdigest(), pin) + self.assertEqual(B._sha(self.template), B.TEMPLATE_PAYLOAD_SHA256) + + def test_exact_inventory_ticks_children_and_no_execution_claim(self): + self.assertTrue(B.validate_plan(self.plan)) + self.assertEqual(len(self.calls), 23) + self.assertEqual(sum(c['steps'] for c in self.calls.values()), 125200) + self.assertEqual(len({c['case_group'] for c in self.calls.values()}), 17) + self.assertEqual(len(self.plan['imported_parents']), 3) + self.assertEqual(len(self.plan['cpu_children']), 7) + self.assertEqual(self.plan['counts']['total_children'], 23+7+1) + self.assertEqual(self.plan['sampler_child']['uniform_batch_calls'], 2) + self.assertEqual(self.plan['sampler_child']['execution'], 'GPU_private_generator') + self.assertIsNone(self.plan['counts']['raw_uniform_draw_count']) + for key in ('brain_executed', 'inputs_sampled', 'acquisition_executed', 'full_retention_experiment', + 'scientific_learning_claimed', 'external_identity_verified_here', 'execution_authorized'): + self.assertIs(self.plan[key], False) + + def test_plan_reconstruction_rejects_mutations_including_booleans(self): + mutations = [lambda p: p['calls'][0].update(steps=999), + lambda p: p['counts'].update(new_brain_calls=20), + lambda p: p['calls'][-1].update(parent='D1--probe--t1--A'), + lambda p: p['calls'][-1].update(neural_reset=True), + lambda p: p['calls'][2].update(overlay_before_role='AP.initial_overlay'), + lambda p: p['mechanical_update'].update(trace_backend='numpy'), + lambda p: p['source_sha256'].update({'conditioning-interface-v2/PROTOCOL.md': 'a'*64}), + lambda p: p.update(execution_authorized=0)] + for mutate in mutations: + plan = deepcopy(self.plan) + mutate(plan) + with self.assertRaises(ValueError): + B.validate_plan(plan) + + def test_seeds_are_exact_existing_evaluation_streams_not_training_streams(self): + streams = B.evaluation_seeds() + for cue, row in streams.items(): + key = self.trial_plan._input_key(B.BLOCK, 'evaluation', cue, 0, 'base-probe') + seed, digest = self.registry.seed_for(B.MASTER, 'evaluation', key) + self.assertEqual(row['key'], list(key)) + self.assertEqual((row['seed'], row['derivation_sha256']), (seed, digest)) + self.assertEqual(row['stream_id'], self.trial_plan._stream_id(B.MASTER, 'evaluation', key)) + train_seed, _ = self.registry.seed_for(B.MASTER, 'training', key) + self.assertNotEqual(seed, train_seed) + self.assertGreater(seed, 2**32) + self.assertLess(seed, 2**63) + self.assertEqual({self.spec(f'D1--probe--{t}--{cue}')['cs_input_seed'] for t in ('t0','t1','t10')}, {seed}) + self.assertNotEqual(streams['A']['seed'], streams['B']['seed']) + + def test_plan_and_derivation_never_sample(self): + with patch('numpy.random.default_rng', side_effect=AssertionError('sampling forbidden')), \ + patch('numpy.random.uniform', side_effect=AssertionError('sampling forbidden')): + B.build_plan() + B.evaluation_seeds() + for call in self.calls: + self.spec(call) + + def test_continuous_idle_clock_and_probe_fork_no_feedback(self): + first, second = self.calls['D1--idle--1s'], self.calls['D1--idle--9s'] + self.assertEqual((first['steps'], first['global_tick_offset'], first['global_next_tick']), (10000,1200,11200)) + self.assertEqual((second['steps'], second['global_tick_offset'], second['global_next_tick']), (90000,11200,101200)) + self.assertEqual(second['parent'], first['call_id']) + self.assertEqual((first['transition'], second['transition']), ('epoch_update','idle')) + for t, parent, tick in (('t0','AP.D1.reference.epoch001',1200), + ('t1',first['call_id'],11200), ('t10',second['call_id'],101200)): + for cue in ('A','B'): + probe = self.calls[f'D1--probe--{t}--{cue}'] + self.assertEqual((probe['parent'],probe['global_tick_offset'],probe['steps']), (parent,tick,1200)) + self.assertTrue(probe['probe_fork_discard']) + for key in ('feedback_to_idle','neural_reset','US','plastic_update'): + self.assertIs(probe[key], False) + self.assertFalse(any('--probe--' in (c['parent'] or '') for c in self.calls.values())) + + def test_template_keydiff_preserves_model_APL_delay_and_reset(self): + saved = deepcopy(self.template) + for name in self.calls: + spec = self.spec(name) + common = set(spec) & set(saved) + changed = {key for key in common if spec[key] != saved[key]} + self.assertLessEqual(changed, B.CASE_CHANGED_KEYS | B.STALE_INPUT_KEYS) + self.assertLessEqual(set(spec)-set(saved), B.CASE_ADDED_KEYS) + self.assertLessEqual(set(saved)-set(spec), B.STALE_INPUT_KEYS) + for key in ('model','apl','apl_delay_steps','apl_precision','precision','seed','dt_ms', + 'graph_dir','apl_bundle_dir','coefficients_path','legacy_apl_worker','kind'): + self.assertEqual(spec[key], saved[key], (name,key)) + self.assertEqual(spec['max_steps'], spec['steps']) + self.assertEqual(spec['duration_ms'], spec['steps'] * saved['dt_ms']) + self.assertEqual(self.template, saved) + + def test_shared_ref_light_oldC_case_groups_produce_identical_specs(self): + for arm in ('D1','D0','frozen','mechanical'): + ref = self.spec(f'{arm}--updated--reference') + self.assertEqual(ref, self.spec(f'{arm}--updated--light')) + if arm != 'mechanical': + self.assertEqual(ref['name'], f'AP-{arm}-suffix') + if arm in ('D0','frozen'): + self.assertEqual(ref, self.spec(f'{arm}--old-C--reference')) + + def test_nonolfactory_cases_remove_stale_odor_metadata(self): + for name, call in self.calls.items(): + spec = self.spec(name) + if 'cue' not in call: + self.assertTrue(B.STALE_INPUT_KEYS.isdisjoint(spec), name) + else: + for key in ('odor_id','rate_sum_hz','source_panel_row','source_panel_split','target_count','expected_CS_requests'): + self.assertNotIn(key, spec) + self.assertEqual(spec['CS_window'], [0,1000]) + + def test_oracle_is_fresh_diagnostic_one_switch_not_restore(self): + for arm in ('D1','mechanical'): + row = self.calls[f'{arm}--live-oracle'] + spec = self.spec(row['call_id']) + self.assertIsNone(row['parent']) + self.assertEqual(row['steps'], 2200) + self.assertEqual(row['global_tick_offset'], 0) + self.assertIs(row['restore_used'], False) + self.assertIs(spec['within_trial_weight_updates'], True) + self.assertEqual(spec['diagnostic_boundary_tick'], 1200) + self.assertIs(spec['scientific_learning_claimed'], False) + self.assertEqual(self.calls['D1--live-oracle']['overlay_before_role'], 'AP.D1.reference.epoch001.recorded_overlay') + for name, row in self.calls.items(): + if row['runner'] != 'boundary_oracle': + self.assertIs(self.spec(name)['within_trial_weight_updates'], False) + + def test_mechanical_before_is_registration_original_zero_not_D0_output(self): + registration = json.loads((EVIDENCE/'protocol/preregistered.json').read_text()) + initial = self.plan['initial_overlay'] + self.assertEqual(initial['role'], 'AP.initial_overlay') + for role in ('overlay_file','overlay_manifest'): + self.assertEqual(initial[role]['path'], registration['initial_overlay'][role]) + self.assertEqual(initial[role]['sha256'], registration['input_sha256'][initial[role]['path']]) + self.assertEqual(self.calls['mechanical--fresh-parent']['overlay_after_role'], initial['role']) + self.assertFalse(self.calls['mechanical--fresh-parent']['inherited_learned_delta']) + update = self.plan['mechanical_update'] + self.assertEqual((update['steps'],update['us_tick'],update['d_command'],update['trace_backend']), (1200,1199,1.0,'torch')) + self.assertEqual(update['before_overlay_role'], initial['role']) + + def test_template_drift_unknown_calls_and_bad_output_roots_rejected(self): + wrong = deepcopy(self.template) + wrong['model']['threshold_mv'] = -44.0 + with self.assertRaises(ValueError): + B.derive_case_spec(wrong,'D1--updated--reference',budget_root=OUTPUT_ROOT) + with self.assertRaises(ValueError): + self.spec('unregistered-extra-call') + for root in ('relative','/',B.AP_ROOT,B.AP_ROOT+'/child','/data','/tmp/../test'): + with self.assertRaises(ValueError): + B.derive_case_spec(self.template,'D1--updated--reference',budget_root=root) + + def test_budgets_are_fixed_stop_proposals_not_trial_shortening(self): + budget = self.plan['budget_proposal'] + self.assertEqual((budget['output_bytes'],budget['free_reserve_bytes']), (4*2**30,10*2**30)) + self.assertEqual((budget['child_timeout_seconds'],budget['workflow_timeout_seconds']), (1800,7200)) + self.assertFalse(budget['approved']) + self.assertIn('never_shorten_trial_or_change_gate', budget['on_limit']) + + def test_performance_slot_replays_natural_AP_input_with_full_fresh_cycle(self): + row = self.calls['performance--natural-light-fresh'] + self.assertEqual((row['diagnostic_mode'],row['steps'],row['global_tick_offset']), ('light',1200,0)) + self.assertIsNone(row['parent']) + self.assertEqual(row['input_policy'],'replay_AP_D1_epoch001_input') + self.assertEqual(row['overlay_after_role'],'AP.D1.reference.epoch001.recorded_overlay') + self.assertFalse(row['new_learning']) + self.assertFalse(row['input_sampled']) + update = self.plan['performance_update'] + self.assertEqual((update['us_tick'],update['d_command'],update['trace_backend']), (900,1.0,'torch')) + self.assertEqual(update['required_regression'],['all_events','final_state','delta_after']) + names = list(self.calls) + self.assertLess(names.index(row['call_id']), names.index('mechanical--fresh-parent')) + + def test_fresh_performance_probe_is_not_retention_continuation(self): + name = 'performance--evaluation-A-light-fresh' + row = self.calls[name] + self.assertEqual((row['runner'],row['steps'],row['global_tick_offset']), ('fresh_probe',1200,0)) + self.assertEqual(row['diagnostic_mode'],'light') + self.assertIsNone(row['parent']) + self.assertTrue(row['neural_reset']) + self.assertEqual(row['overlay_after_role'],'AP.initial_overlay') + for key in ('US','plastic_update','feedback_to_idle'): + self.assertIs(row[key],False) + spec = self.spec(name) + self.assertEqual(spec['cs_input_seed'],B.evaluation_seeds()['A']['seed']) + self.assertNotIn('parent_checkpoint_manifest_sha256',spec) + names = list(self.calls) + self.assertEqual(names.index(name),names.index('performance--natural-light-fresh')+1) + self.assertNotEqual(row['case_group'],self.calls['D1--probe--t0--A']['case_group']) + + +class MechanicalRequestTests(unittest.TestCase): + def setUp(self): + self.mapping = dict(edge_id=np.arange(B.CANDIDATES,dtype=np.int64), + source_neuron_idx=np.full(B.CANDIDATES,33,dtype=np.int32), + target_neuron_idx=np.full(B.CANDIDATES,900,dtype=np.int32), + target_compartment_verbatim=np.full(B.CANDIDATES,'other',dtype='U8')) + self.mapping['target_compartment_verbatim'][[4,700]] = B.COMPARTMENT + self.mapping['source_neuron_idx'][[0,4,700]] = [1,21,7] + self.dan = np.array([101,102,104,108],dtype=np.int32) + self.binding = dict(candidate_map_sha256='1'*64,pam15_membership_sha256='2'*64) + + def run_request(self): + return B.mechanical_requests(self.mapping,self.dan,identity_binding=self.binding) + + def test_single_minimum_matching_KC_plus_four_DAN_last_tick(self): + before = {key:a.copy() for key,a in self.mapping.items()} + request, audit = self.run_request() + np.testing.assert_array_equal(request['tick'], np.full(5,1199,dtype=np.int32)) + np.testing.assert_array_equal(request['neuron_idx'], np.array([7,101,102,104,108],dtype=np.int32)) + self.assertEqual(audit['selected_KC_source_neuron_idx'], 7) + self.assertFalse(audit['external_identity_verified_here']) + self.assertFalse(audit['accepted_spikes_verified']) + self.assertFalse(audit['olfactory_input']) + for key in self.mapping: + np.testing.assert_array_equal(self.mapping[key],before[key]) + self.dan[0] = 100 + self.assertEqual(request['neuron_idx'][1],101) + + def test_array_and_identity_schema_mutations_fail_closed(self): + mutations = [lambda: self.mapping.update(edge_id=self.mapping['edge_id'].astype(np.int32)), + lambda: self.mapping['edge_id'].__setitem__(1,0), + lambda: self.mapping.update(source_neuron_idx=self.mapping['source_neuron_idx'][:-1]), + lambda: self.mapping['source_neuron_idx'].__setitem__(0,-1), + lambda: self.mapping['target_compartment_verbatim'].__setitem__(slice(None),'y5B2a'), + lambda: setattr(self,'dan',self.dan.astype(np.int64)), + lambda: self.dan.__setitem__(1,101), + lambda: setattr(self,'dan',self.dan[::-1]), + lambda: self.dan.__setitem__(0,7), + lambda: self.binding.update(pam15_membership_sha256='not-authenticated')] + for mutation in mutations: + self.setUp() + mutation() + with self.assertRaises(ValueError): + self.run_request() + + +if __name__ == '__main__': + unittest.main() diff --git a/models/male-cns-lif/conditioning-execution-v1/test_boundary_sampler.py b/models/male-cns-lif/conditioning-execution-v1/test_boundary_sampler.py new file mode 100644 index 0000000..55fab2d --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/test_boundary_sampler.py @@ -0,0 +1,82 @@ +"""Source/API and synthetic orchestration tests; never actual CUDA sampling.""" +import copy +import hashlib +import importlib.util +import json +from pathlib import Path +import sys +import tempfile +import types +import unittest +from unittest.mock import patch + +import numpy as np +import boundary_sampler as S + + +class SamplerTests(unittest.TestCase): + def test_actual_frozen_dependency_pins(self): + for name,sha in S.PINS.items(): + with self.subTest(name=name):self.assertEqual(S.A.digest(S.LAB/name),sha) + + def test_real_sampler_signature_lazy_GPU_and_request_window(self): + raw=(S.LAB/'conditioning_inputs.py').read_bytes() + module=types.ModuleType('_ci_cpu_fixture');exec(compile(raw,str(S.LAB/'conditioning_inputs.py'),'exec'),module.__dict__) + import inspect + self.assertEqual(list(inspect.signature(module.generate_cs_requests).parameters), + ['targets','rates_hz','seed','steps','cs_start_tick','cs_stop_tick','device','max_uniform_draws','dt_ms']) + targets=np.array([5,2],np.int32);forced=np.ones((1200,2),np.bool_) + result=module.requests_from_forced(forced,targets,cs_start_tick=0,cs_stop_tick=1000) + self.assertEqual(len(result['requests']['tick']),2000) + self.assertEqual(int(result['requests']['tick'].max()),999) + self.assertTrue(np.array_equal(result['requests']['neuron_idx'][:2],np.array([2,5],np.int32))) + + def run_fixture(self,*,mutate_seed=False): + """Fake sampler is intentionally NOT a stochastic or neural validation.""" + with tempfile.TemporaryDirectory() as td: + root=Path(td).resolve();(root/'protocol').mkdir();(root/'inputs/static').mkdir(parents=True);(root/'runs').mkdir() + regfile=root/'protocol/preregistered.json';full=root/'full.json';full.write_text('{}') + plan=S.PLAN.build_plan();tables={};streams={};calls=[] + for cue in ('A','B'): + tables[cue]=dict(targets=np.array([5,2],np.int32),rates_hz=np.array([10.,20.],np.float32)) + with (root/f'inputs/static/{cue}-rates.npz').open('wb') as f:np.savez(f,**tables[cue]) + stream=copy.deepcopy(plan['evaluation_streams'][cue]);stream['semantic_key']=stream.pop('key') + if mutate_seed and cue=='B':stream['seed']+=1 + streams[(S.PLAN.BLOCK,cue)]=stream + reg=dict(schema='conditioning-execution-registration-v1',plan=plan,source_sha256={},prepared_file_sha256={}, + input_sha256={str(S.LAB/n):sha for n,sha in S.PINS.items()},original_full_plan=S.A.pin(full)) + regfile.write_text(json.dumps(reg)) + ci=types.ModuleType('conditioning_inputs');i=types.ModuleType('conditioning_preflight_inputs');ep=types.ModuleType('evaluation_plan') + i.load_rate_tables=lambda root:dict(tables=tables) + ep._streams=lambda full:streams + def generate(targets,rates,**kw): + calls.append(kw) + self.assertEqual(kw['steps'],1200);self.assertEqual(kw['cs_start_tick'],0) + self.assertEqual(kw['cs_stop_tick'],1000);self.assertEqual(kw['max_uniform_draws'],2400) + return dict(requests=dict(tick=np.array([0,999],np.int32),neuron_idx=np.array([2,5],np.int32)),audit={'synthetic_fixture':True}) + ci.generate_cs_requests=generate + real_import=__import__ + def intercepted(name,*args,**kwargs): + if name in ('conditioning_inputs','conditioning_preflight_inputs','evaluation_plan'): + return {'conditioning_inputs':ci,'conditioning_preflight_inputs':i,'evaluation_plan':ep}[name] + return real_import(name,*args,**kwargs) + old_path=list(sys.path) + try: + with patch('builtins.__import__',side_effect=intercepted): + result=S.run(regfile,S.A.digest(regfile),root/'runs/generated') + self.assertEqual(len(calls),2);self.assertNotEqual(calls[0]['seed'],calls[1]['seed']) + self.assertEqual(result['uniform_draws'],4800);self.assertFalse(result['brain_executed']) + self.assertFalse(result['reuse_executed_here']);self.assertFalse(result['US_added']) + self.assertEqual(set(result['files_sha256']),{'A-requests.npz','A-sampler.json','B-requests.npz','B-sampler.json'}) + with patch('builtins.__import__',side_effect=intercepted): + with self.assertRaisesRegex(ValueError,'Exclusive new sampler'):S.run(regfile,S.A.digest(regfile),root/'runs/generated') + return result + finally:sys.path[:]=old_path + + def test_only_two_fixed_batches_and_honest_scope(self):self.run_fixture() + + def test_wrong_full_semantic_seed_rejected(self): + with self.assertRaisesRegex(ValueError,'Evaluation semantic stream differs'):self.run_fixture(mutate_seed=True) + + +if __name__=='__main__':unittest.main() diff --git a/models/male-cns-lif/conditioning-execution-v1/test_budget_report.py b/models/male-cns-lif/conditioning-execution-v1/test_budget_report.py new file mode 100644 index 0000000..763ecdc --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/test_budget_report.py @@ -0,0 +1,213 @@ +"""Small synthetic filesystem receipts; these are NOT real cost measurements.""" +from copy import deepcopy +import hashlib +import json +from pathlib import Path +import tempfile +import unittest +from unittest.mock import patch + +import numpy as np +import boundary_plan as P +import budget_report as R + + +class BudgetReportTests(unittest.TestCase): + def setUp(self): + self.tmp=tempfile.TemporaryDirectory();self.addCleanup(self.tmp.cleanup) + self.root=Path(self.tmp.name);self.exp=self.root/'phase4-fixture';self.model=self.root/'model' + for name in ('audit','receipts','jobs','logs','runs','updates','authorities','cases','protocol','extension-build'): + (self.exp/name).mkdir(parents=True,exist_ok=True) + self.model.mkdir();self.worker=self.model/'isolated_execution.py';self.ap=self.model/'ap_worker.py' + for path in (self.worker,self.ap,self.model/'runtime_light.py',self.model/'runtime_continuation.py',self.model/'conditioning_epoch.py'): + path.write_text('# CPU fixture '+path.name,encoding='utf-8') + self.patch('EXP',self.exp);self.patch('MODEL',self.model);self.patch('AP_WORKER',self.ap) + p=patch.object(R.F.W,'AP_SHA',R.A.digest(self.ap));p.start();self.addCleanup(p.stop) + self.plan=P.build_plan();self.calls={c['call_id']:c for c in self.plan['calls']} + self.zero=self.exp/'protocol/zero.npz';R.F.save_npz(self.zero,dict(delta=np.array([0.],np.float32))) + self.reg=dict(plan=self.plan,source_sha256={str(self.worker):R.A.digest(self.worker)}, + initial_overlay=dict(overlay_file=str(self.zero),overlay_manifest=str(self.zero)), + input_sha256={},prepared_file_sha256={}) + R.F.save(self.exp/'protocol/preregistered.json',self.reg);self.regsha=R.A.digest(self.exp/'protocol/preregistered.json') + p=patch.object(R.F,'registration',side_effect=self.registration);p.start();self.addCleanup(p.stop) + self.specs={};self.processes={};self.cycle_times={} + self.put(self.exp/'extension-build/kernel.bin','actual-fixture-cache') + for index,call in enumerate(self.plan['calls']): + name=call['call_id'];start=(index+1)*100.;self.cycle_times[name]=(start,start+40.) + if call['runner'] in ('runtime_light','fresh_probe'): + case=self.exp/'cases'/name;case.mkdir() + spec=dict(steps=call['steps'],dt_ms=.1,model=dict(frozen_fixture=True),apl=dict(gain=.01), + precision='float64',apl_precision='float32',apl_delay_steps=18, + graph_dir='pinned-fullgraph-fixture',apl_bundle_dir='pinned-APL-fixture') + R.F.save(case/'spec.json',spec) + R.F.save_npz(case/'requests.npz',dict(tick=np.array([5],np.int32),neuron_idx=np.array([20],np.int32))) + self.specs[name]=case + self._epoch(R.TRAIN);self._epoch('mechanical--fresh-parent') + for arm in ('D1','D0','frozen'):self._process('AP-'+arm+'--cpu-verify','validate_cpu',10.) + for call in self.plan['calls']: + name=call['call_id'];start=self.cycle_times[name][0] + kind={'runtime_light':'brain','fresh_probe':'brain','runtime_continuation':'unchanged', + 'updated_continuation':'updated','boundary_oracle':'oracle'}[call['runner']] + self._process(name,kind,start+1) + if name in (R.TRAIN,'mechanical--fresh-parent'): + self._process(name+'--original-E','update',start+11,base=name) + self._process(name+'--cpu-verify','validate_cpu',start+21,base=name) + self._process('evaluation-inputs','sample',self.cycle_times[R.TRAIN][1]+10) + build=R.F.tree(self.exp/'extension-build') + for call in self.plan['calls']: + name=call['call_id'];start,end=self.cycle_times[name] + rows=[self.processes[event] for _,event in R.SELECTED[name]] if name in R.SELECTED else [] + inventory=R._cycle_files(name,rows,R.Evidence()) if rows else {} + value=dict(call=call,complete=True,start_monotonic=start,end_monotonic=end,wall_seconds=end-start, + output_inventory=inventory,output_bytes=sum(v['bytes'] for v in inventory.values()), + mutable_build_inventory_before=build,mutable_build_inventory_after=build, + mutable_build_bytes_before=sum(v['bytes'] for v in build.values()), + mutable_build_bytes_after=sum(v['bytes'] for v in build.values()), + source_registration_sha256=self.regsha,learning_pass_claimed=False) + R.F.save(self.exp/'receipts'/f'{name}.cycle.json',value) + R.F.save(self.exp/'audit/execution-attempt.json',dict(registration_sha256=self.regsha,monotonic_origin=1.)) + R.F.save(self.exp/'audit/execution-complete.json',dict(complete=True,registration_sha256=self.regsha, + scientific_learning_pass_claimed=False,completed_calls=[c['call_id'] for c in self.plan['calls']], + processes={name:str(self.exp/'receipts'/f'{name}.process.json') for name in self.processes})) + + def patch(self,key,value): + p=patch.object(R.F,key,value);p.start();self.addCleanup(p.stop) + + def registration(self,sha): + R.require(sha==R.A.digest(self.exp/'protocol/preregistered.json'),'Registration differs') + return self.reg + + def put(self,path,text): + Path(path).write_text(text,encoding='utf-8') + + def rewrite(self,path,mutate): + value=R.A.read(path);mutate(value) + self.put(path,json.dumps(value,sort_keys=True,allow_nan=False)) + + def _epoch(self,name): + R.F.save(self.exp/'authorities'/f'{name}.epoch.json',dict(checkpoint_dir=str(self.exp/'runs'/name), + command=dict(steps=1200,compartment="y5B'2a",us_tick=900 if name==R.TRAIN else 1199, + d_command=1.,frozen=False,trace_backend='torch'))) + + def _process(self,name,kind,start,base=None): + ap=kind in ('brain','update');script=self.ap if ap else self.worker + output=self.exp/('updates' if kind=='update' else 'runs')/(base if kind=='update' else name);output.mkdir() + sources={role:dict(path=str(self.model/(role+'.py')),sha256=R.A.digest(self.model/(role+'.py'))) + for role in (('runtime_light','runtime_continuation') if kind=='brain' else ('conditioning_epoch',))} if ap else {} + job=dict(job_id=name,kind=kind,registration_sha256=self.regsha,worker_sha256=R.A.digest(script), + receipt_file=str(self.exp/'receipts'/f'{name}.worker.json'),output_dir=str(output), + input_sha256={str(self.exp/'protocol/preregistered.json'):self.regsha},sources=sources,source_sha256={}) + result={};environment=dict(gpu_uuid='FIXTURE',python_version='fixture',torch_version='fixture') + if kind=='brain': + job.update(case_dir=str(self.specs[name]),diagnostic_mode=self.calls[name]['diagnostic_mode'],overlay_file=str(self.zero)) + summary=dict(steps=1200,completed_ticks=1200,explicit_steps_executed=1200,max_steps=1200,nodes=R.B.N,edges=R.B.E, + diagnostic_mode=job['diagnostic_mode'],entire_neural_trial_executed=True,within_trial_weight_updates=False, + precision='float64',apl_precision='float32') + R.F.save(output/'summary.json',summary);result['complete_fresh_trial']=True + elif kind=='sample': + for cue in ('A','B'): + R.F.save_npz(output/f'{cue}-requests.npz',dict(tick=np.array([5],np.int32),neuron_idx=np.array([20],np.int32))) + else:R.F.save(output/'fixture.json',dict(complete=True)) + if base: + job.update(authority_file=str(self.exp/'authorities'/f'{base}.epoch.json')) + if kind=='validate_cpu':job['update_dir']=str(self.exp/'updates'/base) + jobpath=self.exp/'jobs'/f'{name}.json';R.F.save(jobpath,job);jobsha=R.A.digest(jobpath) + expected={**job['input_sha256'],str(jobpath):jobsha,str(script):job['worker_sha256']} + if ap:expected.update({v['path']:v['sha256'] for v in sources.values()}) + else:expected[str(self.ap)]=R.F.W.AP_SHA + logs={} + for side in ('stdout','stderr'): + path=self.exp/'logs'/f'{name}.{side}.log';self.put(path,'');logs[side+'_sha256']=R.A.digest(path) + attempt=self.exp/'receipts'/f'{name}.worker.json.attempt.json';R.F.save(attempt,dict(fixture=True)) + inventory=R.F.tree(output);hashes={k:v['sha256'] for k,v in inventory.items()} + worker=dict(schema='phase4ap-isolated-worker-receipt-v1' if ap else 'conditioning-isolated-execution-receipt-v1', + complete=True,job_id=name,kind=kind,registration_sha256=self.regsha,worker_sha256=job['worker_sha256'], + job_sha256=jobsha,attempt_sha256=R.A.digest(attempt),output_dir=str(output),output_files_sha256=hashes, + environment=environment,result=result,wall_seconds=2.) + worker['verified_input_sha256' if ap else 'input_sha256']=expected + workerpath=Path(job['receipt_file']);R.F.save(workerpath,worker) + command=['fixture-python','-B',str(script)]+([kind] if ap else [])+['--job',str(jobpath),'--job-sha256',jobsha] + process=dict(schema='conditioning-execution-process-v1',complete=True,name=name,kind=kind, + registration_sha256=self.regsha,exit_code=0,timed_out=False,job_sha256=jobsha,argv=command, + worker_receipt_sha256=R.A.digest(workerpath),spawn_monotonic=start,exit_monotonic=start+4., + validation_end_monotonic=start+5.,child_wall_seconds=4.,end_to_end_seconds=5.,validation_wall_seconds=1., + output_bytes=sum(v['bytes'] for v in inventory.values()), + output_inventory_sha256=hashlib.sha256(json.dumps(hashes,sort_keys=True).encode()).hexdigest(),**logs) + processpath=self.exp/'receipts'/f'{name}.process.json';R.F.save(processpath,process) + self.processes[name]=dict(process=process,job=job,worker=worker,output_inventory=inventory, + binding=dict(receipt_id=str(processpath),sha256=R.A.digest(processpath))) + + def report(self,save=False):return R.report(self.regsha,save_report=save) + + def test_actual_cycles_tail_complete_bytes_and_no_forecast_without_future_policy(self): + value=self.report() + self.assertEqual(value['status'],'INSUFFICIENT-MEASUREMENTS') + self.assertEqual(value['policy_status'],'FUTURE-POLICY-NOT-REGISTERED') + self.assertEqual(value['registration_sha256'],self.regsha) + self.assertEqual(value['inventory']['brain_calls'],33920) + self.assertEqual({r['cycle_id'] for r in value['measurements']},{R.TRAIN,R.PROBE}) + for name in (R.TRAIN,R.PROBE): + account=value['accounting'][name] + self.assertEqual((account['recorded_cycle_seconds'],account['tail_upper_bound_seconds'], + account['conservative_complete_cycle_seconds']),(40.,60.,100.)) + self.assertEqual(account['total_conservative_bytes'],account['created_bytes']+ + account['cycle_receipt_bytes']+account['repeated_full_cache_bytes']) + self.assertGreater(account['cycle_receipt_bytes'],0) + self.assertIsNone(value['estimated_wall_seconds']);self.assertIsNone(value['estimated_output_bytes']) + self.assertIsNone(value['strict_projection']['estimated_wall_seconds']) + self.assertFalse(value['learning_pass_claimed']);self.assertFalse(value['execution_authorized']) + + def test_saved_report_reproduces_exactly_and_is_exclusive(self): + first=self.report(True);second=self.report(False) + self.assertEqual(R._canonical(first),R._canonical(second)) + self.assertEqual(R._canonical(first),R._canonical(R.A.read(self.exp/'audit/budget-report.json'))) + with self.assertRaisesRegex(ValueError,'never overwrite'):self.report(True) + self.assertNotIn('generated_at',first);self.assertNotIn('free_bytes',first) + + def test_worker_byte_tampering_and_wrong_registration_rejected(self): + path=self.exp/'receipts'/f'{R.TRAIN}.worker.json' + self.rewrite(path,lambda r:r.update(wall_seconds=.01)) + with self.assertRaisesRegex(ValueError,'Evidence bytes changed'):self.report() + with self.assertRaises(ValueError):R.report('f'*64,save_report=False) + + def test_unsuccessful_parent_process_cannot_be_reclassified_by_worker(self): + path=self.exp/'receipts'/f'{R.TRAIN}.process.json' + self.rewrite(path,lambda r:r.update(exit_code=1)) + with self.assertRaisesRegex(ValueError,'Unsuccessful'):self.report() + + def test_cycle_inventory_omission_not_mistaken_for_full_output_bytes(self): + path=self.exp/'receipts'/f'{R.TRAIN}.cycle.json' + def omit(row): + key=next(iter(row['output_inventory']));del row['output_inventory'][key] + row['output_bytes']=sum(v['bytes'] for v in row['output_inventory'].values()) + self.rewrite(path,omit) + with self.assertRaisesRegex(ValueError,'complete fixed-branch scope'):self.report() + + def test_missing_cycle_evidence_returns_insufficient_without_inventing_measurement(self): + (self.exp/'receipts'/f'{R.TRAIN}.cycle.json').unlink() + value=self.report() + self.assertEqual(value['status'],'INSUFFICIENT-MEASUREMENTS') + self.assertEqual(value['measurements'],[]) + self.assertTrue(any('evidence missing' in item for item in value['missing'])) + self.assertIsNone(value['estimated_output_bytes']) + + def test_inconsistent_adjacent_clock_cannot_supply_tail_upper_bound(self): + path=self.exp/'receipts'/f'{R.PROBE}.cycle.json' + self.rewrite(path,lambda r:r.update(start_monotonic=self.cycle_times[R.TRAIN][1]-1)) + with self.assertRaisesRegex(ValueError,'adjacent-cycle tail'):self.report() + + def test_mutable_cache_final_bytes_must_match_observed_final_inventory(self): + self.put(self.exp/'extension-build/kernel.bin','changed-after-observation') + with self.assertRaisesRegex(ValueError,'mutable cache inventory changed'):self.report() + + def test_reference_or_continuation_not_admitted_as_fresh_performance(self): + real=R._parent_process + def different(name,*args): + row=real(name,*args) + if name==R.PROBE:row=deepcopy(row);row['job']['diagnostic_mode']='reference' + return row + with patch.object(R,'_parent_process',side_effect=different): + with self.assertRaisesRegex(ValueError,'fresh light'):self.report() + + +if __name__=='__main__':unittest.main() diff --git a/models/male-cns-lif/conditioning-execution-v1/test_execution_authority.py b/models/male-cns-lif/conditioning-execution-v1/test_execution_authority.py new file mode 100644 index 0000000..05171e1 --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/test_execution_authority.py @@ -0,0 +1,103 @@ +"""Pure authority construction tests; synthetic files, no graph or GPU claims.""" +import copy +import json +from pathlib import Path +import tempfile +import unittest +from unittest.mock import patch + +import execution_authority as A + + +class AuthorityTests(unittest.TestCase): + def setUp(self): + self.tmp=tempfile.TemporaryDirectory();self.root=Path(self.tmp.name).resolve() + self.case=self.root/'case';self.parent=self.root/'parent';self.update=self.root/'update' + for p in (self.case,self.parent,self.update):p.mkdir() + self.write(self.parent/'checkpoint-manifest.json',{'fixture':True}) + self.write(self.parent/'summary.json',{'steps':1200,'effective_weights_raw_sha256':'a'*64,'diagnostic_mode':'reference'}) + self.write(self.case/'spec.json',{'steps':1000,'global_tick_offset':1200, + 'parent_checkpoint_manifest_sha256':A.digest(self.parent/'checkpoint-manifest.json')}) + (self.case/'requests.npz').write_bytes(b'synthetic-file-not-a-real-npz') + (self.parent/'state.npz').write_bytes(b'synthetic-state-not-a-real-npz') + self.before=dict(overlay_file=str(self.root/'before.npz'),overlay_manifest=str(self.root/'before.json')) + self.after=dict(overlay_file=str(self.root/'after.npz'),overlay_manifest=str(self.root/'after.json')) + for p in (*self.before.values(),*self.after.values()):Path(p).write_bytes(b'fixture') + self.epoch=self.root/'epoch.json';self.cpu=self.root/'cpu.json' + self.write(self.epoch,{});self.write(self.cpu,{}) + for n in ('chain-receipt.json','update-receipt.json','delta-after.npz','overlay-manifest.json'): + (self.update/n).write_bytes(b'fixture') + + def tearDown(self):self.tmp.cleanup() + + def write(self,path,value):path.write_text(json.dumps(value),encoding='utf-8') + + def make(self,**changes): + kw=dict(boundary_id='fixture',registration_sha256='c'*64,case=self.case,parent=self.parent, + transition='epoch_update',input_policy='empty_idle',mode='light',before_overlay=self.before, + after_overlay=self.after,source_files={'fixture':A.pin(self.cpu)},frozen_inputs={str(self.epoch):A.digest(self.epoch)}, + epoch=self.epoch,update_dir=self.update,cpu_receipt=self.cpu) + kw.update(changes) + # Hash computation is stubbed only here: no effective-weight verification claim. + with patch.object(A,'effective_hash',side_effect=['a'*64,'b'*64] if kw['transition']=='epoch_update' else ['a'*64]*2): + return A.boundary_authority(**kw) + + def test_exact_boundary_fields_and_live_payload_pins(self): + value=self.make() + self.assertEqual(value['global_tick_offset'],1200) + self.assertEqual(value['parent']['checkpoint_manifest_sha256'],A.digest(self.parent/'checkpoint-manifest.json')) + self.assertEqual(value['update']['files']['delta-after.npz'],A.pin(self.update/'delta-after.npz')) + self.assertEqual(value['expected_effective_weight_after_sha256'],'b'*64) + + def test_changed_clock_or_parent_rejected(self): + original=A.read(self.case/'spec.json') + for key,value in (('global_tick_offset',1199),('parent_checkpoint_manifest_sha256','0'*64)): + spec={**original,key:value};self.write(self.case/'spec.json',spec) + with self.subTest(key=key),self.assertRaises(ValueError):self.make() + + def test_later_parent_uses_absolute_next_tick(self): + summary=A.read(self.parent/'summary.json');summary['global_next_tick']=11200 + self.write(self.parent/'summary.json',summary) + spec=A.read(self.case/'spec.json');spec['global_tick_offset']=11200;self.write(self.case/'spec.json',spec) + value=self.make(transition='idle',epoch=None,update_dir=None,cpu_receipt=None) + self.assertEqual(value['global_tick_offset'],11200);self.assertIsNone(value['update']) + + def test_epoch_update_requires_all_evidence(self): + for name in ('epoch','update_dir','cpu_receipt'): + with self.subTest(name=name),self.assertRaises(ValueError):self.make(**{name:None}) + + def test_idle_cannot_carry_new_update(self): + with self.assertRaisesRegex(ValueError,'Idle cannot'):self.make(transition='idle') + + def test_boundary_copies_descriptors_not_aliases(self): + sources={'role':A.pin(self.cpu)};frozen={str(self.epoch):A.digest(self.epoch)} + value=self.make(source_files=sources,frozen_inputs=frozen) + sources['role']['sha256']='0'*64;frozen.clear() + self.assertNotEqual(value['source_files'],sources);self.assertTrue(value['frozen_inputs']) + + def test_complete_direct_closure_and_conflicting_pin(self): + value=self.make();pins=A.files_from_authority(value) + for p in (self.epoch,self.cpu,self.case/'requests.npz',self.parent/'checkpoint-manifest.json',self.update/'delta-after.npz'): + self.assertEqual(pins[str(p)],A.digest(p)) + value['frozen_inputs'][str(self.cpu)]='0'*64 + with self.assertRaisesRegex(ValueError,'Conflicting'):A.files_from_authority(value) + + def test_epoch_template_is_not_modified(self): + spec=A.read(self.case/'spec.json');spec['steps']=1200;self.write(self.case/'spec.json',spec) + template=dict(schema='phase4an-epoch-postprocess-authority-v1',files={'old':{'path':'literal','sha256':'1'*64}}, + frozen_inputs={'literal':'1'*64}) + original=copy.deepcopy(template) + result=A.epoch_authority(template,case=self.case,checkpoint=self.parent,overlay=self.before, + epoch_id='mechanical',us_tick=1199) + self.assertEqual(template,original);self.assertEqual(result['command']['us_tick'],1199) + self.assertEqual(result['files']['state'],A.pin(self.parent/'state.npz')) + with self.assertRaises(ValueError):A.epoch_authority(template,case=self.case,checkpoint=self.parent, + overlay=self.before,epoch_id='bad',us_tick=1200) + + def test_duplicate_or_nonfinite_JSON_rejected(self): + for data in ('{"a":1,"a":2}','{"a":NaN}'): + self.cpu.write_text(data) + with self.assertRaises(ValueError):A.read(self.cpu) + + +if __name__=='__main__':unittest.main() diff --git a/models/male-cns-lif/conditioning-execution-v1/test_execution_budget.py b/models/male-cns-lif/conditioning-execution-v1/test_execution_budget.py new file mode 100644 index 0000000..a491a27 --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/test_execution_budget.py @@ -0,0 +1,142 @@ +"""Synthetic CPU-only budget contract tests, not measured performance evidence.""" +from copy import deepcopy +import unittest + +import execution_budget as B + + +CONTEXT = 'a'*64 + + +class BudgetTests(unittest.TestCase): + def setUp(self): + self.auth = {} + self.reserves = {role:dict(wall_seconds=1.,output_bytes=2,basis='explicit fixture allowance') + for role in B.RESERVE_ROLES} + self.reserves['cycle_multiplier'] = 1.25 + + def bind(self, name): + self.auth[name] = 'b'*64 + return dict(receipt_id=name,sha256='b'*64) + + def cycle(self, name, kind='training', mode='light', steps=1200): + events = [] + roles = ['brain','original_E','cpu_validator'] if kind == 'training' else ['brain'] + for i,role in enumerate(roles): + events.append(dict(event_id=name+'-'+role,role=role,receipt_kind='parent-process', + receipt=self.bind(name+'-'+role),spawn_s=1.+i*4,exit_s=3.+i*4,validated_s=4.+i*4, + exit_code=0,core_seconds=.5,runtime_seconds=1.,child_wall_seconds=2.)) + return dict(schema=B.SCHEMA,cycle_id=name,kind=kind,diagnostic_mode=mode,steps=steps, + neurons=B.N,edges=B.E,dt_ms=.1,context_sha256=CONTEXT,clock_id='parent-clock-1', + started_s=0.,validated_s=len(roles)*4.+1,authority=self.bind(name+'-cycle'),events=events, + outputs=dict(inventory=self.bind(name+'-inventory'),scope=B.OUTPUT_SCOPE,complete=True,total_bytes=1000),complete=True) + + def estimate(self, rows, **kwargs): + return B.estimate_normal_suite(rows,authenticated_receipts=self.auth,target_context_sha256=CONTEXT, + reserves=self.reserves,**kwargs) + + def test_full_training_probe_cycles_use_only_end_to_end_and_complete_bytes(self): + train,probe = self.cycle('train'),self.cycle('probe','probe') + report = self.estimate([train,probe]) + self.assertEqual(report['status'],'ESTIMATED-NOT-AUTHORIZED') + self.assertEqual(report['inventory']['brain_calls'],33920) + self.assertEqual(report['inventory']['biological_seconds'],4070.4) + self.assertEqual(report['estimated_wall_seconds'],int(1.25*(30000*13+3920*5)+5)) + self.assertEqual(report['estimated_output_bytes'],int(1.25*33920*1000+10)) + self.assertEqual(report['measured_cycles'][0]['timing'],dict(core_seconds=1.5,runtime_seconds=3., + child_wall_seconds=6.,end_to_end_seconds=13.)) + for field in ('execution_authorized','scientific_learning_claimed','external_identity_verified_here'): + self.assertFalse(report[field]) + + def test_missing_either_cycle_returns_symbolic_not_pseudo_precise_total(self): + for rows in ([],[self.cycle('only-train')],[self.cycle('only-probe','probe')]): + report = self.estimate(rows) + self.assertEqual(report['status'],'INSUFFICIENT-MEASUREMENTS') + self.assertIsNone(report['estimated_wall_seconds']) + self.assertIsNone(report['estimated_output_bytes']) + self.assertTrue(report['missing']) + self.assertIn('30000',report['symbolic_wall']) + + def test_reference_idle_short_wrong_context_are_reported_but_not_training_samples(self): + rows = [self.cycle('ref',mode='reference'),self.cycle('idle','state_only_idle',steps=10000), + self.cycle('short',steps=1000),self.cycle('other')] + rows[-1]['context_sha256'] = 'c'*64 + report = self.estimate(rows+[self.cycle('probe','probe')]) + self.assertEqual(report['status'],'INSUFFICIENT-MEASUREMENTS') + self.assertEqual(report['eligible_cycle_ids']['training'],[]) + self.assertEqual(len(report['measured_cycles']),5) + + def test_actual_parent_event_order_receipts_and_complete_output_required(self): + mutations = [lambda r:r['events'].pop(),lambda r:r['events'][0].update(receipt_kind='AP-worker-slot'), + lambda r:r['events'][1].update(spawn_s=0.),lambda r:r['events'][0].update(exit_code=1), + lambda r:r['events'][0]['receipt'].update(sha256='f'*64), + lambda r:r['outputs'].update(complete=False),lambda r:r['outputs'].update(scope='state-only'), + lambda r:r['outputs'].update(total_bytes=True),lambda r:r.update(neurons=16), + lambda r:r['events'][0].update(child_wall_seconds=1.),lambda r:r.update(complete=1), + lambda r:r['events'][0].update(runtime_seconds=3.),lambda r:r.update(validated_s=float('nan'))] + for i,mutate in enumerate(mutations): + row = self.cycle('mutate'+str(i)) + mutate(row) + with self.assertRaises(ValueError): + self.estimate([row]) + with self.assertRaises(ValueError): + self.estimate([dict(wall_seconds=10,slot='D1')]) + + def test_reserves_have_no_implicit_defaults_and_caps_stop_without_inventory_change(self): + rows = [self.cycle('train'),self.cycle('probe','probe')] + report = self.estimate(rows,limits=dict(wall_seconds=1,output_bytes=1)) + self.assertEqual(report['status'],'STOP-BUDGET-EXCEEDED') + self.assertFalse(report['caps_pass']) + self.assertEqual(report['inventory'],B.INVENTORY) + original = deepcopy(self.reserves) + for role in B.RESERVE_ROLES | {'cycle_multiplier'}: + self.reserves = deepcopy(original) + del self.reserves[role] + with self.assertRaises(ValueError): + self.estimate(rows) + self.reserves = deepcopy(original) + self.reserves['cycle_multiplier'] = .99 + with self.assertRaises(ValueError): + self.estimate(rows) + + def test_maxima_not_fitted_or_pooled_with_reference_and_no_mutation(self): + slow = self.cycle('slow') + slow['validated_s'] = 25. + slow['outputs']['total_bytes'] = 2000 + ref = self.cycle('ref',mode='reference') + ref['validated_s'] = 10000. + rows = [self.cycle('fast'),slow,ref,self.cycle('probe','probe')] + saved = deepcopy(rows) + report = self.estimate(rows) + self.assertEqual(report['estimated_wall_seconds'],int(1.25*(30000*25+3920*5)+5)) + self.assertEqual(report['estimated_output_bytes'],int(1.25*(30000*2000+3920*1000)+10)) + self.assertEqual(rows,saved) + + def test_missing_core_runtime_are_unknown_not_required_for_end_to_end(self): + train,probe = self.cycle('train'),self.cycle('probe','probe') + for event in train['events']: + event.update(core_seconds=None,runtime_seconds=None) + report = self.estimate([train,probe]) + self.assertEqual(report['status'],'ESTIMATED-NOT-AUTHORIZED') + self.assertIsNone(report['measured_cycles'][0]['timing']['core_seconds']) + self.assertIsNone(report['measured_cycles'][0]['timing']['runtime_seconds']) + self.assertEqual(report['measured_cycles'][0]['timing']['end_to_end_seconds'],13.) + + def test_duplicate_events_and_probe_updates_rejected(self): + train = self.cycle('train') + duplicate = deepcopy(train) + duplicate['cycle_id'] = 'forged-second-cycle' + with self.assertRaises(ValueError): + self.estimate([train,duplicate]) + for event in duplicate['events']: + event['event_id'] += '-renamed' + with self.assertRaises(ValueError): + self.estimate([train,duplicate]) + probe = self.cycle('probe','probe') + probe['events'].append(deepcopy(train['events'][1])) + with self.assertRaises(ValueError): + self.estimate([probe]) + + +if __name__ == '__main__': + unittest.main() diff --git a/models/male-cns-lif/conditioning-execution-v1/test_execution_workflow.py b/models/male-cns-lif/conditioning-execution-v1/test_execution_workflow.py new file mode 100644 index 0000000..b13dace --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/test_execution_workflow.py @@ -0,0 +1,333 @@ +"""CPU-only workflow mocks: real local receipts/files, never GPU/remote calls.""" +from copy import deepcopy +import importlib.util +import json +from pathlib import Path +import subprocess +import sys +import tempfile +from types import SimpleNamespace +import unittest +from unittest.mock import patch + +import numpy as np +import boundary_plan as P +import execution_workflow as F + + +class WorkflowTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + path=Path(__file__).resolve().parent.parent/'conditioning-interface-v2/isolated_workers.py' + spec=importlib.util.spec_from_file_location('workflow_test_frozen_AP_worker',path) + cls.ap=importlib.util.module_from_spec(spec);spec.loader.exec_module(cls.ap) + + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.addCleanup(self.tmp.cleanup) + self.base = Path(self.tmp.name) + self.exp,self.model,self.lab = (self.base/n for n in ('phase4-fixture','model','lab')) + for name in ('protocol/case-templates','inputs/static','cases','runs','updates','jobs','authorities', + 'receipts','logs','audit','extension-build'): + (self.exp/name).mkdir(parents=True,exist_ok=True) + self.model.mkdir(); self.lab.mkdir() + self.ap_worker = self.base/'ap_worker.py' + self.put(self.ap_worker,'# fixture AP worker') + sources = {} + for name in set().union(*F.W.SOURCES.values()) | {'isolated_execution.py'}: + path = self.model/name; self.put(path,'# fixture source '+name); sources[str(path)]=F.digest(path) + for name in ('runtime_light','runtime_continuation','conditioning_update','eligibility_bridge', + 'phase4j','am_overlay','conditioning_epoch','conditioning_preflight_compare','engineering_compare'): + self.put(self.lab/(name+'.py'),'# fixture '+name) + self.patches = [patch.object(F,'EXP',self.exp),patch.object(F,'MODEL',self.model), + patch.object(F,'LAB',self.lab),patch.object(F,'AP_WORKER',self.ap_worker)] + for item in self.patches: + item.start(); self.addCleanup(item.stop) + self.plan=P.build_plan(); self.calls={c['call_id']:c for c in self.plan['calls']} + self.requests=dict(tick=np.array([3,900],np.int32),neuron_idx=np.array([12,15],np.int32)) + F.save_npz(self.exp/'inputs/static/mechanical-requests.npz',self.requests) + self.initial=self.overlay('zero') + parents={} + for arm,descriptor in self.plan['imported_parents'].items(): + directory=self.base/('AP-'+arm); directory.mkdir() + self.checkpoint(directory,1200,'reference') + update=self.base/('AP-'+arm+'-update'); self.update_files(update) + before=self.overlay('before-'+arm) + case=self.base/('AP-'+arm+'-case');case.mkdir() + F.save(case/'spec.json',dict(steps=1200));F.save_npz(case/'requests.npz',self.requests) + epoch=dict(schema='phase4an-epoch-postprocess-authority-v1',checkpoint_dir=str(directory), + checkpoint_manifest_sha256=F.digest(directory/'checkpoint-manifest.json'), + files={'requests':F.A.pin(case/'requests.npz')},frozen_inputs={}) + for role in ('runtime_continuation','runtime_light','conditioning_update','eligibility_bridge','phase4j','am_overlay'): + epoch['files'][role+'_source']=F.A.pin(self.lab/(role+'.py')) + ep=self.base/('AP-'+arm+'.epoch.json');F.save(ep,epoch) + parents[descriptor['role']]=dict(directory=str(directory),checkpoint_sha256=epoch['checkpoint_manifest_sha256'], + epoch_authority=F.A.pin(ep),update_dir=str(update),before_overlay=before, + after_overlay=dict(overlay_file=str(update/'delta-after.npz'),overlay_manifest=str(update/'overlay-manifest.json'))) + templates={} + for call in self.plan['calls']: + group=call['case_group'] + if group not in templates: + path=self.exp/'protocol/case-templates'/f'{group}.json' + F.save(path,dict(name=group,steps=call['steps'],max_steps=call['steps'], + within_trial_weight_updates=call.get('within_trial_weight_updates',False))) + templates[group]=str(path) + self.reg=dict(plan=self.plan,parent_records=parents,initial_overlay=self.initial,case_templates=templates, + source_sha256=sources,input_sha256={},prepared_file_sha256={},budget=dict(root=str(self.exp), + output_cap_bytes=2**30,minimum_free_reserve_bytes=0,child_timeout_seconds=20,workflow_timeout_seconds=7200)) + F.save(self.exp/'protocol/preregistered.json',self.reg) + self.w=F.Workflow.__new__(F.Workflow) + self.w.sha=F.digest(self.exp/'protocol/preregistered.json');self.w.reg=self.reg + self.w.parents=deepcopy(parents);self.w.jobs={};self.w.boundaries={};self.w.completed=[];self.w.cycles=[];self.w.sampler=None + self.w.started=0.;self.w.deadline=10**12 + self.w.h=SimpleNamespace(GPU='fixture-gpu',COMFY_URLS=('fixture-queue',), + SOURCE_PINS={n:F.digest(self.lab/(n+'.py')) for n in ('runtime_light','runtime_continuation','conditioning_epoch')}) + self.launched=[];self.boundary_args=[] + + def put(self,path,text): + path=Path(path);path.parent.mkdir(parents=True,exist_ok=True) + path.write_text(text,encoding='utf-8') + + def overlay(self,name): + npz=self.base/(name+'.npz');meta=self.base/(name+'.json') + F.save_npz(npz,dict(delta=np.array([0.],np.float32)));F.save(meta,dict(fixture=name)) + return dict(overlay_file=str(npz),overlay_manifest=str(meta)) + + def checkpoint(self,directory,steps,mode,offset=0): + F.save(directory/'summary.json',dict(steps=steps,diagnostic_mode=mode,global_next_tick=offset+steps, + effective_weights_raw_sha256='f'*64)) + F.save_npz(directory/'state.npz',dict(step=np.array([0],np.int32))) + F.save(directory/'checkpoint-manifest.json',dict(fixture=True,steps=steps)) + + def update_files(self,directory): + directory.mkdir() + for name in ('chain-receipt.json','update-receipt.json','overlay-manifest.json'): + F.save(directory/name,dict(fixture=True)) + F.save_npz(directory/'delta-after.npz',dict(delta=np.array([-.01],np.float32))) + + def fake_boundary(self,**kw): + self.boundary_args.append(deepcopy(kw)) + case=Path(kw['case']);parent=Path(kw['parent']) + return dict(schema='conditioning-updated-boundary-v1',transition=kw['transition'], + diagnostic_mode=kw['mode'],boundary_id=kw['boundary_id'], + case=dict(spec=F.A.pin(case/'spec.json'),requests=F.A.pin(case/'requests.npz')), + parent=dict(directory=str(parent),checkpoint_manifest_sha256=F.digest(parent/'checkpoint-manifest.json')), + overlay_before={k:F.A.pin(v) for k,v in kw['before_overlay'].items()}, + overlay_after={k:F.A.pin(v) for k,v in kw['after_overlay'].items()}, + source_files=kw['source_files'],frozen_inputs={}) + + def fake_launch(self,name,job,*,ap=False): + self.launched.append((name,deepcopy(job),ap));output=Path(job['output_dir']) + kind=job['kind'];result={} + if kind=='update':self.update_files(output) + else: + output.mkdir() + if kind=='sample': + for cue,node in (('A',20),('B',30)): + F.save_npz(output/f'{cue}-requests.npz',dict(tick=np.array([5],np.int32),neuron_idx=np.array([node],np.int32))) + elif kind=='validate_cpu':F.save(output/'validation.json',dict(fixture=True)) + else: + if kind=='brain':case=Path(job['case_dir']) + elif kind=='oracle':case=Path(job['oracle_case']) + else:case=Path(F.read(job['authority_file'])['case']['spec']['path']).parent + spec=F.read(case/'spec.json') + self.checkpoint(output,spec['steps'],job.get('diagnostic_mode','reference'),spec.get('global_tick_offset',0)) + result['checkpoint_manifest_sha256']=F.digest(output/'checkpoint-manifest.json') + receipt=dict(output_dir=str(output),result=result) + loc=self.exp/'receipts'/f'{name}.fixture-process.json';F.save(loc,dict(name=name,fixture=True)) + self.w.jobs[name]=dict(loc=dict(process=str(loc)),job=job,receipt=receipt) + self.put(self.exp/'extension-build/.ninja_log',str(len(self.launched))) + self.put(self.exp/'extension-build/new-extension.bin','compiled fixture artifact') + return receipt + + def test_new_job_exact_schema_and_direct_authority_parent_closure(self): + cp=self.base/'AP-D1' + auth=self.exp/'authorities/test.json' + file=self.base/'binding.txt';self.put(file,'bound') + F.save(auth,dict(parent=dict(directory=str(cp),checkpoint_manifest_sha256=F.digest(cp/'checkpoint-manifest.json')), + files=dict(x=F.A.pin(file)),frozen_inputs={str(self.ap_worker):F.digest(self.ap_worker)})) + job=self.w.new_job('test','updated',auth,build_root=str(self.exp/'extension-build'),diagnostic_mode='light') + self.assertEqual(set(job),F.W.COMMON | F.W.FIELDS['updated']) + self.assertEqual(set(job['source_sha256']),F.W.SOURCES['updated']) + for path in (file,auth,cp/'checkpoint-manifest.json',self.exp/'protocol/preregistered.json'): + self.assertEqual(job['input_sha256'][str(path)],F.digest(path)) + sample=self.w.new_job('evaluation-inputs','sample',self.exp/'protocol/preregistered.json') + self.assertEqual(set(sample),F.W.COMMON | F.W.FIELDS['sample']) + self.assertEqual(set(sample['source_sha256']),F.W.SOURCES['sample']) + + def test_full_fixed_workflow_23_brains_31_processes_and_shared_case_lineage(self): + with patch.object(self.w,'launch',side_effect=self.fake_launch),patch.object(F.A,'boundary_authority',side_effect=self.fake_boundary): + self.w.run() + self.assertEqual(len(self.w.completed),23);self.assertEqual(len(self.w.jobs),31) + self.assertEqual(len(list((self.exp/'cases').iterdir())),17) + byname={n:j for n,j,_ in self.launched} + for _,job,ap in self.launched: + expected=self.ap.COMMON | (self.ap.BRAIN_FIELDS if job['kind']=='brain' else self.ap.UPDATE_FIELDS) if ap else F.W.COMMON | F.W.FIELDS[job['kind']] + self.assertEqual(set(job),expected) + self.assertEqual(sum(j['kind'] in ('brain','updated','oracle','unchanged') for j in byname.values()),23) + self.assertEqual(sum(j['kind']=='sample' for j in byname.values()),1) + for arm in ('D1','D0','frozen','mechanical'): + r=F.read(byname[f'{arm}--updated--reference']['authority_file']) + l=F.read(byname[f'{arm}--updated--light']['authority_file']) + self.assertEqual(r['case'],l['case']);self.assertEqual(r['parent'],l['parent']) + for arm in ('D0','frozen'): + job=byname[f'{arm}--old-C--reference'];p=self.reg['parent_records'][f'AP.{arm}.reference.epoch001'] + self.assertEqual(job['overlay_file'],p['before_overlay']['overlay_file']) + self.assertNotEqual(job['overlay_file'],p['after_overlay']['overlay_file']) + for arm in ('D1','mechanical'): + self.assertEqual(byname[f'{arm}--live-oracle']['authority_file'],byname[f'{arm}--updated--reference']['authority_file']) + after=self.reg['parent_records']['AP.D1.reference.epoch001']['after_overlay'] + idle=next(v for v in self.boundary_args if v['boundary_id']=='D1--idle--9s') + self.assertEqual(idle['before_overlay'],after);self.assertEqual(idle['after_overlay'],after) + self.assertIsNone(idle['epoch']);self.assertIsNone(idle['update_dir']);self.assertIsNone(idle['cpu_receipt']) + self.assertEqual(self.w.parents['D1--idle--9s']['after_overlay'],after) + for t in ('t0','t1','t10'): + for cue,node in (('A',20),('B',30)): + request=F.npz(self.exp/'cases'/f'D1--probe--{t}--{cue}'/'requests.npz') + np.testing.assert_array_equal(request['neuron_idx'],np.array([node],np.int32)) + performance=byname['performance--natural-light-fresh'] + prior=self.reg['parent_records']['AP.D1.reference.epoch001'] + self.assertEqual(performance['overlay_file'],prior['before_overlay']['overlay_file']) + update=F.read(byname['performance--natural-light-fresh--original-E']['authority_file']) + self.assertEqual((update['command']['us_tick'],update['command']['trace_backend'],update['diagnostic_mode']),(900,'torch','light')) + self.assertIn('performance--natural-light-fresh--cpu-verify',byname) + cpu=byname['performance--natural-light-fresh--cpu-verify'] + self.assertEqual(cpu['authority_file'],byname['performance--natural-light-fresh--original-E']['authority_file']) + self.assertEqual(cpu['update_dir'],byname['performance--natural-light-fresh--original-E']['output_dir']) + for output in Path(cpu['update_dir']).iterdir(): + self.assertEqual(cpu['input_sha256'][str(output)],F.digest(output)) + probe=byname['performance--evaluation-A-light-fresh'] + self.assertEqual(probe['overlay_file'],self.initial['overlay_file']) + self.assertNotIn('parent_checkpoint_manifest_sha256',F.read(Path(probe['case_dir'])/'spec.json')) + self.assertFalse(any(n.startswith('performance--evaluation-A-light-fresh--') for n in byname)) + complete=F.read(self.exp/'audit/execution-complete.json') + self.assertFalse(complete['scientific_learning_pass_claimed']) + + def test_mutable_build_inventory_visible_new_artifacts_not_silently_ignored(self): + self.w.reg=deepcopy(self.reg) + self.w.reg['plan']=dict(imported_parents={},calls=self.plan['calls'][:2], + counts=dict(new_brain_calls=2,total_children=0)) + def execute(call): + self.put(self.exp/'extension-build/.ninja_log',call['call_id']) + self.put(self.exp/'extension-build/new-extension.bin','compiled fixture artifact') + F.save(self.exp/'runs'/(call['call_id']+'.json'),dict(complete=True)) + with patch.object(self.w,'execute_call',side_effect=execute): + self.w.run() + self.assertNotIn('extension-build/.ninja_log',F.tree(self.exp,exclude_build=True)) + self.assertIn('extension-build/.ninja_log',F.tree(self.exp)) + for cycle in self.w.cycles: + self.assertIn('.ninja_log',cycle['mutable_build_inventory_after']) + self.assertIn('new-extension.bin',cycle['mutable_build_inventory_after']) + self.assertGreater(cycle['mutable_build_bytes_after'],0) + self.assertTrue(cycle['output_inventory']) + old=self.exp/'audit/pinned-result.json';F.save(old,dict(value=1)) + before=F.tree(self.exp,exclude_build=True);self.put(old,'{"value":2}') + after=F.tree(self.exp,exclude_build=True) + self.assertNotEqual(before['audit/pinned-result.json'],after['audit/pinned-result.json']) + + def test_probe_scope_mutation_rejected_and_shared_case_mismatch_rejected(self): + call=deepcopy(self.calls['performance--evaluation-A-light-fresh']);call['US']=True + with self.assertRaises(ValueError):self.w.execute_call(call) + call=self.calls['D1--updated--reference'];parent=self.w.parents[call['parent']] + case=self.w.materialize(call,parent) + altered=deepcopy(parent);altered['checkpoint_sha256']='0'*64 + with self.assertRaises(ValueError):self.w.materialize(call,altered) + self.assertEqual(F.read(case/'spec.json')['parent_checkpoint_manifest_sha256'],parent['checkpoint_sha256']) + + def child_receipt(self,command,*,ap=False,mutate=None,**kwargs): + jobfile=Path(command[command.index('--job')+1]);job=F.read(jobfile) + out=Path(job['output_dir']);out.mkdir();F.save(out/'result.json',dict(fixture=True)) + attempt=Path(job['receipt_file']+'.attempt.json');F.save(attempt,dict(fixture=True)) + script=self.ap_worker if ap else self.model/'isolated_execution.py' + pins={**job['input_sha256'],str(jobfile):F.digest(jobfile),str(script):job['worker_sha256']} + if ap:pins.update({v['path']:v['sha256'] for v in job['sources'].values()}) + else: + pins[str(self.ap_worker)]=F.W.AP_SHA + pins.update({str(self.model/k):v for k,v in job['source_sha256'].items()}) + receipt=dict(schema='phase4ap-isolated-worker-receipt-v1' if ap else 'conditioning-isolated-execution-receipt-v1', + complete=True,job_sha256=F.digest(jobfile),registration_sha256=self.w.sha,kind=job['kind'], + job_id=job['job_id'],worker_sha256=job['worker_sha256'],output_dir=job['output_dir'], + output_files_sha256={'result.json':F.digest(out/'result.json')},attempt_sha256=F.digest(attempt)) + receipt['verified_input_sha256' if ap else 'input_sha256']=pins + if mutate:mutate(receipt) + F.save(job['receipt_file'],receipt) + return SimpleNamespace(returncode=0) + + def sample_job(self,name): + return self.w.new_job(name,'sample',self.exp/'protocol/preregistered.json') + + def test_real_parent_process_receipt_binds_exit_inputs_outputs_and_wall_layers(self): + job=self.sample_job('success') + with patch.object(self.w,'admission'),patch.object(F,'verify'), \ + patch.object(F.subprocess,'run',side_effect=self.child_receipt) as run, \ + patch.object(F.time,'perf_counter',side_effect=[100.,104.,107.]): + self.w.deadline=110.;self.w.launch('success',job) + process=F.read(self.exp/'receipts/success.process.json') + self.assertEqual((process['exit_code'],process['child_wall_seconds'],process['validation_wall_seconds'],process['end_to_end_seconds']),(0,4.,3.,7.)) + self.assertEqual(run.call_args.kwargs['timeout'],10.) + self.assertEqual(process['worker_receipt_sha256'],F.digest(self.exp/'receipts/success.worker.json')) + self.assertGreater(process['output_bytes'],0) + self.assertTrue(process['complete']) + + def test_frozen_AP_worker_parent_receipt_uses_verified_source_closure(self): + prior=self.reg['parent_records']['AP.D1.reference.epoch001'] + case=Path(F.read(prior['epoch_authority']['path'])['files']['requests']['path']).parent + job=self.w.ap_job('performance-ap','brain',case=case,overlay=prior['before_overlay'],output=self.exp/'runs/performance-ap') + self.assertEqual(set(job),self.ap.COMMON | self.ap.BRAIN_FIELDS) + with patch.object(self.w,'admission'),patch.object(F,'verify'), \ + patch.object(F.subprocess,'run',side_effect=lambda *a,**kw:self.child_receipt(*a,ap=True,**kw)) as run: + self.w.launch('performance-ap',job,ap=True) + self.assertEqual(run.call_args.args[0][3],'brain') + receipt=F.read(self.exp/'receipts/performance-ap.worker.json') + for role in ('runtime_light','runtime_continuation'): + binding=job['sources'][role] + self.assertEqual(receipt['verified_input_sha256'][binding['path']],binding['sha256']) + + def test_exit_failure_timeout_and_receipt_mutation_preserved_and_not_retried(self): + variants=[('exit',lambda *a,**kw:SimpleNamespace(returncode=9)), + ('timeout',lambda *a,**kw:(_ for _ in ()).throw(subprocess.TimeoutExpired('fixture',1))), + ('identity',lambda *a,**kw:self.child_receipt(*a,mutate=lambda r:r.update(job_id='wrong'),**kw)), + ('closure',lambda *a,**kw:self.child_receipt(*a,mutate=lambda r:r['input_sha256'].clear(),**kw)), + ('output',lambda *a,**kw:self.child_receipt(*a,mutate=lambda r:r['output_files_sha256'].clear(),**kw))] + for name,action in variants: + job=self.sample_job(name) + with patch.object(self.w,'admission'),patch.object(F,'verify'),patch.object(F.subprocess,'run',side_effect=action) as run: + with self.assertRaises(ValueError):self.w.launch(name,job) + self.assertFalse(F.read(self.exp/f'receipts/{name}.process.json')['complete']) + with self.assertRaises(ValueError):self.w.launch(name,job) + self.assertEqual(run.call_count,1) + self.assertNotIn(name,self.w.jobs) + + def test_workflow_attempt_prevents_restart(self): + F.save(self.exp/'audit/execution-attempt.json',dict(partial=True)) + with patch.object(self.w,'cpu_verify') as verify: + with self.assertRaises(ValueError):self.w.run() + verify.assert_not_called() + + def test_whole_deadline_starts_before_registration_verification(self): + clock=[10.] + def registration(sha):clock[0]=50.;return self.reg + with patch.object(F,'registration',side_effect=registration),patch.object(F.W,'ap_helpers',return_value=self.w.h), \ + patch.object(F.time,'perf_counter',side_effect=lambda:clock[0]): + workflow=F.Workflow('a'*64) + self.assertEqual(workflow.started,10.) + self.assertEqual(workflow.deadline,10.+self.reg['budget']['workflow_timeout_seconds']) + + def test_no_complete_after_final_cycle_inventory_crosses_deadline(self): + call=self.calls['D1--updated--reference'];self.w.reg=deepcopy(self.reg) + self.w.reg['plan']=dict(imported_parents={},calls=[call],counts=dict(new_brain_calls=1,total_children=0)) + clock=[1.];self.w.started=0.;self.w.deadline=5. + def execute(_):clock[0]=6. + with patch.object(self.w,'execute_call',side_effect=execute),patch.object(F.time,'perf_counter',side_effect=lambda:clock[0]): + with self.assertRaisesRegex(ValueError,'wall limit'): + self.w.run() + self.assertFalse((self.exp/'audit/execution-complete.json').exists()) + + def test_import_does_not_load_old_coordinator_neural_runtime_or_torch(self): + code="import sys;sys.path.insert(0,sys.argv[1]);import execution_workflow;bad=[k for k in sys.modules if k in ('torch','runtime_light','runtime_continuation','conditioning_epoch','_conditioning_execution_prior_integrity')];assert not bad,bad" + completed=subprocess.run([sys.executable,'-B','-c',code,str(Path(__file__).resolve().parent)], + capture_output=True,text=True,timeout=20) + self.assertEqual(completed.returncode,0,completed.stderr) + + +if __name__=='__main__':unittest.main() diff --git a/models/male-cns-lif/conditioning-execution-v1/test_finalize_execution.py b/models/male-cns-lif/conditioning-execution-v1/test_finalize_execution.py new file mode 100644 index 0000000..0d53772 --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/test_finalize_execution.py @@ -0,0 +1,147 @@ +"""Synthetic seal checks only; no historical integrity or brain execution.""" +import copy +from contextlib import contextmanager +import json +from pathlib import Path +import tempfile +from types import SimpleNamespace +import unittest +from unittest.mock import Mock, patch +import finalize_execution as F + + +class FinalizeTests(unittest.TestCase): + @contextmanager + def complete_fixture(self): + """Temporary evidence + mocked auditors/child; no real integrity run.""" + helper = Path(__file__).parents[1]/'pn-recruitment-audit-v1/publication.py' + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp).resolve() + for folder in ('protocol', 'audit', 'regression'): + (root/folder).mkdir() + reg = dict(schema='conditioning-execution-registration-v1', engineering_only=True, + scientific_acquisition_authorized=False, source_sha256={}, input_sha256={}, + prepared_file_sha256={}, source_code_commit='a'*40) + F.save(root/'protocol/preregistered.json', reg) + regsha = F.digest(root/'protocol/preregistered.json') + numerical = dict(schema='conditioning-execution-numerical-audit-v1', complete=True, + registration_sha256=regsha, actual_new_brain_calls=23, actual_processes=31, + actual_new_brain_ticks=125200, learning_pass_claimed=False, candidate_generated=False, + status='PASS-EXACT-UPDATED-BOUNDARY-ENGINEERING-NOT-LEARNING', pass_all=True, + process_evidence={'synthetic': {'worker_sha256':'b'*64}}) + budget = dict(schema='synthetic-budget-report-not-scientific', complete=True, + registration_sha256=regsha, learning_pass_claimed=False, measured_fixture_seconds=1.) + integrity = dict(complete=True, AP={'pass_all':True}, prior={'synthetic_old_roots':'unchanged'}, + anchors={'synthetic_reference':'c'*64}, old_experiments_reexecuted=False, + old_files_modified=False, five_frozen_reference_trials_rerun_here=False, + scientific_learning_pass_claimed=False) + files = (root/'audit/numerical-comparison.json', root/'audit/budget-report.json', + root/'regression/integrity-after.json') + for file, value in zip(files, (numerical, budget, integrity)): + F.save(file, value) + numerical_module = SimpleNamespace(audit=Mock(return_value=copy.deepcopy(numerical)), + same_json=lambda a,b: json.dumps(a,sort_keys=True,allow_nan=False)==json.dumps(b,sort_keys=True,allow_nan=False)) + budget_module = SimpleNamespace(report=Mock(return_value=copy.deepcopy(budget))) + def child(command, **kwargs): + self.assertEqual(command, [F.sys.executable,'-B',str(F.MODEL/'prepare_execution.py'), + 'integrity','--output',str(root/'regression/integrity-at-seal.json')]) + self.assertEqual(kwargs, dict(cwd=F.MODEL,capture_output=True,text=True,timeout=1800)) + F.save(root/'regression/integrity-at-seal.json', integrity) + return SimpleNamespace(returncode=0, stdout='synthetic child', stderr='') + with patch.object(F,'EXP',root),patch.object(F,'HELPER',helper), \ + patch.dict(F.sys.modules,{'audit_execution':numerical_module,'budget_report':budget_module}), \ + patch.object(F.subprocess,'run',side_effect=child) as process: + yield SimpleNamespace(root=root, numerical=numerical, integrity=integrity, + audit_module=numerical_module,budget_module=budget_module,process=process, + args=(regsha,*(F.digest(p) for p in files))) + + def test_source_helper_matches_actual_local_frozen_file(self): + path = Path(__file__).parents[1]/'pn-recruitment-audit-v1/publication.py' + with patch.object(F, 'HELPER', path): + module = F.helper() + self.assertTrue(callable(module.verify_seal)) + + def test_exact_seal_detects_mutated_and_extra_payload(self): + path = Path(__file__).parents[1]/'pn-recruitment-audit-v1/publication.py' + with tempfile.TemporaryDirectory() as tmp, patch.object(F, 'HELPER', path): + root = Path(tmp) + F.save(root/'one.json', {'learning':False}) + result = F.seal(root) + self.assertTrue(result['pass_all']) + self.assertEqual(result['payload_file_count'], 1) + with self.assertRaisesRegex(ValueError, 'Never replace'): + F.seal(root) + F.save(root/'unlisted.json', {}) + with self.assertRaisesRegex(ValueError, 'unlisted/missing'): + F.helper().verify_seal(root) + + def test_refuses_empty_and_bytecode_trees(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + with self.assertRaisesRegex(ValueError, 'Empty'): + F.seal(root) + (root/'__pycache__').mkdir() + with self.assertRaisesRegex(ValueError, 'Unsafe'): + F.seal(root) + + def test_stopped_workflow_never_gets_normal_final(self): + with tempfile.TemporaryDirectory() as tmp, patch.object(F, 'EXP', Path(tmp)): + (Path(tmp)/'audit').mkdir() + F.save(Path(tmp)/'audit/stopped.json', {'complete':False}) + with self.assertRaisesRegex(ValueError, 'separate explicit failure audit'): + F.finalize('a'*64, 'b'*64, 'c'*64, 'd'*64) + + def test_full_mock_finalize_recomputes_and_seals_actual_independent_report(self): + with self.complete_fixture() as fixture: + result = F.finalize(*fixture.args) + fixture.audit_module.audit.assert_called_once_with(fixture.args[0],save_report=False) + fixture.budget_module.report.assert_called_once_with(fixture.args[0],save_report=False) + fixture.process.assert_called_once() + final = F.read(fixture.root/'audit/final-status.json') + self.assertEqual(result['status'],'PASS-EXACT-UPDATED-BOUNDARY-ENGINEERING-NOT-LEARNING') + self.assertEqual(final['integrity_at_seal_sha256'],F.digest(fixture.root/'regression/integrity-at-seal.json')) + self.assertTrue(final['numerical_and_budget_recomputed_at_seal']) + self.assertFalse(final['scientific_acquisition_executed']) + self.assertFalse(final['learning_pass_claimed']); self.assertFalse(final['candidate_generated']) + self.assertEqual(final['historical_five_trial_regression'],'AM sealed evidence preserved; not rerun here') + sealed = F.helper().verify_seal(fixture.root) + self.assertTrue(sealed['pass_all']) + payload = {row['path'] for row in F.read(fixture.root/'manifest.json')['files']} + self.assertTrue({'audit/numerical-comparison.json','audit/budget-report.json', + 'regression/integrity-after.json','regression/integrity-at-seal.json', + 'audit/final-status.json'}.issubset(payload)) + + def test_recomputed_numerical_or_budget_mutation_rejected_before_integrity_and_seal(self): + for kind in ('numerical','budget'): + with self.subTest(kind=kind),self.complete_fixture() as fixture: + if kind=='numerical': + changed=copy.deepcopy(fixture.numerical) + changed['process_evidence']['synthetic']['worker_sha256']='d'*64 + fixture.audit_module.audit.return_value=changed + else: + changed=copy.deepcopy(fixture.budget_module.report.return_value) + changed['measured_fixture_seconds']=2. + fixture.budget_module.report.return_value=changed + with self.assertRaisesRegex(ValueError,'evidence changed after the independent audit'): + F.finalize(*fixture.args) + fixture.process.assert_not_called() + for name in ('audit/final-status.json','manifest.json','checksums.sha256'): + self.assertFalse((fixture.root/name).exists()) + + def test_independent_integrity_failure_missing_output_or_changed_prior_refuses_seal(self): + for mode in ('nonzero_exit','missing_file','changed_prior'): + with self.subTest(mode=mode),self.complete_fixture() as fixture: + def child(*args,**kwargs): + if mode=='changed_prior': + changed=copy.deepcopy(fixture.integrity); changed['prior']['synthetic_old_roots']='changed' + F.save(fixture.root/'regression/integrity-at-seal.json',changed) + return SimpleNamespace(returncode=1 if mode=='nonzero_exit' else 0,stdout='',stderr='synthetic failure') + fixture.process.side_effect=child + with self.assertRaisesRegex(ValueError,'integrity-at-seal process failed|Frozen integrity changed'): + F.finalize(*fixture.args) + for name in ('audit/final-status.json','manifest.json','checksums.sha256'): + self.assertFalse((fixture.root/name).exists()) + + +if __name__ == '__main__': + unittest.main() diff --git a/models/male-cns-lif/conditioning-execution-v1/test_isolated_execution.py b/models/male-cns-lif/conditioning-execution-v1/test_isolated_execution.py new file mode 100644 index 0000000..20f83e2 --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/test_isolated_execution.py @@ -0,0 +1,176 @@ +"""CPU/path/admission tests only; no subprocess brain or scientific PASS.""" +import copy +import json +from pathlib import Path +import tempfile +from types import SimpleNamespace +import unittest +from unittest.mock import Mock, patch + +import isolated_execution as W + +H = W.ap_helpers() + + +class WorkerTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory() + self.root = Path(self.temp.name).resolve() / 'phase4aq-fixture' + for name in ('inputs', 'updates', 'jobs', 'runs', 'receipts'): + (self.root / name).mkdir(parents=True) + self.authority = self.root / 'inputs' / 'authority.json' + self.chain = self.root / 'updates' / 'chain-receipt.json' + self.authority.write_text('{}'); self.chain.write_text('{}') + self.jobfile = self.root / 'jobs' / 'one.json' + self.job = dict(schema=W.SCHEMA, job_id='cpu-one', kind='validate_cpu', + registration_sha256='1'*64, worker_sha256=H.digest(W.__file__), + source_sha256={'validate_update_cpu.py': H.digest(W.MODEL/'validate_update_cpu.py')}, + input_sha256={str(p): H.digest(p) for p in (self.authority, self.chain)}, + output_dir=str(self.root/'runs'/'one'), receipt_file=str(self.root/'receipts'/'one.json'), + budget=dict(root=str(self.root), output_cap_bytes=1024**2, minimum_free_reserve_bytes=1024**2), + authority_file=str(self.authority), authority_sha256=H.digest(self.authority), + update_dir=str(self.chain.parent), chain_sha256=H.digest(self.chain)) + + def tearDown(self): + self.temp.cleanup() + + def check(self, job=None): + self.jobfile.write_text(json.dumps(self.job if job is None else job)) + return W.validate_job(H, self.jobfile, H.digest(self.jobfile)) + + def test_real_pinned_AP_helper_and_CPU_job(self): + self.assertEqual(H.digest(W.AP), W.AP_SHA) + job, paths, pins = self.check() + self.assertEqual(job['kind'], 'validate_cpu') + self.assertEqual(pins[W.AP], W.AP_SHA) + self.assertEqual(paths['output'], self.root/'runs'/'one') + + def test_wrong_or_missing_fields(self): + changes = [('kind', 'brain'), ('job_id', '../x'), ('job_id', 'a/b'), + ('registration_sha256', ''), ('worker_sha256', '0'*64), + ('source_sha256', {}), ('input_sha256', {})] + for key, value in changes: + with self.subTest(key=key, value=value): + job = copy.deepcopy(self.job); job[key] = value + with self.assertRaises(ValueError): self.check(job) + job = copy.deepcopy(self.job); job['extra'] = True + with self.assertRaises(ValueError): self.check(job) + + def test_source_bytes_are_checked_before_import(self): + job = copy.deepcopy(self.job); job['source_sha256']['validate_update_cpu.py'] = '0'*64 + with self.assertRaisesRegex(ValueError, 'Pinned file changed'): self.check(job) + + def test_parent_input_pin_and_chain_are_mandatory(self): + for key in ('authority_sha256', 'chain_sha256'): + job = copy.deepcopy(self.job); job[key] = '0'*64 + with self.subTest(key=key), self.assertRaises(ValueError): self.check(job) + job = copy.deepcopy(self.job); del job['input_sha256'][str(self.chain)] + with self.assertRaises(ValueError): self.check(job) + + def test_partial_output_and_attempt_never_retried(self): + output = Path(self.job['output_dir']); output.mkdir() + with self.assertRaises(ValueError): self.check() + output.rmdir() + attempt = Path(self.job['receipt_file']+'.attempt.json'); attempt.write_text('{}') + with self.assertRaises(ValueError): self.check() + + def test_output_cannot_overlap_input_update_or_receipt(self): + for path in (self.authority.parent/'child', self.chain.parent/'child', + self.root/'receipts'/'child', self.root.parent/'escape'): + job = copy.deepcopy(self.job); job['output_dir'] = str(path) + with self.subTest(path=path), self.assertRaises(ValueError): self.check(job) + + def test_linked_output_parent_rejected_when_supported(self): + target = self.root/'runs'; linked = self.root/'linked' + try: linked.symlink_to(target, target_is_directory=True) + except OSError: self.skipTest('Platform does not allow test symlinks') + job = copy.deepcopy(self.job); job['output_dir'] = str(linked/'one') + with self.assertRaisesRegex(ValueError, 'Linked'): self.check(job) + + def test_explicit_resource_limits(self): + job, paths, _ = self.check() + result = W.budget_check(H, job, paths) + self.assertGreater(result['output_bytes'], 0) + job['budget']['output_cap_bytes'] = 1 + with self.assertRaisesRegex(ValueError, 'Resource budget exceeded'): W.budget_check(H, job, paths) + for field in ('output_cap_bytes', 'minimum_free_reserve_bytes'): + job = copy.deepcopy(self.job); job['budget'][field] = 0 + with self.subTest(field=field), self.assertRaises(ValueError): self.check(job) + + def test_fresh_process_loaders_are_not_reused(self): + with self.assertRaisesRegex(ValueError, 'Fresh exec helper'): W.ap_helpers() + + def unchanged(self): + case=self.root/'inputs'/'case';case.mkdir() + parent=self.root/'inputs'/'checkpoint';parent.mkdir() + for p in (case/'spec.json',case/'requests.npz',parent/'checkpoint-manifest.json'): + p.write_text('{}') + before=self.root/'inputs'/'before.npz';bm=self.root/'inputs'/'before.json' + after=self.root/'inputs'/'after.npz';am=self.root/'inputs'/'after.json' + before.write_bytes(b'zero-values');after.write_bytes(b'zero-values') + bm.write_text('{"phase":"before"}');am.write_text('{"phase":"after"}') + files=(case/'spec.json',case/'requests.npz',parent/'checkpoint-manifest.json',before,bm,after,am,W.C_PATH) + authority=dict(schema='conditioning-updated-boundary-v1',transition='epoch_update',diagnostic_mode='reference', + registration_sha256='1'*64,expected_effective_weight_before_sha256='a'*64,expected_effective_weight_after_sha256='a'*64, + parent=dict(directory=str(parent),checkpoint_manifest_sha256=H.digest(parent/'checkpoint-manifest.json')), + case={key:dict(path=str(case/name),sha256=H.digest(case/name)) for key,name in (('spec','spec.json'),('requests','requests.npz'))}, + overlay_before={key:dict(path=str(p),sha256=H.digest(p)) for key,p in (('file',before),('manifest',bm))}, + overlay_after={key:dict(path=str(p),sha256=H.digest(p)) for key,p in (('file',after),('manifest',am))},frozen_inputs={}) + self.authority.write_text(json.dumps(authority)) + self.job=dict(schema=W.SCHEMA,job_id='unchanged-one',kind='unchanged',registration_sha256='1'*64, + worker_sha256=H.digest(W.__file__),source_sha256={},input_sha256={str(p):H.digest(p) for p in (*files,self.authority)}, + output_dir=str(self.root/'runs'/'one'),receipt_file=str(self.root/'receipts'/'one.json'), + budget=dict(root=str(self.root),output_cap_bytes=1024**2,minimum_free_reserve_bytes=1024**2), + authority_file=str(self.authority),authority_sha256=H.digest(self.authority),case_dir=str(case), + build_root=str(self.root/'build'),diagnostic_mode='reference',overlay_file=str(before),overlay_manifest=str(bm), + checkpoint_dir=str(parent),checkpoint_sha256=H.digest(parent/'checkpoint-manifest.json')) + return authority + + def test_unchanged_binds_exact_before_not_equal_after_manifest(self): + self.unchanged();job,paths,pins=self.check() + self.assertEqual(W.bind_unchanged_authority(H,job,paths,pins)['transition'],'epoch_update') + self.job['overlay_file']=str(self.root/'inputs'/'after.npz') + self.job['overlay_manifest']=str(self.root/'inputs'/'after.json') + job,paths,pins=self.check() + with self.assertRaisesRegex(ValueError,'actual before'):W.bind_unchanged_authority(H,job,paths,pins) + + def test_rehashed_wrong_authority_parent_mode_case_registration_rejected(self): + original=self.unchanged() + for field in ('parent','diagnostic_mode','registration_sha256','case'): + value=copy.deepcopy(original) + if field=='parent':value[field]['checkpoint_manifest_sha256']='f'*64 + elif field=='diagnostic_mode':value[field]='light' + elif field=='registration_sha256':value[field]='f'*64 + else:value[field]['requests']['sha256']='f'*64 + self.authority.write_text(json.dumps(value)) + self.job['authority_sha256']=self.job['input_sha256'][str(self.authority)]=H.digest(self.authority) + job,paths,pins=self.check() + with self.subTest(field=field),self.assertRaises(ValueError):W.bind_unchanged_authority(H,job,paths,pins) + + def test_unchanged_missing_nested_after_authority_pin_rejected(self): + self.unchanged();del self.job['input_sha256'][str(self.root/'inputs'/'after.json')] + job,paths,pins=self.check() + with self.assertRaisesRegex(ValueError,'external input pin'):W.bind_unchanged_authority(H,job,paths,pins) + + def test_unchanged_original_entry_already_seals_no_double_seal(self): + self.unchanged();self.check() + summary={'apl_component':{'environment':{'synthetic':True}},'complete':True,'steps':5} + def simulated_entry(case,out,build,overlay,manifest,parent,*,mode): + out.mkdir();H.save_json(out/'summary.json',summary) + H.save_json(out/'checkpoint-manifest.json',{'synthetic_seal':True}) + return dict(summary,checkpoint_manifest_sha256=H.digest(out/'checkpoint-manifest.json')) + runtime=SimpleNamespace(run_continued=Mock(side_effect=simulated_entry), + load_checkpoint=Mock(return_value={'summary':summary}), + seal_checkpoint=Mock(side_effect=AssertionError('Double seal would be a real blocker'))) + with patch.object(W,'ap_helpers',return_value=H),patch.object(H,'_clean_process') as clean, \ + patch.object(H,'_admit_brain',return_value={'synthetic_no_GPU':True}), \ + patch.object(H,'_load_source',return_value=runtime),patch.object(H,'_environment',return_value={'synthetic_no_GPU':True}), \ + patch.object(H,'_verify_precision'): + result=W.execute(self.jobfile,H.digest(self.jobfile)) + runtime.run_continued.assert_called_once();runtime.load_checkpoint.assert_called_once();runtime.seal_checkpoint.assert_not_called() + clean.assert_called_once_with('brain');self.assertTrue(result['complete']) + self.assertFalse(result['process_exit_independently_observed']) + self.assertFalse(result['scientific_learning_pass_claimed']) + + +if __name__ == '__main__': unittest.main() diff --git a/models/male-cns-lif/conditioning-execution-v1/test_learning_gates.py b/models/male-cns-lif/conditioning-execution-v1/test_learning_gates.py new file mode 100644 index 0000000..86e9785 --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/test_learning_gates.py @@ -0,0 +1,424 @@ +"""CPU synthetic fixtures only: no real brain, file receipts or science data.""" +import copy +import hashlib +import json +import unittest + +import numpy as np + +import learning_gates as g + + +NODES = np.arange(6, dtype=np.int32) +POPS = {'MBON': np.array([0, 1, 2], np.int32), 'legacyDownstream': NODES.copy(), + 'nonMBONDownstream': np.array([3, 4, 5], np.int32), 'MBON01': np.array([0], np.int32), + 'MBON12': np.array([1], np.int32)} +C, N = g.M.CUES + + +def sha(value): + return hashlib.sha256(str(value).encode()).hexdigest() + + +def registry(): + blocks = {} + counter = 0 + for mode in g.M.MODES: + for dan in g.M.DAN_TYPES: + for cue in g.M.CUES: + for rep in range(1, 6): + block = f'{mode}/{dan}/{cue}/{rep}' + base = 2**40 + counter * 10 + blocks[block] = dict(mode=mode, dan_type=dan, compartment_verbatim=g.DAN_COMPARTMENTS[dan], + conditioned_cue=cue, replicate=rep, + training_sources=[dict(stream_id=f'{block}/{split}/{assay}', seed=base + j, split=split) + for j, (split, assay) in enumerate((('training', 'normal'), ('training', 'delayed'), + ('training', 'reversal'), ('design', 'order')))], + evaluation_sources={cue: dict(stream_id=f'{block}/evaluation/{cue}', seed=base + 7 + j, split='evaluation') + for j, cue in enumerate(g.M.CUES)}, + evaluation_input_sha256={cue: sha(f'{block}/input/{cue}') for cue in g.M.CUES}) + counter += 1 + return dict(schema=g.REGISTRY_SCHEMA, protocol_sha256=sha('synthetic-unregistered-protocol'), + population_binding_sha256=sha('synthetic-population-binding'), nodes_raw_sha256=g.M._hash(NODES), + population_membership_sha256=g.M.validate_populations(NODES, POPS)['membership_sha256'], blocks=blocks) + + +def authority(reg): + return dict(schema=g.AUTHORITY_SCHEMA, registry_sha256=g.descriptor_sha256(reg), + verification_receipt_sha256=sha('SYNTHETIC-NOT-A-REAL-VERIFICATION'), + execution_inventory_sha256=sha('SYNTHETIC-NOT-EXECUTED'), population_binding_sha256=reg['population_binding_sha256']) + + +def state(direction=0, *, downstream=True, g_effect=True, offset=0): + counts = [1, 1, 0, 1, 0, 0] + if direction: + counts[1 if direction > 0 else 0] += abs(direction) + if downstream: + counts[3] += abs(direction) + events = sorted((10 + k * 20 + offset, i) for i, count in enumerate(counts) for k in range(count)) + conductance = np.zeros(6, np.float64) + if direction and g_effect: + conductance[0] = .002 * abs(direction) + return dict(step=np.array([tick for tick, _ in events], np.int32), + neuron_idx=np.array([i for _, i in events], np.int32), counts=np.array(counts, np.int32), + v=np.full(6, -.052, np.float64), g=conductance, ref=np.zeros(6, np.int32)) + + +def states(conditioned=C, strength=0, *, neutral_strength=0, downstream=True): + neutral = next(cue for cue in g.M.CUES if cue != conditioned) + return {conditioned: state(strength, downstream=downstream), neutral: state(neutral_strength, downstream=downstream)} + + +def pair(reg, block, label, values, *, delta, checkpoint, policy='common_frozen_reset'): + return dict(schema=g.PAIR_SCHEMA, block_id=block, snapshot_id=f'{block}/{label}', + receipt_sha256=sha(f'{block}/{label}/pair'), + probe_receipt_sha256={cue: sha(f'{block}/{label}/probe/{cue}') for cue in g.M.CUES}, + evaluation_input_sha256=dict(reg['blocks'][block]['evaluation_input_sha256']), + state_raw_sha256={cue: g.M.validate_state(values[cue], nodes=NODES, steps=1200)['hashes'] for cue in g.M.CUES}, + states=values, state_policy=policy, source_checkpoint_sha256=checkpoint, delta_sha256=delta, + probe_steps=1200, cs_start_tick=0, cs_stop_tick_exclusive=1000, US_requested=False, + weight_updates_enabled=False, feedback_to_training_or_idle=False) + + +def rebind(pair_value): + pair_value['state_raw_sha256'] = {cue: g.M.validate_state(pair_value['states'][cue], nodes=NODES, steps=1200)['hashes'] + for cue in g.M.CUES} + + +def execution(block, arm, cue, *, initial=None): + return dict(receipt_sha256=sha(f'{block}/{arm}/execution'), recording_verified=True, complete=True, + completed_epochs=100, reinforcement_opportunities=50, terminal_pairings=50, epoch_duration_ms=120., + cs_duration_ms=100., us_tick=900, initial_delta_sha256=initial or sha('zero-delta'), + terminal_delta_sha256=sha(f'{block}/{arm}/delta'), terminal_checkpoint_sha256=sha(f'{block}/{arm}/terminal'), + neural_state_policy='fresh_epoch_delta_only', weight_rule='frozen_temporal_eligibility_v2', reinforced_cue=cue) + + +def common(reg, block): + value = reg['blocks'][block] + return dict(replicate_id=block, replicate_unit='independent_training', + training_stream_ids=[x['stream_id'] for x in value['training_sources']], + evaluation_stream_ids=[value['evaluation_sources'][cue]['stream_id'] for cue in g.M.CUES]) + + +def blocks(reg, mode, dan, cue): + return [key for key, value in reg['blocks'].items() + if (value['mode'], value['dan_type'], value['conditioned_cue']) == (mode, dan, cue)] + + +def reversal_rows(reg, mode='physiological', dan='PAM15', cue=C): + rows = [] + neutral = next(x for x in g.M.CUES if x != cue) + direction = 1 if dan == 'PAM15' else -1 + for block in blocks(reg, mode, dan, cue): + acq = execution(block, 'acquisition', cue) + executions = dict(acquisition=acq, + reversal=execution(block, 'reversal', neutral, initial=acq['terminal_delta_sha256']), + no_reversal=execution(block, 'no_reversal', cue, initial=acq['terminal_delta_sha256'])) + row = common(reg, block) + row['execution'] = executions + row['pre'] = pair(reg, block, 'pre', states(cue), delta=acq['initial_delta_sha256'], checkpoint=sha('reset')) + values = dict(acquisition=states(cue, direction), reversal=states(cue, direction, neutral_strength=2 * direction), + no_reversal=states(cue, 2 * direction)) + for arm, value in values.items(): + e = executions[arm] + row[arm] = pair(reg, block, arm, value, delta=e['terminal_delta_sha256'], checkpoint=e['terminal_checkpoint_sha256']) + rows.append(row) + return rows + + +def idle(block, branch, cue): + source = execution(block, 'paired' if branch == 'trained' else 'frozen', cue) + if branch == 'frozen': + source['terminal_delta_sha256'] = source['initial_delta_sha256'] + checkpoints, previous = {}, source['terminal_checkpoint_sha256'] + for second in (0, 1, 10): + current = sha(f'{block}/{branch}/idle-checkpoint/{second}') + checkpoints[str(second)] = dict(checkpoint_sha256=current, parent_checkpoint_sha256=previous, + elapsed_ticks=second * 10000, absolute_tick=1200 + second * 10000, + delta_sha256=source['terminal_delta_sha256'], carry_sha256={key: sha(f'{block}/{branch}/{second}/{key}') for key in g.CARRY}) + previous = current + return dict(source_checkpoint_sha256=source['terminal_checkpoint_sha256'], delta_sha256=source['terminal_delta_sha256'], + idle_lineage_id=f'{block}/{branch}/idle10', idle_receipt_sha256=sha(f'{block}/{branch}/idle-receipt'), + total_idle_ticks=100000, no_input=True, updates_disabled=True, reset_state=False, + analytic_time_advance=False, one_second_is_prefix_of_ten_second=True, checkpoints=checkpoints, source_execution=source) + + +def retention_rows(reg, mode='physiological', dan='PAM15', cue=C): + rows = [] + direction = 1 if dan == 'PAM15' else -1 + for block in blocks(reg, mode, dan, cue): + row = common(reg, block) + row.update(execution={branch: idle(block, branch, cue) for branch in ('trained', 'frozen')}, pre={}, checkpoints={}) + for branch in ('trained', 'frozen'): + e = row['execution'][branch] + row['pre'][branch] = pair(reg, block, 'pre-' + branch, states(cue), + delta=e['source_execution']['initial_delta_sha256'], checkpoint=sha('reset')) + for second in (0, 1, 10): + row['checkpoints'][str(second)] = {} + for branch in ('trained', 'frozen'): + e = row['execution'][branch] + row['checkpoints'][str(second)][branch] = pair(reg, block, f'{branch}/{second}', + states(cue, direction if branch == 'trained' else 0), delta=e['delta_sha256'], + checkpoint=e['checkpoints'][str(second)]['checkpoint_sha256'], policy='continuous_checkpoint') + rows.append(row) + return rows + + +def acquisition_rows(reg, mode='physiological', dan='PAM15', cue=C, assay='normal'): + rows = [] + for block in blocks(reg, mode, dan, cue): + row = common(reg, block) + row.update(assay=assay, mode=mode, dan_type=dan, conditioned_cue=cue, arms={}) + for arm in g.M.ARMS: + strength = (1 if dan == 'PAM15' else -1) if arm == 'paired' else 0 + metric = g.M.arm_metrics(states(cue), states(cue, strength), conditioned_cue=cue, nodes=NODES, populations=POPS) + e = dict(complete=True, recording_verified=True, receipt_sha256=sha(f'{block}/{assay}/{arm}/execution'), assay=assay, + epoch_duration_ms=180. if assay == 'delayed' else 120., cs_duration_ms=100., us_tick=1500 if assay == 'delayed' else 900, + completed_epochs=150 if arm == 'unpaired' else 100, reinforcement_opportunities=0 if arm == 'CS-only' else 50, terminal_pairings=50) + row['arms'][arm] = dict(metrics=metric, execution=e) + rows.append(row) + return rows + + +class GatesTests(unittest.TestCase): + def setUp(self): + self.reg = registry() + self.auth = authority(self.reg) + + def evaluate(self, assay, rows, *, mode='physiological', dan='PAM15', cue=C): + kwargs = dict(mode=mode, dan_type=dan, conditioned_cue=cue, registry=self.reg, authority=self.auth) + if assay in ('normal', 'delayed'): + return g.evaluate_acquisition(rows, assay=assay, **kwargs) + return getattr(g, 'evaluate_' + assay)(rows, nodes=NODES, populations=POPS, downstream_policy=g.DOWNSTREAM_POLICY, **kwargs) + + def test_registry_checks_real_seed_collision_and_exact_40_cells(self): + for mutation in ('eval_train', 'eval_eval', 'train_train', 'duplicate_cell', '63bit', 'boolseed', 'extra_eval', 'missing_block'): + reg = registry() + values = list(reg['blocks'].values()) + if mutation == 'eval_train': values[0]['evaluation_sources'][C]['seed'] = values[1]['training_sources'][0]['seed'] + if mutation == 'eval_eval': values[1]['evaluation_sources'][C]['seed'] = values[0]['evaluation_sources'][C]['seed'] + if mutation == 'train_train': values[1]['training_sources'][0]['stream_id'] = values[0]['training_sources'][0]['stream_id'] + if mutation == 'duplicate_cell': values[1]['replicate'] = values[0]['replicate'] + if mutation == '63bit': values[0]['training_sources'][0]['seed'] = 2**63 + if mutation == 'boolseed': values[0]['training_sources'][0]['seed'] = True + if mutation == 'extra_eval': values[0]['evaluation_sources']['extra'] = values[0]['evaluation_sources'][C] + if mutation == 'missing_block': reg['blocks'].pop(next(iter(reg['blocks']))) + with self.subTest(mutation=mutation), self.assertRaises(ValueError): + g.validate_registry(reg, expected_sha256=g.descriptor_sha256(reg)) + self.assertEqual(g.validate_registry(self.reg, expected_sha256=self.auth['registry_sha256'])['blocks'], 40) + + def test_registry_hash_and_population_mutation_rejected(self): + self.reg['protocol_sha256'] = sha('other') + with self.assertRaisesRegex(ValueError, 'changed'): + self.evaluate('reversal', reversal_rows(self.reg)) + self.reg = registry(); self.auth = authority(self.reg) + bad = dict(POPS, nonMBONDownstream=np.array([3, 4], np.int32)) + with self.assertRaises(ValueError): + g.evaluate_reversal(reversal_rows(self.reg), mode='physiological', dan_type='PAM15', conditioned_cue=C, + registry=self.reg, authority=self.auth, nodes=NODES, populations=bad, downstream_policy=g.DOWNSTREAM_POLICY) + + def test_all_four_assays_both_signs_reciprocal_cues_and_no_claims(self): + for assay, factory in (('normal', acquisition_rows), ('delayed', acquisition_rows), ('reversal', reversal_rows), ('retention', retention_rows)): + for dan in g.M.DAN_TYPES: + for cue in g.M.CUES: + rows = factory(self.reg, dan=dan, cue=cue, **({'assay': assay} if assay in ('normal', 'delayed') else {})) + result = self.evaluate(assay, rows, dan=dan, cue=cue) + with self.subTest(assay=assay, dan=dan, cue=cue): + self.assertTrue(result['predicate_pass']) + self.assertEqual(result['successful_same_replicate_conjunctions'], 5) + self.assertFalse(result['scientific_learning_pass_claimed']) + self.assertFalse(result['external_authority_verified']) + self.assertFalse(result['data_backed_claimed']) + json.dumps(result, allow_nan=False) + + def test_reversal_zero_is_failure_but_legacy_style_inequality_is_true(self): + rows = reversal_rows(self.reg) + for row in rows: + row['reversal']['states'] = states(C, 1, neutral_strength=1); rebind(row['reversal']) + result = self.evaluate('reversal', rows) + self.assertFalse(result['predicate_pass']) + self.assertTrue(all(row['legacy_style_sign_inequality_on_registered_S'] for row in result['replicates'])) + + def test_reversal_old_cue_only_and_mbon_only_cannot_satisfy_new_downstream_gate(self): + for only_old_cue in (True, False): + rows = reversal_rows(self.reg) + for row in rows: + if only_old_cue: + row['reversal']['states'] = states(C, -1) + else: + row['reversal']['states'][N] = state(2, downstream=False) + rebind(row['reversal']) + result = self.evaluate('reversal', rows) + self.assertFalse(result['predicate_pass']) + if not only_old_cue: + change = result['replicates'][0]['acquisition_to_reversal_changes_by_cue'][N]['populations'] + self.assertTrue(change['legacyDownstream']['events_changed']) + self.assertFalse(change['nonMBONDownstream']['events_changed']) + + def test_reversal_same_replicate_conjunction_and_exact_three(self): + rows = reversal_rows(self.reg) + rows[2]['no_reversal']['states'] = states(C, -1); rebind(rows[2]['no_reversal']) + rows[3]['reversal']['states'] = states(C, 1, neutral_strength=1); rebind(rows[3]['reversal']) + rows[4]['reversal']['states'][N] = state(2, downstream=False); rebind(rows[4]['reversal']) + result = self.evaluate('reversal', rows) + self.assertEqual(result['successful_same_replicate_conjunctions'], 2) + self.assertFalse(result['predicate_pass']) + rows[2] = reversal_rows(self.reg)[2] + self.assertTrue(self.evaluate('reversal', rows)['predicate_pass']) + + def test_retention_same_replicate_at_both_times_not_separate_time_majorities(self): + rows = retention_rows(self.reg) + for index, times in ((2, ('10',)), (3, ('1',)), (4, ('1', '10'))): + for second in times: + p = rows[index]['checkpoints'][second]['trained'] + p['states'] = states(); rebind(p) + result = self.evaluate('retention', rows) + self.assertEqual(result['successful_same_replicate_conjunctions'], 2) + self.assertFalse(result['predicate_pass']) + rows[2] = retention_rows(self.reg)[2] + self.assertTrue(self.evaluate('retention', rows)['predicate_pass']) + + def test_retention_frozen_equality_opposite_sign_and_no_nonmbon_change_fail(self): + for variant in ('equal_frozen', 'opposite', 'mbon_only'): + rows = retention_rows(self.reg) + for row in rows: + for second in ('1', '10'): + pair_value = row['checkpoints'][second]['trained'] + if variant == 'equal_frozen': + pair_value = row['checkpoints'][second]['frozen']; pair_value['states'] = states(C, 1) + elif variant == 'opposite': pair_value['states'] = states(C, -1) + else: pair_value['states'] = states(C, 1, downstream=False) + rebind(pair_value) + self.assertFalse(self.evaluate('retention', rows)['predicate_pass']) + + def test_retention_chain_full_state_clock_and_probe_policy_mutations_rejected(self): + for variant in ('clock', 'delta', 'parent', 'queue', 'reset', 'analytic', 'prefix', 'missing_time', 'input', 'feedback', 'newUS', 'short_probe', 'fresh_probe', 'raw_state'): + rows = retention_rows(self.reg) + execution_value = rows[0]['execution']['trained'] + cp = execution_value['checkpoints']['10'] + p = rows[0]['checkpoints']['10']['trained'] + if variant == 'clock': cp['absolute_tick'] += 1 + if variant == 'delta': cp['delta_sha256'] = sha('changed') + if variant == 'parent': cp['parent_checkpoint_sha256'] = execution_value['source_checkpoint_sha256'] + if variant == 'queue': cp['carry_sha256'].pop('apl_pending') + if variant == 'reset': execution_value['reset_state'] = True + if variant == 'analytic': execution_value['analytic_time_advance'] = True + if variant == 'prefix': execution_value['one_second_is_prefix_of_ten_second'] = False + if variant == 'missing_time': rows[0]['checkpoints'].pop('0') + if variant == 'input': p['evaluation_input_sha256'][C] = sha('other') + if variant == 'feedback': p['feedback_to_training_or_idle'] = True + if variant == 'newUS': p['US_requested'] = True + if variant == 'short_probe': p['probe_steps'] = 1000 + if variant == 'fresh_probe': p['state_policy'] = 'common_frozen_reset' + if variant == 'raw_state': p['states'][C]['v'][0] += .001 + with self.subTest(variant=variant), self.assertRaises(ValueError): self.evaluate('retention', rows) + + def test_reversal_delta_lineage_terminal_inventory_and_receipt_mutations_rejected(self): + for variant in ('resetdelta', 'early', 'epochs', 'cue', 'receipt', 'snapshot_receipt', 'probeAB', 'evaldraw', 'wrongcell', 'trainstream'): + rows = reversal_rows(self.reg) + e = rows[0]['execution']['reversal'] + if variant == 'resetdelta': e['initial_delta_sha256'] = sha('zero') + if variant == 'early': e['terminal_pairings'] = 20 + if variant == 'epochs': e['completed_epochs'] = 99 + if variant == 'cue': e['reinforced_cue'] = C + if variant == 'receipt': e['receipt_sha256'] = rows[0]['execution']['acquisition']['receipt_sha256'] + if variant == 'snapshot_receipt': rows[0]['reversal']['receipt_sha256'] = rows[0]['acquisition']['receipt_sha256'] + if variant == 'probeAB': rows[0]['reversal']['probe_receipt_sha256'][N] = rows[0]['reversal']['probe_receipt_sha256'][C] + if variant == 'evaldraw': rows[0]['replicate_unit'] = 'evaluation' + if variant == 'wrongcell': rows[1]['replicate_id'] = rows[0]['replicate_id'] + if variant == 'trainstream': rows[0]['training_stream_ids'] = rows[1]['training_stream_ids'] + with self.subTest(variant=variant), self.assertRaises(ValueError): self.evaluate('reversal', rows) + + def test_acquisition_controls_and_frozen_gate_are_preserved(self): + for variant in ('missing_control', 'delayed_timing', 'metric_mutation', 'population'): + rows = acquisition_rows(self.reg, assay='delayed') + if variant == 'missing_control': rows[0]['arms'].pop('DAN-only') + if variant == 'delayed_timing': rows[0]['arms']['frozen']['execution']['us_tick'] = 900 + if variant == 'metric_mutation': rows[0]['arms']['paired']['metrics']['rawS'] = 999. + if variant == 'population': rows[0]['arms']['paired']['metrics']['cues'][C]['nodes_raw_sha256'] = sha('other') + with self.subTest(variant=variant), self.assertRaises(ValueError): self.evaluate('delayed', rows) + + def normal_reports(self): + return [self.evaluate('normal', acquisition_rows(self.reg, mode, dan, cue), mode=mode, dan=dan, cue=cue) + for mode in g.M.MODES for dan in g.M.DAN_TYPES for cue in g.M.CUES] + + def test_cohort_exact_eight_cells_missing_not_run_and_mutation_rejected(self): + reports = self.normal_reports() + result = g.summarize_cohort(reports, registry=self.reg, authority=self.auth, required_assays=('normal',)) + self.assertTrue(result['all_functional_predicates_pass']) + self.assertFalse(result['legacy_H_and_integrity_gates_included']) + missing = g.summarize_cohort(reports[:-1], registry=self.reg, authority=self.auth, required_assays=('normal',)) + self.assertIsNone(missing['all_functional_predicates_pass']) + self.assertEqual(sum(x['status'] == 'NOT-RUN' for x in missing['table']), 1) + for variant in ('duplicate', 'predicate', 'conjunction', 'controlgain', 'unsupportedclaim'): + changed = copy.deepcopy(reports) + if variant == 'duplicate': changed[-1] = changed[0] + if variant == 'predicate': changed[0]['predicate_pass'] = False + if variant == 'conjunction': changed[0]['replicates'][0]['conjunction_pass'] = False + if variant == 'controlgain': next(iter(changed[0]['legacy_control_MBON_gains'].values()))[changed[0]['replicates'][0]['replicate_id']] = 900. + if variant == 'unsupportedclaim': changed[0]['scientific_learning_pass_claimed'] = True + with self.subTest(variant=variant), self.assertRaises(ValueError): + g.summarize_cohort(changed, registry=self.reg, authority=self.auth, required_assays=('normal',)) + + def test_legacy_H_mapping_absent_and_both_DAN_control_sources_explicit(self): + reports = self.normal_reports() + self.assertEqual(g.legacy_h_cohort_audit(reports, registry=self.reg, authority=self.auth, declared_mapping=None)['status'], 'NOT-TESTABLE') + good = g.legacy_h_cohort_audit(reports, registry=self.reg, authority=self.auth, declared_mapping=g.H_MAPPING) + self.assertEqual(len(good['groups']), 4) + self.assertTrue(good['both_pass']) + rows = acquisition_rows(self.reg, dan='PPL103') + for row in rows: + row['arms']['random reward']['metrics'] = g.M.arm_metrics(states(), states(C, -2), conditioned_cue=C, nodes=NODES, populations=POPS) + replacement = self.evaluate('normal', rows, dan='PPL103') + reports = [replacement if (x['mode'], x['dan_type'], x['conditioned_cue']) == ('physiological', 'PPL103', C) else x for x in reports] + result = g.legacy_h_cohort_audit(reports, registry=self.reg, authority=self.auth, declared_mapping=g.H_MAPPING) + group = next(x for x in result['groups'] if x['mode'] == 'physiological' and x['conditioned_cue'] == C) + self.assertTrue(group['control_sources']['PAM15']['predicate_pass']) + self.assertFalse(group['control_sources']['PPL103']['predicate_pass']) + self.assertFalse(result['both_pass']) + + def test_full_cohort_recomputes_followup_predicates_and_keeps_assay_majorities_separate(self): + reports = [] + for mode in g.M.MODES: + for dan in g.M.DAN_TYPES: + for cue in g.M.CUES: + for assay, factory in (('normal', acquisition_rows), ('delayed', acquisition_rows), + ('reversal', reversal_rows), ('retention', retention_rows)): + rows = factory(self.reg, mode, dan, cue, **({'assay': assay} if assay in ('normal', 'delayed') else {})) + reports.append(self.evaluate(assay, rows, mode=mode, dan=dan, cue=cue)) + result = g.summarize_cohort(reports, registry=self.reg, authority=self.auth, required_assays=g.ASSAYS) + self.assertEqual(len(result['table']), 32) + self.assertTrue(result['all_functional_predicates_pass']) + for variant in ('reversal_sign', 'reversal_predicate', 'retention_sign', 'retention_predicate', 'retention_time0'): + changed = copy.deepcopy(reports) + rev = next(x for x in changed if x['assay'] == 'reversal')['replicates'][0] + ret = next(x for x in changed if x['assay'] == 'retention')['replicates'][0] + if variant == 'reversal_sign': rev['rawS']['reversal'] = 999. + if variant == 'reversal_predicate': rev['predicates']['strict_three_signed_relations'] = False + if variant == 'retention_sign': ret['times']['10']['rawS_trained'] = 999. + if variant == 'retention_predicate': ret['times']['10']['predicates']['beyond_time_matched_frozen'] = False + if variant == 'retention_time0': ret['times']['0']['contributes_to_gate'] = True + with self.subTest(variant=variant), self.assertRaises(ValueError): + g.summarize_cohort(changed, registry=self.reg, authority=self.auth, required_assays=g.ASSAYS) + + def test_frozen_retention_source_cannot_claim_updated_delta(self): + rows = retention_rows(self.reg) + rows[0]['execution']['frozen']['source_execution']['initial_delta_sha256'] = sha('another-zero') + with self.assertRaisesRegex(ValueError, 'Frozen training'): + self.evaluate('retention', rows) + + def test_inputs_not_mutated_and_output_does_not_alias_inputs(self): + rows = reversal_rows(self.reg) + before = copy.deepcopy(rows) + result = self.evaluate('reversal', rows) + for old, new in zip(before, rows): + for snapshot in ('pre', 'acquisition', 'reversal', 'no_reversal'): + for cue in g.M.CUES: + for key in g.M.STATE_KEYS: + np.testing.assert_array_equal(old[snapshot]['states'][cue][key], new[snapshot]['states'][cue][key]) + result['replicates'][0]['metrics']['acquisition']['rawS'] = 888. + self.assertEqual(self.evaluate('reversal', rows)['replicates'][0]['rawS']['acquisition'], 1.) + + +if __name__ == '__main__': + unittest.main() diff --git a/models/male-cns-lif/conditioning-execution-v1/test_prepare_execution.py b/models/male-cns-lif/conditioning-execution-v1/test_prepare_execution.py new file mode 100644 index 0000000..f77c353 --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/test_prepare_execution.py @@ -0,0 +1,129 @@ +"""Pure temporary preparation integration; all old context/Git/biology mocked. + +No historical files, sampling, brain, source-module execution or GPU is used. +Real plan/anatomy/complete-source authentication belongs to separate validation. +""" +import copy +import json +from pathlib import Path +import sys +import tempfile +from types import SimpleNamespace +import unittest +from unittest.mock import Mock,patch + +import numpy as np + +import prepare_execution as P + + +def save(path,value): + path.parent.mkdir(parents=True,exist_ok=True);path.write_text(json.dumps(value),encoding='utf-8') + + +class PrepareFixture: + def __init__(self,root): + self.root=root;self.model=root/'models/conditioning-execution-v1';self.model.mkdir(parents=True) + (self.model/'synthetic.py').write_text('# synthetic source only\n') + self.exp=root/'experiments/phase4aq-fixture';self.exp.mkdir(parents=True) + self.ap=root/'experiments/phase4ap-fixture';self.ap.mkdir() + self.ao=root/'experiments/phase4ao-fixture';self.ao.mkdir() + self.checkpoint=self.ap/'runs/D1--reference--epoch-001';self.checkpoint.mkdir(parents=True) + save(self.checkpoint/'checkpoint-manifest.json',{'synthetic':True}) + self.before=root/'before.npz';self.bm=root/'before.json' + np.savez_compressed(self.before,edge_id=np.array([0],np.int64),plastic_delta_mv=np.array([0.],np.float32));save(self.bm,{'synthetic':'before'}) + self.mapping=root/'mapping.npz' + np.savez_compressed(self.mapping,edge_id=np.array([0],np.int64),source_neuron_idx=np.array([1],np.int32), + target_neuron_idx=np.array([2],np.int32),target_compartment_verbatim=np.array(["y5B'2a"])) + self.authority=self.ap/'jobs/D1--reference--epoch-001.authority.json' + save(self.authority,dict(checkpoint_dir=str(self.checkpoint),checkpoint_manifest_sha256=P.digest(self.checkpoint/'checkpoint-manifest.json'), + files={'candidate_map':P.A.pin(self.mapping),'overlay_before':P.A.pin(self.before),'overlay_manifest_before':P.A.pin(self.bm)})) + update=self.ap/'updates/D1--reference--epoch-001';update.mkdir(parents=True) + for name in ('chain-receipt.json','update-receipt.json','delta-after.npz','overlay-manifest.json'):(update/name).write_bytes(b'synthetic') + save(self.ap/'cases/D1--reference--epoch-001/spec.json',{'synthetic':'pinned-template'}) + static=self.ao/'inputs/static';static.mkdir(parents=True) + self.populations=static/'populations.npz' + np.savez_compressed(self.populations,PAM15=np.array([3,4,5,6],np.int32),Kenyon_Cell=np.array([1],np.int32)) + self.tables={} + for cue in ('A','B'): + self.tables[cue]={'targets':np.array([10,11],np.int32),'rates_hz':np.array([1.,2.],np.float32)} + np.savez_compressed(static/(cue+'-rates.npz'),**self.tables[cue]) + self.fullplan=self.ao/'protocol/full-trial-plan.json';save(self.fullplan,{'synthetic':'not sampled'}) + pins={str(p):P.digest(p) for p in (self.before,self.bm,self.mapping,self.populations,self.fullplan)} + self.reg=dict(input_sha256=pins,source_sha256={},prepared_file_sha256={}, + initial_overlay={'overlay_file':str(self.before),'overlay_manifest':str(self.bm)}) + self.plan=dict(imported_parents={'D1':dict(role='AP.D1.reference.epoch001',checkpoint_manifest=str(self.checkpoint/'checkpoint-manifest.json'), + sha256=P.digest(self.checkpoint/'checkpoint-manifest.json'))},initial_overlay={'overlay_file':P.A.pin(self.before),'overlay_manifest':P.A.pin(self.bm)}, + calls=[{'call_id':'ref','case_group':'shared'},{'call_id':'light','case_group':'shared'}]) + self.old=SimpleNamespace(C=SimpleNamespace(load_checkpoint=Mock(return_value={'fresh':True,'steps':1200})), + helper=lambda:SimpleNamespace(verify_seal=Mock(return_value={'synthetic_verified':True})), + previous_integrity=Mock(return_value={'synthetic_old_checksum':True})) + self.inputs=SimpleNamespace(load_rate_tables=Mock(return_value={'tables':copy.deepcopy(self.tables),'audit':{'synthetic_no_sampler':True}})) + self.mechanical=Mock(return_value=({'tick':np.array([1199],np.int32),'neuron_idx':np.array([1],np.int32)}, {'synthetic':True})) + self.planobj=SimpleNamespace(build_plan=lambda:copy.deepcopy(self.plan),validate_plan=Mock(return_value=True), + derive_case_spec=lambda template,call_id,**kw:{'steps':1000,'case_group':'shared'},mechanical_requests=self.mechanical) + + def git(self,command): + if command[-2:]==['rev-parse','HEAD']:return b'a'*40+b'\n' + if command[-2]=='show':return (self.root/command[-1].split(':',1)[1]).read_bytes() + raise AssertionError('Unexpected external command') + + def run(self,git=None): + with patch.object(P,'ROOT',self.root),patch.object(P,'MODEL',self.model),patch.object(P,'EXP',self.exp), \ + patch.object(P,'AP',self.ap),patch.object(P,'AO',self.ao),patch.object(P,'PLAN',self.planobj), \ + patch.object(P,'old_context',return_value=(self.old,self.reg,{})), \ + patch.object(P.subprocess,'check_output',side_effect=self.git if git is None else git), \ + patch.object(P.shutil,'disk_usage',return_value=SimpleNamespace(free=100*1024**3)), \ + patch.dict(sys.modules,{'conditioning_preflight_inputs':self.inputs}): + return P.prepare() + + +class PreparationTests(unittest.TestCase): + def setUp(self): + self.tmp=tempfile.TemporaryDirectory();self.f=PrepareFixture(Path(self.tmp.name).resolve()) + def tearDown(self):self.tmp.cleanup() + + def test_exclusive_registration_source_snapshot_and_shared_case(self): + sha=self.f.run();reg=P.read(self.f.exp/'protocol/preregistered.json') + self.assertEqual(sha,P.digest(self.f.exp/'protocol/preregistered.json')) + self.assertEqual(set(reg['case_templates']),{'shared'}) + self.assertEqual((self.f.exp/'protocol/source-snapshot/synthetic.py').read_bytes(),(self.f.model/'synthetic.py').read_bytes()) + self.assertFalse(reg['brain_or_sampler_run_during_preparation']);self.assertFalse(reg['future_hashes_fabricated']) + self.assertFalse(reg['scientific_acquisition_authorized']);self.assertFalse(reg['learning_success_claimed']) + self.f.inputs.load_rate_tables.assert_called_once_with(self.f.root) + self.f.old.C.load_checkpoint.assert_called_once_with(self.f.checkpoint,self.f.plan['imported_parents']['D1']['sha256']) + with self.assertRaisesRegex(ValueError,'empty experiment'):self.f.run() + + def test_uncommitted_source_blob_rejected_before_new_outputs(self): + def mismatch(command):return b'a'*40+b'\n' if command[-2:]==['rev-parse','HEAD'] else b'wrong committed source' + with self.assertRaisesRegex(ValueError,'committed blob'):self.f.run(git=mismatch) + self.assertEqual(list(self.f.exp.iterdir()),[]) + + def test_shared_template_must_be_fully_equal(self): + self.f.planobj.derive_case_spec=lambda template,call_id,**kw:{'steps':1000,'wrong_mode_leak':call_id} + with self.assertRaisesRegex(ValueError,'Shared comparison case templates differ'):self.f.run() + self.assertFalse((self.f.exp/'protocol/preregistered.json').exists()) + + def test_pinned_rate_arrays_need_dtype_and_bits_not_json_similarity(self): + self.f.inputs.load_rate_tables.return_value['tables']['A']['rates_hz']=self.f.tables['A']['rates_hz'].astype(np.float64) + with self.assertRaisesRegex(ValueError,'PN mapping differs'):self.f.run() + self.assertFalse((self.f.exp/'protocol/preregistered.json').exists()) + + def test_candidate_source_outside_KC_is_not_silently_selected(self): + np.savez_compressed(self.f.populations,PAM15=np.array([3,4,5,6],np.int32),Kenyon_Cell=np.array([99],np.int32)) + self.f.reg['input_sha256'][str(self.f.populations)]=P.digest(self.f.populations) + with self.assertRaisesRegex(ValueError,'outside original KC'):self.f.run() + + def test_imported_parent_authority_must_match_selected_checkpoint(self): + value=P.read(self.f.authority);value['checkpoint_manifest_sha256']='f'*64;save(self.f.authority,value) + with self.assertRaisesRegex(ValueError,'selected parent'):self.f.run() + self.assertFalse((self.f.exp/'protocol/preregistered.json').exists()) + + def test_no_bytecode_or_nested_source_dirs_in_inventory(self): + with patch.object(P,'MODEL',self.f.model): + self.assertEqual(len(P.source_inventory()),1) + (self.f.model/'__pycache__').mkdir() + with self.assertRaisesRegex(ValueError,'source files'):P.source_inventory() + + +if __name__=='__main__':unittest.main() diff --git a/models/male-cns-lif/conditioning-execution-v1/test_updated_continuation.py b/models/male-cns-lif/conditioning-execution-v1/test_updated_continuation.py new file mode 100644 index 0000000..5c36bbf --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/test_updated_continuation.py @@ -0,0 +1,573 @@ +"""Synthetic CPU admission/state/AST tests only. No GPU or scientific input.""" +import ast +import copy +import json +from pathlib import Path +import tempfile +from types import SimpleNamespace, ModuleType +import unittest +from unittest.mock import patch + +import numpy as np + +import updated_continuation as m + + +ROOT = Path(__file__).resolve().parent.parent +AJ = ROOT / 'apl-timing-lab-v1/gpu_timing.py' + + +def save(path, value): + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(value, sort_keys=True, allow_nan=False), encoding='utf-8') + + +def pin(path): + return dict(path=str(path), sha256=m._digest(path)) + + +class ArrayTarget: + def __init__(self, a): + self.a = np.array(a, copy=True) + + def copy_(self, a): + self.a[...] = a + + def any(self): + return np.asarray(self.a.any()) + + def detach(self): + return self + + def cpu(self): + return self + + def numpy(self): + return self.a + + +def tiny_parent(): + parent = dict(global_next_tick=41, state=dict(v=np.array([-.051, -.047], np.float64), + g=np.array([-0., .00013], np.float64), ref=np.array([22, 3], np.int32)), + terminal=dict(spike_ring=np.zeros((19, 2), np.uint8), next_tick=np.asarray(5, np.int64), + apl_pending=np.arange(36, dtype=np.float32).reshape(18, 2), apl_state=np.array([.5, 1.], np.float32))) + parent['terminal']['spike_ring'][4, 1] = 1 + return parent + + +class RuntimeTests(unittest.TestCase): + def runtime(self): + obj = object.__new__(m.UpdatedRuntime) + obj.parent, obj.installations, obj.restorations = tiny_parent(), 1, 0 + obj.authority = {'value': {'expected_effective_weight_after_sha256': 'a'*64}} + obj.torch = SimpleNamespace(float64=np.float64, float32=np.float32, int32=np.int32, uint8=np.uint8) + return obj + + def targets(self): + state = [ArrayTarget(np.zeros(2, dtype)) for dtype in (np.float64, np.float64, np.int32, np.int32)] + return [*state, ArrayTarget(np.zeros((19, 2), np.uint8)), ArrayTarget(np.zeros((18, 2), np.float32)), + SimpleNamespace(state=ArrayTarget(np.zeros(2, np.float32))), lambda a, dtype: np.array(a, dtype=dtype)] + + def test_restore_all_bytes_and_no_retroactive_g_rescale(self): + obj, args = self.runtime(), self.targets() + before = {name:a.tobytes() for name,a in obj.parent['state'].items()} + obj.restore(*args) + self.assertEqual(args[1].a.tobytes(), before['g']) + self.assertEqual(args[0].a.tobytes(), before['v']) + self.assertEqual(args[2].a.tobytes(), before['ref']) + self.assertFalse(args[3].a.any()) + self.assertTrue(m._same(args[4].a, m.C.rotate_spike_ring(obj.parent['terminal']['spike_ring'], 5))) + self.assertTrue(m._same(args[5].a, obj.parent['terminal']['apl_pending'])) + self.assertTrue(m._same(args[6].state.a, obj.parent['terminal']['apl_state'])) + self.assertFalse(obj.restore_audit['existing_g_rescaled']) + self.assertTrue(obj.restore_audit['pending_spike_arrivals_use_current_installed_weight']) + self.assertEqual(before, {name:a.tobytes() for name,a in obj.parent['state'].items()}) + with self.assertRaisesRegex(ValueError, 'once'): + obj.restore(*args) + + def test_restore_rejects_nonzero_counts_or_before_installation(self): + obj, args = self.runtime(), self.targets() + obj.installations = 0 + with self.assertRaises(ValueError): obj.restore(*args) + obj.installations = 1 + args[3].a[0] = 1 + with self.assertRaisesRegex(ValueError, 'counts'): obj.restore(*args) + + def test_copy_corruption_is_not_hidden_by_receipt(self): + obj, args = self.runtime(), self.targets() + args[1].copy_ = lambda value: args[1].a.fill(0.) + with self.assertRaisesRegex(ValueError, 'state bytes: g'): obj.restore(*args) + + def test_authorized_new_hash_not_parent_old_hash_and_no_double_install(self): + obj = self.runtime(); obj.installations = 0 + def synthetic_original_install(target, *args, **kwargs): + target.installations += 1 + target.effective_sha = 'a'*64 + obj.parent['summary'] = {'effective_weights_raw_sha256':'b'*64} + with patch.object(m.R._Runtime, 'install', synthetic_original_install): + obj.install(None) + self.assertEqual(obj.installations, 1) + with self.assertRaisesRegex(ValueError, 'Double'): obj.install(None) + obj.installations = 0 + obj.authority['value']['expected_effective_weight_after_sha256'] = 'c'*64 + with patch.object(m.R._Runtime, 'install', synthetic_original_install): + with self.assertRaisesRegex(ValueError, 'authorized after'): obj.install(None) + + def test_actual_source_AST_keeps_only_frozen_restore_and_admission(self): + for mode in ('reference', 'light'): + aj = ModuleType('synthetic_aj'); aj.__file__ = str(AJ) + old = dict(aj.__dict__) + runtime = SimpleNamespace(write_json=lambda *_:None, audit_sequence=lambda *_:None) + function, audit = m.compile_updated(aj, runtime, mode) + self.assertTrue(audit['full_AST_rollback_identical']) + self.assertTrue(audit['kernels_sorting_and_local_ring_math_unchanged']) + self.assertEqual(old, aj.__dict__) + self.assertIs(function.__globals__['_runtime'], runtime) + self.assertIs(function.__globals__['_continuation'], runtime) + self.assertEqual(function.__globals__['__file__'], str(Path(m.__file__).resolve())) + + def test_pending_source_uses_new_weight_and_old_APL_sample(self): + # Independent numerical toy: a source emitted before boundary arrives + # afterwards; no captured edge weight is stored in the source ring. + for cut in (5, 17, 18, 19, 20, 41): + source_tick = cut - 1 + ring = np.zeros((19, 2), np.uint8); ring[source_tick % 19, 0] = 1 + child = m.C.rotate_spike_ring(ring, cut) + g = np.float64(.00013) + delta = np.float32(-.00275) + base = np.float64(.000275) + after = np.add(base, np.multiply(np.float64(delta), np.float64(.001))) + arrived = [] + for tick in range(19): + child[tick % 19] = 0 + if cut+tick >= 18 and child[(tick-18) % 19, 0]: + g = np.add(g, after); arrived.append(tick) + self.assertEqual(arrived, [17]) + self.assertEqual(g.tobytes(), np.add(np.float64(.00013), after).tobytes()) + self.assertNotEqual(g.tobytes(), np.add(np.float64(.00013), base).tobytes()) + initial = np.arange(36, dtype=np.float32).reshape(18, 2) + generated = np.ones((5, 2), np.float32) + joined = np.concatenate((initial, generated)) + self.assertTrue(m.C.audit_carried_sequence(generated, joined[:5], joined[5:], initial)['pass_all']) + source = (ROOT/'author-runtime-lab-v1/author_ext.cu').read_text() + self.assertIn('weights[edges[j]]', source) + + +class AdmissionFixture: + """Private tiny input scaffolding. Actual production loaders are mocked.""" + def __init__(self, root): + self.root=root; self.case=root/'case'; self.case.mkdir() + self.parentdir=root/'parent'; self.parentdir.mkdir() + self.graph=root/'graph'; self.graph.mkdir(); self.apl=root/'apl'; self.apl.mkdir() + self.coeff=root/'coeff.json'; save(self.coeff, {}) + for name in m.GRAPH_FILES: (self.graph/name).write_bytes(b'synthetic graph '+name.encode()) + for name in m.APL_FILES: (self.apl/name).write_bytes(b'synthetic apl '+name.encode()) + self.before=root/'before.npz'; self.before.write_bytes(b'before snapshot') + self.bm=root/'before.json'; save(self.bm, {'synthetic':True}) + self.after=root/'after.npz'; self.after.write_bytes(b'after snapshot') + self.am=root/'after.json'; save(self.am, {'synthetic':False}) + (self.parentdir/'overlay-input.npz').write_bytes(self.before.read_bytes()) + (self.parentdir/'overlay-manifest.json').write_bytes(self.bm.read_bytes()) + save(self.parentdir/m.MANIFEST, {'synthetic':True}) + self.parentpin=m._digest(self.parentdir/m.MANIFEST) + self.spec=dict(steps=5,max_steps=5,dt_ms=.1,duration_ms=.5,apl_delay_steps=18, + record_history=False,within_trial_weight_updates=False,model={'fixed':1},apl={'fixed':2}, + graph_dir=str(self.graph),apl_bundle_dir=str(self.apl),coefficients_path=str(self.coeff), + parent_checkpoint_manifest_sha256=self.parentpin,global_tick_offset=41) + save(self.case/'spec.json',self.spec) + np.savez_compressed(self.case/'requests.npz',tick=np.empty(0,np.int32),neuron_idx=np.empty(0,np.int32)) + lab=ROOT/'olfactory-conditioning-lab-v1' + self.sources={name:lab/(name.removesuffix('_source')+'.py') for name in m.SOURCE_PINS} + self.sources['phase4j_source']=ROOT/'models/male-cns-lif/phase4j.py' + if not self.sources['phase4j_source'].exists(): self.sources['phase4j_source']=ROOT/'mb-physiology-v1/phase4j.py' + self.sources['am_overlay_source']=ROOT/'wholebrain-plastic-interface-v1/overlay.py' + self.sources['eligibility_bridge_source']=ROOT/'wholebrain-plastic-interface-v1/eligibility_bridge.py' + self.sources['updated_continuation_source']=Path(m.__file__).resolve() + self.sources['cpu_validator_source']=Path(m.__file__).resolve().parent/'validate_update_cpu.py' + frozen={str(p):m._digest(p) for p in [*(self.graph/n for n in m.GRAPH_FILES),*(self.apl/n for n in m.APL_FILES),self.coeff]} + frozen.update({str((ROOT/relative).resolve()):sha for relative,sha in m.R.PINS.items()}) + self.value=dict(schema=m.SCHEMA,boundary_id='synthetic',registration_sha256='a'*64, + transition='epoch_update',diagnostic_mode='light',input_policy='empty_idle', + case={'spec':pin(self.case/'spec.json'),'requests':pin(self.case/'requests.npz')}, + parent={'directory':str(self.parentdir),'checkpoint_manifest_sha256':self.parentpin},global_tick_offset=41, + overlay_before={'file':pin(self.before),'manifest':pin(self.bm)},overlay_after={'file':pin(self.after),'manifest':pin(self.am)}, + expected_effective_weight_before_sha256='b'*64,expected_effective_weight_after_sha256='c'*64, + update=None,cpu_validation=None,source_files={k:pin(p) for k,p in self.sources.items()},frozen_inputs=frozen) + self.path=root/'authority.json'; self.refresh() + self.plan={'edge_id':np.array([1],np.int64),'plastic_delta_mv':np.array([0],np.float32),'base_candidate_mv':np.array([.275],np.float32)} + self.meta=dict(mask_path=str(root/'mask.npy'),mask_sha256='m'*64,base_candidate_path=str(root/'base.npy'), + base_candidate_sha256='d'*64,aj_model_dir=str(ROOT/'apl-timing-lab-v1'), + am_overlay_path=str(self.sources['am_overlay_source']),am_overlay_sha256=m.R.AM_OVERLAY_SHA) + self.parent=dict(fresh=True,global_tick_offset=0,global_next_tick=41,steps=41,directory=self.parentdir, + manifest_sha256=self.parentpin,manifest={'schema':'phase4an-checkpoint-v1'}, + summary={'effective_weights_raw_sha256':'b'*64,'input_sha256':{'coefficients.json':m._digest(self.coeff),'base_weights.npy':m._digest(self.graph/'weights.npy')}}, + overlay={'delta_raw_sha256':m.R._raw(self.plan['plastic_delta_mv'])},ancestry_directories=[self.parentdir]) + self.helper=object() + + def refresh(self): save(self.path,self.value) + + def admit(self, effective=None): + with patch.object(m,'load_checkpoint',return_value=self.parent), patch.object(m.C,'_parent_spec',return_value=self.spec), \ + patch.object(m.R,'load_overlay',return_value=(self.helper,self.plan,self.meta,{})), \ + patch.object(m,'_expected_effective',side_effect=['b'*64,'c'*64] if effective is None else effective), \ + patch.object(m,'_epoch_update',return_value={'pass_all':True}) as ep: + result=m.validate_authority(self.path,m._digest(self.path)) + return result,ep.call_count + + +class AdmissionTests(unittest.TestCase): + def setUp(self): + self.tmp=tempfile.TemporaryDirectory(); self.f=AdmissionFixture(Path(self.tmp.name).resolve()) + def tearDown(self): self.tmp.cleanup() + + def test_all_actual_frozen_sources_and_return_contract(self): + result,calls=self.f.admit() + self.assertEqual(calls,1) + for key in ('value','authority','authority_sha256','paths','pins','parent','case_dir','spec','helper', + 'sources','before_plan','after_plan','before_meta','after_meta'): + self.assertIn(key,result) + self.assertEqual(result['update_root_authority_sha256'],m._digest(self.f.path)) + self.assertNotIn('torch',result) + + def test_wrong_absolute_clock_and_wrong_case_parent(self): + for field,new in (('global_tick_offset',40),('parent_checkpoint_manifest_sha256','f'*64)): + with self.subTest(field=field): + original=copy.deepcopy(self.f.spec) + self.f.spec[field]=new;save(self.f.case/'spec.json',self.f.spec) + self.f.value['case']['spec']=pin(self.f.case/'spec.json');self.f.refresh() + with self.assertRaisesRegex(ValueError,'Case parent/clock'):self.f.admit() + self.f.spec=original;save(self.f.case/'spec.json',original) + self.f.value['case']['spec']=pin(self.f.case/'spec.json');self.f.refresh() + self.f.value['global_tick_offset']=42;self.f.refresh() + with self.assertRaisesRegex(ValueError,'absolute continuation clock'):self.f.admit() + + def test_second_update_is_rejected(self): + self.f.parent['fresh']=False + with self.assertRaisesRegex(ValueError,'only once'):self.f.admit() + + def test_authority_hash_and_actual_before_copy_not_self_asserted(self): + with self.assertRaisesRegex(ValueError,'changed'): + m.validate_authority(self.f.path,'e'*64) + (self.f.parentdir/'overlay-input.npz').write_bytes(b'wrong bytes') + with self.assertRaisesRegex(ValueError,'Before snapshot'):self.f.admit() + + def test_empty_idle_rejects_requests_probe_does_not_claim_semantics(self): + np.savez_compressed(self.f.case/'requests.npz',tick=np.array([0],np.int32),neuron_idx=np.array([1],np.int32)) + self.f.value['case']['requests']=pin(self.f.case/'requests.npz');self.f.refresh() + with self.assertRaisesRegex(ValueError,'empty_idle'):self.f.admit() + self.f.value['input_policy']='externally_pinned_CS_probe';self.f.refresh() + self.assertEqual(self.f.admit()[0]['value']['input_policy'],'externally_pinned_CS_probe') + + def test_extra_fields_or_source_replacement_fail(self): + self.f.value['trust_me']=True;self.f.refresh() + with self.assertRaisesRegex(ValueError,'schema'):self.f.admit() + del self.f.value['trust_me'] + self.f.value['source_files']['conditioning_update_source']['sha256']='f'*64;self.f.refresh() + with self.assertRaisesRegex(ValueError,'Frozen source'):self.f.admit() + + def test_missing_compiled_pin_not_accepted(self): + del self.f.value['frozen_inputs'][str((ROOT/'author-runtime-lab-v1/author_ext.cu').resolve())];self.f.refresh() + with self.assertRaisesRegex(ValueError,'dependency authority'):self.f.admit() + + def test_identical_CPU_validator_copy_is_not_the_actual_executor_path(self): + copied=self.f.root/'validator-copy.py' + copied.write_bytes(self.f.sources['cpu_validator_source'].read_bytes()) + self.f.value['source_files']['cpu_validator_source']=pin(copied);self.f.refresh() + with self.assertRaisesRegex(ValueError,'validator source path'):self.f.admit() + + def idle(self): + self.f.value.update(transition='idle',update=None,cpu_validation=None, + expected_effective_weight_after_sha256='b'*64,overlay_after=copy.deepcopy(self.f.value['overlay_before'])) + self.f.parent.update(fresh=False,manifest={'schema':m.CHECKPOINT_SCHEMA},input_policy='empty_idle', + update_root_authority_sha256='e'*64) + self.f.refresh() + + def test_idle_retains_original_update_root_no_E_or_cpu_recompute(self): + self.idle() + result,calls=self.f.admit(['b'*64,'b'*64]) + self.assertEqual(calls,0) + self.assertEqual(result['update_root_authority_sha256'],'e'*64) + self.assertIsNone(result['paths']['epoch_authority']) + self.assertIsNone(result['paths']['cpu_validation']) + + def test_probe_parent_cannot_flow_back_into_idle_or_another_probe(self): + self.idle();self.f.parent['input_policy']='externally_pinned_CS_probe' + for policy in ('empty_idle','externally_pinned_CS_probe'): + self.f.value['input_policy']=policy;self.f.refresh() + with self.assertRaisesRegex(ValueError,'probe branch'):self.f.admit(['b'*64,'b'*64]) + + def test_idle_cannot_authorize_a_second_snapshot_change(self): + self.idle() + self.f.value['overlay_after']={'file':pin(self.f.after),'manifest':pin(self.f.am)};self.f.refresh() + with self.assertRaisesRegex(ValueError,'Idle changed'):self.f.admit(['b'*64,'b'*64]) + self.idle();self.f.value['update']={};self.f.refresh() + with self.assertRaisesRegex(ValueError,'no new update'):self.f.admit(['b'*64,'b'*64]) + + def test_wrong_expected_weight_hash(self): + self.f.value['expected_effective_weight_after_sha256']='e'*64;self.f.refresh() + with self.assertRaisesRegex(ValueError,'After effective'):self.f.admit() + + def test_output_and_build_reject_entire_ancestry_before_Torch(self): + admitted,_=self.f.admit() + ancestor=self.f.root/'ancestor';ancestor.mkdir() + admitted['parent']['ancestry_directories'].append(ancestor) + for output,build in ((ancestor/'out',self.f.root/'build'),(self.f.root/'out',ancestor/'build')): + with patch.object(m,'validate_authority',return_value=admitted),patch.object(m.R,'_load') as loader: + with self.assertRaises(ValueError):m.run(self.f.path,m._digest(self.f.path),output,build) + loader.assert_not_called() + self.assertFalse(output.exists());self.assertFalse(build.exists()) + + def test_dtype_sorting_and_request_schema_reject(self): + for ticks,nodes in ((np.array([0],np.int64),np.array([1],np.int32)), + (np.array([1,0],np.int32),np.array([1,1],np.int32)), + (np.array([5],np.int32),np.array([1],np.int32))): + np.savez_compressed(self.f.case/'requests.npz',tick=ticks,neuron_idx=nodes) + with self.assertRaises(ValueError):m._requests(self.f.case/'requests.npz',self.f.spec,'externally_pinned_CS_probe') + + +class CheckpointDispatchTests(unittest.TestCase): + def test_old_checkpoint_is_honestly_dispatched_to_old_C(self): + with tempfile.TemporaryDirectory() as tmp: + root=Path(tmp);save(root/m.MANIFEST,{'schema':'phase4an-checkpoint-v1'}) + with patch.object(m.C,'load_checkpoint',return_value={'fresh':True}) as old: + self.assertEqual(m.load_checkpoint(root,m._digest(root/m.MANIFEST)),{'fresh':True}) + old.assert_called_once() + + def test_new_checkpoint_cannot_copy_old_provenance(self): + with tempfile.TemporaryDirectory() as tmp: + root=Path(tmp) + save(root/'runtime-provenance.json',{'schema':'phase4an-continuation-runtime-v1','complete':True}) + with self.assertRaisesRegex(ValueError,'genuine new'):m._record(root,{root}) + + def test_new_checkpoint_cycle_and_wrong_external_pin(self): + with tempfile.TemporaryDirectory() as tmp: + root=Path(tmp).resolve();save(root/m.MANIFEST,{'schema':m.CHECKPOINT_SCHEMA}) + with self.assertRaisesRegex(ValueError,'External checkpoint'):m.load_checkpoint(root,'e'*64) + with self.assertRaisesRegex(ValueError,'cycle'):m.load_checkpoint(root,m._digest(root/m.MANIFEST),_seen={root}) + + +class NewCheckpointFixture: + def __init__(self, f): + self.f=f;self.admitted,_=f.admit();self.out=f.root/'new-output';self.out.mkdir() + self.parent=self.admitted['parent'];self.parent.update(tiny_parent()) + self.state=dict(step=np.array([0],np.int32),neuron_idx=np.array([1],np.int32),counts=np.array([0,1],np.int32), + v=np.array([-.05,-.052],np.float64),g=np.array([0.,-.003],np.float64),ref=np.array([0,18],np.int32)) + # Production APL dimensions, tiny canonical neural dimension only in tests. + self.parent['terminal']['apl_pending']=np.zeros((18,4064),np.float32) + self.parent['terminal']['apl_pending'][1,0]=np.float32(.5) + ring=m.C.rotate_spike_ring(self.parent['terminal']['spike_ring'],5) + for tick in range(5):ring[tick]=0 + ring[0,1]=1 + self.terminal=dict(spike_ring=ring,apl_pending=np.zeros((18,4064),np.float32), + apl_state=np.array([1.,2.],np.float32),next_tick=np.asarray(5,np.int64)) + np.savez_compressed(self.out/'state.npz',**self.state) + np.savez_compressed(self.out/'terminal-queues.npz',**self.terminal) + np.save(self.out/'apl-pending-tail.float32.npy',self.terminal['apl_pending']) + save(self.out/'apl-timing-audit.json',dict(pending_raw_sha256=m.R._raw(self.terminal['apl_pending']), + apl_delay_steps=18,brain_tail_advanced=False,final_apl_state_u32_bits=self.terminal['apl_state'].view(np.uint32).tolist(), + global_tick_offset=41,global_next_tick=46,initial_queue_raw_sha256=m.R._raw(self.parent['terminal']['apl_pending']))) + self.summary=dict(complete=True,pass_all=True,all_finite=True,steps=5,completed_ticks=5,nodes=2,edges=6, + precision='float64',apl_precision='float32',within_trial_weight_updates=False,checkpoint_continuation_used=True, + global_tick_offset=41,global_next_tick=46,explicit_steps_executed=5,max_steps=5,hidden_early_stop=False, + entire_neural_trial_executed=True,record_history=False,counts_scope='segment_only_not_lifetime',event_tick_scope='segment_local', + weight_update_at_boundary_authorized=True,frozen_same_weight_retention_claimed=False, + parent_checkpoint_manifest_sha256=self.parent['manifest_sha256'],diagnostic_mode='light', + boundary_authority_sha256=self.admitted['authority_sha256'],update_root_authority_sha256=self.admitted['update_root_authority_sha256'], + updated_boundary_schema=m.SCHEMA,boundary_transition='epoch_update',input_policy='empty_idle',effective_weights_raw_sha256='c'*64, + input_sha256={'spec.json':m._digest(f.case/'spec.json'),'requests.npz':m._digest(f.case/'requests.npz'), + 'base_weights.npy':m._digest(f.graph/'weights.npy'),'coefficients.json':m._digest(f.coeff)}) + save(self.out/'summary.json',self.summary) + save(self.out/'finite-audit.json',dict(all_finite=True,first_failure_tick=-1,finite_observed_ticks=5)) + save(self.out/'overlay-audit.json',dict(complete=True,weights_updated_during_run=False,effective_weights_raw_sha256='c'*64, + delta_raw_sha256=m.R._raw(f.plan['plastic_delta_mv']),mask_sha256=m.R.MASK_SHA,am_helper_sha256=m.R.AM_OVERLAY_SHA)) + (self.out/'overlay-input.npz').write_bytes(f.after.read_bytes()) + (self.out/'overlay-manifest.json').write_bytes(f.am.read_bytes()) + _,audit=m.C.derive_continued(AJ.read_bytes(),'light') + self.provenance=dict(schema=m.PROVENANCE_SCHEMA,complete=True,mode='light',source_file=str(Path(m.__file__).resolve()), + source_sha256=m._digest(m.__file__),authority_file=str(f.path),authority_sha256=self.admitted['authority_sha256'],ast=audit, + input_sha256=self.admitted['pins'],old_globals_patched=False,weights_updated_during_run=False,U_recomputed_here=False, + learning_pass_claimed=False,parent_checkpoint_manifest_sha256=f.parentpin,global_tick_offset=41,global_next_tick=46, + external_CPU_validation_receipt_bound=True,external_registration_process_and_probe_semantics_verified_here=False) + restored=dict(v=self.parent['state']['v'],g=self.parent['state']['g'],ref=self.parent['state']['ref'], + spike_ring=m.C.rotate_spike_ring(self.parent['terminal']['spike_ring'],5),apl_pending=self.parent['terminal']['apl_pending'], + apl_state=self.parent['terminal']['apl_state']) + save(self.out/'boundary-restore-audit.json',dict(restored_raw_sha256={k:m.R._raw(a) for k,a in restored.items()}, + all_state_and_queue_bytes_exact=True,existing_g_rescaled=False,retroactive_arrival_reweighting=False, + pending_spike_arrivals_use_current_installed_weight=True,pending_APL_samples_preserved=True, + boundary_installations=1,restorations=1,learning_pass_claimed=False)) + self.receipt=dict(schema='conditioning-updated-receipt-v1',complete=True,authority_file=str(f.path), + authority_sha256=self.admitted['authority_sha256'],parent_checkpoint_dir=str(f.parentdir),parent_checkpoint_manifest_sha256=f.parentpin, + global_tick_offset=41,global_next_tick=46,segment_steps=5,update_root_authority_sha256=self.admitted['update_root_authority_sha256'], + input_policy='empty_idle',weights_updated_during_run=False,counts_scope='segment_only_not_lifetime', + checkpoint_continuation_verified_by_GPU_comparison=False,learning_pass_claimed=False) + self.rebind() + + def rebind(self): + for key,name in (('summary_sha256','summary.json'),('finite_audit_sha256','finite-audit.json'), + ('terminal_queues_sha256','terminal-queues.npz'),('overlay_audit_sha256','overlay-audit.json'),('state_sha256','state.npz')): + self.provenance[key]=m._digest(self.out/name) + save(self.out/'runtime-provenance.json',self.provenance) + payload=m.C._tree_hashes(self.out);payload.pop(m.RECEIPT,None) + self.receipt['payload_sha256']=payload;save(self.out/m.RECEIPT,self.receipt) + + def record(self): + with patch.object(m,'validate_authority',return_value=self.admitted),patch.object(m.C,'N',2),patch.object(m.C,'E',6): + return m._record(self.out,{self.out}) + + +class NewCheckpointTests(unittest.TestCase): + def setUp(self): + self.tmp=tempfile.TemporaryDirectory();self.f=NewCheckpointFixture(AdmissionFixture(Path(self.tmp.name).resolve())) + def tearDown(self):self.tmp.cleanup() + + def test_full_payload_exclusive_seal_round_trip(self): + self.assertEqual(self.f.record()['global_next_tick'],46) + with patch.object(m,'validate_authority',return_value=self.f.admitted),patch.object(m.C,'N',2),patch.object(m.C,'E',6): + sealed=m.seal_checkpoint(self.f.out) + loaded=m.load_checkpoint(self.f.out,sealed['sha256']) + self.assertEqual(loaded['manifest']['schema'],m.CHECKPOINT_SCHEMA) + self.assertTrue(m._same(loaded['state']['g'],self.f.state['g'])) + self.assertFalse(loaded['manifest']['checkpoint_continuation_verified_by_GPU_comparison']) + with self.assertRaisesRegex(ValueError,'already sealed'):m.seal_checkpoint(self.f.out) + (self.f.out/'extra.json').write_text('{}') + with self.assertRaisesRegex(ValueError,'inventory'):m.load_checkpoint(self.f.out,sealed['sha256']) + + def test_rehashed_wrong_parent_and_clock_still_rejected(self): + self.f.provenance['parent_checkpoint_manifest_sha256']='f'*64;self.f.rebind() + with self.assertRaisesRegex(ValueError,'boundary/clock'):self.f.record() + self.f.provenance['parent_checkpoint_manifest_sha256']=self.f.f.parentpin + self.f.summary['global_next_tick']=47;save(self.f.out/'summary.json',self.f.summary);self.f.rebind() + with self.assertRaisesRegex(ValueError,'global_next_tick'):self.f.record() + + def test_rehashed_changed_unconsumed_queue_rejected(self): + self.f.terminal['spike_ring'][18,0]^=np.uint8(1) + np.savez_compressed(self.f.out/'terminal-queues.npz',**self.f.terminal);self.f.rebind() + with self.assertRaisesRegex(ValueError,'Unwritten carried'):self.f.record() + + def test_rehashed_restoration_g_receipt_not_trusted(self): + path=self.f.out/'boundary-restore-audit.json';value=m.R._read_json(path) + value['restored_raw_sha256']['g']='f'*64;save(path,value);self.f.rebind() + with self.assertRaisesRegex(ValueError,'Boundary restore'):self.f.record() + + def test_actual_state_dtype_rejected_after_rehash(self): + self.f.state['g']=self.f.state['g'].astype(np.float32) + np.savez_compressed(self.f.out/'state.npz',**self.f.state);self.f.rebind() + with self.assertRaisesRegex(ValueError,'parent g'):self.f.record() + + +class EpochChainFixture: + """Full candidate cardinality and actual metadata bindings; no U execution. + + Q is mocked explicitly: its frozen pure arithmetic has separate tests. + This fixture exercises E/CPU file chain and supplied full-array authority. + """ + def __init__(self,root): + self.f=AdmissionFixture(root);f=self.f + self.update=root/'postprocess';self.update.mkdir() + self.before={'edge_id':np.arange(14232,dtype=np.int64),'plastic_delta_mv':np.zeros(14232,np.float32), + 'base_candidate_mv':np.full(14232,.275,np.float32)} + self.after={k:a.copy() for k,a in self.before.items()};self.after['plastic_delta_mv'][0]=np.float32(-.00275) + kc=np.arange(4064,dtype=np.int32);source=(np.arange(14232)%4064).astype(np.int32);target=np.full(14232,4500,np.int32) + comp=np.full(14232,"y5B'2a") + np.save(f.apl/'kc_globals.npy',kc);np.save(root/'kc.npy',kc);np.save(root/'mask.npy',self.before['edge_id']) + np.save(root/'base.npy',self.before['base_candidate_mv']);np.save(f.graph/'src.npy',source);np.save(f.graph/'dst.npy',target) + np.savez_compressed(root/'map.npz',edge_id=self.before['edge_id'],source_neuron_idx=source,target_neuron_idx=target,target_compartment_verbatim=comp) + np.savez_compressed(f.before,edge_id=self.before['edge_id'],plastic_delta_mv=self.before['plastic_delta_mv']) + self.bm={**f.meta,'mask_sha256':m._digest(root/'mask.npy'),'base_candidate_sha256':m._digest(root/'base.npy')} + save(f.bm,self.bm) + self.afterfile=self.update/'delta-after.npz';np.savez_compressed(self.afterfile,edge_id=self.after['edge_id'],plastic_delta_mv=self.after['plastic_delta_mv']) + self.am={**self.bm,'parent_snapshot_sha256':m._digest(f.before)};save(self.update/'overlay-manifest.json',self.am) + self.state=dict(step=np.array([0],np.int32),neuron_idx=np.array([0],np.int32),counts=np.array([1],np.int32), + v=np.array([-.05]),g=np.array([.001]),ref=np.array([18],np.int32)) + np.savez_compressed(f.parentdir/'state.npz',**self.state) + self.parent=dict(fresh=True,global_tick_offset=0,steps=4,directory=f.parentdir,manifest_sha256=f.parentpin,state=self.state, + summary={'diagnostic_mode':'light','effective_weights_raw_sha256':'b'*64}, + manifest={'files_sha256':{'state.npz':m._digest(f.parentdir/'state.npz')}},provenance={'input_sha256':{}}) + self.epaths=dict(spec=f.case/'spec.json',requests=f.case/'requests.npz',overlay_before=f.before,overlay_manifest_before=f.bm, + state=f.parentdir/'state.npz',kc=root/'kc.npy',canonical_kc=f.apl/'kc_globals.npy',candidate_map=root/'map.npz', + mask=root/'mask.npy',base_candidate=root/'base.npy',**{key:f.sources[key] for key in m.E_SOURCE_ROLES}) + self.epath=root/'epoch-authority.json' + frozen={str(p):m._digest(p) for p in [*(f.graph/n for n in m.GRAPH_FILES),*(f.apl/n for n in m.APL_FILES),f.coeff]} + frozen.update({str((ROOT/relative).resolve()):sha for relative,sha in m.R.PINS.items()}) + command=dict(steps=4,compartment="y5B'2a",us_tick=3,d_command=1.,frozen=False,trace_backend='numpy') + self.evalue=dict(schema='phase4an-epoch-postprocess-authority-v1',epoch_id='synthetic-e0',diagnostic_mode='light',command=command, + checkpoint_dir=str(f.parentdir),checkpoint_manifest_sha256=f.parentpin,files={role:pin(path) for role,path in self.epaths.items()},frozen_inputs=frozen) + save(self.epath,self.evalue) + self.epins={str(self.epath):m._digest(self.epath),str(f.parentdir/m.MANIFEST):f.parentpin, + **{str(p):m._digest(p) for p in self.epaths.values()},**frozen} + self.parent['provenance']['input_sha256']={str(self.epaths[k]):m._digest(self.epaths[k]) for k in ('spec','requests','overlay_before','overlay_manifest_before')} + arrays=dict(step=self.state['step'],neuron_idx=self.state['neuron_idx'],kc=kc,candidate_edge_id=self.before['edge_id'], + candidate_sources=source,candidate_targets=target,candidate_compartments=comp,delta_before=self.before['plastic_delta_mv'],base_candidate=self.before['base_candidate_mv']) + self.receipt=dict(input_arrays={k:m._evidence(a) for k,a in arrays.items()},command=dict(steps=4,n_neurons=5000, + compartment="y5B'2a",us_tick=3,D_scheduled=1.,frozen=False,trace_backend='numpy'),payload_sha256='d'*64) + save(self.update/'update-receipt.json',self.receipt) + self.chain=dict(schema='phase4an-epoch-postprocess-chain-v1',complete=True,epoch_id='synthetic-e0',authority_file=str(self.epath), + authority_sha256=m._digest(self.epath),coordinator_source_sha256=m.SOURCE_PINS['conditioning_epoch_source'],command=command, + diagnostic_mode='light',source_neural_checkpoint_dir=str(f.parentdir),source_neural_checkpoint_manifest_sha256=f.parentpin, + before_overlay_file_sha256=m._digest(f.before),before_overlay_manifest_sha256=m._digest(f.bm), + after_overlay_file_sha256=m._digest(self.afterfile),after_overlay_manifest_sha256=m._digest(self.update/'overlay-manifest.json'), + source_state_file_sha256=m._digest(self.epaths['state']),source_spec_sha256=m._digest(self.epaths['spec']), + source_requests_sha256=m._digest(self.epaths['requests']),input_files_sha256=self.epins, + output_files_sha256={name:m._digest(self.update/name) for name in m.UPDATE_FILES if name!='chain-receipt.json'}, + runtime_delta_before_raw_sha256=m.R._raw(self.before['plastic_delta_mv']),runtime_effective_weights_raw_sha256='b'*64, + update_receipt_payload_sha256='d'*64,runtime_learned_manifest_loader_verified=True,input_and_checkpoint_bytes_reverified=True, + candidate_endpoints_equal_graph=True,KC_equals_supplied_canonical_membership=True,weights_installed=False, + brain_executed=False,learning_pass_claimed=False,candidate_generated=False) + save(self.update/'chain-receipt.json',self.chain) + self.paths=dict(before_overlay=f.before,before_manifest=f.bm,after_overlay=self.afterfile,after_manifest=self.update/'overlay-manifest.json') + self.value=dict(update={'epoch_authority':pin(self.epath),'files':{name:pin(self.update/name) for name in m.UPDATE_FILES}}) + self.cp=root/'cpu-validation.json' + self.cpu=dict(schema='conditioning-update-cpu-validation-v1',complete=True,epoch_authority_sha256=m._digest(self.epath), + parent_checkpoint_manifest_sha256=f.parentpin,update_files_sha256={name:m._digest(self.update/name) for name in m.UPDATE_FILES}, + before_overlay_sha256=m._digest(f.before),before_manifest_sha256=m._digest(f.bm),after_overlay_sha256=m._digest(self.afterfile), + after_manifest_sha256=m._digest(self.update/'overlay-manifest.json'),before_delta_raw_sha256=m.R._raw(self.before['plastic_delta_mv']), + after_delta_raw_sha256=m.R._raw(self.after['plastic_delta_mv']),update_receipt_payload_sha256='d'*64, + validator_source=pin(f.sources['cpu_validator_source']),frozen_U_source_sha256=m.SOURCE_PINS['conditioning_update_source'], + frozen_U_verify_receipt_recomputed=True,actual_sealed_events_used=True,full_candidate_mapping_verified=True, + brain_executed=False,learning_pass_claimed=False) + save(self.cp,self.cpu);self.value['cpu_validation']=pin(self.cp) + + def run(self): + pins={str(p):m._digest(p) for p in [*self.paths.values(),*self.f.sources.values()]} + with patch.object(m,'_Q',return_value=SimpleNamespace(audit_delta_binding=lambda *a,**k:{'pass_all':True})), \ + patch.object(m.C,'_parent_spec',return_value=self.f.spec),patch.object(m.C,'N',5000): + return m._epoch_update(self.value,self.paths,self.f.sources,pins,self.parent,self.before,self.after,self.bm,self.am) + + +class EpochChainTests(unittest.TestCase): + def setUp(self): + self.tmp=tempfile.TemporaryDirectory();self.f=EpochChainFixture(Path(self.tmp.name).resolve()) + def tearDown(self):self.tmp.cleanup() + + def test_full_four_files_actual_inputs_and_cpu_schema_bind(self): + self.assertTrue(self.f.run()['pass_all']) + + def test_chain_wrong_parent_rehashed_still_rejected(self): + self.f.chain['source_neural_checkpoint_manifest_sha256']='e'*64 + save(self.f.update/'chain-receipt.json',self.f.chain) + self.f.value['update']['files']['chain-receipt.json']=pin(self.f.update/'chain-receipt.json') + with self.assertRaisesRegex(ValueError,'Original E chain'):self.f.run() + + def test_cpu_no_recomputation_or_wrong_after_raw_rejected(self): + for key,value in (('frozen_U_verify_receipt_recomputed',False),('after_delta_raw_sha256','e'*64)): + original=copy.deepcopy(self.f.cpu);self.f.cpu[key]=value;save(self.f.cp,self.f.cpu) + self.f.value['cpu_validation']=pin(self.f.cp) + with self.assertRaisesRegex(ValueError,'CPU recomputation'):self.f.run() + self.f.cpu=original + + def test_update_four_file_hash_changed_or_extra_file(self): + (self.f.update/'extra.json').write_text('{}') + with self.assertRaisesRegex(ValueError,'four-file'):self.f.run() + + def test_actual_array_not_receipt_booleans_is_checked(self): + self.f.parent['state']['step'][0]=1 + with self.assertRaisesRegex(ValueError,'actual array binding'):self.f.run() + + def test_candidate_endpoint_change_rejected_even_with_same_shape(self): + mapping=m.C._npz(self.f.epaths['candidate_map'],('edge_id','source_neuron_idx','target_neuron_idx','target_compartment_verbatim')) + mapping['source_neuron_idx'][0]=22 + np.savez_compressed(self.f.epaths['candidate_map'],**mapping) + with self.assertRaisesRegex(ValueError,'changed'):self.f.run() + + +if __name__=='__main__':unittest.main() diff --git a/models/male-cns-lif/conditioning-execution-v1/test_validate_update_cpu.py b/models/male-cns-lif/conditioning-execution-v1/test_validate_update_cpu.py new file mode 100644 index 0000000..c59df40 --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/test_validate_update_cpu.py @@ -0,0 +1,68 @@ +"""CPU-only chain/mutation/real-source loading tests; no production events.""" +import copy +from pathlib import Path +import subprocess +import sys +import tempfile +import unittest + +import numpy as np +import validate_update_cpu as V + + +class ValidationTests(unittest.TestCase): + def fixture(self): + parent=Path('/synthetic/parent');auth={'epoch_id':'one','command':{'fixture':True}, + 'diagnostic_mode':'reference','checkpoint_manifest_sha256':'2'*64} + roles=('state','spec','requests','overlay_before','overlay_manifest_before') + paths={r:Path('/synthetic/'+r) for r in roles};pins={p:str(i+3)*64 for i,p in enumerate(paths.values())} + hashes={name:format(i+8,'x')*64 for i,name in enumerate(V.FILES)} + chain=dict(schema='phase4an-epoch-postprocess-chain-v1',complete=True, + weights_installed=False,brain_executed=False,learning_pass_claimed=False,candidate_generated=False, + coordinator_source_sha256=V.E_SHA,authority_sha256='1'*64,epoch_id='one',command=auth['command'], + diagnostic_mode='reference',source_neural_checkpoint_dir=str(parent), + source_neural_checkpoint_manifest_sha256='2'*64,input_files_sha256={str(p):v for p,v in pins.items()}, + output_files_sha256={n:s for n,s in hashes.items() if n!='chain-receipt.json'}, + after_overlay_file_sha256=hashes['delta-after.npz'],after_overlay_manifest_sha256=hashes['overlay-manifest.json']) + for field,role in (('source_state_file_sha256','state'),('source_spec_sha256','spec'), + ('source_requests_sha256','requests'),('before_overlay_file_sha256','overlay_before'), + ('before_overlay_manifest_sha256','overlay_manifest_before')):chain[field]=pins[paths[role]] + return chain,auth,'1'*64,pins,paths,parent,hashes + + def test_exact_chain(self):V.chain_binding(*self.fixture()) + + def test_wrong_parent_command_or_payload_rejected(self): + for field,value in (('source_neural_checkpoint_dir','elsewhere'),('source_neural_checkpoint_manifest_sha256','0'*64), + ('authority_sha256','0'*64),('command',{}),('before_overlay_file_sha256','0'*64),('source_state_file_sha256','0'*64), + ('after_overlay_file_sha256','0'*64),('output_files_sha256',{}),('input_files_sha256',{}),('complete',False), + ('weights_installed',True),('learning_pass_claimed',True),('coordinator_source_sha256','0'*64)): + args=list(self.fixture());args[0]=copy.deepcopy(args[0]);args[0][field]=value + with self.subTest(field=field),self.assertRaises(ValueError):V.chain_binding(*args) + + def test_bit_identity_includes_signed_zero(self): + self.assertTrue(V.same(np.zeros(2,np.float32),np.zeros(2,np.float32))) + self.assertFalse(V.same(np.zeros(2,np.float32),np.zeros(2,np.float64))) + self.assertFalse(V.same(np.array([0.],np.float32),np.array([-0.],np.float32))) + + def test_source_substitution_rejected_before_exec(self): + with tempfile.TemporaryDirectory() as d: + p=Path(d)/'fake.py';p.write_text('raise RuntimeError("should not execute")') + with self.assertRaisesRegex(ValueError,'Unverified source'):V.load('_fixture_not_loaded',p,'0'*64) + self.assertNotIn('_fixture_not_loaded',sys.modules) + + def test_actual_frozen_sources_load_without_torch_or_ordinary_R(self): + code=""" +import sys +sys.path.insert(0,sys.argv[1]);import validate_update_cpu as V +e=V.load('_v_cpu_test_E',V.LAB/'conditioning_epoch.py',V.E_SHA) +V.load('engineering_compare',V.LAB/'engineering_compare.py',V.CMP_SHA) +q=V.load('_v_cpu_test_Q',V.LAB/'conditioning_preflight_compare.py',V.Q_SHA) +assert 'torch' not in sys.modules and 'runtime_light' not in sys.modules +assert callable(e._authority) and callable(q.audit_delta_binding) +print('ACTUAL_SOURCE_IMPORT_OK') +""" + result=subprocess.run([sys.executable,'-B','-c',code,str(Path(V.__file__).parent)],text=True,capture_output=True) + self.assertEqual(result.returncode,0,result.stderr);self.assertIn('ACTUAL_SOURCE_IMPORT_OK',result.stdout) + + +if __name__=='__main__':unittest.main() diff --git a/models/male-cns-lif/conditioning-execution-v1/updated_continuation.py b/models/male-cns-lif/conditioning-execution-v1/updated_continuation.py new file mode 100644 index 0000000..db80591 --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/updated_continuation.py @@ -0,0 +1,601 @@ +"""Externally authorized, single-update same-state continuation; not learning proof. + +Only epoch_update from a sealed fresh AN checkpoint and its original E four-file +postprocess is admitted. Later idle transitions retain the installed snapshot. +The input policy is empty_idle or externally_pinned_CS_probe. Probe CS/seed/no-US +semantics and branch lineage belong to the registered coordinator, not this module. +E/U are NEVER imported/executed here. An external CPU validation receipt binds U's +recomputation; its executor/process/registration authority belongs to the caller. +No public small-graph mode. CPU synthetic tests exercise private pure helpers. +""" +from __future__ import annotations + +import hashlib +import importlib.util +import json +from pathlib import Path +import sys + +import numpy as np + + +SOURCE_PINS = { + 'runtime_continuation_source': '0b8e8e0a6a23a079dc62a88355ce355d53b3ae22f6665519a8f6aef2dcfc5aba', + 'runtime_light_source': '522b8c31ea938890fe15364fccd25ccc5bded54101754909f255532e7ad01aae', + 'conditioning_epoch_source': '757935a4815d9f625640df685184ed3fe44a8845323562796da7aa43cbb194e1', + 'conditioning_update_source': '96efff678cff910654eb08e86f1a17789e4e5589e340345f419c7027a7d547e2', + 'conditioning_preflight_compare_source': 'b88fd9a88d9f92d5c4d042b90f2129b015e8d4bd810c41351ee573241e2d4c2e', + 'engineering_compare_source': '5a2403f11bc08d65a9d242ac2cf9686b850e0c3680d65659f785e9b6b50c0238', + 'eligibility_bridge_source': '174fb8d14619189e74b095e77a6db18b14c93046dc431c0b55710a5aa2dd2e61', + 'phase4j_source': 'abb4574da85f00bd9ef61024c65993ef81d42651ff8d8d63c15dd69c6222471e', + 'am_overlay_source': '7f891d0ba79b393c96e75fd2dee2eddc51e57d8213eaac2779703bc2c18712b4', +} +SCHEMA = 'conditioning-updated-boundary-v1' +CHECKPOINT_SCHEMA = 'conditioning-updated-checkpoint-v1' +PROVENANCE_SCHEMA = 'conditioning-updated-runtime-v1' +RECEIPT = 'updated-continuation-receipt.json' +MANIFEST = 'checkpoint-manifest.json' +GRAPH_FILES = ('nodes.npy', 'edge_id.npy', 'src.npy', 'dst.npy', 'signed_raw.npy', 'ptr.npy', 'weights.npy') +APL_FILES = ('input_matrix.npy', 'output_matrix.npy', 'kc_globals.npy', 'apl_globals.npy') +UPDATE_FILES = ('chain-receipt.json', 'update-receipt.json', 'delta-after.npz', 'overlay-manifest.json') +E_SOURCE_ROLES = ('runtime_light_source', 'runtime_continuation_source', 'conditioning_update_source', + 'eligibility_bridge_source', 'phase4j_source', 'am_overlay_source') +E_ROLES = frozenset(('spec', 'requests', 'overlay_before', 'overlay_manifest_before', 'state', 'kc', + 'canonical_kc', 'candidate_map', 'mask', 'base_candidate', *E_SOURCE_ROLES)) + + +def _require(ok, message): + if not ok: + raise ValueError(message) + + +def _digest(path): + h = hashlib.sha256() + with Path(path).open('rb') as stream: + for block in iter(lambda: stream.read(8 << 20), b''): + h.update(block) + return h.hexdigest() + + +def _continuation(): + path = Path(__file__).resolve().parent.parent / 'olfactory-conditioning-lab-v1/runtime_continuation.py' + raw = path.read_bytes() + expected = SOURCE_PINS['runtime_continuation_source'] + _require(hashlib.sha256(raw).hexdigest() == expected, 'Frozen C source pin changed') + name = 'conditioning_updated_pinned_C' + existing = sys.modules.get(name) + if existing is not None: + _require(getattr(existing, '__updated_pin__', None) == expected and + Path(existing.__file__).resolve() == path, 'C module identity collision') + return existing + spec = importlib.util.spec_from_file_location(name, path) + module = importlib.util.module_from_spec(spec) + sys.modules[name] = module + try: + exec(compile(raw, str(path), 'exec'), module.__dict__) + except BaseException: + sys.modules.pop(name, None) + raise + module.__updated_pin__ = expected + return module + + +C = _continuation() +R = C.R # The ONLY runtime/helper cache owner. Never import ordinary R or E. + + +def _canonical(value): + return json.dumps(value, sort_keys=True, separators=(',', ':'), ensure_ascii=False, allow_nan=False) + + +def _same(a, b): + return a.dtype == b.dtype and a.shape == b.shape and a.tobytes() == b.tobytes() + + +def _entry(entry, pins): + R.require(type(entry) is dict and set(entry) == {'path', 'sha256'} and R._sha(entry['sha256']), 'Malformed file pin') + raw = Path(entry['path']) + R.require(raw.is_absolute() and raw.is_file() and not raw.is_symlink(), 'Expected absolute regular unlinked file') + path = raw.resolve(strict=True) + R.require(str(path) not in pins or pins[str(path)] == entry['sha256'], 'Conflicting file pins') + pins[str(path)] = entry['sha256'] + return path + + +def _check(pins): + for path, sha in pins.items(): + R.require(R._sha(sha) and _digest(path) == sha, 'Source/input changed: ' + str(path)) + + +def _evidence(value): + return dict(dtype=value.dtype.str, shape=list(value.shape), nbytes=int(value.nbytes), + raw_c_order_sha256=R._raw(value)) + + +def _Q(sources): + R._load('engineering_compare', sources['engineering_compare_source'], SOURCE_PINS['engineering_compare_source']) + return R._load('conditioning_updated_pinned_Q', sources['conditioning_preflight_compare_source'], + SOURCE_PINS['conditioning_preflight_compare_source']) + + +def _requests(path, spec, policy): + values = C._npz(path, ('tick', 'neuron_idx')) + tick, node = values['tick'], values['neuron_idx'] + R.require(tick.dtype == node.dtype == np.int32 and tick.ndim == node.ndim == 1 and tick.shape == node.shape, + 'Invalid request dtype/shape') + R.require(np.all((tick >= 0) & (tick < spec['steps'])) and np.all((node >= 0) & (node < C.N)), 'Request outside case') + keys = tick.astype(np.int64) * C.N + node + R.require(len(keys) < 2 or np.all(keys[1:] > keys[:-1]), 'Requests must be sorted unique') + R.require(policy in ('empty_idle', 'externally_pinned_CS_probe'), 'Invalid input policy') + R.require(policy != 'empty_idle' or len(tick) == 0, 'empty_idle contains requests') + return values + + +def _expected_effective(helper, plan, spec): + maps = {} + try: + graph = Path(spec['graph_dir']) + for name, dtype, size in (('nodes', 'int32', C.N), ('edge_id', 'int64', C.E), + ('src', 'int32', C.E), ('weights', 'float64', C.E)): + a = np.load(graph / (name + '.npy'), mmap_mode='r', allow_pickle=False) + maps[name] = a + R.require(a.dtype == np.dtype(dtype) and a.shape == (size,), 'Full graph dtype/shape changed: ' + name) + R.require(np.array_equal(maps['nodes'], np.arange(C.N, dtype=np.int32)), 'Noncanonical node order') + apl = np.load(Path(spec['apl_bundle_dir']) / 'apl_globals.npy', allow_pickle=False) + R.require(apl.dtype == np.int32 and apl.shape == (2,), 'Invalid APL IDs') + apl_mask = np.isin(maps['src'], apl) + R.require(int(apl_mask.sum()) == 5845, 'APL output mask changed') + mapping = helper.map_installation(maps['weights'], maps['edge_id'], apl_mask, plan) + R.require(mapping['missing_candidate_count'] == 0 and mapping['mapped_candidate_count'] == 14232, 'Full candidate mapping required') + return helper.expected_hashes(maps['weights'], apl_mask, mapping)[1] + finally: + for a in maps.values(): + a._mmap.close() + + +def _epoch_update(value, paths, sources, pins, parent, before, after, before_meta, after_meta): + update = value['update'] + R.require(type(update) is dict and set(update) == {'epoch_authority', 'files'}, 'Missing epoch update authority') + ep = _entry(update['epoch_authority'], pins) + paths['epoch_authority'] = ep + R.require(type(update['files']) is dict and set(update['files']) == set(UPDATE_FILES), 'All four original E outputs required') + files = {name: _entry(update['files'][name], pins) for name in UPDATE_FILES} + directory = files['chain-receipt.json'].parent + R.require(all(path == directory / name for name, path in files.items()) and + {p.name for p in directory.iterdir()} == set(UPDATE_FILES), 'E output must be exact four-file directory') + R.require(files['delta-after.npz'] == paths['after_overlay'] and files['overlay-manifest.json'] == paths['after_manifest'], + 'Installed after snapshot is not the original E output') + _check(pins) + authority = R._read_json(ep) + R.require(type(authority) is dict and set(authority) == {'schema', 'epoch_id', 'diagnostic_mode', 'command', + 'checkpoint_dir', 'checkpoint_manifest_sha256', 'files', 'frozen_inputs'} and + authority['schema'] == 'phase4an-epoch-postprocess-authority-v1', 'Original E authority schema changed') + R.require(parent['fresh'] is True and parent['global_tick_offset'] == 0 and + Path(authority['checkpoint_dir']).resolve(strict=True) == parent['directory'] and + authority['checkpoint_manifest_sha256'] == parent['manifest_sha256'] and + authority['diagnostic_mode'] == parent['summary']['diagnostic_mode'], 'Wrong fresh E parent') + R.require(type(authority['files']) is dict and set(authority['files']) == E_ROLES, 'Incomplete E file roles') + epins = {str(ep): pins[str(ep)], str(parent['directory'] / MANIFEST): parent['manifest_sha256']} + epaths = {role: _entry(entry, epins) for role, entry in authority['files'].items()} + for role in E_SOURCE_ROLES: + R.require(epins[str(epaths[role])] == SOURCE_PINS[role] and epaths[role] == sources[role], 'E source role mismatch: ' + role) + R.require(type(authority['frozen_inputs']) is dict and authority['frozen_inputs'], 'Missing E frozen input pins') + for path, sha in authority['frozen_inputs'].items(): + _entry(dict(path=path, sha256=sha), epins) + R.require(epaths['state'] == parent['directory'] / 'state.npz' and + parent['manifest']['files_sha256']['state.npz'] == epins[str(epaths['state'])], 'E state is not sealed parent') + for role, actual in (('overlay_before', paths['before_overlay']), ('overlay_manifest_before', paths['before_manifest'])): + R.require(epaths[role] == actual, 'E before overlay path differs') + for role in ('spec', 'requests', 'overlay_before', 'overlay_manifest_before'): + R.require(parent['provenance']['input_sha256'].get(str(epaths[role])) == epins[str(epaths[role])], 'E/runtime input differs: ' + role) + for path, sha in epins.items(): + _entry(dict(path=path, sha256=sha), pins) + _check(pins) + chain = R._read_json(files['chain-receipt.json']) + receipt = R._read_json(files['update-receipt.json']) + command = authority['command'] + R.require(type(command) is dict and set(command) == {'steps', 'compartment', 'us_tick', 'd_command', 'frozen', 'trace_backend'}, 'E command schema') + R.require(type(command['steps']) is int and command['steps'] == parent['steps'], 'E command clock mismatch') + R.require(type(command['d_command']) in (int, float) and command['d_command'] in (0, 1) and + type(command['frozen']) is bool and command['trace_backend'] in ('numpy', 'torch') and + command['compartment'] in ("B2B'2a", 'a1', "y2a'1", "y5B'2a") and + (command['us_tick'] is None or (type(command['us_tick']) is int and 0 <= command['us_tick'] < command['steps'])) and + (command['us_tick'] is not None or command['d_command'] == 0), 'E command value/type mismatch') + required_chain = dict(schema='phase4an-epoch-postprocess-chain-v1', complete=True, + epoch_id=authority['epoch_id'], authority_file=str(ep), authority_sha256=pins[str(ep)], + coordinator_source_sha256=SOURCE_PINS['conditioning_epoch_source'], command=command, + diagnostic_mode=authority['diagnostic_mode'], source_neural_checkpoint_dir=str(parent['directory']), + source_neural_checkpoint_manifest_sha256=parent['manifest_sha256'], + before_overlay_file_sha256=pins[str(paths['before_overlay'])], + before_overlay_manifest_sha256=pins[str(paths['before_manifest'])], + after_overlay_file_sha256=pins[str(paths['after_overlay'])], + after_overlay_manifest_sha256=pins[str(paths['after_manifest'])], + source_state_file_sha256=epins[str(epaths['state'])], source_spec_sha256=epins[str(epaths['spec'])], + source_requests_sha256=epins[str(epaths['requests'])], input_files_sha256=epins, + output_files_sha256={name: pins[str(files[name])] for name in UPDATE_FILES if name != 'chain-receipt.json'}, + runtime_delta_before_raw_sha256=R._raw(before['plastic_delta_mv']), + runtime_effective_weights_raw_sha256=parent['summary']['effective_weights_raw_sha256'], + update_receipt_payload_sha256=receipt.get('payload_sha256'), runtime_learned_manifest_loader_verified=True, + input_and_checkpoint_bytes_reverified=True, candidate_endpoints_equal_graph=True, + KC_equals_supplied_canonical_membership=True, weights_installed=False, brain_executed=False, + learning_pass_claimed=False, candidate_generated=False) + R.require(all(_canonical(chain.get(k)) == _canonical(v) for k, v in required_chain.items()), 'Original E chain mismatch') + for role, prefix in (('mask', 'mask'), ('base_candidate', 'base_candidate'), ('am_overlay_source', 'am_overlay')): + R.require(Path(before_meta[prefix + '_path']).resolve() == epaths[role] and + before_meta[prefix + '_sha256'] == epins[str(epaths[role])], 'E mapping/base authority mismatch') + mapping = C._npz(epaths['candidate_map'], ('edge_id', 'source_neuron_idx', 'target_neuron_idx', 'target_compartment_verbatim')) + kc = np.load(epaths['kc'], allow_pickle=False) + canonical_kc = np.load(epaths['canonical_kc'], allow_pickle=False) + base = np.load(epaths['base_candidate'], allow_pickle=False) + R.require(epaths['canonical_kc'] == Path(C._parent_spec(parent)['apl_bundle_dir']).resolve() / 'kc_globals.npy' and + kc.dtype == np.int32 and kc.shape == (4064,) and _same(kc, canonical_kc), 'Full canonical KC mismatch') + R.require(_same(mapping['edge_id'], before['edge_id']) and _same(after['edge_id'], before['edge_id']) and + _same(base, before['base_candidate_mv']) and _same(base, after['base_candidate_mv']), 'Full before/after mask/base mismatch') + graph = Path(C._parent_spec(parent)['graph_dir']) + parent_spec = C._parent_spec(parent) + required_epins = [graph / name for name in GRAPH_FILES] + [Path(parent_spec['apl_bundle_dir']) / name for name in APL_FILES] + required_epins.append(Path(parent_spec['coefficients_path'])) + required_epins.extend(Path(before_meta['aj_model_dir']).parent / name for name in R.PINS) + R.require(all(str(path.resolve(strict=True)) in authority['frozen_inputs'] for path in required_epins), + 'Original E authority omitted frozen graph/APL/compiled sources') + for role, filename in (('source_neuron_idx', 'src.npy'), ('target_neuron_idx', 'dst.npy')): + a = np.load(graph / filename, mmap_mode='r', allow_pickle=False) + try: + R.require(mapping[role].dtype == np.int32 and mapping[role].shape == (14232,) and + _same(mapping[role], a[before['edge_id']]), 'Actual candidate endpoint mismatch') + finally: + a._mmap.close() + compartments = mapping['target_compartment_verbatim'] + R.require(compartments.dtype.kind == 'U' and compartments.shape == (14232,), 'Invalid full compartment metadata') + arrays = dict(step=parent['state']['step'], neuron_idx=parent['state']['neuron_idx'], kc=kc, + candidate_edge_id=before['edge_id'], candidate_sources=mapping['source_neuron_idx'], + candidate_targets=mapping['target_neuron_idx'], candidate_compartments=compartments, + delta_before=before['plastic_delta_mv'], base_candidate=base) + R.require(_canonical(receipt.get('input_arrays')) == _canonical({k: _evidence(a) for k, a in arrays.items()}), 'U receipt actual array binding failed') + expected_command = dict(steps=command['steps'], n_neurons=C.N, compartment=command['compartment'], + us_tick=command['us_tick'], D_scheduled=float(command['d_command']), frozen=command['frozen'], trace_backend=command['trace_backend']) + R.require(_canonical(receipt.get('command')) == _canonical(expected_command), 'U receipt command differs') + binding = _Q(sources).audit_delta_binding(before['plastic_delta_mv'], after['plastic_delta_mv'], after['plastic_delta_mv'], + update_receipt=receipt, parent_snapshot_sha256=pins[str(paths['before_overlay'])], child_manifest=after_meta) + R.require(binding['pass_all'] is True, 'Q actual before/after delta binding failed') + cp = _entry(value['cpu_validation'], pins) + paths['cpu_validation'] = cp + cpu = R._read_json(cp) + expected_cpu = dict(schema='conditioning-update-cpu-validation-v1', complete=True, + epoch_authority_sha256=pins[str(ep)], parent_checkpoint_manifest_sha256=parent['manifest_sha256'], + update_files_sha256={name: pins[str(files[name])] for name in UPDATE_FILES}, + before_overlay_sha256=pins[str(paths['before_overlay'])], before_manifest_sha256=pins[str(paths['before_manifest'])], + after_overlay_sha256=pins[str(paths['after_overlay'])], after_manifest_sha256=pins[str(paths['after_manifest'])], + before_delta_raw_sha256=R._raw(before['plastic_delta_mv']), after_delta_raw_sha256=R._raw(after['plastic_delta_mv']), + update_receipt_payload_sha256=receipt['payload_sha256'], + validator_source=dict(path=str(sources['cpu_validator_source']), sha256=pins[str(sources['cpu_validator_source'])]), + frozen_U_source_sha256=SOURCE_PINS['conditioning_update_source'], frozen_U_verify_receipt_recomputed=True, + actual_sealed_events_used=True, full_candidate_mapping_verified=True, brain_executed=False, learning_pass_claimed=False) + R.require(_canonical(cpu) == _canonical(expected_cpu), 'External CPU recomputation receipt mismatch') + return binding + + +def validate_authority(authority_json, authority_sha256, *, _seen=None): + """Read-only CPU admission. Does not recompute U, run E, import Torch or restore. + + Returns value/authority/authority_sha256, paths, pins (absolute str -> SHA), + sources, parent, case_dir/spec, helper, before_plan/after_plan/before_meta/ + after_meta; suitable for an independent monolithic oracle without restore. + """ + pins = {} + authority_path = _entry(dict(path=str(authority_json), sha256=authority_sha256), pins) + _check(pins) + value = R._read_json(authority_path) + fields = {'schema', 'boundary_id', 'registration_sha256', 'transition', 'diagnostic_mode', 'input_policy', + 'case', 'parent', 'global_tick_offset', 'overlay_before', 'overlay_after', + 'expected_effective_weight_before_sha256', 'expected_effective_weight_after_sha256', 'update', + 'cpu_validation', 'source_files', 'frozen_inputs'} + R.require(type(value) is dict and set(value) == fields and value['schema'] == SCHEMA, 'Boundary authority schema mismatch') + R.require(type(value['boundary_id']) is str and value['boundary_id'].strip() == value['boundary_id'] and value['boundary_id'], 'Invalid boundary ID') + R.require(R._sha(value['registration_sha256']) and value['transition'] in ('epoch_update', 'idle') and + value['diagnostic_mode'] in ('reference', 'light'), 'Invalid boundary scope') + R.require(type(value['case']) is dict and set(value['case']) == {'spec', 'requests'}, 'Exact case pins required') + paths = {key: _entry(value['case'][role], pins) for key, role in (('case_spec', 'spec'), ('requests', 'requests'))} + for moment in ('before', 'after'): + pair = value['overlay_' + moment] + R.require(type(pair) is dict and set(pair) == {'file', 'manifest'}, 'Exact overlay pair required') + paths[moment + '_overlay'] = _entry(pair['file'], pins) + paths[moment + '_manifest'] = _entry(pair['manifest'], pins) + source_values = value['source_files'] + R.require(type(source_values) is dict and set(source_values) == set(SOURCE_PINS) | + {'updated_continuation_source', 'cpu_validator_source'}, 'Exact source roles required') + sources = {name: _entry(entry, pins) for name, entry in source_values.items()} + for name, sha in SOURCE_PINS.items(): + R.require(pins[str(sources[name])] == sha, 'Frozen source may not be replaced: ' + name) + R.require(sources['updated_continuation_source'] == Path(__file__).resolve() and + sources['runtime_continuation_source'] == Path(C.__file__).resolve() and + sources['runtime_light_source'] == Path(R.__file__).resolve() and + sources['cpu_validator_source'] == Path(__file__).resolve().parent / 'validate_update_cpu.py', + 'Loaded runtime/CPU validator source path differs') + R.require(type(value['frozen_inputs']) is dict and value['frozen_inputs'], 'Full frozen inputs required') + for path, sha in value['frozen_inputs'].items(): + _entry(dict(path=path, sha256=sha), pins) + p = value['parent'] + R.require(type(p) is dict and set(p) == {'directory', 'checkpoint_manifest_sha256'} and Path(p['directory']).is_absolute(), 'Exact parent authority required') + parentdir = Path(p['directory']).resolve(strict=True) + _entry(dict(path=str(parentdir / MANIFEST), sha256=p['checkpoint_manifest_sha256']), pins) + _check(pins) + parent = load_checkpoint(parentdir, p['checkpoint_manifest_sha256'], _seen=_seen) + R.require(type(value['global_tick_offset']) is int and value['global_tick_offset'] == parent['global_next_tick'], 'Wrong absolute continuation clock') + case = paths['case_spec'].parent + R.require(paths['case_spec'].name == 'spec.json' and paths['requests'] == case / 'requests.npz', 'Case filenames differ') + spec = R._read_json(paths['case_spec']) + R.require(spec.get('parent_checkpoint_manifest_sha256') == p['checkpoint_manifest_sha256'] and + type(spec.get('global_tick_offset')) is int and spec['global_tick_offset'] == value['global_tick_offset'], 'Case parent/clock differs') + fresh = dict(spec) + fresh.pop('parent_checkpoint_manifest_sha256') + R.validate_spec(fresh, case) + R.require(parent['global_next_tick'] + spec['steps'] <= np.iinfo(np.int64).max, 'Global clock overflow') + _requests(paths['requests'], spec, value['input_policy']) + previous = C._parent_spec(parent) + for key in ('dt_ms', 'model', 'apl', 'apl_delay_steps'): + R.require(_canonical(spec[key]) == _canonical(previous[key]), 'Frozen dynamics differ: ' + key) + for key in ('graph_dir', 'apl_bundle_dir'): + R.require(Path(spec[key]).resolve(strict=True) == Path(previous[key]).resolve(strict=True), 'Frozen directory differs') + R.require(_digest(spec['coefficients_path']) == parent['summary']['input_sha256']['coefficients.json'] and + _digest(Path(spec['graph_dir']) / 'weights.npy') == parent['summary']['input_sha256']['base_weights.npy'], 'Base/coefficients differ') + required = [Path(spec['graph_dir']).resolve() / n for n in GRAPH_FILES] + [Path(spec['apl_bundle_dir']).resolve() / n for n in APL_FILES] + required.append(Path(spec['coefficients_path']).resolve(strict=True)) + R.require(all(str(path) in value['frozen_inputs'] for path in required), 'Full graph/APL/coeff pins missing') + helper, before, bm, bpins = R.load_overlay(paths['before_overlay'], paths['before_manifest']) + helper2, after, am, apins = R.load_overlay(paths['after_overlay'], paths['after_manifest']) + R.require(helper2 is helper, 'Overlay cache owner differs') + for path, sha in {**bpins, **apins}.items(): + _entry(dict(path=str(path), sha256=sha), pins) + for relative, sha in R.PINS.items(): + path = (Path(am['aj_model_dir']).resolve().parent / relative).resolve(strict=True) + R.require(value['frozen_inputs'].get(str(path)) == sha, 'Compiled/source dependency authority missing: ' + relative) + for key in ('mask_path', 'mask_sha256', 'base_candidate_path', 'base_candidate_sha256', 'aj_model_dir', 'am_overlay_path', 'am_overlay_sha256'): + R.require(bm[key] == am[key], 'Before/after anatomical/source authority differs: ' + key) + R.require(_digest(parentdir / 'overlay-input.npz') == pins[str(paths['before_overlay'])] and + _digest(parentdir / 'overlay-manifest.json') == pins[str(paths['before_manifest'])], 'Before snapshot differs from actual neural parent') + R.require(parent['overlay']['delta_raw_sha256'] == R._raw(before['plastic_delta_mv']), 'Parent delta raw identity mismatch') + for key in ('expected_effective_weight_before_sha256', 'expected_effective_weight_after_sha256'): + R.require(R._sha(value[key]), 'Expected effective SHA required') + before_effective = _expected_effective(helper, before, spec) + after_effective = _expected_effective(helper, after, spec) + R.require(before_effective == value['expected_effective_weight_before_sha256'] == parent['summary']['effective_weights_raw_sha256'], 'Before effective weights differ') + R.require(after_effective == value['expected_effective_weight_after_sha256'], 'After effective weights differ') + paths.update(epoch_authority=None, cpu_validation=None) + binding = None + if value['transition'] == 'epoch_update': + R.require(parent['fresh'] is True, 'An update boundary is allowed only once, from fresh AN parent') + binding = _epoch_update(value, paths, sources, pins, parent, before, after, bm, am) + root = authority_sha256 + else: + R.require(parent['manifest']['schema'] == CHECKPOINT_SCHEMA and value['update'] is None and value['cpu_validation'] is None, + 'Idle requires own updated parent and no new update authority') + R.require(pins[str(paths['before_overlay'])] == pins[str(paths['after_overlay'])] and + pins[str(paths['before_manifest'])] == pins[str(paths['after_manifest'])] and + before_effective == after_effective, 'Idle changed the installed snapshot') + R.require(parent['input_policy'] == 'empty_idle', 'A probe branch cannot feed back into a continuation lineage') + root = parent['update_root_authority_sha256'] + _check(pins) + return dict(value=value, authority=authority_path, authority_sha256=authority_sha256, paths=paths, pins=pins, + sources=sources, parent=parent, case_dir=case, spec=spec, helper=helper, before_plan=before, after_plan=after, + before_meta=bm, after_meta=am, delta_binding=binding, update_root_authority_sha256=root) + + +class UpdatedRuntime(C.ContinuedRuntime): + """Only weight admission differs from C; restoration and kernels unchanged.""" + def __init__(self, *args, authority, **kwargs): + super().__init__(*args, **kwargs) + self.authority = authority + self.restore_audit = None + + def install(self, *args, **kwargs): + R.require(self.installations == 0 and self.restorations == 0, 'Double/belated weight installation') + R._Runtime.install(self, *args, **kwargs) # Original base + full delta once; not old effective + delta. + R.require(self.effective_sha == self.authority['value']['expected_effective_weight_after_sha256'], 'Installed weight differs from authorized after') + + def restore(self, v, g, ref, counts, ring, apl_ring, apl, upload): + super().restore(v, g, ref, counts, ring, apl_ring, apl, upload) + expected = dict(v=self.parent['state']['v'], g=self.parent['state']['g'], ref=self.parent['state']['ref'], + spike_ring=C.rotate_spike_ring(self.parent['terminal']['spike_ring'], int(self.parent['terminal']['next_tick'])), + apl_pending=self.parent['terminal']['apl_pending'], apl_state=self.parent['terminal']['apl_state']) + actual = dict(v=v, g=g, ref=ref, spike_ring=ring, apl_pending=apl_ring, apl_state=apl.state) + for key, tensor in actual.items(): + R.require(_same(tensor.detach().cpu().numpy(), expected[key]), 'Restoration changed actual state bytes: ' + key) + self.restore_audit = dict(restored_raw_sha256={key: R._raw(a) for key, a in expected.items()}, + all_state_and_queue_bytes_exact=True, existing_g_rescaled=False, retroactive_arrival_reweighting=False, + pending_spike_arrivals_use_current_installed_weight=True, pending_APL_samples_preserved=True, + boundary_installations=1, restorations=1, learning_pass_claimed=False) + + def summary(self, value): + result = super().summary(value) + result.update(updated_boundary_schema=SCHEMA, boundary_authority_sha256=self.authority['authority_sha256'], + update_root_authority_sha256=self.authority['update_root_authority_sha256'], + boundary_transition=self.authority['value']['transition'], input_policy=self.authority['value']['input_policy'], + weight_update_at_boundary_authorized=True, frozen_same_weight_retention_claimed=False) + return result + + +def compile_updated(aj, runtime, mode): + tree, audit = C.derive_continued(Path(aj.__file__).read_bytes(), mode) + namespace = dict(aj.__dict__) + namespace.update(__name__=__name__ + '.derived_' + mode, __file__=str(Path(__file__).resolve()), + _runtime=runtime, _continuation=runtime, write_json=runtime.write_json, audit_sequence=runtime.audit_sequence) + exec(compile(tree, str(Path(__file__).resolve()) + '::updated-continuation-' + mode, 'exec'), namespace) + return namespace['run_gpu'], audit + + +def _record(directory, seen): + provenance = R._read_json(directory / 'runtime-provenance.json') + R.require(provenance.get('schema') == PROVENANCE_SCHEMA and provenance.get('complete') is True and + provenance.get('source_sha256') == _digest(__file__) and + Path(provenance['source_file']).resolve() == Path(__file__).resolve(), 'Not a genuine new continuation record') + R.require(provenance.get('old_globals_patched') is False and provenance.get('weights_updated_during_run') is False and + provenance.get('U_recomputed_here') is False and provenance.get('learning_pass_claimed') is False, 'Invalid continuation scope') + C._verify_receipt_hashes(directory, provenance) + R.require(provenance.get('state_sha256') == _digest(directory / 'state.npz'), 'Actual state is not bound') + authority = validate_authority(provenance['authority_file'], provenance['authority_sha256'], _seen=seen) + parent, spec, value = authority['parent'], authority['spec'], authority['value'] + steps, offset = spec['steps'], parent['global_next_tick'] + R.require(provenance['input_sha256'] == authority['pins'], 'Receipt input closure differs from authority') + R.require(provenance.get('mode') == value['diagnostic_mode'] and + provenance.get('parent_checkpoint_manifest_sha256') == parent['manifest_sha256'] and + provenance.get('global_tick_offset') == offset and provenance.get('global_next_tick') == offset+steps and + provenance.get('external_CPU_validation_receipt_bound') is (value['transition'] == 'epoch_update') and + provenance.get('external_registration_process_and_probe_semantics_verified_here') is False, + 'Runtime provenance boundary/clock/scope differs') + _, expected_ast = C.derive_continued((Path(authority['after_meta']['aj_model_dir']) / 'gpu_timing.py').read_bytes(), value['diagnostic_mode']) + R.require(provenance.get('ast') == expected_ast, 'Derived source AST provenance differs') + summary = R._read_json(directory / 'summary.json') + finite = R._read_json(directory / 'finite-audit.json') + for name, expected in dict(complete=True, pass_all=True, all_finite=True, steps=steps, completed_ticks=steps, + nodes=C.N, edges=C.E, precision='float64', apl_precision='float32', within_trial_weight_updates=False, + checkpoint_continuation_used=True, global_tick_offset=offset, global_next_tick=offset+steps, + explicit_steps_executed=steps, max_steps=spec['max_steps'], hidden_early_stop=False, + entire_neural_trial_executed=True, record_history=False, counts_scope='segment_only_not_lifetime', + event_tick_scope='segment_local', weight_update_at_boundary_authorized=True, frozen_same_weight_retention_claimed=False, + parent_checkpoint_manifest_sha256=parent['manifest_sha256'], diagnostic_mode=value['diagnostic_mode'], + boundary_authority_sha256=authority['authority_sha256'], update_root_authority_sha256=authority['update_root_authority_sha256'], + updated_boundary_schema=SCHEMA, boundary_transition=value['transition'], input_policy=value['input_policy'], + effective_weights_raw_sha256=value['expected_effective_weight_after_sha256']).items(): + R.require(_canonical(summary.get(name)) == _canonical(expected), 'Summary binding mismatch: ' + name) + R.require(finite.get('all_finite') is True and finite.get('first_failure_tick') == -1 and + finite.get('finite_observed_ticks') == steps, 'Missing full finite coverage') + expected_input = {'spec.json': authority['pins'][str(authority['paths']['case_spec'])], + 'requests.npz': authority['pins'][str(authority['paths']['requests'])], + 'base_weights.npy': _digest(Path(spec['graph_dir']) / 'weights.npy'), 'coefficients.json': _digest(spec['coefficients_path'])} + R.require(summary.get('input_sha256') == expected_input, 'Summary case/base inputs differ') + state = C._npz(directory / 'state.npz', ('step', 'neuron_idx', 'counts', 'v', 'g', 'ref')) + terminal = C._npz(directory / 'terminal-queues.npz', ('spike_ring', 'apl_state', 'apl_pending', 'next_tick')) + C.validate_state(state, terminal, steps, fresh=False) + if steps < 19: + restored = C.rotate_spike_ring(parent['terminal']['spike_ring'], int(parent['terminal']['next_tick'])) + R.require(_same(terminal['spike_ring'][steps:], restored[steps:]), 'Unwritten carried spike slots differ') + if steps < 18: + R.require(_same(terminal['apl_pending'][:18-steps], parent['terminal']['apl_pending'][steps:]), 'Carried APL tail differs') + pending = np.load(directory / 'apl-pending-tail.float32.npy', allow_pickle=False) + timing = R._read_json(directory / 'apl-timing-audit.json') + R.require(_same(pending, terminal['apl_pending']) and timing.get('pending_raw_sha256') == R._raw(pending) and + timing.get('apl_delay_steps') == 18 and timing.get('brain_tail_advanced') is False and + timing.get('final_apl_state_u32_bits') == terminal['apl_state'].view(np.uint32).tolist() and + timing.get('global_tick_offset') == offset and timing.get('global_next_tick') == offset+steps and + timing.get('initial_queue_raw_sha256') == R._raw(parent['terminal']['apl_pending']), 'APL metadata/terminal mismatch') + overlay = R._read_json(directory / 'overlay-audit.json') + R.require(overlay.get('complete') is True and overlay.get('weights_updated_during_run') is False and + overlay.get('effective_weights_raw_sha256') == value['expected_effective_weight_after_sha256'] and + overlay.get('delta_raw_sha256') == R._raw(authority['after_plan']['plastic_delta_mv']) and + overlay.get('mask_sha256') == R.MASK_SHA and overlay.get('am_helper_sha256') == R.AM_OVERLAY_SHA, 'Installed snapshot receipt mismatch') + for dest, source in (('overlay-input.npz', 'after_overlay'), ('overlay-manifest.json', 'after_manifest')): + R.require(_digest(directory / dest) == authority['pins'][str(authority['paths'][source])], 'Copied after snapshot differs') + receipt = R._read_json(directory / RECEIPT) + payload = C._tree_hashes(directory) + payload.pop(RECEIPT, None) + expected_receipt = dict(schema='conditioning-updated-receipt-v1', complete=True, + authority_file=str(authority['authority']), authority_sha256=authority['authority_sha256'], + parent_checkpoint_dir=str(parent['directory']), parent_checkpoint_manifest_sha256=parent['manifest_sha256'], + global_tick_offset=offset, global_next_tick=offset+steps, segment_steps=steps, + update_root_authority_sha256=authority['update_root_authority_sha256'], input_policy=value['input_policy'], + payload_sha256=payload, weights_updated_during_run=False, counts_scope='segment_only_not_lifetime', + checkpoint_continuation_verified_by_GPU_comparison=False, learning_pass_claimed=False) + R.require(_canonical(receipt) == _canonical(expected_receipt), 'New receipt payload/ancestry mismatch') + restored = dict(v=parent['state']['v'], g=parent['state']['g'], ref=parent['state']['ref'], + spike_ring=C.rotate_spike_ring(parent['terminal']['spike_ring'], int(parent['terminal']['next_tick'])), + apl_pending=parent['terminal']['apl_pending'], apl_state=parent['terminal']['apl_state']) + boundary = R._read_json(directory / 'boundary-restore-audit.json') + R.require(boundary == dict(restored_raw_sha256={k:R._raw(a) for k,a in restored.items()}, + all_state_and_queue_bytes_exact=True, existing_g_rescaled=False, retroactive_arrival_reweighting=False, + pending_spike_arrivals_use_current_installed_weight=True, pending_APL_samples_preserved=True, + boundary_installations=1, restorations=1, learning_pass_claimed=False), 'Boundary restore audit differs from real parent') + return dict(directory=directory, summary=summary, provenance=provenance, overlay=overlay, state=state, terminal=terminal, + steps=steps, global_tick_offset=offset, global_next_tick=offset+steps, fresh=False, + ancestry_directories=[directory, *parent['ancestry_directories']], input_policy=value['input_policy'], + update_root_authority_sha256=authority['update_root_authority_sha256']) + + +def load_checkpoint(checkpoint_dir, expected_sha, *, _seen=None): + directory = Path(checkpoint_dir).resolve(strict=True) + R.require(R._sha(expected_sha) and _digest(directory / MANIFEST) == expected_sha, 'External checkpoint pin differs') + manifest = R._read_json(directory / MANIFEST) + if manifest.get('schema') == 'phase4an-checkpoint-v1': + return C.load_checkpoint(directory, expected_sha, _seen=_seen) + seen = set() if _seen is None else set(_seen) + R.require(directory not in seen, 'Updated checkpoint ancestry cycle') + seen.add(directory) + R.require(set(manifest) == {'schema', 'complete', 'files_sha256', 'segment_steps', 'global_tick_offset', 'global_next_tick', + 'counts_scope', 'update_root_authority_sha256', 'input_policy', 'learning_pass_claimed', + 'checkpoint_continuation_verified_by_GPU_comparison'} and + manifest.get('schema') == CHECKPOINT_SCHEMA and manifest.get('complete') is True and + manifest.get('learning_pass_claimed') is False and manifest.get('checkpoint_continuation_verified_by_GPU_comparison') is False and + manifest.get('counts_scope') == 'segment_only' and manifest.get('files_sha256') == C._tree_hashes(directory), 'New checkpoint exact inventory differs') + record = _record(directory, seen) + for key in ('global_tick_offset', 'global_next_tick', 'update_root_authority_sha256', 'input_policy'): + R.require(manifest.get(key) == record[key], 'Checkpoint metadata mismatch: ' + key) + R.require(manifest.get('segment_steps') == record['steps'] and manifest['files_sha256'] == C._tree_hashes(directory) and + _digest(directory / MANIFEST) == expected_sha, 'Checkpoint changed while loading') + record.update(manifest=manifest, manifest_sha256=expected_sha) + return record + + +def seal_checkpoint(checkpoint_dir): + directory = Path(checkpoint_dir).resolve(strict=True) + R.require(not (directory / MANIFEST).exists(), 'Checkpoint is already sealed') + before = C._tree_hashes(directory) + record = _record(directory, {directory}) + R.require(before == C._tree_hashes(directory), 'Checkpoint changed while sealing') + value = dict(schema=CHECKPOINT_SCHEMA, complete=True, files_sha256=before, segment_steps=record['steps'], + global_tick_offset=record['global_tick_offset'], global_next_tick=record['global_next_tick'], + counts_scope='segment_only', update_root_authority_sha256=record['update_root_authority_sha256'], + input_policy=record['input_policy'], learning_pass_claimed=False, + checkpoint_continuation_verified_by_GPU_comparison=False) + R._save(directory / MANIFEST, value) + return dict(path=str(directory / MANIFEST), sha256=_digest(directory / MANIFEST), **value) + + +def run(authority_json, authority_sha256, out_dir, build_root, *, mode='light'): + R.require(sys.dont_write_bytecode, 'Use python -B') + admitted = validate_authority(authority_json, authority_sha256) + value, parent, spec = admitted['value'], admitted['parent'], admitted['spec'] + R.require(mode == value['diagnostic_mode'], 'Mode differs from authority') + protected = [*parent['ancestry_directories'], *(Path(path).parent for path in admitted['pins'])] + case, out, build = R.execution_paths(admitted['case_dir'], out_dir, build_root, protected) + ajdir = Path(admitted['after_meta']['aj_model_dir']) + R._load('timing_queue', ajdir / 'timing_queue.py', R.PINS['apl-timing-lab-v1/timing_queue.py']) + aj = R._load('conditioning_updated_sealed_AJ', ajdir / 'gpu_timing.py', R.AJ_SHA) + runtime = UpdatedRuntime(mode, spec, admitted['helper'], admitted['after_plan'], admitted['after_meta'], + admitted['pins'], aj.torch, admitted['paths']['after_overlay'], admitted['paths']['after_manifest'], + parent=parent, authority=admitted) + function, ast_audit = compile_updated(aj, runtime, mode) + try: + inherited = function(case, out, build) + R.require(runtime.finished and runtime.restorations == runtime.installations == 1 and inherited['complete'], 'Incomplete new continuation') + R.require(inherited['effective_weights_raw_sha256'] == value['expected_effective_weight_after_sha256'], 'Runtime effective identity differs') + _check(admitted['pins']) + load_checkpoint(parent['directory'], parent['manifest_sha256']) + summary = R._read_json(out / 'summary.json') + R.require(summary == runtime.summary(inherited), 'Stored updated summary differs') + R._save(out / 'boundary-restore-audit.json', runtime.restore_audit) + provenance = dict(schema=PROVENANCE_SCHEMA, complete=True, mode=mode, + source_file=str(Path(__file__).resolve()), source_sha256=_digest(__file__), + authority_file=str(admitted['authority']), authority_sha256=authority_sha256, + ast=ast_audit, input_sha256=admitted['pins'], old_globals_patched=False, weights_updated_during_run=False, + global_tick_offset=parent['global_next_tick'], global_next_tick=parent['global_next_tick']+spec['steps'], + parent_checkpoint_manifest_sha256=parent['manifest_sha256'], U_recomputed_here=False, + external_CPU_validation_receipt_bound=value['transition'] == 'epoch_update', + external_registration_process_and_probe_semantics_verified_here=False, learning_pass_claimed=False) + for key, name in (('summary_sha256','summary.json'), ('finite_audit_sha256','finite-audit.json'), + ('terminal_queues_sha256','terminal-queues.npz'), ('overlay_audit_sha256','overlay-audit.json'), ('state_sha256','state.npz')): + provenance[key] = _digest(out / name) + R._save(out / 'runtime-provenance.json', provenance) + receipt = dict(schema='conditioning-updated-receipt-v1', complete=True, + authority_file=str(admitted['authority']), authority_sha256=authority_sha256, + parent_checkpoint_dir=str(parent['directory']), parent_checkpoint_manifest_sha256=parent['manifest_sha256'], + global_tick_offset=parent['global_next_tick'], global_next_tick=parent['global_next_tick']+spec['steps'], + segment_steps=spec['steps'], update_root_authority_sha256=admitted['update_root_authority_sha256'], + input_policy=value['input_policy'], payload_sha256=C._tree_hashes(out), weights_updated_during_run=False, + counts_scope='segment_only_not_lifetime', checkpoint_continuation_verified_by_GPU_comparison=False, learning_pass_claimed=False) + R._save(out / RECEIPT, receipt) + sealed = seal_checkpoint(out) + return dict(summary, checkpoint_manifest_sha256=sealed['sha256']) + except Exception as error: + if out.is_dir() and not (out / 'updated-error.json').exists(): + R._save(out / 'updated-error.json', dict(complete=False, type=type(error).__name__, message=str(error), retry_forbidden=True)) + raise diff --git a/models/male-cns-lif/conditioning-execution-v1/validate_update_cpu.py b/models/male-cns-lif/conditioning-execution-v1/validate_update_cpu.py new file mode 100644 index 0000000..a642021 --- /dev/null +++ b/models/male-cns-lif/conditioning-execution-v1/validate_update_cpu.py @@ -0,0 +1,174 @@ +"""Fresh CPU-process revalidation of a sealed epoch's actual update. + +This does not run a brain or E.postprocess_epoch and writes only one exclusive +receipt. The caller must pin the source, authority and successful process to its +registration. A supplied SHA is not independent scientific authorization. +""" +from __future__ import annotations + +import argparse +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import sys + +import numpy as np + +LAB = Path(__file__).resolve().parent.parent/'olfactory-conditioning-lab-v1' +E_SHA = '757935a4815d9f625640df685184ed3fe44a8845323562796da7aa43cbb194e1' +Q_SHA = 'b88fd9a88d9f92d5c4d042b90f2129b015e8d4bd810c41351ee573241e2d4c2e' +U_SHA = '96efff678cff910654eb08e86f1a17789e4e5589e340345f419c7027a7d547e2' +CMP_SHA = '5a2403f11bc08d65a9d242ac2cf9686b850e0c3680d65659f785e9b6b50c0238' +FILES = ('chain-receipt.json','update-receipt.json','delta-after.npz','overlay-manifest.json') + + +def require(ok,message): + if not ok: raise ValueError(message) + + +def digest(path): + h=hashlib.sha256() + with Path(path).open('rb') as stream: + for b in iter(lambda:stream.read(8<<20),b''):h.update(b) + return h.hexdigest() + + +def same(a,b): return a.dtype==b.dtype and a.shape==b.shape and a.tobytes()==b.tobytes() + + +def unlinked(path): + path=Path(path).absolute() + require(not any(p.is_symlink() or (hasattr(p,'is_junction') and p.is_junction()) + for p in (path,*path.parents)), 'Linked receipt/update path forbidden') + return path + + +def load(name,path,expected): + raw=Path(path).read_bytes() + require(hashlib.sha256(raw).hexdigest()==expected and name not in sys.modules,'Unverified source/module binding') + spec=importlib.util.spec_from_file_location(name,path);module=importlib.util.module_from_spec(spec) + sys.modules[name]=module + exec(compile(raw,str(path),'exec'),module.__dict__) + return module + + +def chain_binding(chain,authority,authority_sha,pins,paths,checkpoint,hashes): + require(chain.get('schema')=='phase4an-epoch-postprocess-chain-v1' and chain.get('complete') is True, + 'Incomplete epoch update chain') + for flag in ('weights_installed','brain_executed','learning_pass_claimed','candidate_generated'): + require(chain.get(flag) is False,'Wrong chain scope: '+flag) + require(chain['coordinator_source_sha256']==E_SHA and chain['authority_sha256']==authority_sha and + chain['epoch_id']==authority['epoch_id'] and chain['command']==authority['command'] and + chain['diagnostic_mode']==authority['diagnostic_mode'],'Epoch authority/command differs') + require(chain['source_neural_checkpoint_dir']==str(checkpoint) and + chain['source_neural_checkpoint_manifest_sha256']==authority['checkpoint_manifest_sha256'] and + chain['input_files_sha256']=={str(p):v for p,v in pins.items()},'Actual checkpoint/input binding differs') + for field,role in (('source_state_file_sha256','state'),('source_spec_sha256','spec'), + ('source_requests_sha256','requests'),('before_overlay_file_sha256','overlay_before'), + ('before_overlay_manifest_sha256','overlay_manifest_before')): + require(chain[field]==pins[paths[role]],'Chain input differs: '+role) + expected={name:hashes[name] for name in FILES if name!='chain-receipt.json'} + require(chain['output_files_sha256']==expected and chain['after_overlay_file_sha256']==hashes['delta-after.npz'] and + chain['after_overlay_manifest_sha256']==hashes['overlay-manifest.json'],'Chain output differs') + + +def validate(epoch_authority,authority_sha,update_dir,chain_sha,receipt_file): + require(sys.dont_write_bytecode and 'torch' not in sys.modules and 'runtime_light' not in sys.modules, + 'Run in a fresh Python -B CPU process') + os.environ['CUDA_VISIBLE_DEVICES']='' + e=load('_conditioning_execution_E_validator',LAB/'conditioning_epoch.py',E_SHA) + load('engineering_compare',LAB/'engineering_compare.py',CMP_SHA) + q=load('_conditioning_execution_Q_validator',LAB/'conditioning_preflight_compare.py',Q_SHA) + value,paths,pins,checkpoint,authority_path=e._authority(epoch_authority,authority_sha) + c,r,u=e._production_dependencies(paths,pins) # The only R is C.R; no ordinary runtime loader. + directory=unlinked(update_dir).resolve(strict=True);out=unlinked(receipt_file) + protected={checkpoint,directory,*[p.parent for p in pins]} + require(out.parent.is_dir() and not out.exists() and not any(out.resolve().is_relative_to(p) for p in protected), + 'Receipt must be new and outside every frozen input/output directory') + require(set(p.name for p in directory.iterdir())==set(FILES) and + all((directory/n).is_file() and not (directory/n).is_symlink() for n in FILES),'Expected exact four update files') + hashes={n:digest(directory/n) for n in FILES} + require(e._sha(chain_sha) and hashes['chain-receipt.json']==chain_sha,'Chain differs from external SHA') + own=digest(__file__);chain=e._read(directory/'chain-receipt.json') + chain_binding(chain,value,authority_sha,pins,paths,checkpoint,hashes) + parent=c.load_checkpoint(checkpoint,value['checkpoint_manifest_sha256']) + command=value['command'];spec=e._read(paths['spec']) + r.validate_spec(spec,paths['spec'].parent) + require(parent['fresh'] is True and parent['global_tick_offset']==0 and parent['steps']==command['steps']==spec['steps'] and + parent['summary']['diagnostic_mode']==value['diagnostic_mode'],'Wrong actual epoch/state scope') + for role in ('spec','requests','overlay_before','overlay_manifest_before'): + require(parent['provenance']['input_sha256'].get(str(paths[role]))==pins[paths[role]],'Runtime input differs: '+role) + require(parent['manifest']['files_sha256']['state.npz']==pins[paths['state']],'State not sealed by this checkpoint') + _,before,before_meta,_=r.load_overlay(paths['overlay_before'],paths['overlay_manifest_before']) + _,after,after_meta,_=r.load_overlay(directory/'delta-after.npz',directory/'overlay-manifest.json') + graph=Path(spec['graph_dir']).resolve();apl=Path(spec['apl_bundle_dir']).resolve() + for location in [graph/n for n in e.GRAPH_FILES]+[apl/n for n in e.APL_FILES]+[Path(spec['coefficients_path']).resolve()]: + require(location in pins,'Missing complete graph/APL/coefficient pin') + require(paths['canonical_kc']==apl/'kc_globals.npy','Canonical KC path differs') + for role,prefix in (('mask','mask'),('base_candidate','base_candidate'),('am_overlay_source','am_overlay')): + require(Path(before_meta[prefix+'_path']).resolve()==paths[role] and before_meta[prefix+'_sha256']==pins[paths[role]],'Overlay input differs') + aj=Path(before_meta['aj_model_dir']).resolve() + for relative,sha in r.PINS.items():require(pins.get((aj.parent/relative).resolve())==sha,'Missing original dependency pin') + mapping=e._npz(paths['candidate_map'],('edge_id','source_neuron_idx','target_neuron_idx','target_compartment_verbatim')) + mask=np.load(paths['mask'],allow_pickle=False);kc=np.load(paths['kc'],allow_pickle=False) + canonical=np.load(paths['canonical_kc'],allow_pickle=False);base=np.load(paths['base_candidate'],allow_pickle=False) + e._vector(mask,'int64',14232,'mask');e._vector(kc,'int32',4064,'KC');e._vector(canonical,'int32',4064,'canonical KC') + e._vector(base,'float32',14232,'base') + require(np.all((mask>=0)&(mask<25582938)) and np.all(mask[1:]>mask[:-1]) and + np.all((kc>=0)&(kc<166700)) and len(np.unique(kc))==len(kc), + 'Candidate/KC range or uniqueness differs') + require(same(kc,canonical) and same(mask,mapping['edge_id']) and same(mask,before['edge_id']) and same(mask,after['edge_id']), + 'KC/candidate membership differs') + require(same(base,before['base_candidate_mv']) and same(base,after['base_candidate_mv']),'Original base changed') + maps={} + try: + for n,dtype,count in (('nodes','int32',166700),('edge_id','int64',25582938),('src','int32',25582938),('dst','int32',25582938)): + maps[n]=np.load(graph/(n+'.npy'),allow_pickle=False,mmap_mode='r');e._vector(maps[n],dtype,count,n) + require(np.array_equal(maps['nodes'],np.arange(166700,dtype=np.int32)) and same(maps['edge_id'][mask],mask),'Not canonical full graph') + require(same(mapping['source_neuron_idx'],maps['src'][mask]) and same(mapping['target_neuron_idx'],maps['dst'][mask]), + 'Candidate endpoints differ from actual connectome') + finally: + for a in maps.values():a._mmap.close() + state=e._npz(paths['state'],('step','neuron_idx','counts','v','g','ref')) + require(all(same(state[n],parent['state'][n]) for n in state),'Actual sealed event/state mismatch') + require(parent['overlay']['delta_raw_sha256']==r._raw(before['plastic_delta_mv']) and + chain['runtime_delta_before_raw_sha256']==r._raw(before['plastic_delta_mv']) and + chain['runtime_effective_weights_raw_sha256']==parent['summary']['effective_weights_raw_sha256'], 'Actual before installation differs') + update=e._read(directory/'update-receipt.json') + require(update['payload_sha256']==chain['update_receipt_payload_sha256'],'Update receipt chain differs') + u.verify_receipt(update,step=state['step'],neuron_idx=state['neuron_idx'],kc=kc,candidate_edge_id=mask, + candidate_sources=mapping['source_neuron_idx'],candidate_targets=mapping['target_neuron_idx'], + candidate_compartments=mapping['target_compartment_verbatim'],delta_before=before['plastic_delta_mv'],base_candidate=base, + steps=command['steps'],n_neurons=166700,compartment=command['compartment'],us_tick=command['us_tick'], + d_command=command['d_command'],frozen=command['frozen'],trace_backend=command['trace_backend'], + bridge_source=paths['eligibility_bridge_source'],phase4j_source=paths['phase4j_source']) + binding=q.audit_delta_binding(before['plastic_delta_mv'],after['plastic_delta_mv'],after['plastic_delta_mv'], + update_receipt=update,parent_snapshot_sha256=pins[paths['overlay_before']],child_manifest=after_meta) + require(binding['pass_all'],'Actual after array differs from recomputed receipt') + import torch + require(os.environ['CUDA_VISIBLE_DEVICES']=='' and not torch.cuda.is_initialized(),'CPU verifier initialized CUDA') + e._check_files(pins);c.load_checkpoint(checkpoint,value['checkpoint_manifest_sha256']) + for file,sha in (('conditioning_epoch.py',E_SHA),('conditioning_preflight_compare.py',Q_SHA),('engineering_compare.py',CMP_SHA)): + require(digest(LAB/file)==sha,'Verifier dependency changed') + require(hashes=={n:digest(directory/n) for n in FILES} and own==digest(__file__),'Source/update changed during validation') + receipt=dict(schema='conditioning-update-cpu-validation-v1',complete=True,epoch_authority_sha256=authority_sha, + parent_checkpoint_manifest_sha256=value['checkpoint_manifest_sha256'],update_files_sha256=hashes, + before_overlay_sha256=pins[paths['overlay_before']],before_manifest_sha256=pins[paths['overlay_manifest_before']], + after_overlay_sha256=hashes['delta-after.npz'],after_manifest_sha256=hashes['overlay-manifest.json'], + before_delta_raw_sha256=r._raw(before['plastic_delta_mv']),after_delta_raw_sha256=r._raw(after['plastic_delta_mv']), + update_receipt_payload_sha256=update['payload_sha256'],validator_source=dict(path=str(Path(__file__).resolve()),sha256=own), + frozen_U_source_sha256=U_SHA,frozen_U_verify_receipt_recomputed=True,actual_sealed_events_used=True, + full_candidate_mapping_verified=True,brain_executed=False,learning_pass_claimed=False) + e._save(out,receipt) + return receipt + + +if __name__=='__main__': + parser=argparse.ArgumentParser(description=__doc__) + parser.add_argument('--authority',required=True);parser.add_argument('--authority-sha256',required=True) + parser.add_argument('--update-dir',required=True);parser.add_argument('--chain-sha256',required=True) + parser.add_argument('--receipt',required=True);args=parser.parse_args() + validate(args.authority,args.authority_sha256,args.update_dir,args.chain_sha256,args.receipt) + print(json.dumps(dict(complete=True,receipt=args.receipt,sha256=digest(args.receipt))),flush=True)