Add S/λ² spectral sequence and B/Z/E classification API#249
Conversation
ExtAlgebra is a first-class Cλ-module, indexed by the coordinate triple Bidegree / BidegreeGenerator / BidegreeElement. SecondaryExtAlgebra is the analogous Cλ²-module but lacked the matching coordinate types; this adds them so you can index into it the same way. New secondary coordinate triple in ext/src/secondary.rs: - Weight: the λ-adic weight (Ext = 0, Lambda = 1; only two weights since λ² = 0), with an as_i32 synthetic coordinate. - SecondaryDegree: a base bidegree b — weight-0 part at b, weight-1 (λ) part at b + LAMBDA_BIDEGREE. Analog of Bidegree. - SecondaryGenerator: base + Weight + idx; into_element places the unit entry in the ext or λ part. Analog of BidegreeGenerator. - SecondaryElement: base + ext/λ FpVectors (promoted from the earlier SyntheticElement). A generic class is not weight-homogeneous, so both parts are stored. Analog of BidegreeElement. Indexing methods on SecondaryExtAlgebra mirror ExtAlgebra (dimension/basis/element/generator, weight_dimension, and unit_* variants). They read ambient generator counts like ExtAlgebra::dimension, so the secondary d2/E3 structure stays a separate computation; undefined bidegrees panic rather than yielding a silent 0. SecondaryProduct.value and both examples use SecondaryElement. The Weight enum is designed to feed a future 3-graded synthetic spectral sequence; the B/Z/E differential filtration is left for that later layer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0125sG5YPRQVfTs5eZ7hN7hA
|
Warning Review limit reached
More reviews will be available in 37 minutes and 26 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughAdds S/λ² classification and spectral-sequence support, stores and queries a lambda2 spectral sequence in the secondary algebra, and updates examples and benchmark outputs to use the new B/Z/E labels and secondary element formatting. ChangesS/λ² spectral sequence and secondary classifications
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 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/examples/lambda2.rs`:
- Around line 139-147: print_table_iii is currently printing raw
lambda2_page_data() entries instead of the advertised B/Z/E-annotated conical
basis. Update print_table_iii to use the new BZE-related API when iterating the
E3/E∞ generators so each row includes the correct B/Z/E type alongside the
tridegree, and keep the output aligned with the table description in the
function’s doc comment.
- Around line 177-183: The Table IV output in the lambda2 example is missing the
commutator column even though the doc comment and table description say it
should be included. Update the Table IV generation logic in the function that
prints the secondary products so it also computes and prints the commutator from
the existing stem parities (using the x·y and y·x relation). Keep the secondary
product output, but extend the same loop/row formatting to include the
commutator information for each pair.
🪄 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: 96149d7c-f3a8-4050-b59d-348c29f958e1
📒 Files selected for processing (5)
ext/crates/sseq/src/sseq.rsext/examples/lambda2.rsext/src/ext_algebra/mod.rsext/src/ext_algebra/secondary.rsext/src/secondary.rs
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/ext_algebra/secondary.rs (1)
228-244: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy liftDon’t expose λ² E3 dimensions when the underlying d₂ is uncomputed.
Line 240 skips unknown d₂ maps, but the λ² copies are already defined and the accessors return definite dimensions. Near the computation boundary this treats “d₂ target/source not computed” as “d₂ is zero”, unlike
d2()which returnsNone. Gate λ² degree definition/accessors on the same computability range, and add a boundary regression test.Possible direction
+ let d2_shift = Bidegree::n_s(-1, 2); + for b in res.iter_stem() { let dim = res.number_of_gens_in_bidegree(b); let [n, s] = b.coords(); - sseq.set_dimension(MultiDegree::new([n, s, 0]), dim); - sseq.set_dimension(MultiDegree::new([n, s, 1]), dim); + + if b.t() <= 0 || res.has_computed_bidegree(b + d2_shift) { + sseq.set_dimension(MultiDegree::new([n, s, 0]), dim); + } + + let incoming_source = b - d2_shift; + if incoming_source.s() < 0 + || incoming_source.t() <= 0 + || res.has_computed_bidegree(incoming_source) + { + sseq.set_dimension(MultiDegree::new([n, s, 1]), dim); + } }Also consider returning
Option<usize>fromlambda2_e3_dimensionifNonecan mean “not computed” rather than “zero”.Also applies to: 272-299, 439-503
🤖 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/ext_algebra/secondary.rs` around lines 228 - 244, The λ² E3 dimension accessors in secondary.rs are exposing definite dimensions even when the corresponding d₂ data is still uncomputed, unlike d2() which reports None. Update the λ² degree setup and the related accessors (including lambda2_e3_dimension and the code around res_lift/homotopy lookups) to gate on the same computability boundary as d₂, so unknown source/target bidegrees are not treated as zero. If appropriate, make the accessor return Option<usize> for not-yet-computed degrees, and add a regression test covering the boundary case.
🤖 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/ext_algebra/secondary.rs`:
- Around line 228-244: The λ² E3 dimension accessors in secondary.rs are
exposing definite dimensions even when the corresponding d₂ data is still
uncomputed, unlike d2() which reports None. Update the λ² degree setup and the
related accessors (including lambda2_e3_dimension and the code around
res_lift/homotopy lookups) to gate on the same computability boundary as d₂, so
unknown source/target bidegrees are not treated as zero. If appropriate, make
the accessor return Option<usize> for not-yet-computed degrees, and add a
regression test covering the boundary case.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 3feb8d09-2af3-42c4-918d-a3fd3fbf3fc1
📒 Files selected for processing (8)
ext/examples/benchmarks/secondary-C2ext/examples/benchmarks/secondary-C2-nassauext/examples/benchmarks/secondary-S_2ext/examples/benchmarks/secondary-tmfext/examples/lambda2.rsext/examples/secondary.rsext/examples/secondary_product.rsext/src/ext_algebra/secondary.rs
Add AdamsLambda2 profile (SseqProfile<3>) for the trigraded Adams spectral sequence of S/λ². Extend SecondaryExtAlgebra with a lambda2_sseq field that builds the E2 page (two copies of Ext at Bockstein degrees 0 and 1) and installs d2 differentials from (n, s, 0) → (n-1, s+2, 1) using the hom_k data. The E3 = E∞ page gives π(S/λ²). New API: lambda2_sseq(), lambda2_page_data(), lambda2_e3_dimension(). New example: lambda2.rs with Table I (B/Z/E decomposition), Table II (Ext products), Table III (π(S/λ²) basis), and Table IV (secondary products with commutators). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017kezHiWcYod7hYovRPczz9
…xample Move the B/Z/E classification into SecondaryExtAlgebra as a public API (BZE enum + classify method) rather than keeping it inline in the example. Fix a panic in SecondaryLift::compute_homotopies when the homotopy range is empty (min.s >= max.s), which occurred for multipliers at high filtration near the resolution boundary. Rewrite the lambda2 example's Table IV to use proper range guards matching secondary_product. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017kezHiWcYod7hYovRPczz9
Update secondary.rs and secondary_product.rs to use the BZE enum for classified output, labelling each result as B/Z/E. This replaces the old unclassified d2 output in secondary.rs and the complement_pivots iteration in secondary_product.rs with the uniform BZE::from_page_data API. Also improve lambda2 Table III to annotate conical basis entries with their B/Z/E type (bock=0: B and Z survive; bock=1: Z and E survive), and Table IV to include commutator information per Proposition 6.4 ([x,y] = 2(x·y) when both stems are odd, 0 otherwise). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017kezHiWcYod7hYovRPczz9
Restore the original filtering in the secondary example: only print generators whose d2 target bidegree has nonzero dimension, keeping the output focused on where d2 can be nontrivial. Each such generator is now labelled B/Z/E. Update the four secondary benchmark files to match the new output format. Fix rustfmt issues in lambda2.rs and secondary.rs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017kezHiWcYod7hYovRPczz9
Use prod.value (SecondaryElement) instead of the removed ext_part/lambda_part fields in lambda2.rs Table IV. Fix nightly rustfmt import grouping. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017kezHiWcYod7hYovRPczz9
BZE classification (boundary/cycle/supports-differential) is intrinsic to any spectral sequence, not specific to the secondary layer. Move the enum to sseq::coordinates::BZE with from_page_data, Display. Add Sseq::classify and Sseq::bze_indices methods that read the page data directly. SecondaryExtAlgebra::classify now delegates to the lambda2 sseq. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017kezHiWcYod7hYovRPczz9
ExtAlgebra now carries a trigraded spectral sequence. With only weight 0 populated, d2 targets the empty weight-1 grading and is trivially zero, so the spectral sequence degenerates at E2 — matching the Adams spectral sequence for M. The secondary layer will clone and extend this base sseq with weight-1 data and d2 differentials on its own copy. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017kezHiWcYod7hYovRPczz9
PiGenerator represents a conical basis generator of π(S/λ²) (Condition 3.2 of the paper), annotated with its Adams BZE classification and weight. PiElement is an element in the E3 subquotient at a specific weight, with coordinates in the surviving-generator basis. New methods on SecondaryExtAlgebra: - adams_classify: full BZE using both weights of the lambda2 sseq - pi_basis: conical basis at a bidegree (B→wt0, Z→both, E→wt1) - to_pi: project a SecondaryElement to E3 subquotients at each weight Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017kezHiWcYod7hYovRPczz9
…arks lambda2.rs: Table III now uses pi_basis() for the conical basis listing, and Table IV uses to_pi() to project products into the E3 subquotient. Both tables use adams_classify for proper B/Z/E distinction. Benchmark files regenerated for secondary_product examples to match the SecondaryElement Display format introduced by the secondary_element branch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017kezHiWcYod7hYovRPczz9
2c19f37 to
fa37ad9
Compare
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 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/examples/lambda2.rs`:
- Around line 35-46: Reject non-unit modules before building the tables in
lambda2’s main example flow: the current use of query_module(...),
ExtAlgebra::from_resolution, and later e2.resolution()/e2.unit_generator(...)
assumes unit-generator indexing that is only valid for a unit input. Update the
lambda2 example to fail fast unless e2.is_unit() is true, and keep the
resolution-dependent table generation behind that check so Table II cannot read
generators from the wrong basis.
In `@ext/examples/secondary_product.rs`:
- Around line 116-123: The classification in `secondary_multiply_into` is
currently inferred from `prod.source.vec().iter_nonzero().next()`, which
mislabels mixed sources by basis order instead of class. Update the
`secondary_product.rs` printing logic to use `SecondaryExtAlgebra::classify()`
for `prod.source` (or explicitly split mixed sources before formatting) so the
`B|Z|E` label reflects the full class rather than the first nonzero basis
element.
In `@ext/src/ext_algebra/mod.rs`:
- Around line 78-84: The cached sseq in ExtAlgebra becomes stale after
compute_through_bidegree() extends the underlying resolution, so sseq() can
return an outdated snapshot that no longer matches dimension() and basis() for
newly computed bidegrees. Update ExtAlgebra::new and the sseq()/build_sseq()
flow so the spectral sequence is rebuilt or invalidated whenever the resolution
grows, or make sseq() compute from the current resolution state instead of
storing a one-time cached value.
In `@ext/src/ext_algebra/secondary.rs`:
- Around line 496-528: `project_to_pi` is building `PiElement` with coordinates
from `sq.reduce(...)` but `basis_indices` from ambient generator filtering, so
the two bases can mismatch. Fix this in `secondary.rs` by deriving the basis
indices from the actual `Subquotient` generator basis used by
`lambda2_page_data(td)`/`Sq::reduce`, or by projecting into the same ambient
basis before constructing `PiElement`. Ensure `PiElement::coords`,
`basis_indices`, and `Display` all refer to the same basis.
- Around line 420-432: The current `classify()` implementation only delegates to
`lambda2_sseq().classify(...)` on the `(n, s, 0)` copy, so it misses the
weight-1 boundary case and can mislabel true `B` classes as `Z`. Update
`SecondaryExt::classify` to use the same boundary-aware logic as
`adams_classify()` (or otherwise incorporate the weight-1 incoming `d₂` check)
while preserving the existing `BZE` decomposition behavior for
`BidegreeGenerator`.
🪄 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: 42d44cc6-5fbc-4ffd-83ae-46ef7ed3bc60
📒 Files selected for processing (17)
ext/crates/sseq/src/coordinates/bze.rsext/crates/sseq/src/coordinates/mod.rsext/crates/sseq/src/sseq.rsext/examples/benchmarks/secondary-C2ext/examples/benchmarks/secondary-C2-nassauext/examples/benchmarks/secondary-S_2ext/examples/benchmarks/secondary-tmfext/examples/benchmarks/secondary_product-h_0-S_2ext/examples/benchmarks/secondary_product-h_0-S_2-nassauext/examples/benchmarks/secondary_product-v_1-C2ext/examples/lambda2.rsext/examples/secondary.rsext/examples/secondary_massey.rsext/examples/secondary_product.rsext/src/ext_algebra/mod.rsext/src/ext_algebra/secondary.rsext/src/secondary.rs
- classify() now inspects both weights of the lambda2 sseq so it reports boundaries (B). The weight-0 copy only carries outgoing d2, so the old single-weight version could never return B and mislabeled boundaries as Z. Consolidates the former adams_classify into classify and drops the redundant method. - ExtAlgebra::sseq() is computed on demand from the current resolution instead of cached in new(), so it never goes stale after the resolution is extended. - secondary_product example classifies each source by membership in the boundary subspace rather than by its first nonzero basis index, which was basis-order dependent for mixed sources. - lambda2 example bails on non-unit modules, since its tables read multiplicands from the resolution basis and feed them as unit generators (valid only when M == k). - Clarify PiElement docs: coords are in the E3 generator basis, labeled by each surviving class's leading (pivot) ambient index, which lines up with Subquotient::reduce entry-for-entry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017kezHiWcYod7hYovRPczz9
Summary
Implements the computational machinery for S/λ² (the cofiber of the Adams d₂ differential) from "The Cofiber of the Adams d₂ Differential", integrated into
SecondaryExtAlgebra.Sseq<3, AdamsLambda2>): E2 has two copies of Ext at each (n, s) — one at Bockstein degree 0 and one at 1. The d₂ maps (n, s, 0) → (n−1, s+2, 1). E3 = E∞ gives π(S/λ²).BZEenum withSecondaryExtAlgebra::classify()andBZE::from_page_data()to decompose Ext = B ⊕ Z ⊕ E at each bidegree, where d₂: E_{(n,s)} ≅ B_{(n−1,s+2)}.lambda2example binary: Generates the four tables from the paper (B/Z/E decomposition, Ext products, conical basis for π(S/λ²), secondary products in Mod_{Cλ²}).SecondaryLift::compute_homotopies: guard against empty homotopy range (min.s >= max.s) which panicked for multipliers at high filtration near the resolution boundary.Changes
ext/crates/sseq/src/sseq.rsAdamsLambda2profile implementingSseqProfile<3>ext/src/ext_algebra/secondary.rsBZEenum,classify(),build_lambda2_sseq(),lambda2_page_data(),lambda2_e3_dimension()ext/src/ext_algebra/mod.rsBZEext/src/secondary.rscompute_homotopiesempty-range panicext/examples/lambda2.rsTest plan
test_lambda2_sseq— verifies dim(E) = dim(B) across bidegrees, h₄ killed, h₀ survivestest_sphere_d2— verifies d₂(h₄) = h₀h₃² and permanent cyclestest_sphere_products— verifies h₀² ≠ 0 and h₀h₁ = 0S_2at (n=16, s=6)test_tempdir_lockfailure)🤖 Generated with Claude Code
https://claude.ai/code/session_017kezHiWcYod7hYovRPczz9
Generated by Claude Code
Summary by CodeRabbit