536 lines
34 KiB
Python
536 lines
34 KiB
Python
#!/usr/bin/env python3
|
|
"""Phase 4D orchestration: stable Brain I/O API, timing, readout and regression."""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import math
|
|
import os
|
|
import platform
|
|
import shutil
|
|
import time
|
|
from collections import Counter
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import numpy as np
|
|
import pyarrow as pa
|
|
import pyarrow.parquet as pq
|
|
import torch
|
|
import yaml
|
|
|
|
from brain_runtime import (
|
|
ACTIVITY_STREAM_VERSION, API_VERSION, INPUT_BATCH_VERSION,
|
|
BrainRuntime, InputBatch, InputEvent, VisualAdapterV1,
|
|
)
|
|
|
|
|
|
def sha256(path: Path, chunk: int = 8 << 20) -> str:
|
|
h = hashlib.sha256()
|
|
with path.open("rb") as f:
|
|
while data := f.read(chunk): h.update(data)
|
|
return h.hexdigest()
|
|
|
|
|
|
def write_json(path: Path, value: Any) -> None:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(json.dumps(value, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
|
|
|
|
def np_json(value: Any) -> Any:
|
|
if isinstance(value, np.ndarray): return value.tolist()
|
|
if isinstance(value, np.generic): return value.item()
|
|
raise TypeError(type(value).__name__)
|
|
|
|
|
|
def metadata_populations(runtime: BrainRuntime) -> dict[str, np.ndarray]:
|
|
return {k: v.copy() for k, v in runtime.output_populations.items()}
|
|
|
|
|
|
def freeze_populations(exp: Path, runtime: BrainRuntime) -> dict[str, Any]:
|
|
expressions = {
|
|
"descending": "annotation_superclass in {'descending_neuron','descending_neuron_tbc'}",
|
|
"motor_related": "annotation_superclass in {'vnc_motor','cb_motor'}",
|
|
"ascending": "annotation_superclass in {'ascending_neuron','sensory_ascending','sensory_ascending_tbc','efferent_ascending'}",
|
|
}
|
|
nt = runtime.table["neurotransmitter"].to_pylist()
|
|
result: dict[str, Any] = {}
|
|
for name, nodes in metadata_populations(runtime).items():
|
|
table = pa.table({
|
|
"neuron_idx": pa.array(nodes, type=pa.int32()),
|
|
"body_id": pa.array(runtime.body_id[nodes], type=pa.uint64()),
|
|
"superclass": pa.array([runtime.superclass[i] for i in nodes], type=pa.string()),
|
|
"type": pa.array([runtime.neuron_type[i] for i in nodes], type=pa.string()),
|
|
"class": pa.array([runtime.neuron_class[i] for i in nodes], type=pa.string()),
|
|
"neurotransmitter": pa.array([nt[i] for i in nodes], type=pa.string()),
|
|
})
|
|
pq.write_table(table, exp / "populations" / f"{name}.parquet", compression="zstd")
|
|
nts = Counter("<null>" if nt[i] is None else str(nt[i]) for i in nodes)
|
|
result[name] = {
|
|
"selection_expression": expressions[name], "count": int(len(nodes)),
|
|
"neurotransmitter_statistics": dict(sorted(nts.items())),
|
|
"membership_file": str(exp / "populations" / f"{name}.parquet"),
|
|
}
|
|
write_json(exp / "populations/summary.json", result)
|
|
return result
|
|
|
|
|
|
def audit_visual_sides(exp: Path, runtime: BrainRuntime, adapter: VisualAdapterV1) -> dict[str, Any]:
|
|
values = runtime.table["annotation_rootSide"].to_pylist() if "annotation_rootSide" in runtime.table.column_names else [None] * runtime.n
|
|
result: dict[str, Any] = {}
|
|
for mode in ("engineering-first-order", "engineering-second-order"):
|
|
nodes = adapter.population(mode); c = Counter("<null>" if values[i] is None else str(values[i]) for i in nodes)
|
|
coverage = 1.0 - c.get("<null>", 0) / len(nodes)
|
|
reliable = coverage >= 0.90 and c.get("L", 0) > 0 and c.get("R", 0) > 0
|
|
result[mode] = {"field": "annotation_rootSide", "counts": dict(c), "non_null_coverage": coverage, "left_right_patterns_enabled": reliable}
|
|
write_json(exp / "visual-adapter/left-right-audit.json", result)
|
|
return result
|
|
|
|
|
|
def freeze_contracts(exp: Path, cfg: dict[str, Any], runtime: BrainRuntime, adapter: VisualAdapterV1) -> dict[str, Any]:
|
|
input_schema = {
|
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
"title": "MaleCNS InputBatch",
|
|
"version": INPUT_BATCH_VERSION,
|
|
"type": "object",
|
|
"required": ["version", "batch_id", "events"],
|
|
"properties": {
|
|
"version": {"const": INPUT_BATCH_VERSION},
|
|
"batch_id": {"type": "string"},
|
|
"events": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"required": ["target_neuron_idx", "stimulation_mode", "value_kind", "value", "start_ms", "duration_ms", "source_id", "channel_id"],
|
|
"properties": {
|
|
"target_neuron_idx": {"type": "array", "items": {"type": "integer", "minimum": 0, "maximum": 166699}},
|
|
"stimulation_mode": {"enum": ["spike_injection", "external_synaptic_drive"]},
|
|
"value_kind": {"enum": ["binary", "scalar_intensity", "poisson_rate_hz", "signed_modulation"]},
|
|
"value": {"oneOf": [{"type": "number"}, {"type": "array", "items": {"type": "number"}}]},
|
|
"start_ms": {"type": "number", "minimum": 0},
|
|
"duration_ms": {"type": "number", "exclusiveMinimum": 0},
|
|
"source_id": {"type": "string"},
|
|
"channel_id": {"type": "string"},
|
|
"amplitude_mv": {"type": "number"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"timestamp_unit": "biological ms",
|
|
"external_drive_is_connectome_transmission": False,
|
|
}
|
|
write_json(exp / "configs/input-batch.schema.json", input_schema)
|
|
mode_records: dict[str, Any] = {}
|
|
for mode, nodes in adapter.modes.items():
|
|
path = exp / "visual-adapter" / f"{mode}.parquet"
|
|
pq.write_table(pa.table({"neuron_idx": pa.array(nodes.astype(np.int32), type=pa.int32()), "body_id": pa.array(runtime.body_id[nodes], type=pa.uint64())}), path, compression="zstd")
|
|
spec = cfg["visual_adapter"]["modes"][mode]
|
|
mode_records[mode] = {"count": int(len(nodes)), "membership_file": str(path), **spec}
|
|
write_json(exp / "visual-adapter/membership-summary.json", mode_records)
|
|
readout = {
|
|
"api_version": API_VERSION, "methods": ["read_spikes", "read_population", "read_output", "fixed_temporal_bins"],
|
|
"features": ["spike count vector", "firing-rate vector", "active mask", "population aggregate rate", "excitatory/inhibitory activity", "descending feature vector", "motor-related feature vector"],
|
|
"aggregations": ["per-neuron", "per-type", "per-class"], "fixed_bins_ms": cfg["readout"]["bins_ms"],
|
|
"action_decoder": False,
|
|
}
|
|
write_json(exp / "readout/readout-contract.json", readout)
|
|
return {"input_batch_schema": str(exp / "configs/input-batch.schema.json"), "visual_modes": mode_records, "readout_contract": readout}
|
|
|
|
|
|
def patterns(nodes: np.ndarray, cfg: dict[str, Any]) -> dict[str, np.ndarray]:
|
|
rng = np.random.default_rng(int(cfg["synthetic_patterns"]["deterministic_seed"]))
|
|
order = rng.permutation(len(nodes))
|
|
sparse_n = max(1, int(round(len(nodes) * float(cfg["synthetic_patterns"]["sparse_fraction"]))))
|
|
dense_n = max(1, int(round(len(nodes) * float(cfg["synthetic_patterns"]["dense_fraction"]))))
|
|
moving_n = max(1, int(round(len(nodes) * float(cfg["synthetic_patterns"]["moving_fraction"]))))
|
|
def vector(selection: np.ndarray, rate: float) -> np.ndarray:
|
|
out = np.zeros(len(nodes), dtype=np.float32); out[selection] = rate; return out
|
|
rate = float(cfg["visual_adapter"]["synthetic_rate_hz"])
|
|
return {
|
|
"sparse-A": vector(order[:sparse_n], rate),
|
|
"sparse-B": vector(order[sparse_n:2*sparse_n], rate),
|
|
"dense": vector(order[:dense_n], rate),
|
|
"moving": vector(order[2*sparse_n:2*sparse_n+moving_n], rate),
|
|
"zero": np.zeros(len(nodes), dtype=np.float32),
|
|
}
|
|
|
|
|
|
def direct_batch(nodes: np.ndarray, mode: str, rate_hz: float, start_ms: float, duration_ms: float, source: str) -> InputBatch:
|
|
return InputBatch(events=(InputEvent(
|
|
target_neuron_idx=nodes.astype(np.int64), stimulation_mode="spike_injection",
|
|
value_kind="poisson_rate_hz", value=float(rate_hz), start_ms=start_ms,
|
|
duration_ms=duration_ms, source_id=source, channel_id=mode,
|
|
metadata={"biological_fidelity": "reference_population", "external_drive_is_connectome_transmission": False},
|
|
),), batch_id=f"{source}:{mode}:{start_ms}")
|
|
|
|
|
|
def feature(runtime: BrainRuntime, window: float) -> np.ndarray:
|
|
d = runtime.read_population("descending", window)["spike_counts"]
|
|
m = runtime.read_population("motor_related", window)["spike_counts"]
|
|
return np.concatenate([d, m]).astype(np.float32)
|
|
|
|
|
|
def output_stats(runtime: BrainRuntime, window: float) -> dict[str, Any]:
|
|
d = runtime.read_population("descending", window); m = runtime.read_population("motor_related", window)
|
|
return {
|
|
"descending_active": d["active_neurons"], "descending_spikes": d["total_spikes"],
|
|
"motor_active": m["active_neurons"], "motor_spikes": m["total_spikes"],
|
|
"descending_rate_hz": d["aggregate_rate_hz"], "motor_rate_hz": m["aggregate_rate_hz"],
|
|
}
|
|
|
|
|
|
def population_distances(runtime: BrainRuntime, sources: np.ndarray) -> np.ndarray:
|
|
return runtime.runtime_module.p4b.directed_distances(runtime.indptr_np, runtime.indices_np, sources.astype(np.int64), runtime.n)
|
|
|
|
|
|
def propagation_depth(runtime: BrainRuntime, distance: np.ndarray) -> int:
|
|
active = runtime.spike_counts.cpu().numpy() > 0
|
|
values = distance[active & (distance >= 0)]
|
|
return int(values.max(initial=0))
|
|
|
|
|
|
def make_source_batch(cfg: dict[str, Any], runtime: BrainRuntime, adapter: VisualAdapterV1,
|
|
source: str, start: float, duration: float, pattern_cache: dict[str, dict[str, np.ndarray]]) -> tuple[InputBatch, np.ndarray]:
|
|
if source.startswith("engineering-"):
|
|
mode = source
|
|
p = pattern_cache[mode]["sparse-A"]
|
|
batch = adapter.encode(mode, p, start, duration, "phase4d-window", "sparse-A")
|
|
targets = adapter.population(mode)[p != 0]
|
|
return batch, targets
|
|
mask = runtime.population_masks[source]
|
|
nodes = np.flatnonzero(mask).astype(np.int64)
|
|
rate = 10.0 if source == "auditory" else 50.0
|
|
return direct_batch(nodes, source, rate, start, duration, "phase4d-window"), nodes
|
|
|
|
|
|
def window_experiments(exp: Path, cfg: dict[str, Any], runtime: BrainRuntime,
|
|
adapter: VisualAdapterV1, pattern_cache: dict[str, dict[str, np.ndarray]]) -> dict[str, Any]:
|
|
records: list[dict[str, Any]] = []; distances: dict[str, np.ndarray] = {}
|
|
for source in cfg["window_experiment"]["sources"]:
|
|
_, targets = make_source_batch(cfg, runtime, adapter, source, 0.0, 5.0, pattern_cache)
|
|
distances[source] = population_distances(runtime, targets)
|
|
for window in cfg["window_experiment"]["windows_ms"]:
|
|
for seed in cfg["window_experiment"]["seeds"]:
|
|
outcomes = []
|
|
for repeat in range(2):
|
|
runtime.reset(int(seed)); batch, _ = make_source_batch(cfg, runtime, adapter, source, 0.0, float(window), pattern_cache)
|
|
torch.cuda.synchronize(); started = time.perf_counter(); step_result = runtime.step(batch, float(window)); torch.cuda.synchronize(); wall = time.perf_counter() - started
|
|
stats = output_stats(runtime, float(window)); f = feature(runtime, float(window))
|
|
outcomes.append((runtime.read_spikes(), runtime.v.cpu().numpy().copy(), runtime.g.cpu().numpy().copy(), f, stats, step_result, wall))
|
|
a, b = outcomes
|
|
deterministic = np.array_equal(a[0]["step"], b[0]["step"]) and np.array_equal(a[0]["neuron_idx"], b[0]["neuron_idx"]) and np.array_equal(a[3], b[3])
|
|
input_count = int(a[5]["new_spikes"] if source in ("auditory", "olfactory") else a[5]["new_external_drive_events"])
|
|
rec = {
|
|
"source": source, "window_ms": float(window), "seed": int(seed),
|
|
"wall_latency_seconds": a[6], "max_theoretical_control_hz": 1.0 / a[6],
|
|
"realtime_factor_wall_over_biological": a[6] / (float(window) / 1000.0),
|
|
"input_spikes_or_drive_events": input_count,
|
|
"total_spikes": int(len(a[0]["neuron_idx"])),
|
|
"propagation_depth": propagation_depth(runtime, distances[source]),
|
|
**a[4], "feature_l2_norm": float(np.linalg.norm(a[3])),
|
|
"repeat_spike_events_exact": deterministic,
|
|
"repeat_feature_exact": bool(np.array_equal(a[3], b[3])),
|
|
"repeat_final_voltage_max_abs_error": float(np.max(np.abs(a[1] - b[1]))),
|
|
"repeat_final_g_max_abs_error": float(np.max(np.abs(a[2] - b[2]))),
|
|
}
|
|
records.append(rec)
|
|
write_json(exp / "latency/window-trials.json", records)
|
|
summary: dict[str, Any] = {"trial_count": len(records), "all_repeat_deterministic": all(x["repeat_spike_events_exact"] for x in records)}
|
|
for source in cfg["window_experiment"]["sources"]:
|
|
rows = [x for x in records if x["source"] == source]
|
|
summary[source] = {}
|
|
for window in cfg["window_experiment"]["windows_ms"]:
|
|
r = [x for x in rows if x["window_ms"] == float(window)]
|
|
summary[source][str(window)] = {
|
|
"wall_latency_seconds_mean": float(np.mean([x["wall_latency_seconds"] for x in r])),
|
|
"max_theoretical_control_hz_mean": float(np.mean([x["max_theoretical_control_hz"] for x in r])),
|
|
"descending_active_mean": float(np.mean([x["descending_active"] for x in r])),
|
|
"motor_active_mean": float(np.mean([x["motor_active"] for x in r])),
|
|
"feature_l2_norm_mean": float(np.mean([x["feature_l2_norm"] for x in r])),
|
|
}
|
|
def shortest(key: str) -> float | None:
|
|
candidates = sorted({x["window_ms"] for x in records if x["source"].startswith("engineering-") and x[key] > 0})
|
|
return candidates[0] if candidates else None
|
|
summary["shortest_engineering_descending_signal_ms"] = shortest("descending_active")
|
|
summary["shortest_engineering_motor_signal_ms"] = shortest("motor_active")
|
|
write_json(exp / "latency/summary.json", summary)
|
|
return {"records": records, "summary": summary}
|
|
|
|
|
|
def continuous_control(exp: Path, cfg: dict[str, Any], runtime: BrainRuntime,
|
|
adapter: VisualAdapterV1, pattern_cache: dict[str, dict[str, np.ndarray]]) -> dict[str, Any]:
|
|
output: dict[str, Any] = {}
|
|
duration = float(cfg["continuous_control"]["biological_duration_ms"])
|
|
sequence = cfg["continuous_control"]["sequence"]
|
|
mode = "engineering-first-order"
|
|
for period in cfg["continuous_control"]["periods_ms"]:
|
|
period = float(period); runtime.reset(int(cfg["continuous_control"]["seed"]))
|
|
intervals: list[dict[str, Any]] = []; previous = None
|
|
total_wall = 0.0
|
|
for k, start in enumerate(np.arange(0.0, duration, period)):
|
|
pattern_name = sequence[k % len(sequence)]
|
|
values = pattern_cache[mode][pattern_name]
|
|
batch = adapter.encode(mode, values, float(start), period, "continuous-control", pattern_name)
|
|
torch.cuda.synchronize(); t0 = time.perf_counter(); step_result = runtime.step(batch, period); torch.cuda.synchronize(); wall = time.perf_counter() - t0; total_wall += wall
|
|
f = feature(runtime, period); stats = output_stats(runtime, period)
|
|
recent = runtime.read_spikes(period); adapter_nodes = adapter.population(mode)
|
|
target_spikes = int(np.count_nonzero(np.isin(recent["neuron_idx"], adapter_nodes)))
|
|
latency = None
|
|
ev = runtime.read_spikes(period)
|
|
output_members = np.concatenate([runtime.output_populations["descending"], runtime.output_populations["motor_related"]])
|
|
omask = np.isin(ev["neuron_idx"], output_members)
|
|
if np.any(omask): latency = float(ev["timestamp_ms"][omask].min() - start)
|
|
intervals.append({
|
|
"interval": k, "start_ms": float(start), "end_ms": float(start + period), "pattern": pattern_name,
|
|
"wall_seconds": wall, "response_latency_ms": latency, **stats,
|
|
"feature_norm": float(np.linalg.norm(f)),
|
|
"feature_change_l2": None if previous is None else float(np.linalg.norm(f - previous)),
|
|
"input_spikes": step_result["new_spikes"], "drive_events": step_result["new_external_drive_events"],
|
|
"adapter_target_spikes": target_spikes,
|
|
})
|
|
previous = f
|
|
zero = [x for x in intervals if x["pattern"] == "zero"]
|
|
change_latencies = [x["response_latency_ms"] for x in intervals if x["response_latency_ms"] is not None]
|
|
output[str(int(period))] = {
|
|
"period_ms": period, "continuous_state_without_reset": True, "interval_count": len(intervals),
|
|
"wall_seconds": total_wall, "biological_ms": duration,
|
|
"median_response_latency_ms": float(np.median(change_latencies)) if change_latencies else None,
|
|
"max_response_latency_ms": float(np.max(change_latencies)) if change_latencies else None,
|
|
"zero_input_descending_spikes": int(sum(x["descending_spikes"] for x in zero)),
|
|
"zero_input_motor_spikes": int(sum(x["motor_spikes"] for x in zero)),
|
|
"zero_input_adapter_target_spikes": int(sum(x["adapter_target_spikes"] for x in zero)),
|
|
"zero_input_intervals_with_activity": int(sum((x["descending_spikes"] + x["motor_spikes"]) > 0 for x in zero)),
|
|
"intervals": intervals,
|
|
}
|
|
write_json(exp / "continuous-control" / f"period-{int(period)}ms.json", output[str(int(period))])
|
|
write_json(exp / "continuous-control/summary.json", {k: {x: v[x] for x in v if x != "intervals"} for k, v in output.items()})
|
|
return output
|
|
|
|
|
|
def metric_pair(a: np.ndarray, b: np.ndarray) -> dict[str, float]:
|
|
an, bn = float(np.linalg.norm(a)), float(np.linalg.norm(b))
|
|
cosine = float(np.dot(a, b) / (an * bn)) if an and bn else (1.0 if an == bn else 0.0)
|
|
if np.std(a) and np.std(b): corr = float(np.corrcoef(a, b)[0, 1])
|
|
else: corr = 1.0 if np.array_equal(a, b) else 0.0
|
|
aa, bb = a > 0, b > 0; union = np.count_nonzero(aa | bb)
|
|
return {"cosine_distance": 1.0 - cosine, "euclidean_distance": float(np.linalg.norm(a - b)), "correlation": corr, "active_population_overlap_jaccard": float(np.count_nonzero(aa & bb) / union) if union else 1.0}
|
|
|
|
|
|
def separability(exp: Path, cfg: dict[str, Any], runtime: BrainRuntime,
|
|
adapter: VisualAdapterV1, pattern_cache: dict[str, dict[str, np.ndarray]], side_audit: dict[str, Any]) -> dict[str, Any]:
|
|
mode = "engineering-first-order"; pats = dict(pattern_cache[mode])
|
|
pats.pop("zero")
|
|
if side_audit[mode]["left_right_patterns_enabled"]:
|
|
root_side = runtime.table["annotation_rootSide"].to_pylist(); nodes = adapter.population(mode); rate = float(cfg["visual_adapter"]["synthetic_rate_hz"])
|
|
pats["left"] = np.asarray([rate if root_side[i] == "L" else 0.0 for i in nodes], dtype=np.float32)
|
|
pats["right"] = np.asarray([rate if root_side[i] == "R" else 0.0 for i in nodes], dtype=np.float32)
|
|
features: dict[str, list[np.ndarray]] = {}
|
|
trial_rows: list[dict[str, Any]] = []
|
|
for name, values in pats.items():
|
|
features[name] = []
|
|
for seed in (44301, 44302, 44303):
|
|
runtime.reset(seed); batch = adapter.encode(mode, values, 0.0, 100.0, "separability", name)
|
|
runtime.step(batch, 100.0); f = feature(runtime, 100.0); features[name].append(f)
|
|
trial_rows.append({"pattern": name, "seed": seed, "active_channels": int(np.count_nonzero(values)), "feature_norm": float(np.linalg.norm(f)), **output_stats(runtime, 100.0)})
|
|
means = {k: np.mean(v, axis=0) for k, v in features.items()}
|
|
pairwise: dict[str, Any] = {}
|
|
names = sorted(means)
|
|
for i, a in enumerate(names):
|
|
for b in names[i+1:]: pairwise[f"{a}__{b}"] = metric_pair(means[a], means[b])
|
|
within = [float(np.linalg.norm(x - means[k])) for k, xs in features.items() for x in xs]
|
|
between = [v["euclidean_distance"] for v in pairwise.values()]
|
|
matrix = np.stack([x for k in names for x in features[k]])
|
|
centered = matrix - matrix.mean(axis=0, keepdims=True)
|
|
u, s, _ = np.linalg.svd(centered, full_matrices=False)
|
|
coords = u[:, :2] * s[:2]
|
|
labels = [k for k in names for _ in features[k]]
|
|
pca_table = pa.table({"pattern": labels, "pc1": coords[:, 0], "pc2": coords[:, 1]})
|
|
pq.write_table(pca_table, exp / "separability/pca-coordinates.parquet", compression="zstd")
|
|
result = {
|
|
"classifier_trained": False, "action_decoder_created": False,
|
|
"patterns": names, "trial_statistics": trial_rows, "pairwise": pairwise,
|
|
"within_condition_distance_mean": float(np.mean(within)),
|
|
"between_condition_distance_mean": float(np.mean(between)),
|
|
"between_to_within_ratio": float(np.mean(between) / np.mean(within)) if np.mean(within) else None,
|
|
"all_pairwise_mean_vectors_distinct": all(v["euclidean_distance"] > 0 for v in pairwise.values()),
|
|
"pca_usage": "offline visualization analysis only; not an action decoder",
|
|
}
|
|
write_json(exp / "separability/summary.json", result)
|
|
return result
|
|
|
|
|
|
def api_tests(exp: Path, runtime: BrainRuntime) -> dict[str, Any]:
|
|
nodes = np.flatnonzero(runtime.population_masks["auditory"]).astype(np.int64)
|
|
batch20 = direct_batch(nodes, "auditory", 10, 0.0, 20.0, "api-test")
|
|
runtime.reset(44400); runtime.step(batch20, 20.0); one = runtime.read_spikes(); one_v = runtime.v.cpu().numpy().copy()
|
|
runtime.reset(44400); runtime.step(batch20, 10.0); checkpoint = runtime.checkpoint_state(); runtime.step(batch20, 10.0); two = runtime.read_spikes(); two_v = runtime.v.cpu().numpy().copy()
|
|
runtime.restore_state(checkpoint); runtime.step(batch20, 10.0); replay = runtime.read_spikes(); replay_v = runtime.v.cpu().numpy().copy()
|
|
per_neuron = runtime.read_output({"population": "descending", "aggregation": "per-neuron"}, 20.0)
|
|
per_type = runtime.read_output({"population": "descending", "aggregation": "per-type"}, 20.0)
|
|
per_class = runtime.read_output({"population": "motor_related", "aggregation": "per-class"}, 20.0)
|
|
bins = {str(x): runtime.fixed_temporal_bins("descending", float(x), 20.0)["spike_counts"].tolist() for x in runtime.cfg["readout"]["bins_ms"] if float(x) <= 20.0}
|
|
result = {
|
|
"reset_same_seed_continuous_chunk_spikes_exact": bool(np.array_equal(one["step"], two["step"]) and np.array_equal(one["neuron_idx"], two["neuron_idx"])),
|
|
"continuous_step_does_not_reset_clock": runtime.simulation_time_ms == 20.0,
|
|
"checkpoint_restore_spikes_exact": bool(np.array_equal(two["step"], replay["step"]) and np.array_equal(two["neuron_idx"], replay["neuron_idx"])),
|
|
"checkpoint_restore_voltage_exact": bool(np.array_equal(two_v, replay_v)),
|
|
"single_vs_chunk_voltage_max_abs_error": float(np.max(np.abs(one_v - two_v))),
|
|
"timestamps_present": "timestamp_ms" in replay,
|
|
"per_neuron_readout_shape": int(len(per_neuron["neuron_idx"])),
|
|
"per_type_aggregation_labels": int(len(per_type["features"])),
|
|
"per_class_aggregation_labels": int(len(per_class["features"])),
|
|
"fixed_temporal_bins_tested": bins,
|
|
"readout_modes_passed": len(per_neuron["neuron_idx"]) == len(runtime.output_populations["descending"]) and len(per_type["features"]) > 0 and len(per_class["features"]) > 0,
|
|
"all_passed": False,
|
|
}
|
|
result["all_passed"] = all(result[k] for k in ("reset_same_seed_continuous_chunk_spikes_exact", "continuous_step_does_not_reset_clock", "checkpoint_restore_spikes_exact", "checkpoint_restore_voltage_exact", "timestamps_present", "readout_modes_passed"))
|
|
write_json(exp / "audit/api-tests.json", result)
|
|
return result
|
|
|
|
|
|
def regression(exp: Path, cfg: dict[str, Any], runtime: BrainRuntime) -> dict[str, Any]:
|
|
root = Path(cfg["paths"]["phase4c_root"]) / "regression/reference"
|
|
results: dict[str, Any] = {}; tolerance = float(cfg["regression"]["float_absolute_tolerance"])
|
|
for spec in cfg["regression"]["reference_trials"]:
|
|
name = spec["name"]; ref_dir = root / name
|
|
events = pq.read_table(ref_dir / "spike-events.parquet", columns=["step", "neuron_idx"])
|
|
ref_steps = events["step"].to_numpy(); ref_nodes = events["neuron_idx"].to_numpy()
|
|
ref_counts = np.load(ref_dir / "per-neuron-spike-counts.int32.npy")
|
|
ref_v = np.load(ref_dir / "final-voltage.float32.npy"); ref_g = np.load(ref_dir / "final-g.float32.npy")
|
|
nodes = np.flatnonzero(runtime.population_masks[spec["population"]]).astype(np.int64)
|
|
batch = direct_batch(nodes, spec["population"], float(spec["rate_hz"]), 0.0, float(spec["duration_ms"]), "phase4c-regression")
|
|
runtime.reset(int(spec["seed"])); t0 = time.perf_counter(); runtime.step(batch, float(spec["duration_ms"])); wall = time.perf_counter() - t0
|
|
got = runtime.read_spikes(); counts = runtime.spike_counts.cpu().numpy()
|
|
vd = np.abs(runtime.v.cpu().numpy() - ref_v); gd = np.abs(runtime.g.cpu().numpy() - ref_g)
|
|
record = {
|
|
"spike_sequence_exact": bool(np.array_equal(ref_steps, got["step"]) and np.array_equal(ref_nodes, got["neuron_idx"])),
|
|
"spike_counts_exact": bool(np.array_equal(ref_counts, counts)),
|
|
"final_voltage_max_abs_error": float(vd.max(initial=0)), "final_voltage_mean_abs_error": float(vd.mean()),
|
|
"final_g_max_abs_error": float(gd.max(initial=0)), "final_g_mean_abs_error": float(gd.mean()),
|
|
"state_within_phase4c_tolerance": bool(vd.max(initial=0) <= tolerance and gd.max(initial=0) <= tolerance),
|
|
"wall_seconds": wall, "event_count": int(len(got["neuron_idx"])),
|
|
}
|
|
record["passed"] = record["spike_sequence_exact"] and record["spike_counts_exact"] and record["state_within_phase4c_tolerance"]
|
|
results[name] = record
|
|
write_json(exp / "regression" / f"{name}.json", record)
|
|
write_json(exp / "regression/summary.json", {"all_passed": all(x["passed"] for x in results.values()), "trials": results})
|
|
return results
|
|
|
|
|
|
def stream_contract(exp: Path, runtime: BrainRuntime) -> dict[str, Any]:
|
|
schema = {
|
|
"$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MaleCNS Activity Packet",
|
|
"contract_version": ACTIVITY_STREAM_VERSION, "type": "object",
|
|
"required": ["contract_version", "simulation_time_ms", "sequence_id", "spike_neuron_idx", "population_aggregates", "active_neuron_count", "input_channel_metadata"],
|
|
"properties": {
|
|
"simulation_time_ms": {"type": "number"}, "sequence_id": {"type": "integer"},
|
|
"spike_neuron_idx": {"type": "array", "items": {"type": "integer"}},
|
|
"spike_timestamp_ms": {"type": "array", "items": {"type": "number"}},
|
|
"population_aggregates": {"type": "object"}, "active_neuron_count": {"type": "integer"},
|
|
"input_channel_metadata": {"type": "object"}, "representation": {"const": "event-only"},
|
|
},
|
|
"dense_time_by_neuron_matrix_forbidden": True,
|
|
"supported_modes": ["event-only stream", "temporal aggregation", "saved-event replay"],
|
|
}
|
|
write_json(exp / "activity-stream/activity-packet.schema.json", schema)
|
|
packet = runtime.activity_packet(20.0, {"source": "contract-test"})
|
|
path = exp / "activity-stream/replay.jsonl"
|
|
path.write_text(json.dumps(packet, default=np_json) + "\n", encoding="utf-8")
|
|
replay = json.loads(path.read_text().splitlines()[0])
|
|
result = {"contract_frozen": True, "version": ACTIVITY_STREAM_VERSION, "event_only": True, "replay_exact": replay == json.loads(json.dumps(packet, default=np_json)), "schema": str(exp / "activity-stream/activity-packet.schema.json")}
|
|
write_json(exp / "activity-stream/replay-test.json", result)
|
|
return result
|
|
|
|
|
|
def freeze_io(exp: Path, cfg: dict[str, Any], code_dir: Path, api_tests_result: dict[str, Any]) -> dict[str, Any]:
|
|
root = Path(cfg["paths"]["model_root"]) / "io-v1"; root.mkdir(parents=True, exist_ok=True)
|
|
for name in ("VERSION.json", "MANIFEST.json", "checksums.sha256"):
|
|
path = root / name
|
|
if path.exists(): path.chmod(0o644)
|
|
sources = [code_dir / "brain_runtime.py", code_dir / "phase4d.py", code_dir / "phase4d.yaml"]
|
|
for src in sources:
|
|
dst = root / src.name
|
|
if dst.exists(): dst.chmod(0o644)
|
|
shutil.copy2(src, dst); dst.chmod(0o444)
|
|
version = {
|
|
"name": cfg["names"]["io_api"], "api_version": API_VERSION,
|
|
"input_batch_version": INPUT_BATCH_VERSION, "activity_stream_version": ACTIVITY_STREAM_VERSION,
|
|
"runtime_dependency": cfg["names"]["runtime"], "game_agnostic": True,
|
|
"api_methods": ["reset", "step", "read_spikes", "read_population", "read_output", "get_state_summary", "checkpoint_state", "restore_state"],
|
|
"continuous_gpu_state": True, "api_tests": api_tests_result,
|
|
"prohibitions": cfg["prohibitions"],
|
|
}
|
|
write_json(root / "VERSION.json", version); (root / "VERSION.json").chmod(0o444)
|
|
files = sorted(p for p in root.iterdir() if p.is_file() and p.name not in ("MANIFEST.json", "checksums.sha256"))
|
|
manifest = {"artifact": "io-v1", "files": [{"path": p.name, "bytes": p.stat().st_size, "sha256": sha256(p)} for p in files]}
|
|
write_json(root / "MANIFEST.json", manifest); (root / "MANIFEST.json").chmod(0o444)
|
|
targets = files + [root / "MANIFEST.json"]
|
|
(root / "checksums.sha256").write_text("".join(f"{sha256(p)} {p.name}\n" for p in targets), encoding="utf-8"); (root / "checksums.sha256").chmod(0o444)
|
|
return version
|
|
|
|
|
|
def seal(exp: Path, cfg: dict[str, Any], code: list[Path], input_files: list[Path], summary: dict[str, Any]) -> None:
|
|
controls = {exp / "manifests/manifest.json", exp / "manifests/checksums.sha256"}
|
|
outputs = sorted(p for p in exp.rglob("*") if p.is_file() and p not in controls)
|
|
manifest = {
|
|
"phase": "4D", "status": summary["status"], "created_unix_time": time.time(),
|
|
"software": {"python": platform.python_version(), "torch": torch.__version__, "numpy": np.__version__, "pyarrow": pa.__version__},
|
|
"code": [{"path": str(p), "sha256": sha256(p)} for p in code],
|
|
"immutable_inputs": [{"path": str(p), "sha256": sha256(p)} for p in input_files],
|
|
"outputs": [{"path": str(p), "bytes": p.stat().st_size, "sha256": sha256(p)} for p in outputs],
|
|
"prohibitions": cfg["prohibitions"],
|
|
}
|
|
write_json(exp / "manifests/manifest.json", manifest); targets = outputs + [exp / "manifests/manifest.json"]
|
|
(exp / "manifests/checksums.sha256").write_text("".join(f"{sha256(p)} {p.relative_to(exp)}\n" for p in targets), encoding="utf-8")
|
|
|
|
|
|
def main() -> None:
|
|
ap = argparse.ArgumentParser(); ap.add_argument("--config", required=True, type=Path); args = ap.parse_args()
|
|
cfg = yaml.safe_load(args.config.read_text(encoding="utf-8")); exp = Path(cfg["paths"]["experiment_root"])
|
|
for sub in ("configs", "populations", "visual-adapter", "readout", "latency", "continuous-control", "separability", "activity-stream", "regression", "audit", "compiler-reports/extension-build", "manifests"):
|
|
(exp / sub).mkdir(parents=True, exist_ok=True)
|
|
shutil.copy2(args.config, exp / "configs/phase4d.yaml")
|
|
runtime_root = Path(cfg["paths"]["runtime_root"]); phase4c = Path(cfg["paths"]["phase4c_root"])
|
|
immutable = [runtime_root / "VERSION.json", runtime_root / "checksums.sha256", phase4c / "manifests/checksums.sha256", phase4c / "visual-audit/summary.json"]
|
|
before = {str(p): sha256(p) for p in immutable}
|
|
runtime = BrainRuntime(args.config, exp / "audit/extension-build")
|
|
adapter = VisualAdapterV1(cfg, phase4c)
|
|
population_summary = freeze_populations(exp, runtime)
|
|
contracts = freeze_contracts(exp, cfg, runtime, adapter)
|
|
side = audit_visual_sides(exp, runtime, adapter)
|
|
pattern_cache = {mode: patterns(adapter.population(mode), cfg) for mode in ("engineering-first-order", "engineering-second-order")}
|
|
pattern_summary = {mode: {name: int(np.count_nonzero(v)) for name, v in pats.items()} for mode, pats in pattern_cache.items()}
|
|
write_json(exp / "visual-adapter/synthetic-patterns.json", pattern_summary)
|
|
write_json(exp / "visual-adapter/modes.json", cfg["visual_adapter"])
|
|
api = api_tests(exp, runtime)
|
|
windows = window_experiments(exp, cfg, runtime, adapter, pattern_cache)
|
|
continuous = continuous_control(exp, cfg, runtime, adapter, pattern_cache)
|
|
sep = separability(exp, cfg, runtime, adapter, pattern_cache, side)
|
|
reg = regression(exp, cfg, runtime)
|
|
stream = stream_contract(exp, runtime)
|
|
after = {str(p): sha256(p) for p in immutable}
|
|
if before != after: raise RuntimeError("Phase 4C/runtime-v1 immutable input changed")
|
|
reg_pass = all(x["passed"] for x in reg.values())
|
|
status = "PASS" if api["all_passed"] and reg_pass and stream["contract_frozen"] else "FAILED"
|
|
summary = {
|
|
"status": status, "brain_runtime_api_frozen": api["all_passed"], "api_tests": api,
|
|
"visual_adapter_modes_complete": True, "output_populations": population_summary,
|
|
"contracts": contracts,
|
|
"window_experiments": windows["summary"],
|
|
"continuous_control": {k: {x: v[x] for x in v if x != "intervals"} for k, v in continuous.items()},
|
|
"separability": sep, "activity_stream": stream,
|
|
"phase4c_regression_all_passed": reg_pass, "phase4c_regression": reg,
|
|
"immutable_inputs_unchanged": True, "left_right_audit": side,
|
|
"no_stable_retro_rom_buttons_training_or_web": True,
|
|
}
|
|
write_json(exp / "audit/summary.json", summary)
|
|
code_dir = Path(__file__).resolve().parent
|
|
io_version = freeze_io(exp, cfg, code_dir, api); summary["io_version"] = io_version
|
|
write_json(exp / "audit/summary.json", summary)
|
|
seal(exp, cfg, [code_dir / "brain_runtime.py", Path(__file__).resolve(), args.config.resolve()], immutable, summary)
|
|
if status != "PASS": raise RuntimeError("Phase 4D validation failed; see audit/summary.json")
|
|
|
|
|
|
if __name__ == "__main__": main()
|