Pure-Rust implementation of the Neuromorphic Intermediate Representation (NIR)
Typed NIR graphs in Rust, with opt-in HDF5 .nir read/write that interoperates
with the official Python reference. The graph model has no system
dependencies; only the hdf5 feature links native libhdf5.
NIR is to spiking neural networks what ONNX is to conventional nets (or GGUF to LLMs): a framework-agnostic graph format so models can move between simulators and hardware without being rewritten.
- Official NIR is primarily Python (neuromorphs/NIR)
- This crate is a pure-Rust graph model with the same wire types and HDF5 layout
- Suitable for embedded, server, and tooling pipelines that should not embed a Python runtime
- Opt-in HDF5 I/O and Serde for debug serialization — enable only what you need
- Spec / reference: github.com/neuromorphs/NIR
- Primitives docs: neuroir.org
- Paper: Nature Communications (2024) (DOI 10.1038/s41467-024-52259-9) — cite
Wire compatibility: HDF5 node type strings must match the Python IR
(CubaLIF, Conv2d, SumPool2d, …), not informal aliases
(CurrLIF, Convolution, …).
This crate provides:
- The NIR graph model and standard node types
- Reading and writing
.nir(HDF5) files - Round-trip fidelity checks and structural validation
- An idiomatic Rust API (
NirGraph, closedNirNodeenum, tensors, errors)
This crate does not provide:
- SNN training or simulation
- Mapping graphs onto specific neuromorphic hardware
- Framework-specific importers/exporters (those belong in the tools that produce or consume NIR)
| Version | Focus |
|---|---|
| 0.4.x (current) | Graph model, HDF5 I/O, Serde/debug DX, crates.io |
| Earlier | Dual license, typed nodes, fixtures, CI hardening |
Release notes and the upstream compatibility matrix:
| Doc | Purpose |
|---|---|
| CHANGELOG.md | Keep a Changelog notes + 0.x versioning policy |
| COMPATIBILITY.md | Release ↔ upstream NIR, fidelity rules, features, MSRV |
Compatibility claims are fixture-backed (tests/fixtures/).
[dependencies]
nir-rs = "0.4.3"HDF5 .nir I/O (needs a system libhdf5, or a static build — see File I/O):
[dependencies]
nir-rs = { version = "0.4.3", features = ["hdf5"] }Debug Serde (JSON / RON / etc.; not a wire standard):
[dependencies]
nir-rs = { version = "0.4.3", features = ["serde"] }From git — pin a release tag (same tree as the matching crates.io release once the tag exists):
nir-rs = { git = "https://github.com/Limen-Neural/nir-rs", tag = "v0.4.3" }For unreleased work on the default branch:
nir-rs = { git = "https://github.com/Limen-Neural/nir-rs", branch = "main" }use nir_rs::nodes::{Input, Output};
use nir_rs::{NirGraph, NirNode};
fn main() -> nir_rs::Result<()> {
let mut g = NirGraph::new();
g.insert_node(
"input",
NirNode::Input(Input {
shape: vec![4],
metadata: Default::default(),
}),
)?;
g.insert_node(
"output",
NirNode::Output(Output {
shape: vec![4],
metadata: Default::default(),
}),
)?;
g.add_edge("input", "output");
g.validate_structure()?;
Ok(())
}validate_structure checks edge endpoints and duplicate directed edges.
Convolution and pooling parameter invariants (weight rank, groups, stride,
padding, bias, pooling windows) are a separate opt-in:
g.validate_parameters()?;HDF5 reads run neither check. The default writer runs structure validation only.
.nir is the official NIR interchange format: an HDF5 container whose layout is
fixed by upstream. Files written here load in Python nir.read, and files
written by nir.write load here.
fn main() -> nir_rs::Result<()> {
let graph = nir_rs::io::read("model.nir")?;
for (name, node) in &graph.nodes {
println!("{name}: {}", node.type_name());
}
nir_rs::io::write("copy.nir", &graph)?;
Ok(())
}Default io::read is permissive, matching Python nir.read: a missing
/version becomes None, and any present string is stored verbatim.
Production importers can fail closed before the graph body is decoded:
use nir_rs::io::{ReadOptions, VersionPolicy};
fn main() -> nir_rs::Result<()> {
// Inspection tool: accept whatever `/version` the file carries.
let graph = nir_rs::io::read("model.nir")?;
println!("{:?}", graph.version);
// Fail-closed importer: paper 0.x fixtures and current 1.x writers.
let opts = ReadOptions::default()
.with_version_policy(VersionPolicy::compatible_major([0, 1]));
let graph = nir_rs::io::read_with("model.nir", &opts)?;
let _ = graph;
Ok(())
}I/O is behind the opt-in hdf5 feature, which links native libhdf5.
Without that feature the crate has no system dependencies:
| Platform | System dependency |
|---|---|
| Debian / Ubuntu | apt install libhdf5-dev |
| Fedora | dnf install hdf5-devel |
| macOS | brew install hdf5 |
| Anywhere | Depend on hdf5-metno = { version = "0.14.1", features = ["static", "zlib"] }. Cargo feature unification enables the vendored build for this crate too; nir-rs requires hdf5-metno-sys >=0.12.3, which supports the vendored HDF5 2.2.0. A dependency cannot enable hdf5/static through this crate's feature list alone; without zlib the vendored build has no gzip filter. |
Without the feature, io::read / io::write still exist and return
NirError::Unimplemented, so downstream code compiles either way.
Round-trip fidelity is graph-level, not byte-level: node names and types,
ordered edges, and parameter values are preserved; HDF5 group order and chunk
layout may differ from h5py. In-memory dtypes (f32, f64, i64, bool)
round-trip exactly; narrower on-disk integers widen to i64 on read. Absent
optional fields (v_reset, w_in) use the same defaults as Python so graphs
match nir.read in memory.
cargo run --example load_inspect_lif --features hdf5
# optional paths:
cargo run --example load_inspect_lif --features hdf5 -- model.nir copy.nirDefault input is tests/fixtures/lif_norse.nir; default output is a
PID-qualified file in the system temp directory.
The opt-in serde feature implements Serialize / Deserialize for the graph
model. It is independent of hdf5:
[dependencies]
nir-rs = { version = "0.4.3", features = ["serde"] }
serde_json = "1"JSON is debug/test output, not a NIR interchange standard. Use HDF5 .nir
for Python and hardware tooling. JSON cannot represent NaN/infinities faithfully.
Rust 1.98.1 — rust-toolchain.toml, package.rust-version, and CI
(Linux / macOS / Windows) all pin the same version.
cargo fmt --check
cargo test # graph model only — no libhdf5
cargo test --features serde
cargo test --all-features # + HDF5 fixtures / round-trip
cargo clippy --all-targets --all-features -- -D warnings
cargo doc --no-deps --all-featuresWire compatibility is checked against real Python-written .nir fixtures under
tests/fixtures/ (BSD-3; see that directory's README). No Python interpreter is
required for default builds, tests, or CI.
API and fidelity details: COMPATIBILITY.md, docs.rs/nir-rs.
If you use NIR in your work (including via this crate), please cite the Nature Communications paper:
@article{NIR2024,
title={Neuromorphic intermediate representation: A unified instruction set for interoperable brain-inspired computing},
author={Pedersen, Jens E. and Abreu, Steven and Jobst, Matthias and Lenz, Gregor and Fra, Vittorio and Bauer, Felix Christian and Muir, Dylan Richard and Zhou, Peng and Vogginger, Bernhard and Heckel, Kade and Urgese, Gianvito and Shankar, Sadasivan and Stewart, Terrence C. and Sheik, Sadique and Eshraghian, Jason K.},
rights={2024 The Author(s)},
DOI={10.1038/s41467-024-52259-9},
number={1},
journal={Nature Communications},
volume={15},
year={2024},
month=sep,
pages={8122},
}Machine-readable form: CITATION.cff (GitHub “Cite this repository”).
NIR was originally conceived at the Telluride Neuromorphic Workshop 2023 by the authors below (alphabetical order), as listed by upstream neuromorphs/NIR:
- Steven Abreu
- Felix Bauer
- Jason Eshraghian
- Matthias Jobst
- Gregor Lenz
- Jens Egholm Pedersen
- Sadique Sheik
- Peng Zhou
This crate is an independent pure-Rust implementation of that IR; it is not the official Python reference package.
Dual-licensed under either:
- Apache License, Version 2.0 (LICENSE-APACHE-2.0 or https://www.apache.org/licenses/LICENSE-2.0)
- MIT License (LICENSE-MIT or https://opensource.org/licenses/MIT)
at your option.