New Sparse Delta Memory kernels: one chunked write/read op for all supported shapes (replacing
v1's per-shape kernels), deterministic, and faster than v1 (README, Speed). For v1's
configurations (two square product-key factors), parameter names and shapes are unchanged and v1
state dicts load with strict=True.
- Imports:
from sparse_delta_memory import SparseDeltaMemory, SparseDeltaMemoryArgs(top-level package; in a checkout,lingua.sparse_delta_memoryis a deprecated alias with the same module objects and oneFutureWarningper process; the pip package does not include it). - v1 configs and checkpoints load unchanged;
memory_block_sizeis nowchunk_size(the old name is still accepted, with aDeprecationWarning; setting both to different values raises). - New defaults:
chunk_size64 (it was 256 in 1.0.0; setchunk_size: 256to keep it; the shipped 1.0.0 configs and pickled 1.0.0 layer arguments set it) andnum_writes/num_reads64 (were 1). The chunk size changes outputs only through floating-point rounding. - Outputs are not bitwise those of v1: the new kernels round differently, and exact top-k ties go to the larger rank sum, then the larger first rank (slot score: the float32 sum of the factor scores).
- pip package:
pip install sparse-delta-memory(2.0.0) installs the top-level packagesparse_delta_memory(Python >= 3.12; pins the testedtorch==2.13.0andtriton==3.7.1: install the CUDA 13.0 build of torch first; also installsninjaandeinops). The CUDA extensions compile on first use and need a CUDA toolkit matchingtorch.version.cudaand a host C++ compiler. The training code (lingua/,apps/), benchmarks and tests stay in this repository.
- Outputs, the final bank and every gradient are bitwise identical across repeated calls, fresh
processes and CUDA-graph replays, for fixed inputs, settings and GPU type (v1 used float atomics);
not across GPU types, library builds, rank counts or
chunk_sizevalues (a different rounding by design). See README, Determinism. grad_bank_fp32(opt-in): the backward accumulates the bank gradient in float32 and casts it back once.- Known limitations: with a cold extension cache the first call compiles the CUDA extensions (about 6 minutes for a no-grad call, 10 for a training call on a GB300 node).
- Any number of product-key factors of any widths (
product_key_factor_dims); exact product-key top-k with a deterministic sparse backward. - CUDA graphs of the write/read (
cuda_graph; on indebug_sdm.yaml,debug_hybrid.yamlandsdm_flagship.yaml), withsuspend_cuda_graphs()andreset_cuda_graphs(); layers of one shape in different checkpoint regions share one set of graphs, and a replayed call's state copies its final bank at the first read (README, CUDA graphs). - A single-token decode kernel; generation decodes SDM models from a CUDA graph of the decode step
(
sdm_decode_cuda_graph, defaulttrue). - bf16 snapshots on a float32 bank (
snapshot_quant="bf16"): training saves the backward snapshot in bfloat16, half the size ofnone's float32 snapshot, on the same CUDA kernels asnonefor every head dim (the forward isnone's, bit for bit, measured on H100 and GB300; the gradients ofk_val, the gates andq_valread the bfloat16-rounded rows). On a bfloat16 bank,snapshot_quant="bf16"changes nothing: bit-identical to"none"(measured on H100 and GB300). A training call these kernels do not take, including one whose snapshot reaches2**31rows, raisesUnsupportedConfigurationErrorbefore the op does any work. benchmarks/(layer, model and decoding speed against v1) and a larger test suite.
- Calls are routed on whether they may need a backward, not on train/eval mode.
- float16 raises. Out-of-range settings (for example more writes or reads per token than
slots_per_head) raise at construction instead of being capped. - Decoding with a cache bank smaller than the configured slots now raises instead of reading out of bounds.
- A direct
sdm_write_readcall that runs several streams over one shared bank must passshared_bank=True, which asserts that the streams' slots are disjoint. - No fallback paths: one native kernel family per supported GPU class, and an explicit error
otherwise. Refusals are raised at the entry of the write/read op (
sdm_write_read); in a layer call, the projections, top-k and bank copy (withcuda_graph, also input copies) run first. Training calls (calls that may need a backward) that v1 accepted but that have no native route in 2.0.0 raiseUnsupportedConfigurationError(aValueError) before the op does any work, naming the reason (by code reading):- int4 snapshots on a float32 bank whose
ceil(head_dim / 128)equal column slices are 4 mod 8 columns wide (68 head dims, e.g. 12, 36, 100, 136, 200, 300): usesnapshot_quant="fp8"or"none"; native support is planned for 2.0.1. - fp8 / int4 snapshots for the head dims without a quantized route (head_dim above 16384, not a
multiple of 4, or not splitting into
ceil(head_dim / 128)equal column slices of 16-byte rows), on every GPU: usesnapshot_quant="none"; support is planned for a later release. - head_dim above
2**20, on every GPU and also without a backward (the native kernels take head dims up to2**20; v1 did not check it): use a smaller head_dim. Also by code reading: on RTX GPUs,chunk_sizeabove 128 raises aValueErrorbefore the op does any work, with or without a backward (v1 accepted it, with 256 as its default): usechunk_size<= 128 there. Calls without a backward (torch.no_grad(), inference) run assnapshot_quant="none", so none of the snapshot refusals apply to them. Training whose kernel plans fit and decode are not refused on any supported GPU; RTX GPUs are not tested yet: each gets a warning when the CUDA extensions load (Known limitations).
- int4 snapshots on a float32 bank whose
- The WY solve always uses IEEE float32; the dual matmul of bfloat16 banks uses TF32; other
float32 matmuls follow
torch.backends.cuda.matmul.allow_tf32. Autocast is off inside the op.
sdm_flagship.yamlandsdm_7B.yaml: 64 writes and reads per token, the paper's setting (previously 128);sdm_flagship.yaml: chunk 64 (previously 256).- SWA window 128, the paper's, in the 1.4B and 7B pairs (previously 1024);
baseline_1.4B.yamlgates the output of its full-attention layers (full_attn_gate).
- The per-shape kernels and their fallbacks,
triton_argsort, the context-parallel code (no config enabled it) andSNAPSHOT_QUANT.md. GatedSparseMemoryWriteRead(usesparse_delta_memory.memory_ops.sdm_write_read),fused_decode_step(usesparse_delta_memory.decode.sdm_decode_step),fast_embedding_bag(no replacement: the op gathers its own rows) andSparseDeltaMemory.read(reads happen inside the write/read call).
- A second backward through one SDM call (
retain_graph=True) raises a RuntimeError (the first backward restores the bank in place), except on calls thecuda_graphoption serves (not a differentiable call that starts from a cache). - Host-side checks: the GPU-build check runs once per device and build environment when the CUDA extensions load, never per call; the argument checks run on every op call and the refusal checks once per device and call signature (a later call is one memo lookup).
- Every supported GPU class runs the default
snapshot_quant="none"on a bfloat16 bank, with no warning except on RTX GPUs (below). A100-class GPUs (sm_80: A100, A30, A800) run every snapshot mode: tested on A100 with this release (training with unquantized, fp8 and int4 snapshots, and bf16 snapshots on a float32 bank). RTX GPUs (sm_86: RTX 30, A10, A40, A16, A2; sm_89: RTX 40, L4, L40, L40S, RTX 6000 Ada; sm_120: RTX 50, RTX PRO 6000 Blackwell) are designed to run on the portable CUDA kernels (chunk_size <= 128); not tested on RTX hardware: each RTX GPU gets one warning per process when the CUDA extensions first load, whatever the snapshot mode (by code reading). A100, H100, B200 and GB300 GPUs never warn. The warning issparse_delta_memory.UntestedHardwareWarning(aUserWarning); silence it withwarnings.filterwarnings("ignore", category=sparse_delta_memory.UntestedHardwareWarning). Inference on these GPUs runs the unquantized path whateversnapshot_quantis set (measured on A100; for bf16 snapshots, by code reading). - Planned for 2.0.1: opt-in per-layer activation checkpointing.
- Tested with torch 2.13.0, triton 3.7.1 and CUDA 13.0 on NVIDIA GB300; the test suite also on H100.