diff --git a/AGENTS.md b/AGENTS.md index 616a665..61a64b8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -51,10 +51,9 @@ nightly fuzz harnesses: see [TESTING.md](TESTING.md) (`cargo +nightly fuzz run `--all-features` enables `hdf5`, which links libhdf5: install `libhdf5-dev` (Ubuntu) or `hdf5` (Homebrew) first. -**Toolchain:** CI and agents pin **Rust 1.97.1** (`rust-toolchain.toml`). -`package.rust-version` (**1.85.1**) is a cargo floor only — do **not** point the -OS matrix at an old rustc. Default/`serde` and `--all-features` tests run on -**Linux, macOS, and Windows** with `toolchain: "1.97.1"`. +**Toolchain:** **Rust 1.97.1** everywhere — `rust-toolchain.toml`, +`package.rust-version`, and CI (`toolchain: "1.97.1"`) on **Linux, macOS, and +Windows**. Keep those three in lockstep when the pin moves. ### Package / semver CI (release gate) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86c25ac..dc0e204 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,25 @@ for the **0.x** series as described under [Versioning](#versioning) below. ## [Unreleased] +## [0.4.2] - 2026-08-13 + +Docs-only patch so crates.io serves a consumer-facing README (0.4.1 is +immutable on the registry). + +### Changed + +- `package.rust-version` raised **1.85.1 → 1.97.1** to match the supported CI / + `rust-toolchain.toml` pin (stop advertising an untested older floor). + +### Documentation + +- Rewrite README for public consumers: drop internal sibling-crate roadmap, + Linear tracking, and CI-only Hub variable language; keep crates.io/docs.rs + install path, GHCR pulls, and scope clear without org-private context. +- Align crate rustdoc non-goals and a few comments with the same public wording. +- Bump advertised install / Docker tags to **0.4.2**. +- Toolchain docs: single **1.97.1** pin (no separate MSRV floor story). + ## [0.4.1] - 2026-08-12 First **crates.io** release of the 0.4 line (package name `nir-rs`). diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 3d3ac76..45859b9 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -7,7 +7,8 @@ and what each release line claims. | nir-rs line | Git tag / crates.io | Upstream NIR (fixtures) | Notes | |-------------|---------------------|-------------------------|--------| -| **0.4.x** | `v0.4.1` / crates.io **0.4.1** | Vendored from neuromorphs/NIR @ `7883c3c` (see `tests/fixtures/README.md`) | Graph model + HDF5 I/O + serde DX + release hardening | +| **0.4.x** | `v0.4.2` / crates.io **0.4.2** | Vendored from neuromorphs/NIR @ `7883c3c` (see `tests/fixtures/README.md`) | Graph model + HDF5 I/O + serde DX + release hardening | +| 0.4.1 | crates.io **0.4.1** | Same fixture commit | First crates.io release; README still org-oriented | | `v0.4.0` | git tag only (pre-crates.io) | Same fixture commit | First git-tagged consumer pin | **Only fixture-backed claims are made.** A newer upstream NIR release is **not** @@ -55,8 +56,7 @@ Default builds and default-feature CI need **no** Python and **no** libhdf5. | Pin | Version | Role | |-----|---------|------| -| Dev / CI | **1.97.1** | Quality bar (`rust-toolchain.toml`, GitHub Actions OS matrix) | -| `package.rust-version` | **1.85.1** | Cargo/crates.io floor (edition 2024 + `hdf5-metno` 0.14) | +| Dev / CI / `package.rust-version` | **1.97.1** | Same pin in `rust-toolchain.toml`, GitHub Actions, and crates.io metadata | We do not matrix CI on the cargo floor. Raise the floor when dependencies or language features require it; document the bump in the changelog. diff --git a/Cargo.lock b/Cargo.lock index 84859ff..2a41624 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -245,7 +245,7 @@ dependencies = [ [[package]] name = "nir-rs" -version = "0.4.1" +version = "0.4.2" dependencies = [ "hdf5-metno", "hdf5-metno-sys", diff --git a/Cargo.toml b/Cargo.toml index c8ba159..c1df018 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,10 @@ [package] name = "nir-rs" -version = "0.4.1" +version = "0.4.2" edition = "2024" -# Floor only (cargo/crates.io metadata). Edition 2024 needs ≥1.85.0; optional -# `hdf5` (hdf5-metno 0.14) needs ≥1.85.1. Day-to-day dev and CI pin **1.97.1** -# via rust-toolchain.toml / GitHub Actions — not this floor. -# Raise when deps or language features require it. -rust-version = "1.85.1" +# Supported toolchain — keep in lockstep with rust-toolchain.toml and CI +# (`toolchain: "1.97.1"`). Raise when the pin moves. +rust-version = "1.97.1" license = "MIT OR Apache-2.0" authors = ["Raul Montoya Cardenas "] description = "Pure-Rust implementation of the Neuromorphic Intermediate Representation (NIR) — the standard interchange format for spiking neural networks." diff --git a/README.md b/README.md index 250f73a..4479cdc 100644 --- a/README.md +++ b/README.md @@ -8,129 +8,100 @@ [![docs.rs](https://docs.rs/nir-rs/badge.svg)](https://docs.rs/nir-rs) [![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#license) -> Pure-Rust NIR graph model (typed nodes, edges, validation), plus opt-in HDF5 `.nir` read/write that interoperates with the Python reference implementation. The graph model has no system dependencies; the `hdf5` feature is the one part that links native libhdf5. +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 SNNs what ONNX is to conventional neural networks (or GGUF to LLMs): a framework-agnostic graph format that lets models move between simulators and hardware without being rewritten. +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. ## Why nir-rs? -- Official NIR is primarily Python-based ([neuromorphs/NIR](https://github.com/neuromorphs/NIR)) -- No mature shared Rust IR crate for the Limen stack -- Enables pure-Rust, embedded, and high-performance pipelines -- Native integration with the rest of Limen Neural (`axon-encoder`, `silicon-bridge`, `neuromod`, …) +- Official NIR is primarily Python ([neuromorphs/NIR](https://github.com/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 ## Upstream - Spec / reference: [github.com/neuromorphs/NIR](https://github.com/neuromorphs/NIR) - Primitives docs: [neuroir.org](https://neuroir.org/docs/) -- Paper: [Nature Communications (2024)](https://www.nature.com/articles/s41467-024-52259-9) (DOI [10.1038/s41467-024-52259-9](https://doi.org/10.1038/s41467-024-52259-9)) +- Paper: [Nature Communications (2024)](https://www.nature.com/articles/s41467-024-52259-9) + (DOI [10.1038/s41467-024-52259-9](https://doi.org/10.1038/s41467-024-52259-9)) -**Wire compatibility:** HDF5 node `type` strings must match the Python IR (`CubaLIF`, `Conv2d`, `SumPool2d`, …), not informal aliases (`CurrLIF`, `Convolution`, …). +**Wire compatibility:** HDF5 node `type` strings must match the Python IR +(`CubaLIF`, `Conv2d`, `SumPool2d`, …), not informal aliases +(`CurrLIF`, `Convolution`, …). ## Scope -This crate **owns**: +**This crate provides:** - The NIR graph model and standard node types -- Reading and writing `.nir` (HDF5) files (v0.3+) -- Round-trip fidelity and basic validation -- A clean, idiomatic Rust API +- Reading and writing `.nir` (HDF5) files +- Round-trip fidelity checks and structural validation +- An idiomatic Rust API (`NirGraph`, closed `NirNode` enum, tensors, errors) -This crate does **not** own: +**This crate does not provide:** -- Training or simulation of SNNs -- Mapping to specific hardware (that lives in `silicon-bridge`) -- Framework-specific converters (those live in producing/consuming crates) +- SNN training or simulation +- Mapping graphs onto specific neuromorphic hardware +- Framework-specific importers/exporters (those belong in the tools that + produce or consume NIR) -## Status / roadmap +## Status -| Milestone | Focus | Status | -|-----------|--------|--------| -| **v0.1** | Dual license, module skeleton, CI, agent docs | Done | -| **v0.2** | Typed graph, wire-accurate nodes, structured errors | Done | -| **v0.3** | HDF5 read/write via `hdf5-metno`, fixtures, round-trip | Done | -| **v0.4** | Serde/debug DX, examples, release hardening | **0.4.1 on crates.io** | -| **v0.5** | Wire consumers (silicon-bridge, axon-encoder, engram-parser) | Planned | +| Version | Focus | +|---------|--------| +| **0.4.x** (current) | Graph model, HDF5 I/O, Serde/debug DX, Docker image, crates.io | +| Earlier | Dual license, typed nodes, fixtures, CI hardening | -Tracking: [GitHub milestones](https://github.com/Limen-Neural/nir-rs/milestones) · [LIM-822](https://linear.app/rpd-34/issue/LIM-822) - -## Changelog & compatibility +Release notes and the upstream compatibility matrix: | Doc | Purpose | |-----|---------| | [CHANGELOG.md](CHANGELOG.md) | Keep a Changelog notes + 0.x versioning policy | -| [COMPATIBILITY.md](COMPATIBILITY.md) | Release ↔ upstream NIR matrix, fidelity semantics, features, MSRV | - -Compatibility claims are **fixture-backed** only; see also `tests/fixtures/`. - -## Docker (GHCR + Docker Hub) +| [COMPATIBILITY.md](COMPATIBILITY.md) | Release ↔ upstream NIR, fidelity rules, features, MSRV | -Published images ship a **Rust 1.97 + libhdf5** toolchain with the crate tree and -the `load_inspect_lif` example binary (not an SNN simulator). CI verifies on -PRs and pushes on `main` / version tags — see [`.github/workflows/docker.yml`](.github/workflows/docker.yml). - -```bash -# Preferred: GitHub Container Registry (stable org path) -docker pull ghcr.io/limen-neural/nir-rs:latest -# Version tag appears after a matching git tag push (e.g. v0.4.1 → :0.4.1) -docker pull ghcr.io/limen-neural/nir-rs:0.4.1 +Compatibility claims are **fixture-backed** (`tests/fixtures/`). -# Docker Hub: published as /nir-rs (same tags as GHCR). -# Use the org/user from GitHub Actions repo variables, not a shell placeholder. +## Install -docker run --rm ghcr.io/limen-neural/nir-rs:latest rustc --version -# Default input: tests/fixtures/lif_norse.nir (writes a temp copy) -docker run --rm ghcr.io/limen-neural/nir-rs:latest load_inspect_lif +```toml +[dependencies] +nir-rs = "0.4.2" ``` -First GHCR publish creates a **private** package by default. The publish job -tries to set visibility to **public**; if that fails, an org admin must set -`ghcr.io/limen-neural/nir-rs` public under GitHub Packages. +HDF5 `.nir` I/O (needs a system libhdf5, or a static build — see [File I/O](#file-io)): -Local build: - -```bash -docker build -t nir-rs:local . +```toml +[dependencies] +nir-rs = { version = "0.4.2", features = ["hdf5"] } ``` -## Toolchain & MSRV - -**CI and local development pin Rust 1.97.1** (`rust-toolchain.toml` -`channel = "1.97.1"`; GitHub Actions `toolchain: "1.97.1"`). - -**Declared floor (`package.rust-version`): 1.85.1** — cargo/crates.io metadata -only (Edition 2024 + `hdf5-metno` 0.14). We do **not** run a multi-OS CI matrix -on that older compiler; the supported quality bar is **1.97.1**. - -| Policy | Detail | -|--------|--------| -| Dev / CI | **1.97.1** on **Linux, macOS, and Windows** | -| `rust-version` | Minimum floor for installers; raise when deps require it | -| Pinning day-to-day work to the cargo floor | **Not** required or recommended | -| Package / semver CI | `.github/workflows/package.yml` — `cargo package` + public API vs `v0.4.0` | -| Deliberate public breaks (`0.x`) | Intentional **minor** bump (`0.5.0`); do not silence the semver job | - -## Quick start +Debug Serde (JSON / RON / etc.; not a wire standard): ```toml [dependencies] -nir-rs = "0.4.1" +nir-rs = { version = "0.4.2", features = ["serde"] } ``` -To also get HDF5 `.nir` I/O, enable the `hdf5` feature (see [File I/O](#file-io) -for the system dependency it brings): +From git — pin a release tag (same tree as the matching crates.io release once +the tag exists): ```toml -[dependencies] -nir-rs = { version = "0.4.1", features = ["hdf5"] } +nir-rs = { git = "https://github.com/Limen-Neural/nir-rs", tag = "v0.4.2" } ``` -Development tip: pin a git tag when you need unreleased `main` fixes: +For unreleased work on the default branch: ```toml -nir-rs = { git = "https://github.com/Limen-Neural/nir-rs", tag = "v0.4.1" } +nir-rs = { git = "https://github.com/Limen-Neural/nir-rs", branch = "main" } ``` +## Quick start + ```rust use nir_rs::nodes::{Input, Output}; use nir_rs::{NirGraph, NirNode}; @@ -174,99 +145,103 @@ fn main() -> nir_rs::Result<()> { } ``` -I/O is behind the opt-in **`hdf5`** feature, which links the native libhdf5 -library. Without this feature, the crate requires no system dependencies: +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", features = ["static", "zlib"] }` directly — Cargo's feature unification applies it to this crate's copy. A dependency's feature list cannot name `hdf5/static`, and without `zlib` the vendored build has no gzip filter. | +| Anywhere | depend on `hdf5-metno = { version = "0.14", features = ["static", "zlib"] }` — Cargo feature unification applies it to this crate's copy. A dependency cannot enable `hdf5/static` via 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 exact parameter values are preserved, while HDF5 details -such as group ordering and chunk layout may differ from h5py. In-memory dtypes -(`f32`, `f64`, `i64`, `bool`) round-trip exactly; narrower on-disk integer -types are widened to `i64` on read. Absent optional fields (`v_reset`, `w_in`) -are filled with the same defaults Python uses, so a graph read here matches -what `nir.read` produces in memory. +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. -### Load, inspect, and save a LIF graph - -The public example loads a `.nir` graph (default: vendored LIF fixture), prints -structure including nested `NIRGraph` nodes, previews every `LIF` parameter -tensor (first eight elements, with a total count when longer), writes a copy, -then verifies round-trip equality for finite graphs (skips the assert if any -float tensor contains NaN): +### Example: load, inspect, save a LIF graph ```bash cargo run --example load_inspect_lif --features hdf5 +# optional paths: +cargo run --example load_inspect_lif --features hdf5 -- model.nir copy.nir ``` -Pass optional input and output paths to use your own model: +Default input is `tests/fixtures/lif_norse.nir`; default output is a +PID-qualified file in the system temp directory. + +## Docker + +Images ship a **Rust 1.97 + libhdf5** environment with the crate sources and the +`load_inspect_lif` example binary (not an SNN simulator). ```bash -cargo run --example load_inspect_lif --features hdf5 -- model.nir copy.nir +docker pull ghcr.io/limen-neural/nir-rs:0.4.2 +docker pull ghcr.io/limen-neural/nir-rs:latest + +docker run --rm ghcr.io/limen-neural/nir-rs:latest rustc --version +docker run --rm ghcr.io/limen-neural/nir-rs:latest load_inspect_lif +``` + +The same tags may also appear on **Docker Hub**; prefer GHCR for a stable, +documented image path. + +Local image: + +```bash +docker build -t nir-rs:local . ``` -When omitted, the input is `tests/fixtures/lif_norse.nir` and the output is a -PID-qualified file in the system temporary directory. ## Debug serialization -The opt-in `serde` feature implements `Serialize` and `Deserialize` for the -graph, all wire node variants, metadata, and tensors. It is independent of the -`hdf5` feature: +The opt-in `serde` feature implements `Serialize` / `Deserialize` for the graph +model. It is independent of `hdf5`: ```toml [dependencies] -nir-rs = { version = "0.4.1", features = ["serde"] } +nir-rs = { version = "0.4.2", features = ["serde"] } serde_json = "1" ``` -Consumers can use self-describing Serde formats (JSON, RON, YAML, etc.) directly, for example -`serde_json::to_string_pretty(&graph)`. Tensor debug data is represented as -`{ "shape": [...], "data": { "F64": [...] } }`, and deserialization checks -the tensor shape/data-length invariant. +**JSON is debug/test output, not a NIR interchange standard.** Use HDF5 `.nir` +for Python and hardware tooling. JSON cannot represent NaN/infinities faithfully. -**JSON is debug/test output, not a NIR interchange standard or a stable schema.** -Use HDF5 `.nir` through `io::read` / `io::write` for Python NIR and hardware -tool interoperability. JSON cannot represent NaN or infinities faithfully, so -graphs containing non-finite floats are not guaranteed to round-trip through -JSON; tests and debug round-trips should use finite values. +## Toolchain -### Develop +**Rust 1.97.1** — `rust-toolchain.toml`, `package.rust-version`, and CI +(Linux / macOS / Windows) all pin the same version. + +## Develop ```bash cargo fmt --check -cargo test # graph model only, no libhdf5 required +cargo test # graph model only — no libhdf5 cargo test --features serde -cargo test --all-features # + HDF5 I/O, fixtures and round-trip +cargo test --all-features # + HDF5 fixtures / round-trip cargo clippy --all-targets --all-features -- -D warnings cargo doc --no-deps --all-features ``` -CI mirrors this on **ubuntu-latest**, **macos-latest**, and **windows-latest** -using **Rust 1.97.1**. Format, clippy, and docs stay on Ubuntu; see -[`.github/workflows/ci.yml`](.github/workflows/ci.yml). - -Wire compatibility is checked against real `.nir` files written by the Python -implementation and vendored under `tests/fixtures/` (BSD-3, see the README -there). Nothing in the default build, tests, or CI needs a Python interpreter -(Windows HDF5 in CI is installed via conda-forge for the native library only). -Full matrix and fidelity rules: [COMPATIBILITY.md](COMPATIBILITY.md). +Wire 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. -See [REVIEW.md](REVIEW.md), [AGENTS.md](AGENTS.md), and [TESTING.md](TESTING.md) -(property tests + optional `cargo-fuzz` harnesses). +API and fidelity details: [COMPATIBILITY.md](COMPATIBILITY.md), +[docs.rs/nir-rs](https://docs.rs/nir-rs). ## License -This project is dual-licensed under either: +Dual-licensed under either: -- Apache License, Version 2.0 ([LICENSE-APACHE-2.0](LICENSE-APACHE-2.0) or https://www.apache.org/licenses/LICENSE-2.0) -- MIT License ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) +- Apache License, Version 2.0 ([LICENSE-APACHE-2.0](LICENSE-APACHE-2.0) or + https://www.apache.org/licenses/LICENSE-2.0) +- MIT License ([LICENSE-MIT](LICENSE-MIT) or + https://opensource.org/licenses/MIT) at your option. diff --git a/src/lib.rs b/src/lib.rs index 4ac905e..96743e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,8 +92,8 @@ //! # Non-goals //! //! - SNN training or simulation -//! - FPGA / hardware mapping (see `silicon-bridge`) -//! - Framework-specific converters (live in producer/consumer crates) +//! - Mapping graphs onto specific neuromorphic hardware +//! - Framework-specific converters (belong in producer/consumer tools) //! //! # Upstream //! diff --git a/src/nodes.rs b/src/nodes.rs index 94d2e56..3d34112 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -44,7 +44,7 @@ impl Padding { /// Closed set of NIR computational nodes. /// -/// Exhaustive matching is intentional for silicon-bridge and other consumers. +/// Exhaustive matching is intentional so downstream tools can cover every wire type. /// This enum is **not** `#[non_exhaustive]` so downstream mappers can cover all /// wire types without a wildcard arm (new wire types are a major API change). /// With the `serde` feature, the representation is internally tagged by diff --git a/tests/hdf5_read_errors.rs b/tests/hdf5_read_errors.rs index 4820891..731bc53 100644 --- a/tests/hdf5_read_errors.rs +++ b/tests/hdf5_read_errors.rs @@ -2,9 +2,9 @@ //! Failure modes when **reading** malformed `.nir` files. //! -//! Consumers such as `silicon-bridge` need to tell "this file is not NIR" apart -//! from "this node type is not supported yet", so each case below asserts the -//! specific [`NirError`] variant, not merely that an error occurred. +//! Downstream tools need to distinguish non-NIR files from "unsupported node +//! type" failures, so each case below asserts the specific [`NirError`] +//! variant, not merely that an error occurred. #![cfg(feature = "hdf5")]