-
Notifications
You must be signed in to change notification settings - Fork 0
backend execution plan
Focused design for workstream B 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. Turn the declared deployment story into an executable one:
wire at least one real, pip-installable, cross-platform backend so deploy
actually compiles and runs a graph and returns comparable trajectories; execute
the capability-matrix substitutions with a report and a post-rewrite drift
check; keep the in-process reference target always available; and degrade
honestly when an SDK is absent.
| Option | Install | Cross-platform | Effort | Fit |
|---|---|---|---|---|
| Norse |
pip install norse, pure PyTorch |
yes | low | recommended first |
| Lava / Loihi 2 |
pip install lava-nc, heavier |
partial | high | first hardware path |
| SpiNNaker2 | SDK + toolchain | partial | high | later |
Speck (sinabs) / Xylo (rockpool) |
vendor SDKs | partial | high | later |
Norse is recommended first because it is pip-installable, pure PyTorch, and
shares the torch stack, so it exercises the whole compile-run-drift pipeline
with minimal new machinery. It also needs exactly one substitution
(IF → beta=0 LIF) that is already declared
(catalog.py), making it the natural
first executed substitution.
Lava is chosen as the first hardware path because its declared substitution
(AvgPool2d → SumPool2d, catalog.py)
is a clean, well-understood rewrite, and the SDK is pip-installable (if not
trivially so). It ships executable-when-present and reports unavailable
otherwise.
| Capability | Current reality | Anchor |
|---|---|---|
| Target registry | Six entries, one available |
registry.py, catalog.py
|
| Availability probe | Isolated module import | probe.py |
| Capability matrix | Classifies supported/substituted/unsupported | capability_matrix.py |
| Substitution record | Declared primitive -> substitute
|
substitution.py, target_spec.py
|
| Deployment report |
deployable is a capability flag |
report.py |
| NIR serialization | save/load graph | serialization.py |
| Reference interpreter | Runs emitted primitives independently | interpreter.py |
| Compile/run a backend | None | n/a |
| Substitution execution | None | n/a |
| Backend trajectory compare | Only snnTorch vs NIR | validator.py |
-
referencestays available unconditionally (catalog.py); its report shape is unchanged (report.py). - Existing
deployment_reportpayload keys and thedeployment_report/target_listmessages stay additive (server_message.py,target_handlers.py). - The declared
substitutionsmapping remains the source of truth for which rewrites apply; execution only consumes it. - No backend import escapes its isolated module; a missing SDK is reported, never raised at import.
-
ruff, the 435-test suite, and the client build stay green.
The executor applies a target's declared substitutions
(TargetSpec.substitutions) to
produce a target-ready graph, reports what changed, and re-validates drift.
spikeforge_targets/
substitute_ops.py one rewrite function per (primitive -> substitute) pair
rewrite.py rewrite(graph_or_spec, target) -> RewriteResult
rewrite_report.py JSON-able deltas: applied, skipped, unfixable
rewrite_result.py RewriteResult dataclass: graph, report
| From | To | Rewrite | Target |
|---|---|---|---|
IF |
LIF |
single nir.LIF with beta = 0 (identity integrator) |
norse |
AvgPool2d |
SumPool2d |
SumPool2d followed by a Scale of 1/k where k is the pooled window product |
lava_loihi2 |
Each rule is a small pure function (node, meta) -> (nodes, edges) so a rule
that expands one node into several (AvgPool) and a rule that is one-for-one
(IF) share one interface. A primitive with no substitution stays
unsupported and is surfaced as unfixable, never dropped
(capability_matrix.py).
RewriteResult carries the rewritten graph plus a report:
{
"target": "norse",
"applied": [{"node": "lif1", "from": "IF", "to": "LIF", "detail": "beta=0"}],
"skipped": [],
"unfixable": [{"node": "pool1", "primitive": "AvgPool2d", "reason": "no substitution declared"}],
"rewritten": true
}After rewriting, the executor runs a drift check: the rewritten graph is
executed by the reference interpreter
(NirInterpreter) on the same
fixture and compared to the pre-rewrite execution using the existing drift
machinery (drift.py). The report
includes the drift and whether it is within tolerance, so a lossy substitution
(e.g. the AvgPool window edge case) is quantified rather than hidden.
spikeforge_targets/backends/
__init__.py public compile_run + backend registry
api.py isolated per-SDK probes and import helpers (the only importer)
result.py BackendResult dataclass: readout, spikes, status, notes
base.py Backend protocol: available(), compile(graph, spec), run(spikes)
reference_backend.py wraps NirInterpreter (always available)
norse_backend.py Norse simulator backend
lava_backend.py Lava/Loihi 2 hardware path (executable when SDK present)
compare.py compare a BackendResult to the reference trajectory
api.py is the single module that imports a backend SDK, mirroring
probe.py and
nir_bridge/api.py. It exposes
module_available(name) and version strings; nothing else imports a backend.
class Backend(Protocol):
def available(self) -> bool: ...
def compile(self, graph: Any, spec: TopologySpec) -> Any: ...
def run(self, compiled: Any, spikes: torch.Tensor) -> BackendResult: ...BackendResult carries readout, per-stage spikes/membranes in the same
shape as InterpreterResult,
a status in {ok, unavailable, error} with an honest notes list, and the
rewritten report when a substitution preceded compilation.
flowchart TD
SPEC[TopologySpec or NIR graph] --> CLASS[Classify vs target]
CLASS --> REW[Substitution executor]
REW --> READY[Target-ready graph]
READY --> PROBE{SDK available}
PROBE -- yes --> COMPILE[Backend compile]
COMPILE --> RUN[Backend run]
PROBE -- no --> UNAVAIL[BackendResult status unavailable]
RUN --> CMP[Compare to reference]
UNAVAIL --> REP[Deployment report]
CMP --> REP
REW --> DRIFT[Post-rewrite drift check]
DRIFT --> REP
Compiles the target-ready NIR graph into a Norse module set (norse.LIF,
norse.LI, linear/conv equivalents), runs it over the [T, ...] spike train,
and returns a BackendResult. The IF→beta=0 rewrite makes norse.LIF
numerically reproduce nir.IF, so the comparison to the reference interpreter
should be within tolerance. Unsupported-by-Norse nodes (CubaLIF, Delay per
catalog.py) are reported
unfixable, and run is refused for a graph containing them.
Ships the same protocol. When lava-nc is absent it reports
status="unavailable" with notes=["install the lava extra"]. When present it
compiles the rewritten graph to a Lava process and runs it, returning a
BackendResult; a hardware timing model or a CPU Loihi emulator
(lava's Loihi2SimCfg) is used when no physical device is attached, and the
report states which path ran (honesty rule).
| Action | Request | Reply | Payload |
|---|---|---|---|
deploy_run |
{train, name: target} |
backend_run |
BackendResult + rewritten + drift + compare |
The existing deployment_report still returns the capability view; deploy_run
adds the executed view. Routed via
protocol_handlers.py to a new
server/backend_handlers.py and server/backend_payloads.py, following
target_handlers.py.
Extended in target_cli.py:
spikeforge-targets rewrite --topology conv_net --target norse # rewrite report + drift
spikeforge-targets run --topology conv_net --target norse # compile + run + compare
Both print JSON; run exits non-zero unless status == "ok" and the compare
is within tolerance, so it is a CI gate (same convention as
deploy_exit).
BackendRunPanel.tsx renders the
backend status, the rewrite report (applied/skipped/unfixable), the drift, and
the reference comparison, reusing
DeploymentReportView.tsx
styling and DeploymentBuckets.tsx.
-
Deliverables:
targets/substitute_ops.py,targets/rewrite.py,targets/rewrite_report.py,targets/rewrite_result.py;spikeforge-targets rewrite. -
Acceptance: rewriting
conv_netfornorseconverts the declaredIFto abeta=0LIF(when present) and reports it; rewriting forlava_loihi2expandsAvgPool2dintoSumPool2d+Scale; an unfixable primitive is listed, not dropped; the post-rewrite drift is reported.
-
Deliverables:
targets/backends/{__init__,api,base,result,compare, reference_backend,norse_backend}.py;spikeforge-targets run. -
Acceptance: with
norseinstalled,conv_netcompiles and runs and its readout matches the reference within tolerance; theIF→beta=0rewrite reproducesnir.IF; withoutnorse,status=="unavailable"and a note, never an exception.
-
Deliverables:
targets/backends/lava_backend.py. -
Acceptance: without
lava-nc,status=="unavailable"; with it, the rewritten graph compiles and runs (device or Loihi emulator) and the report names which path ran.
-
Deliverables:
server/backend_handlers.py,server/backend_payloads.py, schema additions,BackendRunPanel.tsx. -
Acceptance:
deploy_runround-trips over a live connection; the client builds and renders; existingdeployment_reportandtarget_listpayloads are unchanged.
-
SDK API drift: isolated in
backends/api.py; each backend'scompilebegins with a probe, mirroringapi.py. -
Lava build weight:
lava-ncis heavy and platform-sensitive — it stays an optional extra and the reference/Norse paths remain the always-available fallback. - Substitution fidelity: the AvgPool→SumPool+Scale rewrite is exact only for non-overlapping windows with no padding; the drift check quantifies any residual and the report flags it rather than claiming exactness.
- Deferred: SpiNNaker2, Speck, and Xylo execution (declared only until their extras land); on-device measured timing (see WS-D energy honesty).
- 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