Skip to content

Split the secondary layer + add SecondaryExtModule::secondary_massey - #278

Open
JoeyBF wants to merge 6 commits into
SpectralSequences:masterfrom
JoeyBF:claude/259-phase3-secondary
Open

Split the secondary layer + add SecondaryExtModule::secondary_massey#278
JoeyBF wants to merge 6 commits into
SpectralSequences:masterfrom
JoeyBF:claude/259-phase3-secondary

Conversation

@JoeyBF

@JoeyBF JoeyBF commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

A follow-up to #259 that applies the same ring/module split, one level up, to the secondary ($d_2$ / $\mathrm{Mod}_{C\lambda^2}$) layer — and gives secondary Massey products a real home instead of a hand-rolled example.

What

secondary.rs had a single SecondaryExtAlgebra that was misnamed: it wrapped an ExtModule and owned both the module-M secondary data and the ring-k secondary data, joined by the same is_unit branching #259 removed from the primary layer. This PR splits it, mirroring ExtAlgebra / ExtModule:

  • SecondaryExtAlgebra — the ring secondary layer over k: the secondary resolution of k and its $E_3$ page.
  • SecondaryExtModule — the module secondary layer over M: the secondary resolution of M, its $E_3$ page, the secondary differential d2/survives, and the $\mathrm{Mod}_{C\lambda^2}$ product. Holds a shared Arc<SecondaryExtAlgebra>; when M == k it shares the ring's secondary resolution and page (is_unit = Arc::ptr_eq), retiring the branches.

secondary_massey

examples/secondary_massey.rs previously hand-built the ring-side secondary multiply-by-b map and hand-rolled the entire ~500-line bracket computation — the secondary analogue of the class_product_map gap #259 filled. That logic now lives in SecondaryExtModule::secondary_massey, which computes $\langle -, b, a\rangle$ over every valid first factor, with a ∈ Ext(M,k) (module side) and b, - ∈ Ext(k,k) (ring side). A small SecondaryClass { degree, ext, lambda } input type carries the non-standard-lift classes ($\mathrm{Ext}$ part + optional $\lambda$ part) the bracket needs. The example collapses to querying the two classes and printing the results.

Notes for review

  • The delicate $\mathbb{Z}/p^2$ / $\lambda$ read-off was ported verbatim from the example into the method and pinned by a new regression test (test_sphere_secondary_massey) before the inline version was deleted — behaviour is unchanged up to the documented sign.
  • build_secondary_hom now asserts the SecondaryClass $\mathrm{Ext}$/$\lambda$ vector lengths match the resolution dimensions (a zero $\lambda$ part means "no $\lambda$ part" and is always valid), giving a clear error instead of a silent zero-pad.

Stacking

Built on #268 (multigrade traits) + #277 (the primary ext split). Its diff currently includes both and will shrink to just the secondary commits as those merge and I rebase.

Testing

cargo test -p ext ext_algebra (test_sphere_d2, test_sphere_secondary_massey, plus the primary suite), full just lint / just test CI-parity matrix, and --examples --features concurrent build — all green.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Nk9J57zb6GpvZxrduhGSdY


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@JoeyBF, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 36 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 reviews.

How do review 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 refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c5b8889e-3aca-47ec-b218-453404372598

📥 Commits

Reviewing files that changed from the base of the PR and between 99ddfb6 and cbdd6d7.

