Relax the dependency graph in Nassau's compute_through_stem - #272
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Team Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe change adds bounded free-module dimension and homomorphism operations, applies them throughout Nassau resolution construction, relaxes concurrent stem scheduling, and adds comparisons against standard-resolution results. ChangesBounded basis and matrix contracts
Restricted resolution computation
Concurrent stem scheduling
Resolution validation
Estimated code review effort: 4 (Complex) | ~60 minutes Merge Risk: ⚪ Minimal · up to This change enables more concurrent Nassau stem computation while using bounded resolution data; current validation compares its results with the standard resolution, and no merge-blocking risk remains. Sequence Diagram(s)sequenceDiagram
participant compute_through_stem
participant step_resolution_with_subalgebra
participant signature_matrix
participant get_partial_matrix_restricted
participant resolution_state
compute_through_stem->>resolution_state: identify ready stem position
compute_through_stem->>step_resolution_with_subalgebra: compute Nassau step
step_resolution_with_subalgebra->>signature_matrix: build bounded target matrix
step_resolution_with_subalgebra->>get_partial_matrix_restricted: build restricted differential
get_partial_matrix_restricted-->>step_resolution_with_subalgebra: return fixed-dimension matrix
step_resolution_with_subalgebra-->>resolution_state: save step and quasi-inverse
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ext/crates/algebra/src/module/homomorphism/free_module_homomorphism.rs`:
- Around line 65-71: Enforce the truncated result bound in the homomorphism
action method containing the result-length assertion: ensure both
output_on_generator and target.act write only within result’s provided prefix,
using a bounded action API or a dedicated restricted method that validates the
invariant. Keep the generic full-dimension behavior unchanged and prevent
shorter buffers from causing out-of-bounds writes.
In `@ext/src/nassau.rs`:
- Around line 1052-1055: Update the row-1 readiness branch in the progress check
so the stem-boundary case `(1, max_n + 1)` does not bypass row 0. Compute the
required one-bidegree row-0 dependency halo before this check, then retain
strict readiness validation against the corresponding row-0 progress rather than
treating `t > max_n` as automatically satisfied.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 6b01d781-3d1c-4c33-ab32-35cc51bf1ca3
📒 Files selected for processing (3)
ext/crates/algebra/src/module/homomorphism/free_module_homomorphism.rsext/src/nassau.rsext/tests/milnor_vs_nassau.rs
…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>
Under SpectralSequences#272's relaxed dependency graph, many more step jobs are ready at once; each checked is_in_parallel() and re-queued itself (send_retry) whenever a guarded rayon region was active, busy-retry-storming the CPU. The heavy nassau matrix builds are now GPU-offloaded, so the guard's priority-inversion avoidance no longer pays for itself. The mechanism is a pure scheduling optimization with no effect on computed results, so removal is safe. Deletes utils::parallel (ParallelGuard/is_in_parallel/PARALLEL_DEPTH) and the retry plumbing in both the nassau and generic resolution schedulers. Verified GPU==CPU still holds on H200 (milnor_vs_nassau 9/9; nassau_gpu 14230 rows/527 bidegrees) with NASSAU_GPU_VERIFY=1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
ext/src/utils.rs (1)
598-662: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider a small unit test for thread-local independence.
This subtle invariant — a stolen job's
PARALLEL_DEPTHis scoped to its worker thread, not global — has no direct test in this file; it's presently exercised only indirectly through Nassau's concurrent scheduling. A focused test (e.g. spawn twostd::threads, hold aParallelGuardon one, assertis_in_parallel()isfalseon the other) would cheaply pin down this behavior against future regressions (e.g. accidentally reverting to a shared counter).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ext/src/utils.rs` around lines 598 - 662, Add a focused unit test alongside ParallelGuard and is_in_parallel that spawns two OS threads, holds a ParallelGuard on one thread, and verifies is_in_parallel() remains false on the other thread while the guard is active. Ensure the test synchronizes the threads so the assertion specifically validates thread-local independence.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@ext/src/utils.rs`:
- Around line 598-662: Add a focused unit test alongside ParallelGuard and
is_in_parallel that spawns two OS threads, holds a ParallelGuard on one thread,
and verifies is_in_parallel() remains false on the other thread while the guard
is active. Ensure the test synchronizes the threads so the assertion
specifically validates thread-local independence.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 679a693a-61e7-4998-a8ba-af03976cc2b8
📒 Files selected for processing (2)
ext/src/nassau.rsext/src/utils.rs
…tiply path
The zero-signature image matrix (d_s applied to the zero-sig source basis,
column-masked to the zero-sig target) was the last per-bidegree Milnor multiply
still on the CPU — a serial per-row apply_to_basis_element_restricted, ~17% of
frontier wall time in the perf profile. But it is the *same* restricted multiply
as the QI-source `full_matrix` already built via restricted_partial_matrix_maybe_gpu,
just on d_s = differentials[b.s()] instead of d_{s-1}, and its target
mask/dimension are exactly the `target_mask`/`target_dim` already computed for the
bidegree (d_s and d_{s-1} share the target module modules[b.s()-1]). So route it
through the same GPU-offloaded, work-gated, already-verified path and apply the
column mask on CPU; drop the serial `signature_matrix` method. (Reinstates the
"signature_matrix offload" win from the original nassau_gpu branch, lost in the
SpectralSequences#272 relaxed-graph merge.) row_reduce stays on CPU — the signature-masked matrices
are very flat (~100 x 100000), a poor RREF target for the GPU.
Correctness: GPU Ext chart byte-identical to CPU-only through (100,152);
NASSAU_GPU_VERIFY passes at stem 130.
This shrinks the serial tail that Amdahl-capped the GPU:CPU ratio, so the
arithmetic-intensity advantage finally shows through and the gap WIDENS with stem
(S_2, s<=152, 16-core H200 box, w=32):
band GPU CPU ratio
130->140 159s 206s 1.30x
140->150 278s 423s 1.52x
cum 0->150 596s 771s 1.29x (was 723s, a near-tie)
Memory stays bounded by the same byte-budget/block machinery (this path reuses
multiply_batch_on_gpu). Next lever: the full-reuse-matrix readback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
540b31f to
97f28f3
Compare
compute_through_stem resolved bidegrees in a strict wavefront, so a bidegree waited on every bidegree of the previous stem rather than on the two it actually depends on. Track per-row progress instead and spawn each bidegree as soon as its same-row and diagonal predecessors are done, letting independent bidegrees resolve concurrently. The scheduler runs on the calling thread via in_place_scope, so it never holds a ParallelGuard and cannot read the per-thread is_in_parallel flag to sense saturation. A job stolen onto a worker that is already a blocked guard holder bounces back a retry; park those bidegrees and re-spawn them on each completion or a short recv_timeout tick. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VuHvdkS1dUru8SfCnyEou8
Give FreeModuleHomomorphism a dedicated apply restricted to a subspace of generators rather than overloading the general path, and cover the stem edges in milnor_vs_nassau. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VuHvdkS1dUru8SfCnyEou8
Address the comment-style review of the relaxed dependency graph. The minimality argument justifying the truncated reads was written out in full in three places. Keep it where the truncation is actually chosen, in step_resolution_with_subalgebra, and reduce signature_mask, restricted_dimension, restricted_partial_matrix and compute_through_stem to pointers at it. restricted_partial_matrix claimed it did not read the concurrently growing target dimension, which the length assert in apply_to_basis_element_restricted in fact does. State what it does instead: it takes the target dimension from the caller. The doc on apply_to_basis_element_restricted linked "Nassau's algorithm" to crate::module::homomorphism. There is no path from algebra to ext::nassau, so that link could never resolve; say it in plain text. Give that assert a message so an error-path test can name it, and assert the d2 chart is non-empty in test_stem_concurrent_secondary, which otherwise compares two empty strings if both guards stop matching. Verified against the real computation: the test compares 14 differentials. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EULwqf8R7n2rgF24Vph4o9
97f28f3 to
43dc7e9
Compare
Implements the relaxation flagged by the long-standing comment in
nassau.rs: computing(s, t)only needs(s, t-1)and(s-1, t-1), not(s-1, t)and(s, t-1). The comment noted that "having the dimensions of the modules change halfway through the computation is annoying to do correctly" — this handles that.What
compute_through_stemnow uses the relaxed wavefront(s, t) <- (s, t-1), (s-1, t-1)fors >= 2, keeping manyt-diagonals (n = t - sfixed) in flight at once. Rowss = 0, 1stay strict — they're cheap, andstep0/step1read their targets through full matrices.step_resolution_with_subalgebrareads its targetC_{s-1}using only generators of degree< t(andC_{s-2}of degree< t-1). By minimality the differentials we lift land in the radical, so the degree-tgenerators — which(s-1, t)may be adding concurrently — contribute neither to the kernel we quotient by nor to those differentials. The read is thus race-free against those append-only writes, with no locking. New helpers: a generator-degree bound onsignature_mask, plusrestricted_dimensionandrestricted_partial_matrix.Magic::Fix) path, which the secondary machinery already handles.ModuleHomomorphism::apply_to_basis_elementnow allows a result buffer shorter than the full target dimension (a prefix of the target basis); the matching truncated differential meansactnever writes past it. This is the only change outsidenassau.rs.Notes
The relaxed graph shortens the critical path to
(S, T)from ~S + Tto ~T: a whole internal-degree column can compute in parallel once the previous one is committed. Nassau can afford this because each bidegree already computes the kernel it consumes locally (it reduces the incoming differentiald_{s-1}), so relaxing shares nothing across bidegrees and duplicates no work.Remark: the classical resolution (
resolution.rs)The same relaxation is mathematically valid for
MuResolution—ker(d_{s-1})_tis a column-(t-1)object there too (see the existingget_kernelcomment) — but it is not a free win. The classical algorithm reduces the outgoing differential at each bidegree and getsker(d_{s-1})_tas the byproduct of the single row reduction it does at(s-1, t)to place that row's generators, handing it forward through the kernel cache. That reuse is exactly the(s-1, t) -> (s, t)edge. Relaxing means decoupling "find kernel" (column-(t-1)) from "place generators" (which needs(s-2, t)) and scheduling/caching the kernel independently — theget_kernelstem-edge path is already a special case of this. Done carefully it costs no extra reductions, only holding the reduced matrix live a little longer (memory) plus the scheduling refactor; done naively (cache only theSubspace, recompute the matrix) it doubles the expensive reductions. Left out of this PR since p=2 workloads use Nassau in practice, but noted here for the record.Test plan
All with
--features concurrent(the parallel path;maybe_rayonruns sequentially otherwise):cargo test -p ext --lib nassau::—test_restart_stem, plus a newtest_stem_concurrent_secondary(save-backed d2 cross-check against the standard resolution, exercising theMagic::Fixquasi-inverse path)cargo test -p ext --test milnor_vs_nassau— existingcompare, plus a new widecompare_stemcross-check against the standard resolution (S_2, C2 to n=40/s=30; Joker to n=30/s=20), run 12× atRAYON_NUM_THREADS=16for determinismcargo test -p ext— green except the pre-existingsave_load_resolution::test_tempdir_lock(unrelated: it relies on read-only directory bits that root bypasses)concurrentfeature configurations🤖 Generated with Claude Code
https://claude.ai/code/session_01MCUtWj6P6suSZqvATCdg6d
Generated by Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Tests