Skip to content

GPU offload for Nassau's Milnor multiply (p = 2) - #271

Open
JoeyBF wants to merge 4 commits into
SpectralSequences:masterfrom
JoeyBF:claude/nassau-gpu-refactor-k1ggps
Open

GPU offload for Nassau's Milnor multiply (p = 2)#271
JoeyBF wants to merge 4 commits into
SpectralSequences:masterfrom
JoeyBF:claude/nassau-gpu-refactor-k1ggps

Conversation

@JoeyBF

@JoeyBFJoeyBF commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Reorganized successor to #264: the GPU Milnor-multiply offload, with the benchmark suite (#269, merged) and the seqno index (#270) split out.

Stacked on #270. This PR is based on the seqno branch, so its diff currently shows the seqno commits too. The net-new commit here is the top one, "Add CubeCL GPU offload…". Once #270 merges I'll rebase and the diff will reduce to the GPU commit alone. Review against #270, or just the head commit.

What

Offloads the admissible-matrix Milnor multiply at the heart of Nassau's get_partial_matrix to the GPU via CubeCL, for the p = 2 / trivial-profile regime. Gated behind the gpu feature.

  • milnor_algebra.rs: an admissible-matrix multiply (multiply_basis_element_by_element_2 + AdmissibleMatrix) as the CPU reference model for the kernel, plus the #[cfg(feature = "gpu")] upload helpers. Covered by admissible_multiply_agrees_with_reference.
  • milnor_gpu.rs: the batched CubeCL kernel (multiply_batch_kernel / multiply_batch_on_gpu), fusing every (R, s) product of one matrix build into a single launch, with dedup-by-R, device-resident admissible buffers, and per-launch memory release pinned to one CUDA stream. Single-kernel building blocks used only to validate it live under #[cfg(test)], so the production surface is just multiply_batch_on_gpu + GpuProduct.
  • ext::nassau_gpu + the build_partial_matrix hook in nassau.rs: route the differential-matrix build through the GPU path when compiled in, opted into (NASSAU_GPU), applicable, and large enough to amortise launch overhead; otherwise the CPU path is unchanged.
  • free_module.rs: skip zero generator blocks in act (a general CPU win).
  • Supporting nassau_e2e example, GPU/CPU consistency tests, and the milnor_gpu_ab A/B bench.

Rebased onto current master

Master has moved a long way since this was opened, in two ways that touch this PR directly.

#280 packed the Milnor p-part into a u64. The host-side extraction is ported to the PPart API throughout. One place this is a straight improvement: the GPU resident cache keyed its per-R admissible-matrix table on Vec<PPartEntry>, and that key is now the packed PPart itself — Copy, Hash, no allocation per lookup. The admissible sweep in multiply_basis_element_by_element_2 was rewritten to assemble its result in a plain u64 and store it once, the same approach #280 took in PPartMultiplier; the trailing-zero trim it used to need is gone, because the packed form does not represent trailing zeros.

fp-cuda arrived, with ext's own gpu feature meaning "fp's Hopper F₂ matmul". Rather than introduce a second GPU feature name, ext's gpu now enables both backends: they sit at different layers (F₂ linear algebra vs the Milnor product) and are independent at runtime. Note the consequence — fp-cuda compiles its kernel with nvcc in a build script, so --features gpu now needs the toolkit at build time, which is why the workspace excludes that crate. The Milnor half alone has no such requirement (cubecl JITs through NVRTC at runtime), so cargo check -p algebra --features gpu type-checks it with no CUDA installed.

The flake.nix changes this PR used to carry are dropped entirely — master's devShells.gpu already provides the CUDA toolkit and loader paths they were adding.

Notes vs #264

Dead code and dev-only profiling scaffolding removed; the validation-only kernels moved behind #[cfg(test)]. The env-var dispatch and the process-global GPU context are unchanged (deferred as larger follow-ups).

