Skip to content

Relax the dependency graph in Nassau's compute_through_stem#272

Open
JoeyBF wants to merge 4 commits into
SpectralSequences:masterfrom
JoeyBF:claude/nassau-relaxed-dependency-graph-d69k0s
Open

Relax the dependency graph in Nassau's compute_through_stem#272
JoeyBF wants to merge 4 commits into
SpectralSequences:masterfrom
JoeyBF:claude/nassau-relaxed-dependency-graph-d69k0s

Conversation

@JoeyBF

@JoeyBF JoeyBF commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

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_stem now uses the relaxed wavefront (s, t) <- (s, t-1), (s-1, t-1) for s >= 2, keeping many t-diagonals (n = t - s fixed) in flight at once. Rows s = 0, 1 stay strict — they're cheap, and step0/step1 read their targets through full matrices.
  • step_resolution_with_subalgebra reads its target C_{s-1} using only generators of degree < t (and C_{s-2} of degree < t-1). By minimality the differentials we lift land in the radical, so the degree-t generators — 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 on signature_mask, plus restricted_dimension and restricted_partial_matrix.
  • Differentials are stored in the restricted (radical) basis, and the quasi-inverse save files always take the existing "incomplete information" (Magic::Fix) path, which the secondary machinery already handles.
  • ModuleHomomorphism::apply_to_basis_element now allows a result buffer shorter than the full target dimension (a prefix of the target basis); the matching truncated differential means act never writes past it. This is the only change outside nassau.rs.

Notes

The relaxed graph shortens the critical path to (S, T) from ~S + T to ~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 differential d_{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 MuResolutionker(d_{s-1})_t is a column-(t-1) object there too (see the existing get_kernel comment) — but it is not a free win. The classical algorithm reduces the outgoing differential at each bidegree and gets ker(d_{s-1})_t as 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 — the get_kernel stem-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 the Subspace, 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_rayon runs sequentially otherwise):

  • cargo test -p ext --lib nassau::test_restart_stem, plus a new test_stem_concurrent_secondary (save-backed d2 cross-check against the standard resolution, exercising the Magic::Fix quasi-inverse path)
  • cargo test -p ext --test milnor_vs_nassau — existing compare, plus a new wide compare_stem cross-check against the standard resolution (S_2, C2 to n=40/s=30; Joker to n=30/s=20), run 12× at RAYON_NUM_THREADS=16 for determinism
  • full cargo test -p ext — green except the pre-existing save_load_resolution::test_tempdir_lock (unrelated: it relies on read-only directory bits that root bypasses)
  • builds and tests pass in both the default and concurrent feature configurations

🤖 Generated with Claude Code

https://claude.ai/code/session_01MCUtWj6P6suSZqvATCdg6d


Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • Added support for applying homomorphisms to truncated basis spans (prefix-sized result buffers).
    • Introduced degree-truncated Nassau resolution computations to enable more flexible concurrent stem scheduling.
  • Performance
    • Reduced resolution work by limiting computations to relevant degree ranges and improving wavefront behavior.
  • Bug Fixes
    • Improved consistency and persistence of quasi-inverse data during save/restore flows.
  • Tests
    • Added cross-check tests ensuring Nassau and standard resolutions match on graded dimensions.

Previously `compute_through_stem` computed a bidegree `(s, t)` only after
both `(s - 1, t)` and `(s, t - 1)`. A comment noted that in theory `(s, t)`
only needs `(s, t - 1)` and `(s - 1, t - 1)`, but that "having the
dimensions of the modules change halfway through the computation is
annoying to do correctly".

This implements that relaxed dependency graph while preserving the
wavefront scheduler, so that many t-diagonals (n = t - s fixed) stay in
flight at once — which is where the parallelism comes from.

The key observation is that computing `(s, t)` only reads generators of
degree strictly less than `t` of the target modules: by minimality the
differentials we lift land in the radical, so the degree-`t` generators
that `(s - 1, t)` adds to `C_{s-1}` (and the degree-`(t-1)` generators of
`C_{s-2}`) contribute neither to the kernel we quotient by nor to those
differentials. `step_resolution_with_subalgebra` is therefore made to read
its target through a generator-degree cutoff (`signature_mask` /
`restricted_dimension` / `restricted_partial_matrix`), so it never touches
the data that `(s - 1, t)` is concurrently appending. This is race-free
against those append-only writes without any locking. The differentials are
stored in this restricted (radical) basis, and the quasi-inverse save files
always take the existing "incomplete information" (`Magic::Fix`) path, which
the secondary machinery already supports.

The wavefront in `compute_through_stem` is rewritten with the relaxed
triggers `(s, t) <- (s, t - 1), (s - 1, t - 1)` for `s >= 2`. Rows `s = 0`
and `s = 1` are kept on the strict schedule (they are cheap and read their
targets through full matrices).

