454 lines
26 KiB
Python
454 lines
26 KiB
Python
"""Phase 4AJ isolated fixed-delay graded-APL timing branch.
|
|
|
|
Copied from sealed AG-r2 gpu_apl.py; only dynamics change is a 0/18-step
|
|
float32 output delay before the unchanged unconditional end-stage g write.
|
|
The sealed LegacyAPL and AE CUDA operators are loaded explicitly, unmodified.
|
|
Generated h is still logged separately. No extra brain/tail ticks are run.
|
|
This is engineering schedule parity, not a measured biological delay claim.
|
|
"""
|
|
from functools import lru_cache
|
|
import csv
|
|
import difflib
|
|
import importlib.util
|
|
import hashlib
|
|
import json
|
|
import math
|
|
from pathlib import Path
|
|
import time
|
|
import traceback
|
|
import sys
|
|
|
|
import numpy as np
|
|
import torch
|
|
|
|
from timing_queue import audit_sequence
|
|
|
|
|
|
SOURCE_ROOT = Path(__file__).resolve().parent
|
|
AE_MODEL = SOURCE_ROOT.parent / "author-runtime-lab-v1"
|
|
AF_MODEL = SOURCE_ROOT.parent / "author-fullgraph-lab-v1"
|
|
AG_MODEL = SOURCE_ROOT.parent / "author-apl-lab-v2"
|
|
N_NEURONS = 166700
|
|
SEALED_SHA256 = {
|
|
"ag_gpu": "69f5d893f47dafff4ef7c3186c3df0b9014666b32c8f4e943b57016bec92b18a",
|
|
"ag_worker": "21ceec65fe42581dc55872010e0025d660e80358905f1cf4df1dadf341a3b291",
|
|
"ae_backend": "03758d07572f4b44d272059d9308fac534268ad201b57bdd77d69fee95e67278",
|
|
"af_queue": "d72003d98f0f1624b12126eff50bd361c43d888f4a64d90ba9247a58976f1961",
|
|
}
|
|
COEFFICIENT_NAMES = ("gd", "c0", "cg", "cv", "threshold", "reset", "seed_volt")
|
|
REQUEST_FIELDS = ("tick", "neuron_idx", "attempted", "accepted", "rejected")
|
|
|
|
|
|
def require(condition, message):
|
|
if not condition:
|
|
raise ValueError(message)
|
|
|
|
|
|
def write_json(path, value):
|
|
with Path(path).open("x", encoding="utf-8") as output:
|
|
json.dump(value, output, indent=2, allow_nan=False)
|
|
output.write("\n")
|
|
|
|
|
|
def raw_hash(value, dtype):
|
|
return hashlib.sha256(np.ascontiguousarray(value, dtype=dtype).tobytes()).hexdigest()
|
|
|
|
|
|
def file_sha256(path):
|
|
digest = hashlib.sha256()
|
|
with Path(path).open("rb") as source:
|
|
for block in iter(lambda: source.read(8 << 20), b""):
|
|
digest.update(block)
|
|
return digest.hexdigest()
|
|
|
|
|
|
def load_source(path, name, expected_hash):
|
|
"""Load a sealed source under a unique name; never patch its globals."""
|
|
path = Path(path)
|
|
require(file_sha256(path) == expected_hash, "Sealed source changed: " + str(path))
|
|
spec = importlib.util.spec_from_file_location(name, path)
|
|
require(spec is not None and spec.loader is not None, "Cannot load sealed module")
|
|
module = importlib.util.module_from_spec(spec)
|
|
sys.modules[name] = module
|
|
try:
|
|
spec.loader.exec_module(module)
|
|
except BaseException:
|
|
sys.modules.pop(name, None)
|
|
raise
|
|
return module
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def sealed_apl():
|
|
return load_source(AG_MODEL / "legacy_apl_worker.py", "phase4aj_sealed_ag_worker",
|
|
SEALED_SHA256["ag_worker"])
|
|
|
|
|
|
def source_audit(out_dir):
|
|
sources = {"ag_gpu": AG_MODEL / "gpu_apl.py", "ag_worker": AG_MODEL / "legacy_apl_worker.py",
|
|
"ae_backend": AE_MODEL / "gpu_backend.py", "af_queue": AF_MODEL / "oracle_full.py"}
|
|
actual = {key: file_sha256(path) for key, path in sources.items()}
|
|
require(actual == SEALED_SHA256, "Sealed implementation hash mismatch")
|
|
diff_path = out_dir / "sealed-gpu-source.diff"
|
|
with diff_path.open("x", encoding="utf-8", newline="\n") as output:
|
|
output.writelines(difflib.unified_diff(
|
|
sources["ag_gpu"].read_text(encoding="utf-8").splitlines(keepends=True),
|
|
Path(__file__).read_text(encoding="utf-8").splitlines(keepends=True),
|
|
fromfile="sealed-author-apl-lab-v2/gpu_apl.py",
|
|
tofile="new-apl-timing-lab-v1/gpu_timing.py"))
|
|
audit = {"sealed_source_sha256": actual, "candidate_sha256": file_sha256(__file__),
|
|
"source_diff_sha256": file_sha256(diff_path),
|
|
"module_globals_patched": False,
|
|
"only_dynamics_change": "fixed zero-initialized 0/18-step h32 delay before unchanged end g write",
|
|
"diagnostic_additions": ["generated/applied/pending trace split", "fixed-sequence bitwise audit",
|
|
"source provenance and final APL state disclosure"]}
|
|
write_json(out_dir / "source-audit.json", audit)
|
|
return audit
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def sealed_backend():
|
|
return load_source(AE_MODEL / "gpu_backend.py", "phase4aj_sealed_ae_backend", SEALED_SHA256["ae_backend"])
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def queue_function():
|
|
return load_source(AF_MODEL / "oracle_full.py", "phase4aj_sealed_af_queue", SEALED_SHA256["af_queue"]).queue_summary
|
|
|
|
|
|
@lru_cache(maxsize=4)
|
|
def load_graph(graph_directory):
|
|
root = Path(graph_directory)
|
|
require(root.is_absolute(), "graph_dir must be absolute")
|
|
graph = {}
|
|
for name, dtype in (("nodes", np.int32), ("edge_id", np.int64), ("src", np.int32),
|
|
("dst", np.int32), ("signed_raw", np.int64), ("ptr", np.int64),
|
|
("weights", np.float64)):
|
|
value = np.load(root / (name + ".npy"), mmap_mode="r", allow_pickle=False)
|
|
require(value.dtype == dtype and value.ndim == 1, "Graph dtype/rank mismatch: " + name)
|
|
graph[name] = value
|
|
nodes, edges = graph["nodes"], graph["edge_id"]
|
|
n, e = len(nodes), len(edges)
|
|
require(0 < n <= N_NEURONS and bool(np.all((nodes >= 0) & (nodes < N_NEURONS))) and
|
|
bool(np.all(np.diff(nodes) > 0)), "Global node IDs must be sorted unique")
|
|
require(n < 512 or (n == N_NEURONS and np.array_equal(nodes, np.arange(N_NEURONS, dtype=np.int32))),
|
|
"Only small fixtures or the intact frozen full graph are permitted")
|
|
require(n != N_NEURONS or e == 25582938, "Full graph edge count changed")
|
|
for name in ("src", "dst", "signed_raw", "weights"):
|
|
require(graph[name].shape == (e,), "Graph edge array length mismatch: " + name)
|
|
ptr = graph["ptr"]
|
|
require(ptr.shape == (n + 1,) and ptr[0] == 0 and ptr[-1] == e and
|
|
bool(np.all(np.diff(ptr) >= 0)), "Invalid graph CSR")
|
|
require(np.array_equal(graph["src"], np.repeat(np.arange(n, dtype=np.int32), np.diff(ptr))),
|
|
"Source order does not match CSR")
|
|
# Global edge IDs need not equal local row indices in a fixture.
|
|
for start in range(0, e, 1 << 20):
|
|
stop = min(start + (1 << 20), e)
|
|
require(bool(np.all((graph["dst"][start:stop] >= 0) & (graph["dst"][start:stop] < n))),
|
|
"Local destination out of range")
|
|
require(bool(np.isfinite(graph["weights"][start:stop]).all()), "Nonfinite base SI weights")
|
|
require(np.array_equal(np.sign(graph["weights"][start:stop]), np.sign(graph["signed_raw"][start:stop])),
|
|
"Base weight sign/zero pattern differs from frozen raw contacts")
|
|
lo = max(0, start - 1)
|
|
require(bool(np.all(edges[start:stop] >= 0)) and bool(np.all(np.diff(edges[lo:stop]) > 0)),
|
|
"Global edge IDs must retain canonical increasing source/edge order")
|
|
return graph
|
|
|
|
|
|
def load_inputs(case_dir):
|
|
spec = json.loads((case_dir / "spec.json").read_text(encoding="utf-8"))
|
|
for name in ("name", "steps", "dt_ms", "duration_ms", "model", "graph_dir", "apl",
|
|
"apl_bundle_dir", "coefficients_path", "record_history", "apl_delay_steps"):
|
|
require(name in spec, "Missing case field: " + name)
|
|
require(type(spec["apl_delay_steps"]) is int and spec["apl_delay_steps"] in (0, 18),
|
|
"apl_delay_steps must be integer 0 or 18")
|
|
steps = spec["steps"]
|
|
require(type(steps) is int and 0 < steps <= 1000 and float(spec["dt_ms"]) == .1 and
|
|
float(spec["duration_ms"]) == steps * .1, "Unexpected steps/dt/duration")
|
|
require(type(spec["record_history"]) is bool, "record_history must be Boolean")
|
|
require(spec["apl"] == {"tau_ms": 20, "gain": .01, "normalization": 1}, "Frozen APL parameters changed")
|
|
for name in ("graph_dir", "apl_bundle_dir", "coefficients_path"):
|
|
require(Path(spec[name]).is_absolute(), "Input path must be absolute: " + name)
|
|
model = spec["model"]
|
|
for name in ("resting_potential_mv", "threshold_mv", "reset_potential_mv", "synaptic_delay_ms",
|
|
"refractory_ms", "w_syn_mv_per_anatomical_synapse"):
|
|
require(name in model and math.isfinite(float(model[name])), "Invalid model field: " + name)
|
|
require(float(model["synaptic_delay_ms"]) == 1.8 and float(model["refractory_ms"]) == 2.2 and
|
|
float(model["w_syn_mv_per_anatomical_synapse"]) == .275, "Frozen delay/ref/W_syn changed")
|
|
coeff = json.loads(Path(spec["coefficients_path"]).read_text(encoding="utf-8"))
|
|
for name in COEFFICIENT_NAMES:
|
|
require(name in coeff and math.isfinite(float(coeff[name])), "Invalid author coefficient: " + name)
|
|
require(coeff["threshold"] == float(model["threshold_mv"]) * .001 and
|
|
coeff["reset"] == float(model["reset_potential_mv"]) * .001, "Coefficient threshold/reset mismatch")
|
|
graph = load_graph(str(Path(spec["graph_dir"]).resolve()))
|
|
n = len(graph["nodes"])
|
|
require(spec["record_history"] == (n < 512), "Fixtures need dense history; full graph must not duplicate dense histories")
|
|
with np.load(case_dir / "requests.npz", allow_pickle=False) as source:
|
|
require(set(source.files) == {"tick", "neuron_idx"}, "Request sparse schema changed")
|
|
ticks, requested = source["tick"].copy(), source["neuron_idx"].copy()
|
|
require(ticks.dtype == np.int32 and requested.dtype == np.int32 and ticks.ndim == 1 and
|
|
ticks.shape == requested.shape, "Invalid sparse request dtype/shape")
|
|
require(bool(np.all((ticks >= 0) & (ticks < steps))), "Request tick outside trial")
|
|
if len(ticks) > 1:
|
|
require(bool(np.all((ticks[1:] > ticks[:-1]) |
|
|
((ticks[1:] == ticks[:-1]) & (requested[1:] > requested[:-1])))),
|
|
"Requests must be sorted unique by tick/global neuron")
|
|
local = np.searchsorted(graph["nodes"], requested)
|
|
require(bool(np.all(local < n)) and np.array_equal(graph["nodes"][local], requested),
|
|
"Requested neuron absent from graph")
|
|
initial = {"initial_v_mv": np.full(n, float(model["resting_potential_mv"]), dtype=np.float64),
|
|
"initial_g_mv": np.zeros(n, dtype=np.float64)}
|
|
initial_path = case_dir / "arrays.npz"
|
|
if initial_path.exists():
|
|
with np.load(initial_path, allow_pickle=False) as source:
|
|
require(set(source.files).issubset(initial), "Unexpected initial-state array")
|
|
for name in source.files:
|
|
value = source[name]
|
|
require(value.dtype == np.float64 and value.shape == (n,) and bool(np.isfinite(value).all()),
|
|
"Invalid initial state: " + name)
|
|
initial[name] = value.copy()
|
|
return spec, graph, coeff, ticks, requested, local, initial
|
|
|
|
|
|
def new_array(path, dtype, shape):
|
|
require(not Path(path).exists(), "Refusing existing output: " + str(path))
|
|
return np.lib.format.open_memmap(path, mode="w+", dtype=dtype, shape=shape)
|
|
|
|
|
|
def run_gpu(case_dir, out_dir, build_root):
|
|
"""Run fresh candidate state; never consume oracle/candidate APL traces.
|
|
|
|
pass_all denotes internal recording/finite integrity only. Independent
|
|
comparison and the original complete representation gate are separate.
|
|
"""
|
|
case_dir, out_dir = Path(case_dir), Path(out_dir)
|
|
require(not out_dir.exists(), "Refusing existing output directory: " + str(out_dir))
|
|
out_dir.mkdir(parents=True, exist_ok=False)
|
|
start, tick, completed = time.perf_counter(), -1, 0
|
|
maps = {}
|
|
try:
|
|
source_provenance = source_audit(out_dir)
|
|
spec, graph, coeff, request_ticks, request_nodes, request_local, initial = load_inputs(case_dir)
|
|
require(torch.cuda.is_available() and torch.cuda.device_count() == 1 and
|
|
"4090" in torch.cuda.get_device_name(), "Only the declared single visible RTX4090 is permitted")
|
|
ext, build = sealed_backend().build_extension(build_root)
|
|
build_done = time.perf_counter()
|
|
apl = sealed_apl().LegacyAPL(spec["apl_bundle_dir"], spec["dt_ms"])
|
|
write_json(out_dir / "apl-component.json", apl.metadata)
|
|
device = apl.device
|
|
torch.cuda.reset_peak_memory_stats(device)
|
|
n, e, steps = len(graph["nodes"]), len(graph["edge_id"]), spec["steps"]
|
|
|
|
def upload(value, dtype):
|
|
return torch.from_numpy(np.array(value, copy=True, order="C")).to(device=device, dtype=dtype).contiguous()
|
|
|
|
indptr, dst = upload(graph["ptr"], torch.int64), upload(graph["dst"], torch.int32)
|
|
global_nodes_gpu = upload(graph["nodes"], torch.int64)
|
|
base_weights = upload(graph["weights"], torch.float64)
|
|
weights = base_weights.clone()
|
|
del base_weights
|
|
local_apl = np.flatnonzero(np.isin(graph["nodes"], apl.apl_globals)).astype(np.int64)
|
|
apl_out_mask = np.isin(graph["src"], local_apl)
|
|
if bool(np.any(apl_out_mask)):
|
|
weights[upload(apl_out_mask, torch.bool)] = 0.0
|
|
effective_hash = hashlib.sha256()
|
|
for offset in range(0, e, 1 << 20):
|
|
effective_hash.update(weights[offset:offset + (1 << 20)].cpu().numpy().astype("<f8", copy=False).tobytes())
|
|
common_kc, local_kc, full_kc = np.intersect1d(graph["nodes"], apl.kc_globals, assume_unique=True, return_indices=True)
|
|
kc_local_gpu, kc_full_gpu = upload(local_kc, torch.int64), upload(full_kc, torch.int64)
|
|
v = upload(initial["initial_v_mv"] * .001, torch.float64)
|
|
g = upload(initial["initial_g_mv"] * .001, torch.float64)
|
|
ref = torch.zeros(n, device=device, dtype=torch.int32)
|
|
counts = torch.zeros(n, device=device, dtype=torch.int32)
|
|
requests = torch.zeros(n, device=device, dtype=torch.uint8)
|
|
ring = torch.zeros((19, n), device=device, dtype=torch.uint8)
|
|
apl_delay = spec["apl_delay_steps"]
|
|
apl_ring = torch.zeros((apl_delay, 4064), device=device, dtype=torch.float32)
|
|
maps["apl_applied"] = new_array(out_dir / "apl-applied.float32.npy", np.float32, (steps, 4064))
|
|
for name, width in (("drive", 2), ("state", 2), ("inhibition", 4064)):
|
|
maps["apl_" + name] = new_array(out_dir / ("apl-" + name + ".float32.npy"), np.float32, (steps, width))
|
|
if spec["record_history"]:
|
|
for name, dtype in (("v", np.float64), ("g", np.float64), ("ref", np.int32)):
|
|
maps[name] = new_array(out_dir / (name + "-history." + np.dtype(dtype).name + ".npy"), dtype, (steps, n))
|
|
maps["pre_" + name] = new_array(out_dir / ("preAPL-" + name + "." + np.dtype(dtype).name + ".npy"), dtype, (steps, n))
|
|
event_steps, event_nodes = [], []
|
|
accepted_requests_total, due_total, accepted_due_total = 0, 0, 0
|
|
queue_summary = queue_function()
|
|
simulation_start = time.perf_counter()
|
|
with (out_dir / "queue.jsonl").open("x", encoding="utf-8", buffering=1) as queue_output, \
|
|
(out_dir / "tick-state-hashes.jsonl").open("x", encoding="utf-8", buffering=1) as hash_output, \
|
|
(out_dir / "requests.csv").open("x", encoding="utf-8", newline="", buffering=1) as request_output:
|
|
writer = csv.DictWriter(request_output, fieldnames=REQUEST_FIELDS)
|
|
writer.writeheader()
|
|
for tick in range(steps):
|
|
requests.zero_()
|
|
begin, end = np.searchsorted(request_ticks, (tick, tick + 1))
|
|
if end > begin:
|
|
requested_local_gpu = upload(request_local[begin:end], torch.int64)
|
|
requests[requested_local_gpu] = 1
|
|
accepted = (ref[requested_local_gpu] <= 1).to(torch.uint8).cpu().numpy()
|
|
accepted_requests_total += int(accepted.sum())
|
|
writer.writerows({"tick": tick, "neuron_idx": int(node), "attempted": 1,
|
|
"accepted": int(ok), "rejected": 1 - int(ok)}
|
|
for node, ok in zip(request_nodes[begin:end], accepted))
|
|
spikes = ring[tick % 19]
|
|
ext.state_step(v, g, ref, requests, spikes, counts,
|
|
*[float(coeff[name]) for name in COEFFICIENT_NAMES], 22)
|
|
expanded_global = np.empty(0, dtype=np.int64)
|
|
accepted_cpu = np.empty(0, dtype=np.uint8)
|
|
if tick >= 18:
|
|
due = ring[(tick - 18) % 19]
|
|
sources = torch.nonzero(due, as_tuple=False).flatten().contiguous()
|
|
if sources.numel():
|
|
degrees = indptr[sources + 1] - indptr[sources]
|
|
offsets = (torch.cumsum(degrees, dim=0) - degrees).contiguous()
|
|
edge_count = int(degrees.sum().item())
|
|
expanded = torch.empty(edge_count, device=device, dtype=torch.int64)
|
|
ext.fill_edges(indptr, sources, offsets, expanded)
|
|
if edge_count:
|
|
destination = dst[expanded]
|
|
# Actual active fanout order, BEFORE stable target grouping.
|
|
expanded_global = graph["edge_id"][expanded.cpu().numpy()].copy()
|
|
accepted_cpu = (ref[destination.long()] == 0).to(torch.uint8).cpu().numpy().copy()
|
|
order = torch.argsort(destination, stable=True)
|
|
ordered_edges, ordered_targets = expanded[order].contiguous(), destination[order]
|
|
boundary = torch.ones(edge_count, device=device, dtype=torch.bool)
|
|
boundary[1:] = ordered_targets[1:] != ordered_targets[:-1]
|
|
starts = torch.nonzero(boundary, as_tuple=False).flatten().contiguous()
|
|
targets = ordered_targets[starts].contiguous()
|
|
ext.apply_arrivals(ordered_edges, starts, targets, weights, g, ref)
|
|
queue = queue_summary(tick, expanded_global, accepted_cpu)
|
|
queue_output.write(json.dumps(queue, allow_nan=False) + "\n")
|
|
due_total += queue["count"]
|
|
accepted_due_total += queue["accepted_count"]
|
|
if spec["record_history"]:
|
|
for name, value in (("v", v), ("g", g), ("ref", ref)):
|
|
maps["pre_" + name][tick] = value.cpu().numpy()
|
|
if n == N_NEURONS:
|
|
full_spikes = spikes
|
|
else:
|
|
apl.spikes.zero_()
|
|
apl.spikes[global_nodes_gpu] = spikes
|
|
full_spikes = apl.spikes
|
|
drive, apl_state, h = apl.advance(full_spikes)
|
|
if apl_delay == 0:
|
|
applied_h = h # Exact legacy identity; no extra arithmetic.
|
|
else:
|
|
slot = tick % apl_delay
|
|
applied_h = apl_ring[slot].clone()
|
|
apl_ring[slot].copy_(h)
|
|
# Mixed precision contract: widen f32 h EXACTLY, THEN multiply
|
|
# in f64, THEN subtract in f64. Do not guard refractory KCs.
|
|
delta_si = applied_h[kc_full_gpu].to(torch.float64) * 0.001
|
|
g[kc_local_gpu] = g[kc_local_gpu] - delta_si
|
|
apl_cpu = {"drive": drive.cpu().numpy().copy(), "state": apl_state.cpu().numpy().copy(),
|
|
"inhibition": h.cpu().numpy().copy(), "applied": applied_h.cpu().numpy().copy()}
|
|
for name, value in apl_cpu.items():
|
|
maps["apl_" + name][tick] = value
|
|
actual = {"v": v.cpu().numpy().copy(), "g": g.cpu().numpy().copy(),
|
|
"ref": ref.cpu().numpy().copy()}
|
|
finite = all(bool(np.isfinite(value).all()) for value in list(actual.values()) + list(apl_cpu.values()))
|
|
row = {"tick": tick, **{name + "_sha256": raw_hash(value, "<i4" if name == "ref" else "<f8")
|
|
for name, value in actual.items()}}
|
|
hash_output.write(json.dumps(row, allow_nan=False) + "\n")
|
|
if spec["record_history"]:
|
|
for name, value in actual.items():
|
|
maps[name][tick] = value
|
|
fired_local = torch.nonzero(spikes, as_tuple=False).flatten().cpu().numpy()
|
|
if len(fired_local):
|
|
event_steps.append(np.full(len(fired_local), tick, dtype=np.int32))
|
|
event_nodes.append(graph["nodes"][fired_local].copy())
|
|
completed += 1
|
|
if not finite:
|
|
with (out_dir / "first-failure-state.npz").open("xb") as target:
|
|
np.savez_compressed(target, tick=np.array(tick, dtype=np.int32), **actual,
|
|
**{"apl_" + name: value for name, value in apl_cpu.items()})
|
|
require(False, "Nonfinite state; first failure saved")
|
|
torch.cuda.synchronize(device)
|
|
|
|
def joined(values):
|
|
return np.concatenate(values).astype(np.int32, copy=False) if values else np.empty(0, dtype=np.int32)
|
|
|
|
state = {"step": joined(event_steps), "neuron_idx": joined(event_nodes), "counts": counts.cpu().numpy(),
|
|
"v": v.cpu().numpy(), "g": g.cpu().numpy(), "ref": ref.cpu().numpy()}
|
|
with (out_dir / "state.npz").open("xb") as target:
|
|
np.savez_compressed(target, **state)
|
|
event_locals = np.searchsorted(graph["nodes"], state["neuron_idx"])
|
|
counts_exact = np.array_equal(np.bincount(event_locals, minlength=n), state["counts"])
|
|
require(counts_exact and completed == steps, "Sparse events/counts/completed ticks mismatch")
|
|
for value in maps.values():
|
|
value.flush()
|
|
# Save the queue state, not an invented future APL/brain decay trace.
|
|
if apl_delay:
|
|
head = steps % apl_delay
|
|
ring_cpu = apl_ring.cpu().numpy()
|
|
pending = np.concatenate((ring_cpu[head:], ring_cpu[:head]), axis=0)
|
|
else:
|
|
pending = np.empty((0, 4064), dtype=np.float32)
|
|
with (out_dir / "apl-pending-tail.float32.npy").open("xb") as target:
|
|
np.save(target, pending, allow_pickle=False)
|
|
timing = audit_sequence(maps["apl_inhibition"], maps["apl_applied"], pending, apl_delay)
|
|
final_apl_state = apl.state.cpu().numpy().copy()
|
|
timing.update({
|
|
"generated_rows": steps,
|
|
"applied_generated_rows": max(steps - apl_delay, 0),
|
|
"pending_shape": [apl_delay, 4064],
|
|
"pending_application_tick_start": steps,
|
|
"pending_application_tick_stop_exclusive": steps + apl_delay,
|
|
"pending_generated_tick_start": max(0, steps - apl_delay),
|
|
"pending_generated_tick_stop_exclusive": steps,
|
|
"generated_trace": "apl-inhibition.float32.npy",
|
|
"applied_trace": "apl-applied.float32.npy",
|
|
"pending_trace": "apl-pending-tail.float32.npy",
|
|
"pending_raw_sha256": raw_hash(pending, "<f4"),
|
|
"final_apl_state_float32": final_apl_state.tolist(),
|
|
"final_apl_state_u32_bits": final_apl_state.view(np.uint32).tolist(),
|
|
"final_apl_state_nonzero": bool(np.any(final_apl_state != 0)),
|
|
"pending_queue_drained": apl_delay == 0,
|
|
"finite_generated_sequence_only": True,
|
|
"future_apl_decay_or_feedback_generated": False,
|
|
"closed_loop_branch_integrals_claimed_equal": False,
|
|
"biological_delay_measurement_claimed": False,
|
|
})
|
|
write_json(out_dir / "apl-timing-audit.json", timing)
|
|
summary = {"case": spec["name"], "complete": True, "pass_all": True,
|
|
"pass_all_scope": "internal finite/recording/event-count integrity only; independent equivalence not claimed",
|
|
"steps": steps, "completed_ticks": completed, "nodes": n, "edges": e, "precision": "float64",
|
|
"apl_precision": "float32", "state_units": "SI volts", "total_spikes": len(state["step"]),
|
|
"requested": len(request_nodes), "accepted_requests": accepted_requests_total,
|
|
"rejected_requests": len(request_nodes) - accepted_requests_total,
|
|
"arriving_edges": due_total, "accepted_arriving_edges": accepted_due_total,
|
|
"counts_match_sparse_events": bool(counts_exact), "all_finite": True,
|
|
"record_history": spec["record_history"], "apl_fast_edges_zeroed": int(apl_out_mask.sum()),
|
|
"kc_targets_present": len(common_kc), "effective_weights_raw_sha256": effective_hash.hexdigest(),
|
|
"apl_component": apl.metadata, "apl_schedule": "end, after threshold/reset and guarded arrivals, unconditional all KC",
|
|
"apl_si_conversion": "applied h32 -> float64 -> multiply float64 0.001 -> separate float64 subtraction",
|
|
"apl_delay_steps": apl_delay, "apl_timing": timing, "source_provenance": source_provenance,
|
|
"pending_generated_count": min(steps, apl_delay),
|
|
"pending_initial_zero_prefix": max(apl_delay - steps, 0),
|
|
"pending_tail_shape": [apl_delay, 4064], "future_generated_decay_tail_included": False,
|
|
"build": build, "sealed_ae_backend_sha256": file_sha256(AE_MODEL / "gpu_backend.py"),
|
|
"shared_apl_component_oracle_limitation": True, "oracle_validation_claim": False,
|
|
"learning": False, "performance_runtime_claim": False, "cpu_sync_each_tick": True,
|
|
"build_or_cache_seconds": build_done - start,
|
|
"diagnostic_simulation_seconds": time.perf_counter() - simulation_start,
|
|
"wall_seconds": time.perf_counter() - start,
|
|
"peak_allocated_vram_bytes": int(torch.cuda.max_memory_allocated(device)),
|
|
"peak_reserved_vram_bytes": int(torch.cuda.max_memory_reserved(device)),
|
|
"input_sha256": {"spec.json": file_sha256(case_dir / "spec.json"),
|
|
"requests.npz": file_sha256(case_dir / "requests.npz"),
|
|
"base_weights.npy": file_sha256(Path(spec["graph_dir"]) / "weights.npy"),
|
|
"coefficients.json": file_sha256(spec["coefficients_path"])}}
|
|
if (case_dir / "arrays.npz").exists():
|
|
summary["input_sha256"]["arrays.npz"] = file_sha256(case_dir / "arrays.npz")
|
|
write_json(out_dir / "summary.json", summary)
|
|
return summary
|
|
except Exception as error:
|
|
for value in maps.values():
|
|
value.flush()
|
|
write_json(out_dir / "error.json", {"complete": False, "pass_all": False,
|
|
"tick_in_progress": tick, "completed_ticks": completed,
|
|
"type": type(error).__name__, "message": str(error), "traceback": traceback.format_exc()})
|
|
raise
|
|
|