Test plan

  • cargo test -p algebra — 89 pass, incl. admissible_multiply_agrees_with_reference, the CPU oracle for the kernel
  • cargo test -p ext --features nassau --test milnor_vs_nassau — CPU Nassau correctness
  • cargo clippy -p algebra --features gpu --all-targets — type-checks the kernel and the cfg(test) validation kernels without CUDA
  • just lint (nightly fmt + cargo hack clippy feature-powerset, -D warnings)
  • GPU-execution tests (tests/nassau_gpu*.rs, and the in-crate milnor_gpu device tests) require a live CUDA device — not run here or in CI

🤖 Generated with Claude Code


Generated by Claude Code

@coderabbitai

coderabbitaiBot commented Jul 15, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 19 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: a22276c3-82fc-4b5e-a869-49f0eb9b6dfe

📥 Commits

Reviewing files that changed from the base of the PR and between e1e0f6f and 0512d8a.

📒 Files selected for processing (15)
  • ext/Cargo.toml
  • ext/benches/milnor_gpu_ab.rs
  • ext/crates/algebra/Cargo.toml
  • ext/crates/algebra/benches/seqno.rs
  • ext/crates/algebra/src/algebra/milnor_algebra.rs
  • ext/crates/algebra/src/algebra/milnor_gpu.rs
  • ext/crates/algebra/src/algebra/mod.rs
  • ext/crates/algebra/src/module/free_module.rs
  • ext/examples/nassau_e2e.rs
  • ext/src/lib.rs
  • ext/src/nassau.rs
  • ext/src/nassau_gpu.rs
  • ext/tests/nassau_gpu.rs
  • ext/tests/nassau_gpu_reuse.rs
  • ext/tests/nassau_gpu_timing.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@JoeyBF