`ModuleHomomorphism::apply_to_basis_element` now permits a result buffer
shorter than the full target dimension (a prefix of the target basis); the
matching truncated differential means `act` never writes past it.

Verified with `--features concurrent` (the parallel path):
- test_restart_stem and milnor_vs_nassau still pass;
- a new wide stem cross-check (compare_stem) against the standard
  resolution (S_2, C2 to n=40/s=30; Joker to n=30/s=20), run repeatedly at
  16 threads to shake out nondeterminism;
- a new save-backed secondary (d2) cross-check whose chart matches the
  standard resolution, exercising the quasi-inverse `Magic::Fix` path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MCUtWj6P6suSZqvATCdg6d
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e74a6949-bb25-45cb-8541-ffe48a738c60

📥 Commits

Reviewing files that changed from the base of the PR and between 727a2c2 and 29f33d8.

📒 Files selected for processing (3)
  • ext/crates/algebra/src/module/homomorphism/free_module_homomorphism.rs
  • ext/src/nassau.rs
  • ext/tests/milnor_vs_nassau.rs

📝 Walkthrough

Walkthrough

Changes

The PR adds truncated basis and generator-degree support to Nassau matrix computation, rewrites stem scheduling around relaxed dependencies for concurrent execution, updates quasi-inverse persistence, and adds comparisons against standard resolutions.

Nassau bounded matrix computation

Layer / File(s) Summary
Bounded basis and matrix contracts
ext/crates/algebra/..., ext/src/nassau.rs
Basis application accepts truncated result buffers; signature masks, dimensions, and matrices support generator-degree bounds.
Restricted resolution step and persistence
ext/src/nassau.rs
Resolution steps use restricted dimensions and partial matrices, while quasi-inverse metadata and full-mask call sites are updated.
Relaxed concurrent stem scheduling
ext/src/nassau.rs
Stem computation seeds rows at the minimum degree and expands ready same-row or diagonal successors using relaxed dependencies.
Resolution equivalence validation
ext/src/nassau.rs, ext/tests/milnor_vs_nassau.rs
New tests compare relaxed and save-backed Nassau results with standard resolution charts and secondary computations.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant compute_through_stem
  participant step_resolution_with_subalgebra
  participant restricted_partial_matrix
  participant signature_matrix
  compute_through_stem->>step_resolution_with_subalgebra: process ready bidegree
  step_resolution_with_subalgebra->>restricted_partial_matrix: build truncated target matrix
  restricted_partial_matrix->>signature_matrix: provide bounded matrix data
  signature_matrix-->>step_resolution_with_subalgebra: return signature matrix
  step_resolution_with_subalgebra-->>compute_through_stem: complete bidegree and enable successors
Loading

Poem

I’m a rabbit with a bounded span,
Hopping through degrees as fast as I can.
Same-row, diagonal—paths now align,
Frozen targets make the schedule fine.
Charts agree when the tests are done—
Carrot-powered proofs for everyone!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: relaxing Nassau's compute_through_stem dependency graph.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MCUtWj6P6suSZqvATCdg6d

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4867b30 and 727a2c2.

📒 Files selected for processing (3)
  • ext/crates/algebra/src/module/homomorphism/free_module_homomorphism.rs
  • ext/src/nassau.rs
  • ext/tests/milnor_vs_nassau.rs

Comment thread ext/crates/algebra/src/module/homomorphism/free_module_homomorphism.rs Outdated
Comment thread ext/src/nassau.rs
- Instead of weakening the shared `ModuleHomomorphism::apply_to_basis_element`
  assertion, keep it strict and add `apply_to_basis_element_restricted` (with a
  documented prefix-buffer contract) for Nassau's restricted matrix builders.
- Add `compare_stem` cases with `max_n = 2^i - 1`, placing a nonzero class
  (`h_i`) on the `s = 1` stem edge `(1, max_n + 1)`, confirming the row-1
  boundary computes correct generators against the standard resolution.
- Fix rustdoc intra-doc link warnings under `-D warnings`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MCUtWj6P6suSZqvATCdg6d
Use the same ungated `use maybe_rayon::prelude::*` with `#[allow(unused_imports)]`
that `algebra::module::homomorphism` uses. The code path is identical in both
feature configurations; the prelude's `enumerate`/`for_each` resolve via rayon's
`IndexedParallelIterator` when `concurrent` is on and via `std::iter::Iterator`
when it is off, which left the import formally unused (hence the allow) in the
sequential build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MCUtWj6P6suSZqvATCdg6d
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>
JoeyBF added a commit to JoeyBF/sseq that referenced this pull request Jul 18, 2026
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>
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