📒 Files selected for processing (48)
  • ext/crates/algebra/Cargo.toml
  • ext/crates/algebra/benches/common/mod.rs
  • ext/crates/algebra/src/algebra/adem_algebra.rs
  • ext/crates/algebra/src/algebra/algebra_trait.rs
  • ext/crates/algebra/src/algebra/field.rs
  • ext/crates/algebra/src/algebra/milnor_algebra.rs
  • ext/crates/algebra/src/algebra/steenrod_algebra.rs
  • ext/crates/algebra/src/module/finite_dimensional_module.rs
  • ext/crates/algebra/src/module/finitely_presented_module.rs
  • ext/crates/algebra/src/module/free_module.rs
  • ext/crates/algebra/src/module/hom_module.rs
  • ext/crates/algebra/src/module/homomorphism/free_module_homomorphism.rs
  • ext/crates/algebra/src/module/homomorphism/full_module_homomorphism.rs
  • ext/crates/algebra/src/module/homomorphism/hom_pullback.rs
  • ext/crates/algebra/src/module/homomorphism/mod.rs
  • ext/crates/algebra/src/module/homomorphism/quotient_homomorphism.rs
  • ext/crates/algebra/src/module/mod.rs
  • ext/crates/algebra/src/module/module_trait.rs
  • ext/crates/algebra/src/module/quotient_module.rs
  • ext/crates/algebra/src/module/rpn.rs
  • ext/crates/algebra/src/module/suspension_module.rs
  • ext/crates/algebra/src/module/tensor_module.rs
  • ext/crates/sseq/src/coordinates/degree.rs
  • ext/examples/bruner.rs
  • ext/examples/ext_m_n.rs
  • ext/examples/lift_hom.rs
  • ext/examples/massey.rs
  • ext/examples/product.rs
  • ext/examples/resolution_size.rs
  • ext/examples/secondary.rs
  • ext/examples/secondary_massey.rs
  • ext/examples/secondary_product.rs
  • ext/examples/sq0.rs
  • ext/examples/steenrod.rs
  • ext/src/chain_complex/chain_homotopy.rs
  • ext/src/chain_complex/finite_chain_complex.rs
  • ext/src/chain_complex/mod.rs
  • ext/src/ext_algebra/massey.rs
  • ext/src/ext_algebra/mod.rs
  • ext/src/ext_algebra/secondary.rs
  • ext/src/nassau.rs
  • ext/src/resolution.rs
  • ext/src/resolution_homomorphism.rs
  • ext/src/secondary.rs
  • ext/src/yoneda.rs
  • ext/tests/extend_identity.rs
  • ext/tests/non_zero_min_degree.rs
  • web_ext/sseq_gui/src/actions.rs

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/259-phase3-secondary branch from 89d131d to e3c76df Compare August 1, 2026 06:29
claude added 2 commits August 1, 2026 07:06
Generalize the `Algebra` trait family to be multigrading-capable, as the
foundation for treating Ext(k,k) as a genuine bigraded algebra (issue SpectralSequences#259).

- `Algebra<const N: usize = 1>`: degree *inputs* now take
  `impl Into<MultiDegree<N>>`, so singly-graded callers keep passing bare
  `i32`s (via new `From<i32> for MultiDegree<1>`). The default `N = 1` keeps
  every existing `A: Algebra` bound and `dyn` usage working unchanged.
- `enum_dispatch` cannot handle a generic trait, so `Algebra` is removed from
  `SteenrodAlgebra`'s dispatch list and hand-rolled via `dispatch_steenrod!`
  (the pattern already used for `PairAlgebra`). `UnstableAlgebra`,
  `GeneratedAlgebra`, and `MuAlgebra` stay singly-graded and keep enum_dispatch.
- Milnor, Adem, and Field implement `Algebra` (default `N = 1`), converting the
  incoming degree to `i32` at each method boundary.
- Degree-returning methods stay `i32` (the distinguished filtration direction),
  so the resolution engine's degree arithmetic is untouched.
- `algebra` now depends on `sseq` for `MultiDegree` (no dependency cycle).

Behavior is unchanged for the singly-graded path; all 51 algebra tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nk9J57zb6GpvZxrduhGSdY
Second half of the multigrading foundation for issue SpectralSequences#259: generalize the
`Module` trait to bigraded (and N-graded) modules, so Ext(M, k) can be a genuine
`Module` over the ring Ext(k, k).

- `Module<const N: usize = 1>` with `type Algebra: Algebra<N>`. The default
  `N = 1` keeps every existing `M: Module` bound and the `dyn Module` /
  `SteenrodModule` alias working unchanged.
- `Module` must stay object-safe (`SteenrodModule = Arc<dyn Module>`), so its
  degree-taking methods take concrete `MultiDegree<N>` and are suffixed `_multi`
  (e.g. `dimension_multi`, `act_on_basis_multi`). A blanket `ModuleExt` trait
  provides the canonical ergonomic names (`dimension`, `act_on_basis`, …) taking
  `impl Into<MultiDegree<N>>`, forwarding to the core methods. It is implemented
  for every module including `dyn Module`, so callers keep passing bare `i32`s.
- All eight module implementors (free, FD, FP, hom, quotient, RPn, suspension,
  tensor) implement the `_multi` methods, converting to `i32` at the boundary.
- Degree-returning methods (`min_degree`, `max_degree`, `max_computed_degree`)
  stay `i32` (the distinguished filtration direction), so the resolution
  engine's degree arithmetic is untouched.
- Downstream call sites (resolution, nassau, yoneda, secondary, chain complexes,
  examples, tests) gain a `use algebra::module::ModuleExt` import; no logic
  changes.
- Also corrects a pre-existing latent bug in the `sq0` example: `DoubleModule`
  (which represents degree 2n) forwarded the full degree to the inner module in
  `element_to_string`, unlike every sibling method; now halves it like
  `basis_element_to_string`.

Behavior is unchanged for the singly-graded path: all algebra and ext tests pass
(the one failing test, `test_tempdir_lock`, is a pre-existing environment issue
about read-only file permissions under root, unrelated to this change).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nk9J57zb6GpvZxrduhGSdY
@JoeyBF
JoeyBF force-pushed the claude/259-phase3-secondary branch from e3c76df to 8be224f Compare August 1, 2026 07:11
claude added 4 commits August 1, 2026 07:30
…equences#259)

Split the conflated `ExtAlgebra` into two objects that match the two things
a resolution computes:

- `ExtAlgebra` is the ring `Ext(k, k)`, backed by a resolution of `k`. It
  owns the ring-product cache (`res(k) -> res(k)`) and is the single home for
  the multiply-by-a-class maps Massey products need
  (`ExtAlgebra::class_product_map`), fixing the awkwardness noted in SpectralSequences#258 where
  `massey_b_hom` rebuilt that map from scratch each call.
- `ExtModule` is a module `Ext(M, k)` over that ring, backed by a resolution
  of `M`. It holds a shared `Arc<ExtAlgebra>` (so all modules over the same `k`
  reuse one ring cache) and its own module-action cache (`res(M) -> res(k)`).

`M == k` collapses to "a module over itself": `ExtModule` shares its
resolution `Arc` with the ring, so `is_unit` is now a structural `ptr_eq`
(`ExtModule::is_unit`) rather than a stored flag. `without_unit` is replaced by
`ExtModule::intrinsic` for the resolution-intrinsic `d2` path.

`ExtAlgebra` implements `algebra::Algebra<2>` and `ExtModule` implements
`algebra::module::Module<2>` (with `Algebra = ExtAlgebra`), realising the
"resolving an algebra yields an Algebra, resolving a module yields a Module"
correspondence on the real trait family. The trait product/action methods are
total; the inherent `multiply_into`/`try_multiply` keep their `Option`-returning
out-of-range ergonomics. The shared product core lives in module-level helpers
(`cached_generator_product_map`, `products_into`, `combine_product`) so ring and
module products do not duplicate logic.

Massey products move to `ExtModule` (a, b in the ring; c in the module), with
`massey_b_hom` now sourced from the shared ring cache via `class_product_map`.
`SecondaryExtAlgebra` wraps an `ExtModule`; the four examples and in-module
tests are updated. Behaviour is unchanged: the three Massey regression tests
(SpectralSequences#116), the product test, and the d2 test all pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nk9J57zb6GpvZxrduhGSdY
Add `ResolutionHomomorphism::linear_combination`, which builds the chain map
`∑ cᵢ·fᵢ` from maps sharing a source, target, and shift by summing their
generator images directly — no quasi-inverse lift. `ExtAlgebra::class_product_map`
now uses it for multi-generator classes: a single generator returns the cached
per-generator map, and a general class *adds* the cached generator maps instead
of re-solving the class from scratch via `from_class`. This realises the "storing
lifts per generator, adding maps is cheap" design and gives the shared ring cache
a real payoff for the multiply-by-a-class maps Massey products consume.

A new regression test cross-checks the two independent linear-combination
strategies at the first two-generator bidegree of Ext(F₂,F₂) — (n=15, s=5):
`class_product_map` sums maps at the chain level, `multiply_into` sums them at
the hom_k level, and the induced products must agree. They do.

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

Mirror the SpectralSequences#259 primary split one level up. The old `SecondaryExtAlgebra` was
misnamed — it wrapped an `ExtModule` and conflated the module-`M` secondary data
(d2, module secondary products) with the ring-`k` secondary data (unit secondary
resolution + E3 page), joined by `is_unit` branching.

Now:
- `SecondaryExtAlgebra` is the ring secondary layer over `k` (wraps `ExtAlgebra`,
  owns k's secondary resolution + E3 page + `page_data`).
- `SecondaryExtModule` is the module secondary layer over `M` (wraps `ExtModule`,
  shares an `Arc<SecondaryExtAlgebra>`, owns M's secondary resolution + E3 page,
  `d2`/`survives`, the module secondary-product cache, and `secondary_multiply_into`).

`M == k` shares the secondary resolution and E3 page `Arc`s between module and
ring (`SecondaryExtModule::is_unit` = the primary `ExtModule::is_unit`), so the
`is_unit` branches collapse into structural `Arc` sharing — `extend_all` computes
k's E3 once and installs it on the ring in the shared case.

The `secondary` and `secondary_product` examples and the `test_sphere_d2`
regression move onto `SecondaryExtModule::from_module`; `unit_page_data` becomes
`algebra().page_data`. Behaviour is unchanged (d2 test passes). This is the
structural half; the ring secondary class-product cache and the `secondary_massey`
method that consumes it follow.

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

The secondary Massey example hand-rolled ~500 lines of Z/p² + λ bookkeeping —
building the two secondary lifts, the chain homotopy, the E3 kernel, and the
Mod_{Cλ²} read-off — the same plumbing SpectralSequences#247 abstracted for primary Massey.

Encapsulate it as `SecondaryExtModule::secondary_massey(a, b, job)`, the
Mod_{Cλ²} analogue of `ExtModule::massey_iter_a`. `a ∈ Ext(M, k)` comes from the
module side, `b, - ∈ Ext(k, k)` from the shared ring side; both are
`SecondaryClass`es (an Ext part plus an optional λ part, since secondary Massey
needs non-standard lifts). A new `SecondaryClass` input type and a shared
`build_secondary_hom` helper (generalising the example's `get_hom`) construct the
secondary lift + optional λ chain map from coordinates. `job = Some(s)` shards the
chain-homotopy computation, matching the other secondary scripts.

The read-off is ported verbatim, so behaviour is unchanged: a new regression test
(`test_sphere_secondary_massey`) pins `<-, h_0, h_1>` on S_2 against the exact
bracket family the pre-refactor example printed, and the refactored example
reproduces that output byte-for-byte. `secondary_massey.rs` is now just the
interactive front-end (querying classes, formatting output).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nk9J57zb6GpvZxrduhGSdY
@JoeyBF
JoeyBF force-pushed the claude/259-phase3-secondary branch from 8be224f to cbdd6d7 Compare August 1, 2026 07:32
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