JoeyBFforce-pushed the claude/nassau-gpu-refactor-k1ggps branch from 7824e7d to 31aced0CompareJuly 15, 2026 05:14
JoeyBF added a commit to JoeyBF/sseq that referenced this pull request Jul 18, 2026
…Sequences#272's restricted graph
PR SpectralSequences#272's restricted_partial_matrix (degree-bounded columns) had displaced
PR SpectralSequences#271's GPU partial-matrix offload and cross-signature row-reuse. Add a
restricted GPU variant (get_partial_matrix_restricted[_verified]) that keeps the
batched Milnor multiply but sizes output to the frozen prefix and masks bits
beyond it, dispatch to it from restricted_partial_matrix_maybe_gpu, and restore
row-reuse (one full restricted matrix per bidegree, select_rows per signature).
Verified GPU==CPU on H200 NVL (CUDA 12.4): nassau_gpu (14230 rows / 527
bidegrees) and milnor_vs_nassau (9 tests) pass with NASSAU_GPU_VERIFY=1.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a flat, arc-swapped `seqno` index for the Milnor basis: an
O(#p-part-entries) rank computed from a precomputed `g` table, with no
hash lookup. This is a general (GPU-agnostic) data structure — it is the
uploadable/on-device index primitive, and an independently benchmarkable
alternative to the basis hashmap.
- `SeqnoTables` + `compute_seqno_tables` (idempotent, concurrency-safe
via `ArcSwapOption::rcu`) + `seqno` in `milnor_algebra.rs`, applicable
at p = 2 with a trivial profile and stable ordering.
- The CPU basis index deliberately still uses the hashmap (the tables lose
to it on the CPU); `compute_basis` does not build them. Documented at
`try_basis_element_to_index`.
- `benches/seqno.rs`: A/B of the table index against the hashmap.
- Covered by the `seqno_matches_enumeration_order` test.
Split out of the Nassau GPU work as standalone infrastructure.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UPYvsLdEfitgCbAiPxxx3U
- `seqno`: add a `debug_assert` that the element's degree is within the loaded
table's `max_degree`, so a not-built-far-enough call fails with a clear
diagnostic instead of a raw slice out-of-bounds panic.
- `seqno_matches_enumeration_order`: build the tables partially, rebuild
identically (no-op), grow, then request a smaller degree — exercising the
idempotent, monotonic (non-shrinking) publish documented on
`compute_seqno_tables`, not just a single build.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UPYvsLdEfitgCbAiPxxx3U
@JoeyBF
JoeyBFforce-pushed the claude/nassau-gpu-refactor-k1ggps branch from 31aced0 to 8936ed8CompareSeptember 2, 2026 18:02
The A/B benchmark stopped at degree 64, where the basis has a few hundred
elements and the per-degree hashmap is a few kilobytes. Both indices are
cache-resident there, so it only ever measured fixed overhead, and the
overhead it measured was largely self-inflicted: `seqno` took an `arc_swap`
guard on every call and re-derived the degree as the sum of r_i * xi_i, while
the hashmap reads the degree straight off the basis element.
`seqno` now takes the degree from the caller, which always has it, and
`seqno_ranker` acquires the tables once for a batch of lookups. Together those
are worth 2.2x-2.8x.
With that fixed and the sweep extended, the two indices separate the way their
shapes predict. The hashmap holds one entry per basis element of a single
degree, so its working set grows with that dimension; the `g` table is shared
across degrees and grows only linearly in the degree. Per lookup, at p = 2:
deg dim hashmap seqno
32 47 7.09 12.39
100 1,189 7.39 15.59
180 10,155 8.53 17.18
260 46,750 13.24 18.15
300 87,977 15.26 18.07
320 117,834 22.27 18.70
370 231,354 46.76 18.98
400 335,566 53.27 19.56
seqno is flat -- 12 ns to 20 ns while the dimension grows by four orders of
magnitude -- and the hashmap degrades 7.5x as it falls out of cache. They cross
between degree 300 and 320, and by degree 400 the table is 2.7x faster.
This is the result Christian Nassau has been reporting since 1998, when the
comparison was against binary search over a written-out basis. It survives the
move to a hashmap keyed on the packed p-part: the constant factor changed, the
asymptotics did not.
The comments claiming the tables simply lose on the CPU are corrected, and
`compute_seqno_tables` gains the degree bound `compute_basis` already asserts,
which CodeRabbit flagged: a negative argument wrapped to a huge row count.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UPYvsLdEfitgCbAiPxxx3U
Offload the admissible-matrix Milnor multiply at the heart of Nassau's
`get_partial_matrix` to the GPU via CubeCL, for the p = 2 / trivial-profile
regime. Gated behind the `gpu` feature; `cargo check`/`build` need no CUDA
toolkit (cudarc dlopens at runtime).
- `milnor_algebra.rs`: an admissible-matrix multiply
(`multiply_basis_element_by_element_2` + `AdmissibleMatrix`) as the CPU
reference model for the kernel, plus the `#[cfg(feature = "gpu")]` upload
helpers (`admissible_matrices`, `seqno_table_u32`, `gpu_multiply_applicable`).
Covered by `admissible_multiply_agrees_with_reference`.
- `milnor_gpu.rs`: the batched CubeCL kernel (`multiply_batch_kernel` /
`multiply_batch_on_gpu`), fusing every `(R, s)` product of one matrix build
into a single launch, with dedup-by-`R`, device-resident admissible buffers,
and per-launch memory release pinned to one CUDA stream. The single-kernel
building blocks used only to validate it live under `#[cfg(test)]`, so the
production surface is just `multiply_batch_on_gpu` + `GpuProduct`.
- `ext::nassau_gpu` + the `build_partial_matrix` hook in `nassau.rs`: route the
differential-matrix build through the GPU path when compiled in, opted into
(`NASSAU_GPU`), applicable, and large enough to amortise launch overhead;
otherwise the CPU path is used unchanged.
- `free_module.rs`: skip zero generator blocks in `act` (a general CPU win).
- Supporting `nassau_e2e` example, GPU/CPU consistency tests, the
`milnor_gpu_ab` A/B bench, and a `gpu` dev shell in `flake.nix`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UPYvsLdEfitgCbAiPxxx3U
@JoeyBF
JoeyBFforce-pushed the claude/nassau-gpu-refactor-k1ggps branch from 8936ed8 to 0512d8aCompareSeptember 2, 2026 18:42
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@JoeyBF@claude