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.

This decoder is the compatibility rate/temporal/ISI surface. The separate ordered event-integrity path preserves raw address, polarity, source timestamps, and sequence through the exact-current diagnostic boundary; callers must not infer ordered lossless delivery from this ring decoder alone.

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.

SpikeBufferTelemetry(accepted_events, evicted_events, rejected_events, high_watermark, overflowed) dataclass

Lifetime ingress and explicit ring-eviction 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.

telemetry property

Return an immutable snapshot of lifetime buffer integrity counters.

__len__()

Return the number of buffered spike events.

push(event)

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

clear()

Remove buffered events while preserving lifetime loss telemetry.

reset_telemetry()

Start a new telemetry epoch without discarding buffered events.

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.