Skip to content

AER Spike-Buffer Decoder

MIF-006 implements the local, upstream-pending AER ingress adapter that turns address-event spike streams into ControlObservation-compatible feature vectors. The surface is allocated to Python and Rust. Go remains reserved for optional network router scaffolding and is not a local decode backend.

Decode Strategies

For a decode window [t_start, t_start + W) and channel address a, the implemented feature strategies are:

rate[a]     = signed_spike_count[a] / W
temporal[a] = 1 - (first_spike_time[a] - t_start) / W
isi[a]      = (n_spikes[a] - 1) / (last_spike_time[a] - first_spike_time[a])

Channels with no applicable spikes decode to 0.0. Event timestamps, explicit window starts, and decode-window lengths are non-negative u64 nanosecond counters on both Python and Rust/PyO3 surfaces. Timestamps must be monotone at buffer insertion time, addresses outside n_channels fail closed at decode time, and decode windows whose exclusive stop would overflow u64 are rejected before feature extraction.

Python API

spike_buffer

AER spike-buffer decoding for MIF-006.

AERSpikeEvent(address, t_ns, polarity=1) dataclass

Single address-event spike.

__post_init__()

Validate and freeze the spike event address, timestamp, and polarity.

AERDecodeSpec(n_channels, window_ns, strategy='rate', start_ns=None) dataclass

Decode settings for AER spike streams.

__post_init__()

Validate the decode channel count, time window, and optional start.

AERDecodedObservation(spec, features, window_start_ns, window_stop_ns, spike_count) dataclass

Decoded ControlObservation-compatible feature vector.

__post_init__()

Freeze decoded features and validate observation counters.

SpikeBuffer(capacity)

Deterministic monotone AER spike ring buffer.

events property

Return buffered events in arrival order.

n_channels property

Return the minimum channel count that covers all buffered addresses.

__len__()

Return the number of buffered spike events.

push(event)

Append one monotone event, dropping the oldest event when full.

clear()

Remove all buffered events and reset timestamp state.

decode(spec)

Decode buffered events according to spec.

AERControlObservation(spike_stream, decode_window_ns, decode_strategy='rate', n_channels=None, start_ns=None) dataclass

Local upstream-pending ControlObservation adapter for AER streams.

spike_count property

Return the number of spikes inside the decode window.

window_start_ns property

Return the inclusive decode-window start timestamp.

window_stop_ns property

Return the exclusive decode-window stop timestamp.

to_features()

Return the decoded feature vector.

decode_spike_features(buffer, spec)

Decode buffer and return only the feature vector.

decode_spike_observation(buffer, spec)

Decode buffer into a ControlObservation-compatible report.

Dispatch

Use scpn_mif_core.aer.dispatched_aer_spike_buffer(...) for the fastest available measured ring-buffer backend:

"aer.spike_buffer" = ["rust", "python"]
"aer.decode_rate" = ["rust", "python", "mojo", "julia"]

The pure Python SpikeBuffer remains available for deterministic debugging and tests. The rate decode also ships a Mojo surface (mojo/aer_decode_rate.mojo, compiled with mojo build -Xlinker -lm): a subprocess CLI with bit-exact parity — the decode is sequential integer-to-float accumulation with no transcendental and no fused multiply-add. Its per-call process spawn places it behind the in-process backends but ahead of the Julia CLI; like Julia it is a measured/parity surface, not the runtime hot path. Parity-tested in tests/unit/aer/test_decode_rate_mojo_parity.py.

Acceptance

The committed acceptance fixture is a local SHD-compatible AER stream with four channels, a 100 ns decode window, and five monotone events. Tests verify:

  • exact rate feature vector [0.02, 0.01, 0.0, 0.02];
  • exact temporal feature vector [1.0, 0.5, 0.0, 0.9];
  • exact ISI feature vector [0.05, 0.0, 0.0, 0.0125];
  • deterministic ring-buffer overflow behaviour;
  • fail-closed handling for non-monotone timestamps and out-of-range addresses;
  • Python/Rust parity for the u64 timestamp domain and decode-window overflow;
  • Python/Rust parity through the PyO3 surface.

Benchmarks

Measured on the local i5-11600K rig using Python 3.12.3 and Rust 1.85.0. This was a non-isolated workstation comparison with the CPU governor set to powersave and nontrivial host load; treat it as regression evidence, not a production latency claim.

Group Backend Mean Result
push_256 Rust 14.96 us fastest
push_256 Python 288.97 us 19.3x slower than Rust
decode_256 Rust 1.31 us fastest
decode_256 Python 51.59 us 39.5x slower than Rust

Raw summaries: bench/results/aer_spike_buffer.json and bench/results/aer_decode_rate.json.

Ownership

SYNC-STATE: upstream-pending applies to all MIF-006 implementation surfaces. SCPN-MIF-CORE owns the local AER ingress adapter until SCPN-CONTROL receives the reusable AERControlObservation surface targeted for the 0.21.0 lane.