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

@JoeyBF JoeyBF 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

coderabbitai Bot 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
JoeyBF force-pushed the claude/nassau-gpu-refactor-k1ggps branch from 7824e7d to 31aced0 Compare July 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
JoeyBF force-pushed the claude/nassau-gpu-refactor-k1ggps branch from 31aced0 to 8936ed8 Compare September 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
JoeyBF force-pushed the claude/nassau-gpu-refactor-k1ggps branch from 8936ed8 to 0512d8a Compare September 2, 2026 18:42
Sign up for free to 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