ADR-025: CSF (Compressed Sparse Fiber) as the Rank-N Sparse Layout
Status
Draft
Context
Hurray already defines three sparse layouts: COO (0x07, any rank), CSR (0x08,
rank-2), and CSC (0x09, rank-2). CSR/CSC compress one mode with a dense outer
pointer array — compact and interop-canonical (SciPy, cuSPARSE, MKL, Eigen;
hurray-python already performs SciPy zero-copy sparse interop) — but they do not
generalise past rank 2. COO generalises to any rank but stores a full coordinate
tuple per non-zero with no hierarchical structure.
TACO uses CSF (Compressed Sparse Fiber): the rank-N generalisation of CSR/CSC. A
CSF tensor is a tree of rank levels, each compressing one mode with a (pos, crd)
pair, plus one shared values array. CSF is the natural higher-rank complement to COO
for structured sparsity — attention masks, sparse activations, and higher-order
factorisations. TODO.md names CSF, and tag 0x0A was reserved for it in ADR-024 to
keep the sparse family contiguous (COO 0x07, CSR 0x08, CSC 0x09, CSF 0x0A).
The settled framing is that CSR/CSC/COO are kept; CSF complements them. CSR's
dense outer row_ptr is more compact and more interop-canonical for rank-2 matrices,
and an all-compressed CSF tree is a physically different layout. Writers SHOULD prefer
CSR/CSC for rank-2 data.
This ADR must respect the format's existing constraints:
- Streamability / self-delimitation (
README.md,interchange.md): descriptors precede their data, all buffer lengths derive from theposchain, and there are no back-references and no end-of-file index. - Single-owner buffers (
buffer-protocol.md; ADR-009). - Hyperrectangle shape model (
data-model.md): the logical shape is always a rectangular index space. byte_offset = 0for sparse layouts (memory-layout.md).- Reuse of the existing quantization schemes (
quantization.md).
This is the pre-1.0 draft period, so assigning the already-reserved tag 0x0A
requires no version_minor bump, exactly as ADR-024 established for 0x0B.
Decision
1. All-compressed CSF, not the full per-mode model
Every level of the CSF tree is a compressed (pos, crd) pair. The TACO per-mode
dense/compressed model is not adopted in this version: dense-outer rank-2 cases
are already covered by CSR/CSC, and a per-level mode_format[rank] array is reader
burden Hurray does not need yet. This is forward-compatible: a future revision MAY add
a mode_format: uint8[rank] field to admit dense levels, and a v1 reader rejecting
that unknown field is intended versioning behaviour.
2. Explicit mode ordering
A CSF descriptor carries mode_order: uint32[rank], a permutation of 0..rank-1.
mode_order[L] is the logical dimension stored at level L (level 0 is outermost;
level rank-1 is the leaf level directly above values). The bounding size for level
L is shape[mode_order[L]]. The logical shape is unchanged — mode_order affects
storage traversal only.
mode_order is a performance knob: it lets a writer match the tree's nesting to
its access pattern. A conforming reader MUST honour any valid mode_order permutation
for lookup and iteration, and MUST NOT reject a descriptor merely because mode_order
is non-identity — permutation support is mandatory for an interchange format. When a
writer has no access-pattern preference, the identity ordering
[0, 1, ..., rank-1] (row-major, outer-to-inner) is the RECOMMENDED default, for
reproducibility and cache-friendliness.
3. Buffer table = 2 × rank + 1
values is at index 0 (consistent with CSR/CSC/COO). Each level L contributes a
pos_L buffer at index 2L + 1 and a crd_L buffer at index 2L + 2. The top level
retains pos_0 = [0, n_0] (length 2) for structural uniformity; omitting the top
pos was rejected. All pos/crd buffers are uint64. Quantization-parameter
buffers, when present, occupy indices 2·rank + 1 and up.
4. Rank scope: rank >= 3
CSR, CSC, and COO own rank ≤ 2. A conforming reader MUST reject a CSF descriptor with
rank < 3. Rank remains capped at 64 by data-model.md.
5. Element lookup by per-level descent
Lookup descends the tree level by level, binary-searching each level's crd slice
delimited by the parent's pos entry. A coordinate not found at any level means the
element is implicitly zero.
6. Storage invariants mirror CSR, per level
For each level L: pos_L[0] = 0; pos_L is non-decreasing; the terminal pos
entry equals the level's stored count (pos_0[1] = n_0, pos_L[n_{L-1}] = n_L, with
n_{rank-1} = nnz); within each parent slice the crd values are strictly increasing
(no duplicate siblings); and 0 <= crd_L[i] < shape[mode_order[L]]. In addition,
mode_order MUST be a valid permutation of 0..rank-1.
7. Cross-cutting properties
byte_offset MUST be 0 (sparse). The layout is self-delimiting: every buffer length
derives from the pos chain, so no back-references or EOF index are needed.
Quantization decorates the values leaves (values is buffer 0); per-tensor,
per-channel, per-block, NF4, and MXFP schemes compose exactly as they do for COO/CSR,
with quant-parameter buffers at index 2·rank + 1 and up. Sharding is forbidden
in this version: the interaction between a shard's shard_offset and the per-level
pos/crd tree is unresolved. CSF is classified as Sparse (implicit zeros).
8. Tag assignment
CSF takes Tier-1 tag 0x0A, classified Sparse. The memory-layout.md row for 0x0A
is promoted from "(reserved — planned)" to a link to layouts/csf.md.
Alternatives Considered
Full per-mode dense/compressed model (TACO mode formats). Rejected for v1:
dense-outer rank-2 cases are covered by CSR/CSC, and a per-level mode_format array is
reader burden Hurray does not need yet. Deferred and forward-compatible via a future
mode_format field.
Replacing CSR/CSC with CSF. Rejected (settled framing): CSR's dense outer pointer is more compact and interop-canonical for rank-2, and all-compressed CSF is a physically different layout.
Omitting the top-level pos_0. Rejected: structural uniformity across all levels
is worth the 16 bytes of a length-2 pos_0.
Allowing CSF at rank 2 and up. Rejected: it would duplicate CSR/CSC/COO. CSF is
restricted to rank >= 3.
uint32 indices. Rejected for v1 to match the uint64 indices used by
CSR/CSC/COO; an opt-in narrower index type may be added later.
Non-Sparse classification. Rejected: CSF has implicit zeros, so it is Sparse.
Consequences
docs/spec/memory-layout.md: the0x0Arow is promoted from reserved to a link tolayouts/csf.md, and the sparse buffer-table clause notes CSF's variable rank-dependent count of2·rank + 1.- A new layout file
docs/spec/layouts/csf.mdis added. - CSF is the first layout with a variable, rank-dependent buffer count and the
first sparse layout with a permutation descriptor field (
mode_order). - A new reader validation surface is introduced:
mode_orderpermutation validity, per-levelposmonotonicity and terminal checks, and per-levelcrdbounds. Readers SHOULD validate these and MUST reject violations unless operating in permissive mode. - The forward-reference note in
csr.md(and the analogous mention) is now satisfied and updated to point atcsf.md(editorial). - Writers SHOULD prefer CSR/CSC for rank-2; CSF MUST NOT be used below rank 3.
- No
version_minorbump (the tag was already reserved; pre-1.0 draft period). - The
TODO.mdCSF entry can be marked done oncecsf.mdlands.
Date: 2026-06-27.