All notable changes to FlashNystrom are recorded here. The format follows Keep a Changelog and the version numbers follow Semantic Versioning.
- Tikhonov ridge for the pseudoinverse, exposed as the
kappa_starparameter (NystromConfig.kappa_star, default5.0). The pinv invertsM = K2^T K2 + lambda*Iwithlambda = (||K2||_1 ||K2||_inf)/kappa_star, guaranteeingcond(M) <= kappa_starand keeping the Newton-Schulz iteration well-conditioned ascond(K2)grows with N.0.0disables it (raw-K2 pinv, the original formulation). Forward and backward both use it (the backward inverts the same ridgedM), and it is threaded identically to the kernel and them > 64reference dispatch. - tf32 tensor-core Newton-Schulz pseudoinverse forward, exposed as
use_tc_pinv(defaultTrue,m == 64only), graph-captured per shape. Its precision floor (~6e-4) is tighter than the fp16-reference's (~1.2e-3); setFalsefor the fp32 scalar kernel. - H200 + B200 (sm_100) support: added
sm_100to the build (one binary now spans A100/H100/H200/B200, verifiedtest_b20099/99) and Modal targetsbench_gaps_h200,bench_gaps_b200,test_h200,test_b200, plus anfa4_imageandbench_fa4_b200/bench_fa4_h200for the FlashAttention-4 comparison. Note: on B200/H200 FN runs its SM80 atoms in compatibility mode while cuBLAS dispatches to native Blackwell/Hopper kernels (not like-for-like); at long context the native-kernel cuBLAS is faster by a constant factor that reflects the not-yet-written per-generation WGMMA/TMEM atom port, not the method. FA4 direct measurement is currently blocked by flash-attn-4's beta packaging (nvidia-cutlass-dsl version churn); FN-vs-FA2 is measured directly on B200. - FP32 at
head_dim=128is now allowed (previously hard-rejected). Mainly a gradient-checking / numerical-verification path; the scalar kernels opt into the ~150 KB SMEM they need, so it runs on datacenter GPUs (A100/H100/B200) and raises a clearinsufficient smemerror on smaller-SMEM consumer cards. tests/test_precision_config.py— regression tests pinningkappa_starvalidation, kernel-vs-reference consistency at matchedkappa_star, and thatfast_dk2invdoes not bias the gradient.benchmarks/README.mdindexing the diagnostic / performance / training scripts and marking the historical (archaeology) ones.benchmarks/bench_5060_refresh.py(local FN/SDPA/cuBLAS latency sweep at the current default config). The figure data inbenchmarks/plot_benchmarks.pywas refreshed from this plus the Modal A100/H100 runs.tests/test_ns_bwd_graph.py— isolation tests for the production cuBLAS + CUDA-graph NS backward (launch_kernel2_inv_bwd). Pins graph-replay correctness, shape-change cache invalidation,reset_caches()behaviour, and a memory-leak smoke test. Previously this code path was only reachable through the end-to-end autograd pipeline, which made bisecting a graph-capture regression painful.flash_nystrom._C.reset_caches()— frees the thread-local NS-backward graph caches and workspaces across all dtypes. Useful between training runs of different shapes, or before measuring residual GPU memory.FLASH_NYSTROM_CUDA_ARCH_LISTenv var to override the compute-capability detection insetup.py. Also acceptsTORCH_CUDA_ARCH_LISTfor compatibility with the PyTorch convention.- Per-tensor input validation in the backward pybind binding: device, contiguity, dtype, and exact-shape checks for every saved tensor.
CHANGELOG.md(this file).- GitHub Actions sdist workflow (
sdist.yml, CPU-only, always-on) and GPU test workflow (tests-gpu.yml): acpu-checksjob always runs the reference + config validation on GitHub-hosted runners, and the GPU job is enabled by setting theGPU_RUNNER_AVAILABLErepository variable once a self-hosted runner is attached.
FN_KAPPA_STARandFN_K2INV_TCenvironment variables. These gated production numerics (the ridge and the pinv path) from the shell, with no validation and no visibility, andFN_KAPPA_STARcould silently desync the kernel from the reference. Replaced by thekappa_star/use_tc_pinvparameters (see Added). The remaining env vars (FLASH_NYSTROM_PROFILE,FN_FP32_BWD,FLASH_NYSTROM_KERNEL3_SPLITS) are diagnostic / perf-tuning only and never affect production correctness.- Dead Bazel config (
MODULE.bazel,.bazelrc,.bazelversion) — there were noBUILDfiles and CI never used it; setuptools is the only build system. - Dead
NystromParams::seg_lenfield (computed with ceil division but never read; the landmark kernel computes its own floor-division segments). - The custom CUDA depthwise-conv kernels (
csrc/kernels/dconv_residual.cuhandcsrc/kernels/backward/dconv_residual_bwd.cuh) and all conv plumbing through the C extension boundary. The conv residual is now exclusively computed at the Python level viaF.conv1d(cuDNN) insideflash_nystrom_attention; the bypassed C++ path was bit-rotting unused code reachable only by passingconv_weight=directly to_C.forward. - Removed conv arguments from
_C.forward,_C.backward,NystromParams,NystromBwdParams, andFlashNystromFunction.{forward, backward}. The public Python API (flash_nystrom_attentionkeyword argsconv_weight=andconv_kernel_size=) is unchanged — those are applied via cuDNN.
kappa_staranduse_tc_pinvare now explicit, validated parameters threaded throughNystromConfig→flash_nystrom_attention→FlashNystromFunction→_C.forward/_C.backward/NystromParams, replacing theFN_KAPPA_STAR/FN_K2INV_TCenv vars. Invalidkappa_star(negative / inf / NaN) is rejected at both the config and the C entry.FlashNystromFunction.applyarity is now 8 (..., fast_dk2inv, kappa_star, use_tc_pinv).reset_caches()now frees all three thread-local GPU caches on the calling thread (NS-backward graph, kernel3 split-N scratch, and the TC-pinv forward graph — the last was previously never freed). Docstring documents the per-thread lifetime and serving guidance.kMaxLandmarksconstant corrected from128to64to match the enforced limit (the public entry hard-rejectsm > 64).- GPU CI is gated on a
GPU_RUNNER_AVAILABLErepository variable instead of a hardif: false; an always-oncpu-checksjob now runs the reference + config validation on GitHub-hosted runners every push. - CUDA errors and cuBLAS errors now throw
std::runtime_errorinstead of callingabort(). A failure inside the kernel pipeline propagates as a normal PythonRuntimeErrorthat autograd can unwind, instead of killing the user's training process. Five call sites updated:csrc/utils.h(FN_CUDA_CHECK, FN_CUDA_KERNEL_CHECK, FN_CHECK),csrc/cublas_helpers.cuh,csrc/kernels/backward/kernel2_inv_bwd.cu. setup.pyno longer requiresCUDA_HOMEat sdist-build time. The torch.utils.cpp_extension import and theCUDAExtension(...)call are deferred behind a command-line check;sdist,egg_info,dist_info,check,clean, and the help commands work in any CUDA-less environment (PyPI sdist builds, CI runners without GPUs).- Compute-capability detection in
setup.py: whennvidia-smiis unavailable, fall back to a multi-arch wheel coveringsm_80, sm_86, sm_89, sm_90instead of a single-archsm_80wheel that would not run on Hopper or Blackwell consumer GPUs. flash_nystrom.__version__is now read from the installed package metadata viaimportlib.metadata.version. Bumping the version inpyproject.tomlis now the only required edit;__init__.pypicks it up automatically.launch_kernel2_inv_bwdAPI: dropped five unused parameters (lse2,k2_inv,dZ_workspace,dK2_workspace,ns_step_scratch). The cuBLAS+graph path owns its own persistent workspaces (per-threadNsBwdGraphStatecache), so caller-side allocation is no longer necessary and was being silently ignored.NystromBwdParamsshrank correspondingly. The orchestrator no longer allocatesns_dZ_workspace,ns_dK2_workspace, orns_step_scratch(saved ~352 KB GPU memory per backward at typical configs).FLASH_NYSTROM_VERBOSE=1replacesFLASH_NYSTROM_QUIET=1as the control fornvcc -Xptxas=-v --resource-usage. Default is now quiet (the noise was only useful for the kernel-tuning iterations).- Built artifacts (
flash_nystrom/_C.*.pyd,_C.*.so) are excluded from the sdist viaglobal-excludeinMANIFEST.in.
kappa_starcould silently desync between the kernel and the reference: the kernel readFN_KAPPA_STARfrom the environment while the Python reference used its own default, so a test or training run could compare a ridged kernel against an un-ridged reference. Both now take the same explicit parameter.kernel1_bwd_scalar_kernelrewritten from a single-line golfed form to readable, commented code (identical semantics; verified by the gradient tests plus clean compute-sanitizer memcheck/racecheck).- Corrected a stale comment claiming the bf16 backward casts through fp16 — it does not (the backward runs in the native input dtype; the precision-sensitive softmax Jacobians are computed in fp32).
test_ns_bwd_kerneltolerance loosened1e-5→2e-4(matching its siblingtest_ns_bwd_graph): the fp32 cuBLAS Sgemm round-off in the trailingdS2 @ k_tildematmul differs by GPU and reaches ~1.6e-5 at D=128 on A100 (vs <1e-5 on consumer Blackwell) — fp32 round-off, not algorithmic error.- Re-measured all latency tables (RTX 5060, A100, H100 vs cuBLAS, H100 vs
FA2/FA3) at the current default config and regenerated the figures; corrected
the
.texmislabel of the RTX 5060 as "SM89, Ada" (it is Blackwell sm_120). - README install instructions used a
<your-fork>placeholder. Replaced with the actual upstream URL. pyproject.tomlhad a staleflash-attn>=2.0dev dependency that nothing referenced. Removed.
- CUTLASS is now a proper git submodule pinned at NVIDIA/cutlass@b78588d
(CUTLASS 3.7). Previously it was a local clone hidden by
.gitignorewith a README that lied about it being a submodule. MANIFEST.inadded. The sdist now shipscsrc/, the CUTLASS include tree,LICENSE,README.md, and excludes*.pyd,*.so, and the rest of the CUTLASS repository (tools, tests, examples, docs).pyproject.tomlfilled in: authors, URLs, classifiers, keywords, optional-deps split intodevandbench.
Forward and backward CUDA kernels for Nyströmformer approximate attention.
- Multi-CTA flash-attention-style tensor-core kernels for the three
softmaxes (
kernel1_output_fused,kernel2_inv,kernel3_output_fused) and their backwards (kernel1_bwd_tc,kernel2_inv_bwd,kernel3_bwd_tc). - FP32 scalar fallbacks for every kernel (the TC atom requires 16-bit operands; FP32 inputs go through the scalar path).
- Newton-Schulz pseudoinverse forward (
kernel2_inv) with all iterates saved for the unrolled backward (no IFT shortcut; gradient is exact irrespective of convergence).
- B-reuse: forward
kernel3_output_fusedsavesB = softmax(Qt @ K^T) @ Vto GMEM so the backward'scompute_dk2invcan skip the N-walk. - Split-K reduction for the
dQ_tildeaccumulator inkernel3_bwd_tc(replaces atomicAdd contention at long N). - cuBLAS-based Newton-Schulz backward step, captured into a per-shape CUDA graph in a thread-local cache.
- cuBLAS GemmEx for the trailing matmuls in
ns_bwd_final.
FlashNystromAttentionnn.Moduleandflash_nystrom_attentionfunctional form.torch.autograd.Functionwrapper with full saved-tensor protocol.- Pure-PyTorch reference implementation (
flash_nystrom.reference) used by the test suite as ground truth.
- 71 pytest tests: 28 forward, 23 backward, 20 kernel-isolation tests for the debug pybind hooks.
- 5-way diagnostic CIFAR-10 harness (
benchmarks/train_five_way.py) that compares SDPA, pure-PyTorch reference, full FlashNystrom, and the two mixed configurations (FN-fwd + torch-bwd, torch-fwd + FN-bwd).