-
Notifications
You must be signed in to change notification settings - Fork 0
sequence primitives plan
Focused design for workstream C of the
professional_roadmap.md. Read the roadmap first for the vision, cross-workstream interfaces, and packaging rules.
Design/spec only. Every claim about the current code is grounded in a file/line reference so a Code-mode agent can execute this file-by-file.
Objective. Widen the layer vocabulary so sequence and attention topologies
are expressible, and let every neuron stage choose its own kind, parameters,
and surrogate. Each new module kind gets a snnTorch/PyTorch factory and an
explicit NIR contract — either a real mapping or a typed UnsupportedStageError,
mirroring the alpha precedent. Ship a demonstration sequence preset so the new
vocabulary is exercisable and NIR-validatable.
Scope boundary (explicit). This enables sequence/token experimentation, a small spiking transformer/sequence topology. It does not claim production LLM training. Long-context, KV caching, and large-vocabulary training are out of scope and noted as deferred.
| Capability | Current reality | Anchor |
|---|---|---|
| Module kinds |
flatten, linear, conv2d, avgpool2d, sumpool2d + add
|
kinds.py |
| Stage model |
Stage(name, kind, params) already per-stage |
stage.py |
| Neuron selection | One kind for all stages in a preset |
presets.py, registry.py
|
| Module factories | Fixed builder table | stage_modules.py |
| NIR builders | Fixed builder table |
mapper.py, node_builders.py
|
| Unexportable precedent |
alpha -> typed error |
neuron_nodes.py, mapper.py
|
| Emitted primitives | Fixed set | primitives.py |
| Input shape | Feature vs. spatial only |
input_shape.py, frames.py
|
| Sequence / attention | None | n/a |
- The default
neuronselection still applies to every neuron stage when no per-stage override is given; a preset built with defaults is byte-identical to today (presets.py). -
fc_legacykeeps_fc1/_lif1/_fc2/_lif2and the legacy wrapper (registry.py). -
TopologySpec.to_dict/from_dictround-trips heterogeneous stages with no schema change — per-stage kind+params are already captured (spec.py). -
alphacontinues to raiseUnsupportedStageError(neuron_nodes.py). - Existing
topology_paramskeys andTrainConfigfields stay valid and additive (train_config.py). - Existing WebSocket payload keys are unchanged.
Stage already stores (kind, params) per stage, and build_neuron already
resolves any registered kind (registry.py).
The only thing forcing homogeneity is the presets: _neuron_stage applies one
neuron/surrogate to every neuron stage
(presets.py). The change is to let a
preset resolve a neuron kind per stage name, with the single-neuron
argument remaining the default.
New preset parameters (additive, forwarded by the registry):
| Param | Type | Meaning |
|---|---|---|
neuron |
str | default kind for every neuron stage (unchanged) |
surrogate |
str? | default surrogate for every neuron stage (unchanged) |
neurons |
map? | per-stage-name kind override |
stage_params |
map? | per-stage-name params merged over the default |
_neuron_stage becomes _resolve_neuron_stage(name, neuron, neurons, ...),
returning neurons[name] when present, else the default, and merging
stage_params[name] over the computed params. Because resolved_params only
forwards keys already in a preset's defaults
(registry.py), neurons and
stage_params are added to each preset's default mapping in
registry.py.
-
TrainConfiggains two additive optional fields (or nesting insidetopology_params, which already exists):stage_neurons: Dict[str, str]andstage_params: Dict[str, Dict[str, Any]](train_config.py). Existing clients are unaffected. - Checkpoint
metaalready stores the resolvedspec, which carries per-stage kind+params (checkpoint_mixin.py). Add a readablestage_neuronssummary tometa(additive), so a checkpoint's heterogeneity is visible without decoding the spec. - Reproducibility: the manifest already includes the spec
(
manifest.py); no change needed.
The mapper already dispatches per stage.kind
(mapper.py) and neurons map
per-kind (neuron_nodes.py).
So heterogeneous neurons export correctly with no mapper change: a leaky
stage emits LI+Threshold, a synaptic stage emits CubaLIF, and so on,
side by side. This is a key reason the spine's per-stage design pays off.
Added to kinds.py:
embedding, conv1d, maxpool1d, maxpool2d, layer_norm, batch_norm,
dropout, positional_encoding, attention, multihead_attention.
Each kind declares a NIR contract with exactly one of three outcomes:
| Outcome | Meaning | Precedent |
|---|---|---|
mapped |
a builder emits NIR node(s) | node_builders.py |
passthrough |
inference-equivalent to identity; emits no node | new, documented |
unexportable |
no faithful NIR primitive; raises typed error |
alpha (neuron_nodes.py) |
Proposed contracts (each verified against the installed nir at build time via
api.py, so a future nir that adds a
primitive flips the contract honestly):
| Kind | Factory | NIR contract |
|---|---|---|
embedding |
nn.Embedding |
unexportable — no embedding primitive; simulation-only |
conv1d |
nn.Conv1d |
mapped if nir.Conv1d exists, else unexportable
|
maxpool1d / maxpool2d
|
nn.MaxPool*d |
unexportable — nir pooling is avg/sum only |
layer_norm / batch_norm
|
nn.LayerNorm / nn.BatchNorm*d
|
unexportable — no norm primitive in nir
|
dropout |
nn.Dropout |
passthrough — identity at inference |
positional_encoding |
deterministic add |
unexportable — no constant node; simulation-only |
attention |
custom single-head |
unexportable — no attention primitive |
multihead_attention |
custom multi-head |
unexportable — no attention primitive |
The unexportable set is collected in a new
nir_bridge/stages_unmappable.py table (one reason string per kind) and
consulted by the mapper exactly where alpha is consulted today, so export
raises a typed error naming the stage instead of silently dropping it.
spikeforge/topology/
kinds.py extend MODULE_KINDS with the new kinds
stage_modules.py register factories in _BUILDERS
sequence_stages.py embedding, positional_encoding, attention, norms factories
spikeforge/nir_bridge/
stage_builders.py conv1d/pool/norm/attention builder table
stages_unmappable.py kind -> reason, for unexportable kinds
mapper.py consult stage_builders then stages_unmappable
node_builders.py add any genuinely mapped builders
Files stay under 250 lines by splitting the builder tables (the codebase
already does this with ops_linear.py/ops_neuron.py).
New emitted primitives (e.g. Conv1d) are added to
EMITTED_PRIMITIVES only when a
mapper can actually emit them, so the capability matrix and the mapper agree on
one vocabulary.
input_shape (input_shape.py)
gains a sequence layout [T, B, L, D] (steps, batch, sequence length, feature
dim), selected when the spec's input stage is a sequence kind (embedding,
attention, conv1d). normalise_frame
(frames.py) must pass sequence frames
through unflattened, exactly as it already does for spatial kinds
(interpreter.py shows the
analogous spatial special-case).
New data/sequence_source.py serves
(tokens, label) pairs for a synthetic/registered sequence task; the dataset
registry (dataset_spec.py) gains a
sequence modality. This is deliberately small — a toy token task that
exercises the vocabulary, not a corpus pipeline.
Two presets, for two honest purposes:
-
sequence_mlp— built only from NIR-mappable kinds (linear,flatten,leaky) applied to a[T, B, L, D]sequence. This is the preset that NIR-validates end to end and proves the sequence data path. It is added topresets.pyandregistry.py. -
sequence_attn— the demonstration spiking-transformer-shaped preset (embedding->positional_encoding->multihead_attention->layer_norm->linear-> neuron, stacked). It is simulation-only; export raises the typedUnsupportedStageErrornaming the first unexportable stage, mirroringalpha. This is the honest way to ship attention without faking NIR.
Both accept the per-stage neuron overrides from section 2, so sequence_attn
can mix leaky/synaptic/recurrent neurons per stage as a demonstration.
flowchart LR
TOK[tokens T B L] --> EMB[embedding]
EMB --> POS[positional_encoding]
POS --> ATT[multihead_attention]
ATT --> LN[layer_norm]
LN --> FC[linear]
FC --> LIF[leaky per stage]
LIF --> OUT[output]
No new actions; the existing train/nir_export/deployment_report actions
carry the heterogeneous configuration through TrainConfig.topology_params.
nir_export returns the graph summary; for sequence_attn it returns the
typed unexportable error on the existing error channel with the stage named.
-
StageEditor.tsx: per-stage kind, neuron kind, params, and surrogate; renders the spec fromgraph_summary. -
TopologyStageList.tsx: the flat list of stages with their heterogeneity. - The neuron picker (
Controls.tsx) keeps its global default and gains a "per-stage" expansion. - An unexportable badge per stage, mirroring the
TargetsPanel.tsxhonesty styling.
python -m spikeforge.cli.verify validate --topology sequence_mlp must
report within_tolerance=True; export --topology sequence_attn must exit
non-zero with the unexportable stage named.
-
Deliverables:
presets.pyper-stage resolution,registry.pydefaults,TrainConfigfields,checkpoint_mixin.pystage_neuronsmeta,StageEditor.tsx. -
Acceptance: a preset with
neurons={"lif1": "synaptic"}builds a spec whoselif1kind issynapticand others stayleaky; a default build is unchanged; the spec round-trips through checkpoint save/load; existingnir_validatestill passes.
-
Deliverables:
kinds.py,stage_modules.py,sequence_stages.py,stages_unmappable.py,stage_builders.py,mapper.py,node_builders.py. -
Acceptance: every new kind builds a module;
conv1dmaps or reports unexportable per the live probe;attention/embedding/norms raise the typed error naming the stage;dropoutexports as a documented passthrough.
-
Deliverables:
input_shape.py,frames.py,data/sequence_source.py,dataset_spec.pymodality. -
Acceptance: a
[T, B, L, D]tensor flows through the simulator unchanged in shape;sequence_mlpruns on sequence tokens.
-
Deliverables:
sequence_mlp,sequence_attnpresets and registry entries; client stage list; docs. -
Acceptance:
validate --topology sequence_mlpis within tolerance;export --topology sequence_attnraises the typed unexportable error; the client renders both.
-
NIR primitive absence: several kinds are genuinely unexportable; the
design leans on the
alphaprecedent rather than inventing lossy mappings. If upstreamniradds a primitive, the probe flips the contract honestly. - Simulator statefulness: attention is stateless per step in the generic loop; any recurrent attention state would need a state container change (out of scope here).
- Sequence scope: small token experiments only; long-context, KV cache, and large vocabularies are deferred.
- Deferred: spiking-specific attention kernels, learned positional encodings, and mixed-precision sequence training.
- Home
- Architecture
- Backend Execution
- Benchmarks
- Dashboard
- Development
- Event Datasets
- Event Runtime And Energy
- Features
- Implications And Boundaries
- Interop Foldins
- Interpreter Spine
- Introspection
- Model Deployment
- Model Hub
- Notes
- Operational Maturity
- Production Workflows
- Project Layout
- Quickstart
- Requirements
- Sequence Primitives
- Streaming Timeseries
- Targets And Interop
- Usage
- Arch 0001 Adr Repo Topology
- Arch 0001 Core Boundary
- Arch 0001 Decision Metrics
- Arch 0001 Migration Plan
- Arch 0001 Packaging Versioning
- Arch 0001 Protocol Contract
- Arch 0001 Risk Register
- Arch 0001 Target Topology
- Backend Execution Plan
- Ecosystem Listings
- Ecosystem Roadmap
- Event Runtime Plan
- Hub Expansion Plan
- Plans
- Interop Foldins Plan
- Interpreter Spine Plan
- Memory System Research
- Model Hub Plan
- Operations Plan
- Production Toolkit Plan
- Production Use Cases
- Professional Roadmap
- Repo Topology Plan
- Sequence Primitives Plan
- Use Case Audio Keyword Spotting
- Use Case Biosignal Medical Monitoring
- Use Case Computational Neuroscience
- Use Case Edge Power Budgets
- Use Case Event Camera Vision
- Use Case Intrusion Anomaly Detection
- Use Case Low Latency Sensor Stream
- Use Case Rl Control Robotics
- Use Case Spiking Transformers
- Use Case Streaming Timeseries