Archive MaleCNS research sources, provenance, bibliography and immutable data inventory

This commit is contained in:
Digital Drosophila Archive
2026-09-29 03:38:32 +08:00
commit 3981628ff8
682 changed files with 161475 additions and 0 deletions
@@ -0,0 +1,37 @@
{
"file_count": 6,
"files": [
{
"bytes": 229,
"path": "VERSION.json",
"sha256": "d98a4074a0523cb1e767a2fca8df909dad74ba541dd4edf58ebbf34f0e196837"
},
{
"bytes": 19442,
"path": "__pycache__/brain_runtime.cpython-310.pyc",
"sha256": "d80d8ecf0a35cc90a3f063a2362938f853d98ed64c88ba4c7864f793813b6117"
},
{
"bytes": 23822,
"path": "brain_runtime.py",
"sha256": "1c4e3e6f06708bfec30b24e92297f320b2b8b87ebbbe6da501f5f2db4f74762a"
},
{
"bytes": 37799,
"path": "phase4f.py",
"sha256": "5524ac0565b9e2a2f96d7a062109c27fe64365fcaaef44ee7b7ded7d3318fe50"
},
{
"bytes": 4359,
"path": "phase4f.yaml",
"sha256": "52393d2c9ad019ceb61de9168dc809ec52aec72b3c66f1b9708d22c0808f678d"
},
{
"bytes": 3795,
"path": "phase4f_extract_retinotopy.py",
"sha256": "6df36ecf4b6aa5565c1f3c10345fe997a2c3eefe88e42e0b194be272ce83e233"
}
],
"root": "/data/digital-drosophila/models/male-cns-lif/visual-encoder-v1",
"total_bytes": 89446
}
@@ -0,0 +1,9 @@
{
"brain_frozen": true,
"candidate_name": null,
"connectome_changed": false,
"lif_semantics_changed": false,
"name": "MaleCNS-VisualEncoder-v1",
"status": "FAIL-NO-V1-CANDIDATE",
"transmitter_sign_changed": false
}
@@ -0,0 +1,415 @@
#!/usr/bin/env python3
"""Stable game-agnostic Brain I/O API for MaleCNS-LIF-runtime-v1."""
from __future__ import annotations
import copy
import json
import math
import sys
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Iterable
import numpy as np
import pyarrow.parquet as pq
import torch
import yaml
API_VERSION = "1.0.0"
INPUT_BATCH_VERSION = "1.0.0"
ACTIVITY_STREAM_VERSION = "1.0.0"
@dataclass(frozen=True)
class InputEvent:
target_neuron_idx: np.ndarray
stimulation_mode: str
value_kind: str
value: float | np.ndarray
start_ms: float
duration_ms: float
source_id: str
channel_id: str
amplitude_mv: float = 0.0
metadata: dict[str, Any] = field(default_factory=dict)
def validate(self, neuron_count: int, dt_ms: float) -> None:
target = np.asarray(self.target_neuron_idx)
if target.ndim != 1 or target.dtype.kind not in "iu":
raise ValueError("target_neuron_idx must be a one-dimensional integer array")
if len(target) and (int(target.min()) < 0 or int(target.max()) >= neuron_count):
raise ValueError("target neuron_idx outside canonical range")
if len(np.unique(target)) != len(target):
raise ValueError("target neuron_idx values must be unique")
if self.stimulation_mode not in {"spike_injection", "external_synaptic_drive"}:
raise ValueError(f"unsupported stimulation_mode {self.stimulation_mode!r}")
if self.value_kind not in {"binary", "scalar_intensity", "poisson_rate_hz", "signed_modulation"}:
raise ValueError(f"unsupported value_kind {self.value_kind!r}")
if self.start_ms < 0 or self.duration_ms <= 0:
raise ValueError("timestamps/duration must be non-negative/positive")
for x in (self.start_ms, self.duration_ms):
if not math.isclose(x / dt_ms, round(x / dt_ms), abs_tol=1e-8):
raise ValueError(f"{x} ms is not aligned to dt={dt_ms} ms")
value = np.asarray(self.value)
if value.ndim > 1 or (value.ndim == 1 and len(value) not in (1, len(target))):
raise ValueError("value must be scalar or match target count")
if self.value_kind == "poisson_rate_hz" and np.any(value < 0):
raise ValueError("Poisson rate cannot be negative")
@dataclass(frozen=True)
class InputBatch:
events: tuple[InputEvent, ...]
batch_id: str
version: str = INPUT_BATCH_VERSION
metadata: dict[str, Any] = field(default_factory=dict)
def validate(self, neuron_count: int, dt_ms: float) -> None:
if self.version != INPUT_BATCH_VERSION:
raise ValueError(f"InputBatch version must be {INPUT_BATCH_VERSION}")
for event in self.events:
event.validate(neuron_count, dt_ms)
class VisualAdapterV1:
"""Maps abstract channels to audited visual hierarchy populations."""
def __init__(self, config: dict[str, Any], phase4c_root: Path):
self.config = config
self.phase4c_root = phase4c_root
self.modes: dict[str, np.ndarray] = {}
for mode, spec in config["visual_adapter"]["modes"].items():
population = spec["hierarchy_population"]
path = phase4c_root / "visual-audit" / f"hierarchy-{population}.parquet"
if not path.is_file():
raise FileNotFoundError(path)
self.modes[mode] = pq.read_table(path, columns=["neuron_idx"])["neuron_idx"].to_numpy().astype(np.int64)
def population(self, mode: str) -> np.ndarray:
if mode not in self.modes:
raise KeyError(mode)
return self.modes[mode].copy()
def encode(
self, mode: str, channel_values: np.ndarray, start_ms: float, duration_ms: float,
source_id: str, channel_id: str, value_kind: str = "poisson_rate_hz",
) -> InputBatch:
if mode not in self.modes:
raise KeyError(mode)
spec = self.config["visual_adapter"]["modes"][mode]
targets = self.modes[mode]
values = np.asarray(channel_values, dtype=np.float32)
if values.shape != (len(targets),):
raise ValueError(f"visual channel must have shape {(len(targets),)}, got {values.shape}")
selected = np.flatnonzero(values != 0).astype(np.int64)
target = targets[selected]
selected_values = values[selected]
event = InputEvent(
target_neuron_idx=target,
stimulation_mode=spec["input_mode"],
value_kind=value_kind,
value=selected_values,
amplitude_mv=float(self.config["visual_adapter"]["external_drive_mv"]),
start_ms=start_ms,
duration_ms=duration_ms,
source_id=source_id,
channel_id=channel_id,
metadata={
"adapter": self.config["names"]["visual_adapter"],
"adapter_mode": mode,
"biological_fidelity": spec["biological_fidelity"],
"connectome_weights_modified": False,
},
)
return InputBatch(events=(event,), batch_id=f"{source_id}:{channel_id}:{start_ms}", metadata={"adapter_mode": mode})
class BrainRuntime:
"""Persistent exact-state BrainRuntime API. It has no game-specific dependency."""
def __init__(self, config_path: Path, extension_build_dir: Path):
self.config_path = config_path.resolve()
self.cfg = yaml.safe_load(self.config_path.read_text(encoding="utf-8"))
self.dt = float(self.cfg["model"]["dt_ms"])
self.n = 166700
self.model_root = Path(self.cfg["paths"]["model_root"])
self.runtime_root = Path(self.cfg["paths"]["runtime_root"])
self.input_root = Path(self.cfg["paths"]["connectome_root"])
runtime_module_dir = str(self.runtime_root)
if runtime_module_dir not in sys.path:
sys.path.insert(0, runtime_module_dir)
import phase4c as runtime_module
self.runtime_module = runtime_module
p4cfg = yaml.safe_load((self.runtime_root / "phase4c.yaml").read_text(encoding="utf-8"))
self.paths = runtime_module.exact_paths(p4cfg)
self.indptr_np, self.indices_np, self.raw_np = runtime_module.p4b.validate_graph(p4cfg, self.paths)
self.table = runtime_module.full_metadata(self.paths["metadata"])
self.population_masks = runtime_module.populations(self.table)
if not torch.cuda.is_available() or torch.cuda.device_count() != 1:
raise RuntimeError("BrainRuntime requires exactly one visible CUDA GPU")
self.device = torch.device("cuda:0")
props = torch.cuda.get_device_properties(self.device)
if self.cfg["device"]["required_name_substring"] not in props.name:
raise RuntimeError(f"required RTX 4090, got {props.name}")
extension_build_dir.mkdir(parents=True, exist_ok=True)
build_experiment_root = extension_build_dir.parent.parent
(build_experiment_root / "compiler-reports/extension-build").mkdir(parents=True, exist_ok=True)
self.extension, report = runtime_module.build_extension(self.runtime_root, build_experiment_root)
if self.extension is None:
raise RuntimeError(f"runtime-v1 extension unavailable: {report}")
base = runtime_module.Runtime(p4cfg, self.table, self.indptr_np, self.indices_np, self.raw_np, self.device, self.extension)
self.indptr, self.indices, self.weights = base.indptr, base.indices, base.weights
self.signs, self.superclass = base.signs, base.superclass
self.neuron_type = np.asarray(self.table["annotation_type"].to_pylist(), dtype=object)
self.neuron_class = np.asarray(self.table["annotation_class"].to_pylist(), dtype=object)
self.body_id = self.table["body_id"].to_numpy().astype(np.uint64)
m = self.cfg["model"]
self.delay_steps = int(round(float(m["synaptic_delay_ms"]) / self.dt))
self.queue_len = self.delay_steps + 1
self.refractory_steps = int(round(float(m["refractory_ms"]) / self.dt))
self.alpha = self.dt / float(m["membrane_tau_ms"])
self.g_decay = 1.0 - self.dt / float(m["synaptic_tau_ms"])
self.rest = float(m["resting_potential_mv"])
self.reset_value = float(m["reset_potential_mv"])
self.threshold = float(m["threshold_mv"])
self.output_populations = self._output_populations()
self.reset(0)
def _output_populations(self) -> dict[str, np.ndarray]:
s = self.superclass
return {
"descending": np.flatnonzero(np.isin(s, ["descending_neuron", "descending_neuron_tbc"])).astype(np.int32),
"motor_related": np.flatnonzero(np.isin(s, ["vnc_motor", "cb_motor"])).astype(np.int32),
"ascending": np.flatnonzero(np.isin(s, ["ascending_neuron", "sensory_ascending", "sensory_ascending_tbc", "efferent_ascending"])).astype(np.int32),
}
def reset(self, seed: int, initial_state: dict[str, Any] | None = None) -> dict[str, Any]:
self.seed = int(seed)
self.generator = torch.Generator(device=self.device); self.generator.manual_seed(self.seed)
self.v = torch.full((self.n,), self.rest, dtype=torch.float32, device=self.device)
self.g = torch.zeros(self.n, dtype=torch.float32, device=self.device)
self.refractory = torch.zeros(self.n, dtype=torch.int32, device=self.device)
self.delay_queue = torch.zeros((self.queue_len, self.n), dtype=torch.float32, device=self.device)
self.spike_counts = torch.zeros(self.n, dtype=torch.int32, device=self.device)
self.simulation_step = 0; self.sequence_id = 0
self.spike_steps: list[np.ndarray] = []; self.spike_nodes: list[np.ndarray] = []
self.spike_origins: list[np.ndarray] = []
self.drive_steps: list[np.ndarray] = []; self.drive_nodes: list[np.ndarray] = []
self.drive_values: list[np.ndarray] = []; self.input_audit: list[dict[str, Any]] = []
if initial_state is not None:
self.restore_state(initial_state)
return self.get_state_summary()
@property
def simulation_time_ms(self) -> float:
return self.simulation_step * self.dt
def _event_values(self, event: InputEvent, targets: torch.Tensor) -> torch.Tensor:
value = np.asarray(event.value, dtype=np.float32)
if value.ndim == 0:
return torch.full((len(targets),), float(value), dtype=torch.float32, device=self.device)
return torch.as_tensor(value, dtype=torch.float32, device=self.device)
def _active(self, event: InputEvent, time_ms: float) -> bool:
return event.start_ms - 1e-9 <= time_ms < event.start_ms + event.duration_ms - 1e-9
def _hybrid_edges(self, sources: torch.Tensor) -> torch.Tensor:
starts = self.indptr[sources]
degrees = self.indptr[sources + 1] - starts
cumulative = torch.cumsum(degrees, 0)
total = int(cumulative[-1].item()) if cumulative.numel() else 0
output = torch.empty(total, dtype=torch.int64, device=self.device)
if total:
self.extension.fill_edges(self.indptr, sources, cumulative - degrees, output)
return output
def step(self, input_batch: InputBatch | None, biological_ms: float) -> dict[str, Any]:
steps = int(round(float(biological_ms) / self.dt))
if not math.isclose(steps * self.dt, float(biological_ms), abs_tol=1e-8) or steps <= 0:
raise ValueError("biological_ms must be a positive multiple of dt")
batch = input_batch or InputBatch(events=(), batch_id="zero-input")
batch.validate(self.n, self.dt)
batch_start = self.simulation_time_ms
prepared = [(e, torch.as_tensor(e.target_neuron_idx, dtype=torch.int64, device=self.device)) for e in batch.events]
for event in batch.events:
self.input_audit.append({
"batch_id": batch.batch_id, "source_id": event.source_id, "channel_id": event.channel_id,
"mode": event.stimulation_mode, "value_kind": event.value_kind,
"start_ms": event.start_ms, "duration_ms": event.duration_ms,
"target_count": int(len(event.target_neuron_idx)), "metadata": event.metadata,
"external_drive_is_connectome_transmission": False,
})
start_event_count = sum(len(x) for x in self.spike_nodes)
start_drive_count = sum(len(x) for x in self.drive_nodes)
for _ in range(steps):
global_step = self.simulation_step; time_ms = self.simulation_time_ms
slot = global_step % self.queue_len
self.g.mul_(self.g_decay).add_(self.delay_queue[slot]); self.delay_queue[slot].zero_()
spike_requests: list[tuple[InputEvent, torch.Tensor, torch.Tensor]] = []
for event, targets in prepared:
if not self._active(event, time_ms) or targets.numel() == 0:
continue
values = self._event_values(event, targets)
if event.value_kind == "poisson_rate_hz":
selected = torch.rand(len(targets), generator=self.generator, device=self.device) < (values * self.dt / 1000.0)
elif event.value_kind == "binary":
selected = values != 0
elif event.value_kind == "scalar_intensity":
selected = values != 0
else: # signed_modulation
selected = torch.rand(len(targets), generator=self.generator, device=self.device) < (torch.abs(values) * self.dt / 1000.0)
if event.stimulation_mode == "external_synaptic_drive":
chosen = targets[selected]
if chosen.numel():
signed_value = values[selected] * float(event.amplitude_mv)
if event.value_kind == "poisson_rate_hz":
signed_value = torch.full_like(signed_value, float(event.amplitude_mv))
self.g.index_add_(0, chosen, signed_value)
node_np = chosen.cpu().numpy().astype(np.int32, copy=True)
val_np = signed_value.cpu().numpy().astype(np.float32, copy=True)
self.drive_steps.append(np.full(len(node_np), global_step, dtype=np.int32))
self.drive_nodes.append(node_np); self.drive_values.append(val_np)
else:
spike_requests.append((event, targets, selected))
self.refractory.clamp_min_(1).sub_(1)
available = self.refractory == 0
self.v[available] += self.alpha * (self.g[available] - (self.v[available] - self.rest))
self.v[~available] = self.reset_value
natural = available & (self.v >= self.threshold)
spikes = natural.clone()
injected = torch.zeros(self.n, dtype=torch.bool, device=self.device)
for _event, targets, selected in spike_requests:
accepted = selected & available[targets]
chosen = targets[accepted]
if chosen.numel():
spikes[chosen] = True; injected[chosen] = ~natural[chosen]
sources = torch.nonzero(spikes, as_tuple=False).flatten()
self.spike_counts.add_(spikes.to(torch.int32))
if sources.numel():
self.v[sources] = self.reset_value; self.refractory[sources] = self.refractory_steps
edge_ids = self._hybrid_edges(sources)
if edge_ids.numel():
self.delay_queue[(global_step + self.delay_steps) % self.queue_len].scatter_add_(0, self.indices[edge_ids].long(), self.weights[edge_ids])
src_np = sources.cpu().numpy().astype(np.int32, copy=True)
origin_np = np.where(injected[sources].cpu().numpy(), "external_spike_injection", "network_spike")
self.spike_steps.append(np.full(len(src_np), global_step, dtype=np.int32))
self.spike_nodes.append(src_np); self.spike_origins.append(origin_np)
self.simulation_step += 1
torch.cuda.synchronize()
self.sequence_id += 1
return {
"api_version": API_VERSION, "sequence_id": self.sequence_id,
"batch_id": batch.batch_id, "start_time_ms": batch_start,
"end_time_ms": self.simulation_time_ms, "biological_ms": biological_ms,
"new_spikes": sum(len(x) for x in self.spike_nodes) - start_event_count,
"new_external_drive_events": sum(len(x) for x in self.drive_nodes) - start_drive_count,
}
def _events(self) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
steps = np.concatenate(self.spike_steps) if self.spike_steps else np.empty(0, np.int32)
nodes = np.concatenate(self.spike_nodes) if self.spike_nodes else np.empty(0, np.int32)
origins = np.concatenate(self.spike_origins) if self.spike_origins else np.empty(0, object)
return steps, nodes, origins
def read_spikes(self, window_ms: float | None = None) -> dict[str, Any]:
steps, nodes, origins = self._events()
if window_ms is not None:
threshold = self.simulation_step - int(round(window_ms / self.dt))
mask = steps >= threshold; steps, nodes, origins = steps[mask], nodes[mask], origins[mask]
return {
"simulation_time_ms": self.simulation_time_ms,
"step": steps, "timestamp_ms": steps.astype(np.float64) * self.dt,
"neuron_idx": nodes, "origin": origins,
}
def read_population(self, population_id: str, window_ms: float) -> dict[str, Any]:
if population_id not in self.output_populations:
raise KeyError(population_id)
members = self.output_populations[population_id]
events = self.read_spikes(window_ms)
mask = np.isin(events["neuron_idx"], members)
nodes = events["neuron_idx"][mask]
counts = np.zeros(len(members), dtype=np.int32)
if len(nodes):
lookup = np.full(self.n, -1, dtype=np.int32); lookup[members] = np.arange(len(members), dtype=np.int32)
np.add.at(counts, lookup[nodes], 1)
seconds = window_ms / 1000.0
return {
"simulation_time_ms": self.simulation_time_ms, "population_id": population_id,
"neuron_idx": members.copy(), "spike_counts": counts,
"firing_rate_hz": counts.astype(np.float32) / seconds,
"active_mask": counts > 0, "aggregate_rate_hz": float(counts.sum() / (len(members) * seconds)),
"total_spikes": int(counts.sum()), "active_neurons": int(np.count_nonzero(counts)),
}
def read_output(self, output_spec: dict[str, Any], window_ms: float) -> dict[str, Any]:
population = output_spec.get("population", "descending")
aggregation = output_spec.get("aggregation", "per-neuron")
base = self.read_population(population, window_ms)
members = base["neuron_idx"]; counts = base["spike_counts"]
if aggregation == "per-neuron":
return base
if aggregation not in {"per-type", "per-class"}:
raise ValueError("aggregation must be per-neuron, per-type, or per-class")
labels = self.neuron_type if aggregation == "per-type" else self.neuron_class
result: dict[str, dict[str, float | int]] = {}
for label in sorted({"<null>" if labels[i] is None else str(labels[i]) for i in members}):
mask = np.asarray([("<null>" if labels[i] is None else str(labels[i])) == label for i in members])
result[label] = {"neuron_count": int(mask.sum()), "spike_count": int(counts[mask].sum()), "mean_rate_hz": float(base["firing_rate_hz"][mask].mean())}
return {"simulation_time_ms": self.simulation_time_ms, "population_id": population, "aggregation": aggregation, "features": result}
def fixed_temporal_bins(self, population_id: str, bin_ms: float, window_ms: float) -> dict[str, Any]:
if bin_ms not in self.cfg["readout"]["bins_ms"]:
raise ValueError("unsupported fixed bin")
ev = self.read_spikes(window_ms); members = self.output_populations[population_id]
mask = np.isin(ev["neuron_idx"], members); times = ev["timestamp_ms"][mask]
start = self.simulation_time_ms - window_ms
edges = np.arange(start, self.simulation_time_ms + bin_ms + 1e-9, bin_ms)
counts, _ = np.histogram(times, bins=edges)
return {"simulation_time_ms": self.simulation_time_ms, "population_id": population_id, "bin_ms": bin_ms, "bin_start_ms": edges[:-1], "spike_counts": counts.astype(np.int32)}
def get_state_summary(self) -> dict[str, Any]:
return {
"api_version": API_VERSION, "runtime": self.cfg["names"]["runtime"],
"simulation_step": self.simulation_step, "simulation_time_ms": self.simulation_time_ms,
"sequence_id": self.sequence_id, "seed": self.seed,
"neurons": self.n, "edges": int(self.indices.numel()),
"active_neurons_total": int(torch.count_nonzero(self.spike_counts).item()),
"spikes_total": int(self.spike_counts.sum().item()),
"external_drive_events_total": sum(len(x) for x in self.drive_nodes),
}
def checkpoint_state(self) -> dict[str, Any]:
return {
"api_version": API_VERSION, "seed": self.seed, "simulation_step": self.simulation_step,
"sequence_id": self.sequence_id, "generator_state": self.generator.get_state().cpu(),
"v": self.v.clone(), "g": self.g.clone(), "refractory": self.refractory.clone(),
"delay_queue": self.delay_queue.clone(), "spike_counts": self.spike_counts.clone(),
"spike_steps": copy.deepcopy(self.spike_steps), "spike_nodes": copy.deepcopy(self.spike_nodes),
"spike_origins": copy.deepcopy(self.spike_origins), "drive_steps": copy.deepcopy(self.drive_steps),
"drive_nodes": copy.deepcopy(self.drive_nodes), "drive_values": copy.deepcopy(self.drive_values),
"input_audit": copy.deepcopy(self.input_audit),
}
def restore_state(self, state: dict[str, Any]) -> dict[str, Any]:
if state.get("api_version") != API_VERSION:
raise ValueError("checkpoint API version mismatch")
self.seed = int(state["seed"]); self.simulation_step = int(state["simulation_step"]); self.sequence_id = int(state["sequence_id"])
self.generator.set_state(state["generator_state"].cpu())
for name in ("v", "g", "refractory", "delay_queue", "spike_counts"):
setattr(self, name, state[name].clone().to(self.device))
for name in ("spike_steps", "spike_nodes", "spike_origins", "drive_steps", "drive_nodes", "drive_values", "input_audit"):
setattr(self, name, copy.deepcopy(state[name]))
return self.get_state_summary()
def activity_packet(self, window_ms: float, input_metadata: dict[str, Any] | None = None) -> dict[str, Any]:
ev = self.read_spikes(window_ms)
aggregates = {name: {k: v for k, v in self.read_population(name, window_ms).items() if k in ("active_neurons", "total_spikes", "aggregate_rate_hz")} for name in self.output_populations}
return {
"contract_version": ACTIVITY_STREAM_VERSION, "simulation_time_ms": self.simulation_time_ms,
"sequence_id": self.sequence_id, "window_ms": window_ms,
"spike_neuron_idx": ev["neuron_idx"].tolist(), "spike_timestamp_ms": ev["timestamp_ms"].tolist(),
"population_aggregates": aggregates, "active_neuron_count": int(len(np.unique(ev["neuron_idx"]))),
"input_channel_metadata": input_metadata or {}, "representation": "event-only",
}
@@ -0,0 +1,7 @@
d98a4074a0523cb1e767a2fca8df909dad74ba541dd4edf58ebbf34f0e196837 VERSION.json
d80d8ecf0a35cc90a3f063a2362938f853d98ed64c88ba4c7864f793813b6117 __pycache__/brain_runtime.cpython-310.pyc
1c4e3e6f06708bfec30b24e92297f320b2b8b87ebbbe6da501f5f2db4f74762a brain_runtime.py
5524ac0565b9e2a2f96d7a062109c27fe64365fcaaef44ee7b7ded7d3318fe50 phase4f.py
52393d2c9ad019ceb61de9168dc809ec52aec72b3c66f1b9708d22c0808f678d phase4f.yaml
6df36ecf4b6aa5565c1f3c10345fe997a2c3eefe88e42e0b194be272ce83e233 phase4f_extract_retinotopy.py
407e20fe42cff605ac83993818e22b76d75cb1f7dd1844a7b387e83d02911958 MANIFEST.json
@@ -0,0 +1,528 @@
#!/usr/bin/env python3
"""Phase 4F: official-data retinotopic visual encoding for frozen MaleCNS-LIF."""
from __future__ import annotations
import argparse
import gc
import hashlib
import json
import math
import os
import platform
import shutil
import subprocess
import sys
import time
from collections import Counter
from pathlib import Path
from typing import Any
import numpy as np
import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq
import torch
import yaml
from scipy.spatial import cKDTree
from brain_runtime import BrainRuntime, InputBatch, InputEvent
def np_json(value: Any) -> Any:
if isinstance(value, (np.generic,)): return value.item()
if isinstance(value, np.ndarray): return value.tolist()
if isinstance(value, Path): return str(value)
raise TypeError(type(value).__name__)
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, default=np_json) + "\n", encoding="utf-8")
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 verify_checksum_file(root: Path, checksum_file: Path) -> dict[str, Any]:
checked, failures = 0, []
for line in checksum_file.read_text(encoding="utf-8").splitlines():
if not line.strip(): continue
expected, rel = line.split(None, 1)
path = root / rel.strip().lstrip("*")
checked += 1
if not path.is_file() or sha256(path) != expected: failures.append(str(path))
return {"checksum_file": str(checksum_file), "checked": checked, "failures": failures, "passed": not failures}
def immutable_audit(cfg: dict[str, Any]) -> tuple[list[Path], dict[str, str], dict[str, Any]]:
p = cfg["paths"]
controls = [
Path(p["reference_root"]) / "checksums.sha256",
Path(p["runtime_root"]) / "checksums.sha256",
Path(p["io_root"]) / "checksums.sha256",
Path(p["phase4c_root"]) / "manifests/checksums.sha256",
Path(p["phase4d_root"]) / "manifests/checksums.sha256",
Path(p["phase4e_root"]) / "manifests/checksums.sha256",
Path(p["model_root"]) / "visual-interface-v2/checksums.sha256",
]
missing = [str(x) for x in controls if not x.is_file()]
if missing: raise FileNotFoundError(f"immutable controls missing: {missing}")
snapshots = {str(x): sha256(x) for x in controls}
checks = []
for x in controls:
root = x.parent.parent if x.parent.name == "manifests" else x.parent
checks.append(verify_checksum_file(root, x))
if not all(x["passed"] for x in checks): raise RuntimeError(f"frozen input checksum failure: {checks}")
return controls, snapshots, {"all_passed": True, "controls": checks}
def git_commit(path: Path) -> str:
return subprocess.check_output(["/usr/bin/git", "-C", str(path), "rev-parse", "HEAD"], text=True).strip()
def external_resource_audit(cfg: dict[str, Any], exp: Path) -> dict[str, Any]:
root = Path(cfg["paths"]["external_root"])
specs = {
"visualpathways": (cfg["official_resources"]["visualpathways_commit"], "GPL-3.0"),
"celltype-explorer": (cfg["official_resources"]["celltype_explorer_commit"], "CC-BY-4.0"),
"flyvis": (cfg["official_resources"]["flyvis_commit"], "MIT"),
}
repos = {}
for name, (expected, license_id) in specs.items():
path = root / name; actual = git_commit(path)
if actual != expected: raise RuntimeError(f"{name} commit {actual} != {expected}")
license_files = [x for x in path.iterdir() if x.is_file() and x.name.lower().startswith(("license", "copying"))]
repos[name] = {"url": {"visualpathways": "https://github.com/reiserlab/visualpathways",
"celltype-explorer": "https://github.com/reiserlab/celltype-explorer-drosophila-male-cns",
"flyvis": "https://github.com/TuragaLab/flyvis"}[name],
"commit": actual, "expected_commit": expected, "license": license_id,
"license_files": [str(x) for x in license_files]}
source = root / "retinotopy-tbars-body-summary.parquet"
source_summary = json.loads((root / "retinotopy-tbars-body-summary.json").read_text())
result = {"repositories": repos, "official_tbar_snapshot": {**source_summary, "sha256": sha256(source)},
"paper": {"doi": cfg["official_resources"]["paper_doi"], "role": "official visual-pathway definition reference"}}
write_json(exp / "audit/external-resource-provenance.json", result)
return result
def _hierarchy_labels(cfg: dict[str, Any], n: int) -> np.ndarray:
labels = np.full(n, None, dtype=object)
root = Path(cfg["paths"]["phase4c_root"]) / "visual-audit"
for name in ("visual_sensory", "first_order", "second_order", "higher_visual", "central_brain"):
path = root / f"hierarchy-{name}.parquet"
if path.is_file():
idx = pq.read_table(path, columns=["neuron_idx"])["neuron_idx"].to_numpy().astype(np.int64)
labels[idx] = name
return labels
def _read_eye_map(path: Path, eye: str) -> pd.DataFrame:
workbook = pd.ExcelFile(path)
candidates = [name for name in workbook.sheet_names if "column" in name.lower()]
if not candidates and len(workbook.sheet_names) == 1:
candidates = workbook.sheet_names
if len(candidates) != 1:
raise RuntimeError(f"cannot uniquely identify curated-column sheet in {path}: {workbook.sheet_names}")
data = pd.read_excel(path, sheet_name=candidates[0])
id_cols = [x for x in data.columns if x not in {"hex1_id", "hex2_id"}]
rows = []
for cell_type in id_cols:
for body, h1, h2 in zip(data[cell_type], data["hex1_id"], data["hex2_id"]):
if pd.notna(body): rows.append((int(body), float(h1), float(h2), eye, str(cell_type)))
out = pd.DataFrame(rows, columns=["body_id", "hex1", "hex2", "eye", "eye_map_cell_type"])
return out.drop_duplicates(["body_id", "hex1", "hex2", "eye", "eye_map_cell_type"])
def build_retinotopy(cfg: dict[str, Any], runtime: BrainRuntime, exp: Path) -> tuple[pd.DataFrame, dict[str, Any]]:
ext = Path(cfg["paths"]["external_root"]); vp_root = ext / "visualpathways"
md = runtime.table.to_pandas(); n = len(md)
body = md["body_id"].to_numpy(np.uint64); body_to_idx = {int(b): i for i, b in enumerate(body)}
h1 = md["annotation_assignedOlHex1"].to_numpy(float); h2 = md["annotation_assignedOlHex2"].to_numpy(float)
direct = np.isfinite(h1) & np.isfinite(h2)
coord1, coord2 = h1.copy(), h2.copy()
kind = np.full(n, None, dtype=object); kind[direct] = "official_annotation_assignedOlHex"
provenance = np.full(n, None, dtype=object); provenance[direct] = "MaleCNS v1.0 annotation assignedOlHex1/assignedOlHex2"
reliability = np.full(n, None, dtype=object); reliability[direct] = "official-direct"
contribution = np.full(n, np.nan); rf_r = np.full(n, np.nan); rf_theta = np.full(n, np.nan); rf_vic = np.full(n, np.nan)
eye = pd.concat([
_read_eye_map(vp_root / "params/ME_columnar-cells_location.xlsx", "R"),
_read_eye_map(vp_root / "params/ME_L_columnar-cells_location.xlsx", "L")], ignore_index=True)
eye = eye[eye.body_id.isin(body_to_idx)].copy()
eye_consistency = []
for row in eye.itertuples(index=False):
i = body_to_idx[int(row.body_id)]
if direct[i]: eye_consistency.append(abs(coord1[i] - row.hex1) < 1e-6 and abs(coord2[i] - row.hex2) < 1e-6)
else:
coord1[i], coord2[i], kind[i], reliability[i] = row.hex1, row.hex2, "official_visualpathways_curated_column", "official-direct"
provenance[i] = f"reiserlab/visualpathways curated column {row.eye} {row.eye_map_cell_type}"
tbar = pq.read_table(ext / "retinotopy-tbars-body-summary.parquet").to_pandas()
tbar_canonical = 0
for row in tbar.itertuples(index=False):
i = body_to_idx.get(int(row.body_id))
if i is None: continue
tbar_canonical += 1
contribution[i] = row.visual_input_contribution_sum; rf_r[i] = row.tbar_r_weighted_mean
rf_theta[i] = row.tbar_theta_weighted_mean; rf_vic[i] = row.tbar_vic_weighted_mean
if not np.isfinite(coord1[i]) and np.isfinite(row.tbar_hex1_weighted_mean) and np.isfinite(row.tbar_hex2_weighted_mean):
coord1[i], coord2[i] = row.tbar_hex1_weighted_mean, row.tbar_hex2_weighted_mean
kind[i], reliability[i] = "official_retinotopy_tbar_weighted_mean", "official-derived-field"
provenance[i] = cfg["official_resources"]["retinotopy_tbar_url"]
# Anatomical receptive-field centroids for projection neurons are derived
# only where no official body coordinate exists. They are explicitly
# labelled derived and never counted as direct official coordinate coverage.
official = np.isfinite(coord1) & np.isfinite(coord2)
src = np.flatnonzero(official)
deg = runtime.indptr_np[src + 1] - runtime.indptr_np[src]
edge_ids = np.concatenate([np.arange(runtime.indptr_np[s], runtime.indptr_np[s + 1]) for s in src])
edge_src = np.repeat(src, deg); dst = runtime.indices_np[edge_ids]; w = runtime.raw_np[edge_ids].astype(np.float64)
sw = np.bincount(dst, weights=w, minlength=n)
sx = np.bincount(dst, weights=w * coord1[edge_src], minlength=n)
sy = np.bincount(dst, weights=w * coord2[edge_src], minlength=n)
superclass = np.asarray(md["annotation_superclass"].tolist(), dtype=object)
vp = np.isin(superclass, ["visual_projection", "visual_projection_tbc"])
derived = vp & ~official & (sw > 0)
coord1[derived] = sx[derived] / sw[derived]; coord2[derived] = sy[derived] / sw[derived]
contribution[derived] = sw[derived]; kind[derived] = "connectome_weighted_centroid_from_official_eye_map"
reliability[derived] = "connectome-derived"; provenance[derived] = "official eye-map coordinates + unchanged MaleCNS v1.0 raw synapse-count CSR"
mapped = np.isfinite(coord1) & np.isfinite(coord2)
xmin, xmax = np.nanmin(coord1[mapped]), np.nanmax(coord1[mapped]); ymin, ymax = np.nanmin(coord2[mapped]), np.nanmax(coord2[mapped])
x = 2 * (coord1 - xmin) / (xmax - xmin) - 1; y = 2 * (coord2 - ymin) / (ymax - ymin) - 1
hierarchy = _hierarchy_labels(cfg, n); hierarchy[vp] = "visual_projection"
instance = np.asarray(md["annotation_instance"].tolist(), object)
path_class, path_source = np.full(n, None, object), np.full(n, None, object)
for filename, field, source_name in (("TableS1_VPN_in_out_summary.csv", "oc_class", "official VPN table"),
("TableS3_VCBN_in_out_summary.csv", "cc_class", "official VCBN table")):
table = pd.read_csv(vp_root / "results/tables" / filename)
lookup = dict(zip(table["instance"].astype(str), table[field]))
for i, val in enumerate(instance):
if val is not None and str(val) in lookup and pd.notna(lookup[str(val)]):
path_class[i], path_source[i] = str(lookup[str(val)]), source_name
eye_by_body = eye.groupby("body_id").agg(eye=("eye", lambda z: ";".join(sorted(set(z)))),
eye_map_cell_type=("eye_map_cell_type", lambda z: ";".join(sorted(set(z))))).to_dict("index")
rows = pd.DataFrame({
"body_id": body, "neuron_idx": np.arange(n, dtype=np.int32), "visual_layer": hierarchy,
"cell_type": md["annotation_type"], "instance": md["annotation_instance"], "side": md["side"],
"neurotransmitter": md["nt_consensus_nt"], "official_hex1": coord1, "official_hex2": coord2,
"retinotopic_x": x, "retinotopic_y": y, "coordinate_kind": kind, "coordinate_reliability": reliability,
"visual_input_contribution": contribution, "receptive_field_r": rf_r, "receptive_field_theta": rf_theta,
"receptive_field_vic": rf_vic, "pathway_class": path_class, "pathway_class_source": path_source,
"provenance": provenance,
"eye": [eye_by_body.get(int(b), {}).get("eye") for b in body],
"eye_map_cell_type": [eye_by_body.get(int(b), {}).get("eye_map_cell_type") for b in body],
})
rows = rows[mapped].copy().reset_index(drop=True)
out = exp / "retinotopy/visual-retinotopy.parquet"; pq.write_table(pa.Table.from_pandas(rows, preserve_index=False), out, compression="zstd")
official_mask = rows.coordinate_reliability.isin(["official-direct", "official-derived-field"])
summary = {
"canonical_neurons": n, "mapped_total_including_connectome_derived": len(rows),
"mapped_total_coverage_percent": 100 * len(rows) / n,
"reliable_official_coordinate_neurons": int(official_mask.sum()),
"reliable_official_coordinate_coverage_percent": 100 * official_mask.sum() / n,
"coordinate_kind_counts": rows.coordinate_kind.value_counts(dropna=False).to_dict(),
"official_annotation_both_coordinates": int(direct.sum()), "official_tbar_unique_bodies": int(len(tbar)),
"official_tbar_canonical_bodies": tbar_canonical, "visual_projection_total": int(vp.sum()),
"visual_projection_mapped": int(np.count_nonzero(vp & mapped)), "connectome_derived_projection": int(derived.sum()),
"visualpathways_eye_map_canonical_rows": int(len(eye)),
"eye_map_vs_annotation_checked": len(eye_consistency), "eye_map_vs_annotation_exact": int(sum(eye_consistency)),
"normalization": {"method": "independent min-max of preserved official-equivalent hex coordinates to [-1,1]",
"hex1_min_max": [xmin, xmax], "hex2_min_max": [ymin, ymax], "no_cartesian_claim": True},
"table": str(out), "schema": str(pq.read_schema(out)),
}
write_json(exp / "retinotopy/summary.json", summary)
return rows, summary
def hex_grid_for_count(count: int) -> tuple[np.ndarray, np.ndarray, int]:
extent = 0
while 1 + 3 * extent * (extent + 1) < count: extent += 1
u, v = [], []
for q in range(-extent, extent + 1):
for r in range(max(-extent, -extent - q), min(extent, extent - q) + 1): u.append(q); v.append(r)
u, v = np.asarray(u), np.asarray(v)
# Exact FlyVis public default convention from flyvis.utils.hex_utils.
x, y = 1.5 * v, -np.sqrt(3) * (u + v / 2)
x /= max(abs(x).max(), 1); y /= max(abs(y).max(), 1)
return x, y, extent
def luminance_frames(pattern: str, x: np.ndarray, y: np.ndarray, segments: int) -> np.ndarray:
frames = []
for t in range(segments):
z = t / max(segments - 1, 1)
if pattern == "full-field": f = np.ones_like(x)
elif pattern == "left-spot": cx, cy, s = -0.65, 0.0, 0.22; f = np.exp(-((x-cx)**2+(y-cy)**2)/(2*s*s))
elif pattern == "right-spot": cx, cy, s = 0.65, 0.0, 0.22; f = np.exp(-((x-cx)**2+(y-cy)**2)/(2*s*s))
elif pattern == "upper-spot": cx, cy, s = 0.0, 0.65, 0.22; f = np.exp(-((x-cx)**2+(y-cy)**2)/(2*s*s))
elif pattern == "lower-spot": cx, cy, s = 0.0, -0.65, 0.22; f = np.exp(-((x-cx)**2+(y-cy)**2)/(2*s*s))
elif pattern in {"left-to-right", "right-to-left", "up-to-down", "down-to-up"}:
p = -0.8 + 1.6*z; p = -p if pattern in {"right-to-left", "down-to-up"} else p
cx, cy = (p, 0.0) if pattern in {"left-to-right", "right-to-left"} else (0.0, -p)
f = np.exp(-((x-cx)**2+(y-cy)**2)/(2*0.18**2))
elif pattern == "looming":
s = 0.08 + 0.48*z; f = np.exp(-(x*x+y*y)/(2*s*s))
elif pattern == "sparse-moving-object":
cx, cy = -0.75+1.5*z, 0.55-1.1*z; f = np.exp(-((x-cx)**2+(y-cy)**2)/(2*0.10**2))
elif pattern == "static-object": f = np.exp(-((x+0.25)**2+(y-0.20)**2)/(2*0.10**2))
else: raise KeyError(pattern)
frames.append(f.astype(np.float32))
return np.asarray(frames)
def feature_channels(frames: np.ndarray, coords: np.ndarray) -> dict[str, np.ndarray]:
previous = np.vstack([np.zeros((1, frames.shape[1]), np.float32), frames[:-1]])
delta = frames - previous
tree = cKDTree(coords); neighbours = tree.query(coords, k=min(7, len(coords)))[1][:, 1:]
dx = np.abs(coords[:, None, 0] - coords[neighbours, 0]); dy = np.abs(coords[:, None, 1] - coords[neighbours, 1])
h, v = [], []
for frame in frames:
diff = np.abs(frame[:, None] - frame[neighbours])
h.append((diff * dx).sum(1) / np.maximum(dx.sum(1), 1e-8)); v.append((diff * dy).sum(1) / np.maximum(dy.sum(1), 1e-8))
return {"luminance": frames, "on_change": np.maximum(delta, 0), "off_change": np.maximum(-delta, 0),
"temporal_motion": np.abs(delta), "horizontal_motion_tendency": np.asarray(h), "vertical_motion_tendency": np.asarray(v)}
def encode_pattern(cfg: dict[str, Any], route: str, pattern: str, pool: pd.DataFrame) -> tuple[InputBatch, np.ndarray, dict[str, Any]]:
t0 = time.perf_counter(); enc = cfg["encoder"]; segments = int(enc["temporal_segments"]); k = int(enc["targets_per_segment"])
coords = pool[["retinotopic_x", "retinotopic_y"]].to_numpy(float)
if len(pool) < k: raise RuntimeError(f"retinotopic visual-projection pool {len(pool)} < target count {k}")
if route == "route-a-malecns-native":
source_coords = coords; frames = luminance_frames(pattern, source_coords[:, 0], source_coords[:, 1], segments)
channels = feature_channels(frames, source_coords); mapped = channels
frontend = "MaleCNS official/derived retinotopic coordinates"
elif route == "route-b-flyvis-assisted":
official_columns = int(pool.attrs["official_unique_eye_columns"])
fx, fy, extent = hex_grid_for_count(official_columns); fly_coords = np.c_[fx, fy]
channels = feature_channels(luminance_frames(pattern, fx, fy, segments), fly_coords)
nearest = cKDTree(fly_coords).query(coords, k=1)[1]
mapped = {name: value[:, nearest] for name, value in channels.items()}
frontend = f"FlyVis axial hex geometry extent={extent}, {len(fx)} hexals; nearest-coordinate functional bridge"
else: raise KeyError(route)
weights = enc["flyvis_bridge"]["channel_weights"]
score = sum(float(weights[name]) * mapped[name] for name in weights)
dt = float(enc["trial_duration_ms"]) / segments; events, unions, selections = [], [], []
for i in range(segments):
order = np.lexsort((pool.neuron_idx.to_numpy(), -score[i])); chosen = np.sort(pool.neuron_idx.to_numpy(np.int64)[order[:k]])
unions.append(chosen); selections.append(chosen)
events.append(InputEvent(target_neuron_idx=chosen, stimulation_mode="external_synaptic_drive", value_kind="poisson_rate_hz",
value=float(enc["poisson_rate_hz"]), start_ms=i*dt, duration_ms=dt,
source_id=route, channel_id=f"{pattern}:{i}", amplitude_mv=float(enc["external_drive_mv"]),
metadata={"route": route, "pattern": pattern, "segment": i, "engineering_feature_channels": list(weights),
"connectome_modified": False, "brain_gain_modified": False}))
union = np.unique(np.concatenate(unions))
audit = {"route": route, "pattern": pattern, "frontend": frontend, "encoder_wall_seconds": time.perf_counter()-t0,
"segments": segments, "targets_per_segment": k, "target_slots": k*segments,
"rate_hz": enc["poisson_rate_hz"], "amplitude_mv": enc["external_drive_mv"], "union_target_count": len(union),
"feature_channels": {name: {"min": float(val.min()), "max": float(val.max()), "mean": float(val.mean())} for name, val in mapped.items()}}
return InputBatch(events=tuple(events), batch_id=f"{route}:{pattern}", metadata={"phase": "4F"}), union, audit
def group_members(cfg: dict[str, Any], runtime: BrainRuntime) -> dict[str, np.ndarray]:
root = Path(cfg["paths"]["phase4c_root"]) / "visual-audit"
optic_parts = [pq.read_table(root/f"hierarchy-{x}.parquet", columns=["neuron_idx"])["neuron_idx"].to_numpy()
for x in ("visual_sensory", "first_order", "second_order", "higher_visual")]
superc = np.asarray(runtime.superclass, object)
return {"optic_lobe": np.unique(np.concatenate(optic_parts)).astype(np.int32),
"visual_projection": np.flatnonzero(np.isin(superc, ["visual_projection", "visual_projection_tbc"])).astype(np.int32),
"central_brain": pq.read_table(root/"hierarchy-central_brain.parquet", columns=["neuron_idx"])["neuron_idx"].to_numpy().astype(np.int32),
"descending": runtime.output_populations["descending"], "motor_related": runtime.output_populations["motor_related"]}
def group_counts(nodes: np.ndarray, members: np.ndarray, n: int) -> np.ndarray:
lookup = np.full(n, -1, np.int32); lookup[members] = np.arange(len(members), dtype=np.int32)
pos = lookup[nodes]; pos = pos[pos >= 0]
return np.bincount(pos, minlength=len(members)).astype(np.int32)
def peak_fraction(steps: np.ndarray, n: int) -> float:
return float(np.bincount(steps.astype(np.int64)).max(initial=0) / n) if len(steps) else 0.0
def run_trial(cfg: dict[str, Any], runtime: BrainRuntime, batch: InputBatch, union: np.ndarray, seed: int,
groups: dict[str, np.ndarray], exp: Path, route: str, pattern: str) -> tuple[dict[str, Any], dict[str, np.ndarray]]:
runtime.reset(seed); t0 = time.perf_counter(); runtime.step(batch, float(cfg["encoder"]["trial_duration_ms"])); brain_wall = time.perf_counter()-t0
spikes = runtime.read_spikes(); nodes, steps = spikes["neuron_idx"], spikes["step"]; unique = np.unique(nodes)
distance = runtime.runtime_module.p4b.directed_distances(runtime.indptr_np, runtime.indices_np, union.astype(np.int64), runtime.n)
dvals = distance[unique] if len(unique) else np.empty(0, np.int16); dvals = dvals[dvals >= 0]
counts = {name: group_counts(nodes, members, runtime.n) for name, members in groups.items()}
masks = {name: np.isin(nodes, members) for name, members in groups.items()}
active_fraction = len(unique)/runtime.n; bio_s = runtime.simulation_time_ms/1000.0
mean_rate = len(nodes)/(runtime.n*bio_s); peak = peak_fraction(steps, runtime.n)
runaway_cfg = cfg["encoder"]["runaway"]
runaway = active_fraction >= float(runaway_cfg["active_fraction_gte"]) or mean_rate >= float(runaway_cfg["mean_firing_rate_hz_gte"]) or peak >= float(runaway_cfg["peak_step_fraction_gte"])
target_mask = np.zeros(runtime.n, bool); target_mask[union] = True
record = {"route": route, "pattern": pattern, "seed": seed, "biological_ms": runtime.simulation_time_ms,
"brain_wall_seconds": brain_wall, "total_spikes": len(nodes), "active_neurons": len(unique), "active_fraction": active_fraction,
"mean_firing_rate_hz": mean_rate, "peak_step_fraction": peak, "runaway": runaway,
"external_event_count": int(sum(len(x) for x in runtime.drive_nodes)),
"external_total_amplitude_mv": float(sum(float(x.sum()) for x in runtime.drive_values)),
"target_union_count": len(union), "target_active": int(np.count_nonzero(target_mask[unique])) if len(unique) else 0,
"non_target_active": int(np.count_nonzero(~target_mask[unique])) if len(unique) else 0,
"propagation_depth": int(dvals.max(initial=0)), "numerically_finite": bool(torch.isfinite(runtime.v).all() and torch.isfinite(runtime.g).all())}
for name in groups:
record[f"{name}_active"] = int(np.count_nonzero(counts[name])); record[f"{name}_spikes"] = int(counts[name].sum())
record[f"first_{name}_latency_ms"] = float(spikes["timestamp_ms"][masks[name]].min()) if np.any(masks[name]) else None
record["real_anatomical_multi_hop"] = bool(record["non_target_active"] > 0 and record["propagation_depth"] >= int(cfg["success_gate"]["minimum_graph_hops"]))
trial_dir = exp / "propagation" / route; trial_dir.mkdir(parents=True, exist_ok=True)
np.savez_compressed(trial_dir/f"{pattern}-seed{seed}.npz", step=steps, neuron_idx=nodes, **{f"counts_{k}": v for k,v in counts.items()})
write_json(trial_dir/f"{pattern}-seed{seed}.json", record)
return record, counts
def metric(a: np.ndarray, b: np.ndarray) -> dict[str, float]:
a, b = a.astype(np.float64), b.astype(np.float64); an, bn = np.linalg.norm(a), np.linalg.norm(b)
cosine = 1-(float(np.dot(a,b)/(an*bn)) if an and bn else (1.0 if an == bn else 0.0))
if np.array_equal(a,b): corr = 0.0
elif np.std(a) and np.std(b): corr = 1-float(np.corrcoef(a,b)[0,1])
else: corr = 1.0
aa, bb = a>0, b>0; union = np.count_nonzero(aa|bb)
return {"cosine_distance": cosine, "correlation_distance": corr,
"jaccard_distance": 1-float(np.count_nonzero(aa&bb)/union) if union else 0.0,
"euclidean_distance": float(np.linalg.norm(a-b))}
def separability(cfg: dict[str, Any], features: dict[str, dict[str, dict[int, dict[str, np.ndarray]]]], exp: Path) -> dict[str, Any]:
result = {}
for route, patterns in features.items():
route_result = {"matched_pairs": {}, "readouts": {}}
for readout in ("visual_projection", "central_brain", "descending", "motor_related"):
within = {}
for pattern, seeds in patterns.items():
vals = [seeds[s][readout] for s in sorted(seeds)]
d = [metric(vals[i], vals[j]) for i in range(len(vals)) for j in range(i+1,len(vals))]
within[pattern] = {k: float(np.mean([x[k] for x in d])) for k in d[0]} if d else {k:0.0 for k in metric(vals[0],vals[0])}
route_result["readouts"][readout] = {"within_condition_mean": within}
for a,b in cfg["encoder"]["matched_pairs"]:
key=f"{a}__{b}"; sa, sb=patterns[a],patterns[b]
same=[metric(sa[s][readout],sb[s][readout]) for s in sorted(set(sa)&set(sb))]
all_between=[metric(x[readout],y[readout]) for x in sa.values() for y in sb.values()]
mean_between={k:float(np.mean([z[k] for z in all_between])) for k in all_between[0]}
max_within=max(within[a]["euclidean_distance"],within[b]["euclidean_distance"])
repeat_nonzero=all(x["euclidean_distance"]>0 for x in same)
repeatable=bool(repeat_nonzero and mean_between["euclidean_distance"]>max_within)
route_result["matched_pairs"].setdefault(key,{})[readout]={"same_seed":same,"between_condition_mean":mean_between,
"within_condition_euclidean_max":max_within,"repeatably_separable":repeatable}
route_result["separable_pairs_in_descending_or_motor"] = [k for k,v in route_result["matched_pairs"].items()
if v["descending"]["repeatably_separable"] or v["motor_related"]["repeatably_separable"]]
route_result["separable_pair_count_in_descending_or_motor"] = len(route_result["separable_pairs_in_descending_or_motor"])
result[route]=route_result; write_json(exp/f"separability/{route}.json",route_result)
write_json(exp/"separability/summary.json",result); return result
def regression_suite(cfg: dict[str, Any], runtime: BrainRuntime, exp: Path) -> dict[str, Any]:
sys.path.insert(0, cfg["paths"]["io_root"]); import phase4d as p4d
reg=p4d.regression(exp,cfg,runtime); api=p4d.api_tests(exp,runtime); stream=p4d.stream_contract(exp,runtime)
result={"phase4c_five_trials_passed":all(x["passed"] for x in reg.values()),"phase4d_brainio_passed":api["all_passed"],
"activity_stream_passed":stream["replay_exact"],"phase4e_input_contract_checksum_verified":True,
"trials":reg,"api":api,"activity_stream":stream}
result["all_passed"]=all(result[k] for k in ("phase4c_five_trials_passed","phase4d_brainio_passed","activity_stream_passed","phase4e_input_contract_checksum_verified"))
write_json(exp/"regression/summary.json",result); return result
def manifest_tree(root: Path, output: Path, exclude: set[Path] | None=None) -> dict[str, Any]:
exclude=exclude or set(); files=[]
for path in sorted(root.rglob("*")):
if path.is_file() and path not in exclude and ".git" not in path.parts:
files.append({"path":str(path.relative_to(root)),"bytes":path.stat().st_size,"sha256":sha256(path)})
value={"root":str(root),"file_count":len(files),"total_bytes":sum(x["bytes"] for x in files),"files":files}
write_json(output,value); return value
def main() -> None:
ap=argparse.ArgumentParser(); ap.add_argument("--config",type=Path,required=True); args=ap.parse_args()
cfg=yaml.safe_load(args.config.read_text()); exp=Path(cfg["paths"]["experiment_root"]); model=Path(cfg["paths"]["encoder_root"])
for sub in ("external-resources","retinotopy","encoders","stimuli","propagation","separability","latency","regression","audit","manifests","compiler-reports/extension-build"):
(exp/sub).mkdir(parents=True,exist_ok=True)
shutil.copy2(args.config,exp/"encoders/phase4f.yaml")
controls,before,immutable=immutable_audit(cfg); write_json(exp/"audit/immutable-inputs-before.json",immutable)
external=external_resource_audit(cfg,exp)
sys.path.insert(0,cfg["paths"]["model_root"]); runtime=BrainRuntime(args.config,exp/"compiler-reports/extension-build")
retino,retino_summary=build_retinotopy(cfg,runtime,exp)
superc=np.asarray(runtime.superclass,object); pool=retino[retino.neuron_idx.isin(np.flatnonzero(np.isin(superc,["visual_projection","visual_projection_tbc"])))].copy()
# Prefer direct official receptive-field records, then retain audited connectome-derived centroids.
rank={"official-direct":0,"official-derived-field":1,"connectome-derived":2}; pool["_rank"]=pool.coordinate_reliability.map(rank)
pool=pool.sort_values(["_rank","neuron_idx"]).drop(columns="_rank").reset_index(drop=True)
official_unique=int(retino.loc[retino.coordinate_reliability=="official-direct",["official_hex1","official_hex2"]].drop_duplicates().shape[0])
pool.attrs["official_unique_eye_columns"]=official_unique
pq.write_table(pa.Table.from_pandas(pool,preserve_index=False),exp/"retinotopy/visual-projection-entry-pool.parquet",compression="zstd")
write_json(exp/"encoders/functional-channel-bridge.json",{"route_a":"features evaluated directly at MaleCNS coordinates",
"route_b":"features evaluated on FlyVis public axial-hex geometry then nearest-coordinate bridged; no neuron IDs shared",
"flyvis_and_malecns_are_distinct_connectomes":True,"target_expression":cfg["encoder"]["flyvis_bridge"]["target_expression"],
"channel_weights":cfg["encoder"]["flyvis_bridge"]["channel_weights"],"biological_claim":"engineering preprocessing only; not phototransduction"})
groups=group_members(cfg,runtime); features={}; trial_records=[]; encoder_audits=[]
for route in ("route-a-malecns-native","route-b-flyvis-assisted"):
features[route]={}
for pattern in cfg["encoder"]["patterns"]:
batch,union,enc_audit=encode_pattern(cfg,route,pattern,pool); encoder_audits.append(enc_audit)
write_json(exp/f"stimuli/{route}-{pattern}.json",enc_audit); features[route][pattern]={}
for seed in map(int,cfg["encoder"]["seeds"]):
rec,counts=run_trial(cfg,runtime,batch,union,seed,groups,exp,route,pattern)
rec["encoder_wall_seconds"]=enc_audit["encoder_wall_seconds"]; rec["total_control_wall_seconds"]=rec["brain_wall_seconds"]+rec["encoder_wall_seconds"]
trial_records.append(rec); features[route][pattern][seed]=counts
# Exact repeatability with the same seed, input, and initial state.
deterministic={}
for route in features:
batch,union,_=encode_pattern(cfg,route,"left-spot",pool); seed=int(cfg["encoder"]["seeds"][0])
runtime.reset(seed); runtime.step(batch,float(cfg["encoder"]["trial_duration_ms"])); a=runtime.read_spikes()
runtime.reset(seed); runtime.step(batch,float(cfg["encoder"]["trial_duration_ms"])); b=runtime.read_spikes()
deterministic[route]=bool(np.array_equal(a["step"],b["step"]) and np.array_equal(a["neuron_idx"],b["neuron_idx"]))
sep=separability(cfg,features,exp)
matched={}
for route in features:
matched[route]={}
for seed in map(int,cfg["encoder"]["seeds"]):
vals=[x["external_event_count"] for x in trial_records if x["route"]==route and x["seed"]==seed]
amps=[x["external_total_amplitude_mv"] for x in trial_records if x["route"]==route and x["seed"]==seed]
matched[route][str(seed)]={"event_counts":sorted(set(vals)),"amplitudes_mv":sorted(set(amps)),"matched":len(set(vals))==1 and len(set(amps))==1}
write_json(exp/"audit/matched-input-energy.json",matched); write_json(exp/"audit/determinism.json",deterministic)
latency={"trials":trial_records,"encoder_wall_seconds":{"min":min(x["encoder_wall_seconds"] for x in trial_records),"max":max(x["encoder_wall_seconds"] for x in trial_records)},
"brain_wall_seconds":{"min":min(x["brain_wall_seconds"] for x in trial_records),"max":max(x["brain_wall_seconds"] for x in trial_records)},
"total_control_wall_seconds":{"min":min(x["total_control_wall_seconds"] for x in trial_records),"max":max(x["total_control_wall_seconds"] for x in trial_records)}}
write_json(exp/"latency/summary.json",latency); write_json(exp/"propagation/summary.json",trial_records)
del runtime; gc.collect(); torch.cuda.empty_cache()
reg_runtime=BrainRuntime(args.config,exp/"compiler-reports/extension-build"); regression=regression_suite(cfg,reg_runtime,exp)
del reg_runtime; gc.collect(); torch.cuda.empty_cache()
after={str(x):sha256(x) for x in controls}; unchanged=before==after
all_matched=all(v["matched"] for r in matched.values() for v in r.values()); all_deterministic=all(deterministic.values())
non_runaway=all(not x["runaway"] and x["numerically_finite"] for x in trial_records)
multi_hop=any(x["real_anatomical_multi_hop"] for x in trial_records)
output_latencies=[z for x in trial_records for z in (x["first_descending_latency_ms"],x["first_motor_related_latency_ms"]) if z is not None]
within_100=bool(output_latencies and min(output_latencies)<=float(cfg["success_gate"]["maximum_output_latency_ms"]))
separable_pairs={r:v["separable_pair_count_in_descending_or_motor"] for r,v in sep.items()}
separability_gate=any(v>=int(cfg["success_gate"]["minimum_separable_matched_pairs_in_descending_or_motor"]) for v in separable_pairs.values())
passed=all([unchanged,regression["all_passed"],all_matched,all_deterministic,non_runaway,multi_hop,within_100,separability_gate])
status="PASS-CANDIDATE" if passed else "FAIL-NO-V1-CANDIDATE"
summary={"status":status,"candidate_generated":passed,"retinotopy":retino_summary,"entry_pool_count":len(pool),
"routes":{"route-a-malecns-native":{"trial_count":sum(x["route"]=="route-a-malecns-native" for x in trial_records),"separability":sep["route-a-malecns-native"]},
"route-b-flyvis-assisted":{"trial_count":sum(x["route"]=="route-b-flyvis-assisted" for x in trial_records),"separability":sep["route-b-flyvis-assisted"]}},
"matched_input_energy":all_matched,"same_seed_exact_reproducibility":deterministic,"real_anatomical_multi_hop":multi_hop,
"non_runaway_and_finite":non_runaway,"minimum_descending_or_motor_latency_ms":min(output_latencies) if output_latencies else None,
"separable_pair_counts":separable_pairs,"separability_gate":separability_gate,"regression":regression,
"frozen_inputs_unchanged":unchanged,"external_resources":external,"game_rl_decoder_web_3d_started":False}
write_json(exp/"audit/summary.json",summary)
model.mkdir(parents=True,exist_ok=True)
for src in (Path(__file__).resolve(),args.config.resolve(),Path(__file__).with_name("phase4f_extract_retinotopy.py")):
if src.is_file() and src.resolve() != (model/src.name).resolve(): shutil.copy2(src,model/src.name)
shutil.copy2(Path(cfg["paths"]["io_root"])/"brain_runtime.py",model/"brain_runtime.py")
write_json(model/"VERSION.json",{"name":cfg["names"]["encoder"],"status":status,"candidate_name":cfg["names"]["candidate"] if passed else None,
"brain_frozen":True,"connectome_changed":False,"lif_semantics_changed":False,"transmitter_sign_changed":False})
external_manifest=manifest_tree(Path(cfg["paths"]["external_root"]),exp/"manifests/external-resources-manifest.json")
model_manifest=manifest_tree(model,model/"MANIFEST.json",{model/"MANIFEST.json",model/"checksums.sha256"})
model_files=sorted(p for p in model.rglob("*") if p.is_file() and p.name not in {"MANIFEST.json","checksums.sha256"})+[model/"MANIFEST.json"]
(model/"checksums.sha256").write_text("".join(f"{sha256(p)} {p.relative_to(model)}\n" for p in model_files),encoding="utf-8")
exp_manifest=manifest_tree(exp,exp/"manifests/manifest.json",{exp/"manifests/manifest.json",exp/"manifests/checksums.sha256"})
outputs=sorted(p for p in exp.rglob("*") if p.is_file() and p != exp/"manifests/checksums.sha256")
(exp/"manifests/checksums.sha256").write_text("".join(f"{sha256(p)} {p.relative_to(exp)}\n" for p in outputs),encoding="utf-8")
print(json.dumps({"status":status,"candidate_generated":passed,"mapped":retino_summary["mapped_total_including_connectome_derived"],
"official_mapped":retino_summary["reliable_official_coordinate_neurons"],"separable_pairs":separable_pairs,
"min_output_latency_ms":summary["minimum_descending_or_motor_latency_ms"]},indent=2))
if __name__=="__main__": main()
@@ -0,0 +1,115 @@
phase: 4F
names:
reference: MaleCNS-LIF-reference-v1
runtime: MaleCNS-LIF-runtime-v1
io_api: MaleCNS-BrainIO-v1
activity_stream: MaleCNS-ActivityStream-v1
encoder: MaleCNS-VisualEncoder-v1
candidate: MaleCNS-VisualEncoder-v1-candidate
paths:
connectome_root: /data/digital-drosophila/datasets/male-cns-v1.0/processed/neuron-only
model_root: /data/digital-drosophila/models/male-cns-lif
reference_root: /data/digital-drosophila/models/male-cns-lif/reference-v1
runtime_root: /data/digital-drosophila/models/male-cns-lif/runtime-v1
io_root: /data/digital-drosophila/models/male-cns-lif/io-v1
phase4c_root: /data/digital-drosophila/experiments/phase4c-runtime
phase4d_root: /data/digital-drosophila/experiments/phase4d-io
phase4e_root: /data/digital-drosophila/experiments/phase4e-visual-calibration
experiment_root: /data/digital-drosophila/experiments/phase4f-retinotopic-vision
encoder_root: /data/digital-drosophila/models/male-cns-lif/visual-encoder-v1
external_root: /data/digital-drosophila/experiments/phase4f-retinotopic-vision/external-resources
device:
required_name_substring: RTX 4090
launcher_visible_gpu_uuid: GPU-79f3a85d-adce-7833-7003-6488140ca596
model:
dt_ms: 0.1
membrane_tau_ms: 20.0
synaptic_tau_ms: 5.0
refractory_ms: 2.2
resting_potential_mv: -52.0
reset_potential_mv: -52.0
threshold_mv: -45.0
synaptic_delay_ms: 1.8
w_syn_mv_per_anatomical_synapse: 0.275
sign_policy:
neurotransmitter_sign: {acetylcholine: 1, gaba: -1, glutamate: -1, dopamine: 1, octopamine: 1, serotonin: 1, histamine: -1}
unknown_sign: 0
unclear_sign: 0
official_resources:
visualpathways_commit: 23f6ac131529b5f56894c6eeb9b88b17894fc00d
celltype_explorer_commit: 789cc6c105798ce2fd70ba85dab394f90899616b
flyvis_commit: 92b3845cc426dd309a1a0e1b3890156c42e14021
retinotopy_tbar_url: https://storage.googleapis.com/flyem-male-cns/auxiliary-data/retinotopy-tbars/retinotopy_tbar_olr-2026-06-26.precomputed
paper_doi: 10.1016/j.cell.2026.08.014
encoder:
trial_duration_ms: 100.0
temporal_segments: 5
targets_per_segment: 460
poisson_rate_hz: 50.0
external_drive_mv: 8.8
seeds: [46101, 46102, 46103]
flyvis_bridge:
geometry: regular axial hex lattice sized from official MaleCNS unique eye columns
coordinate_equations: exact public flyvis.utils.hex_utils get_hex_coords/hex_to_pixel default convention
channel_weights: {luminance: 1.0, on_change: 1.0, off_change: 1.0, temporal_motion: 0.5, horizontal_motion_tendency: 1.0, vertical_motion_tendency: 1.0}
target_expression: annotation_superclass in [visual_projection, visual_projection_tbc] with retinotopic coordinate
patterns:
- full-field
- left-spot
- right-spot
- upper-spot
- lower-spot
- left-to-right
- right-to-left
- up-to-down
- down-to-up
- looming
- sparse-moving-object
- static-object
matched_pairs:
- [left-spot, right-spot]
- [upper-spot, lower-spot]
- [left-to-right, right-to-left]
- [up-to-down, down-to-up]
- [looming, left-to-right]
- [sparse-moving-object, static-object]
runaway: {active_fraction_gte: 0.80, mean_firing_rate_hz_gte: 100.0, peak_step_fraction_gte: 0.10}
success_gate:
minimum_separable_matched_pairs_in_descending_or_motor: 2
minimum_graph_hops: 2
maximum_output_latency_ms: 100.0
require_non_runaway: true
require_frozen_regression: true
readout:
bins_ms: [1, 5, 10, 20, 50]
regression:
float_absolute_tolerance: 0.00390625
reference_trials:
- {name: auditory-10hz, population: auditory, rate_hz: 10, seed: 42001, duration_ms: 1000.0}
- {name: auditory-100hz, population: auditory, rate_hz: 100, seed: 42001, duration_ms: 1000.0}
- {name: olfactory-50hz, population: olfactory, rate_hz: 50, seed: 42003, duration_ms: 1000.0}
- {name: mechanosensory-50hz, population: mechanosensory, rate_hz: 50, seed: 42003, duration_ms: 1000.0}
- {name: gustatory-50hz, population: gustatory, rate_hz: 50, seed: 42003, duration_ms: 1000.0}
prohibitions:
connectome_changes: false
raw_weight_changes: false
lif_semantics_changes: false
transmitter_sign_changes: false
brain_gain_changes: false
training: false
reinforcement_learning: false
game_integration: false
action_decoder: false
web_server: false
three_d_frontend: false
phase4g: false
@@ -0,0 +1,89 @@
#!/usr/bin/env python3
"""Snapshot the official MaleCNS retinotopy T-bar annotations at body level."""
from __future__ import annotations
import argparse
import json
import time
from collections import defaultdict
from pathlib import Path
import numpy as np
import pyarrow as pa
import pyarrow.parquet as pq
from neuroglancer.read_precomputed_annotations import AnnotationReader
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--url", required=True)
ap.add_argument("--output", type=Path, required=True)
ap.add_argument("--summary", type=Path, required=True)
args = ap.parse_args()
reader = AnnotationReader(args.url)
prop_ids = [p.id for p in reader.properties]
expected = ["hex1", "hex2", "area_to_count", "r", "theta", "v", "h", "ht", "vic", "area_fit", "body", "roi"]
if prop_ids != expected:
raise RuntimeError(f"official schema changed: {prop_ids}")
# Values are accumulated with area_to_count as the official contribution
# weight. This is a lossless body-level sufficient statistic for the
# coordinate means used by Phase 4F; no body IDs are inferred.
names = ["hex1", "hex2", "r", "theta", "v", "h", "ht", "vic", "area_fit"]
accum: dict[int, dict[str, float]] = defaultdict(lambda: defaultdict(float))
roi_sets: dict[int, set[int]] = defaultdict(set)
t0 = time.perf_counter()
annotations = 0
for ann in reader.get_within_spatial_bounds(max_parallelism=128):
p = ann.props
body = int(p[10])
weight = float(p[2])
row = accum[body]
row["weight_sum"] += weight
row["annotation_count"] += 1.0
for i, name in enumerate(names):
value = float(p[i if i < 2 else i + 1])
if np.isfinite(value):
row[f"{name}_weighted_sum"] += weight * value
row[f"{name}_finite_weight"] += weight
roi_sets[body].add(int(p[11]))
annotations += 1
if annotations % 250_000 == 0:
print(json.dumps({"annotations": annotations, "bodies": len(accum), "wall_seconds": time.perf_counter() - t0}), flush=True)
bodies = np.asarray(sorted(accum), dtype=np.uint64)
cols: dict[str, pa.Array] = {
"body_id": pa.array(bodies, type=pa.uint64()),
"tbar_annotation_count": pa.array([int(accum[int(b)]["annotation_count"]) for b in bodies], type=pa.int32()),
"visual_input_contribution_sum": pa.array([accum[int(b)]["weight_sum"] for b in bodies], type=pa.float64()),
}
for name in names:
values = []
for body in bodies:
row = accum[int(body)]
den = row[f"{name}_finite_weight"]
values.append(row[f"{name}_weighted_sum"] / den if den else None)
cols[f"tbar_{name}_weighted_mean"] = pa.array(values, type=pa.float64())
cols["tbar_roi_codes"] = pa.array([sorted(roi_sets[int(b)]) for b in bodies], type=pa.list_(pa.uint8()))
table = pa.table(cols)
args.output.parent.mkdir(parents=True, exist_ok=True)
pq.write_table(table, args.output, compression="zstd", compression_level=9)
summary = {
"source_url": args.url,
"official_property_schema": prop_ids,
"coordinate_space": reader.metadata["dimensions"],
"lower_bound": reader.lower_bound,
"upper_bound": reader.upper_bound,
"annotation_count": annotations,
"unique_body_count": len(bodies),
"aggregation": "body-level area_to_count-weighted means; official body property used verbatim",
"wall_seconds": time.perf_counter() - t0,
"output": str(args.output),
}
args.summary.write_text(json.dumps(summary, indent=2) + "\n", encoding="utf-8")
print(json.dumps(summary), flush=True)
if __name__ == "__main__":
main()