Skip to content

Split ExtAlgebra into a ring ExtAlgebra and a module ExtModule - #277

Open
JoeyBF wants to merge 4 commits into
SpectralSequences:masterfrom
JoeyBF:claude/259-phase2-ext-split
Open

Split ExtAlgebra into a ring ExtAlgebra and a module ExtModule#277
JoeyBF wants to merge 4 commits into
SpectralSequences:masterfrom
JoeyBF:claude/259-phase2-ext-split

Conversation

@JoeyBF

@JoeyBF JoeyBF commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Closes #259.

What

ExtAlgebra today conflates two different objects: the ring $\mathrm{Ext}(k,k)$ and a module $\mathrm{Ext}(M,k)$ over it, glued together by an is_unit flag and a without_unit constructor. This PR splits them into two types that implement the actual algebra::Algebra / algebra::Module traits:

  • ExtAlgebra — the ring $\mathrm{Ext}(k,k)$. Owns the resolution of $k$ and the per-generator ring-product cache (class_product_map). impl Algebra<2> for ExtAlgebra.
  • ExtModule — the module $\mathrm{Ext}(M,k)$ over it. Owns the resolution of $M$ and a shared Arc<ExtAlgebra>, plus its own module-action cache. impl Module<2> for ExtModule with type Algebra = ExtAlgebra.

"An algebra is a module over itself" makes the $M = k$ case structural: it's an ExtModule sharing the ring's Arc, and is_unit is just Arc::ptr_eq. without_unit becomes ExtModule::intrinsic.

The bigrading is why this is possible now: Ext is bigraded $(s,t)$, so it needs Algebra<2> / Module<2>. That capability is the previous PR in the stack (#268), which makes the Algebra/Module trait family multigrading-capable with zero behavior change for the singly-graded engine.

Also in this PR

ResolutionHomomorphism::linear_combination — a cheap primitive that assembles a multiply-by-a-class chain map by adding the cached per-generator maps (no quasi-inverse solves). It backs the trait product methods and is cross-validated against multiply_into by a new test.

Notes for review

  • Trait products are total, Option helpers stay inherent. Algebra::multiply_basis_elements / Module::act_on_basis panic if the bidegree isn't resolved (the trait contract — call compute_basis first). The existing Option-returning multiply_into / try_multiply remain as inherent helpers for the "maybe out of computed range" ergonomics the examples rely on.
  • Odd-prime sign. The product/action impls realise the ring product / right action up to the Koszul sign, exact only at $p = 2$. This is documented loudly at the impl sites so nobody wires it into odd-prime machinery generically — it matches the pre-existing up-to-sign product API and is out of scope for Split ExtAlgebra into a ring ExtAlgebra (over k) and an ExtModule (over M) #259.
  • Axis reinterpretation. min_degree / max_computed_degree report the filtration s axis (bounded-below direction) rather than t; documented at each site, consistent with the "degree-returns stay i32 = filtration" convention from Make the Algebra and Module traits multigrading-capable #268.

Stacking

This is built on #268 (the multigrade trait work). Until #268 merges, this PR's diff includes those commits too; it will collapse to just the ext-split commits once #268 lands and I rebase.

Testing

cargo test -p ext ext_algebra (10 tests incl. the #116 Massey regressions, class_product_map vs multiply_into, a trait-surface smoke test, an $M \neq k$ product test, and an odd-prime product test), plus the full just lint / just test CI-parity matrix, all green.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Nk9J57zb6GpvZxrduhGSdY


Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • Added support for multigraded algebra and module computations while preserving convenient single-degree usage.
    • Introduced separate Ext algebra and Ext module workflows, including intrinsic and non-unit module constructions.
    • Added linear combinations of resolution homomorphisms.
  • Bug Fixes
    • Improved Massey product calculations, including kernel handling, zero targets, indeterminacy, and homotopy validation.
    • Improved product-map reuse and support for products across distinct resolutions.
  • Compatibility
    • Updated Steenrod and module operations to work consistently with the expanded grading API.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 37 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: c1e86498-0d79-4554-8146-f5f167899a97

📥 Commits

Reviewing files that changed from the base of the PR and between 5adc5f1 and 285e8bc.

📒 Files selected for processing (47)
  • 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_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
📝 Walkthrough

Walkthrough

This PR generalizes algebra and module APIs to MultiDegree, adds ModuleExt compatibility wrappers, and separates ExtAlgebra from ExtModule. It also updates Massey products, secondary computations, resolution maps, examples, and feature forwarding.

Changes

Multidegree API

Layer / File(s) Summary
Generalized algebra degree contracts
ext/crates/algebra/src/algebra/*, ext/crates/sseq/src/coordinates/degree.rs, ext/crates/algebra/Cargo.toml
Algebra methods accept multidegrees, scalar conversions are added, and Steenrod dispatch becomes manual.
Multigraded module contract and implementations
ext/crates/algebra/src/module/*
Module<N> uses MultiDegree<N>, while ModuleExt provides compatible scalar-style methods. Concrete modules implement the new API.
ModuleExt consumer migration
ext/src/chain_complex/*, ext/src/resolution*.rs, ext/src/secondary.rs, web_ext/sseq_gui/src/actions.rs, ext/tests/*
Consumers import ModuleExt and use its dimension and action wrappers.

Ext algebra and module abstractions

Layer / File(s) Summary
Separate ExtAlgebra and ExtModule
ext/src/ext_algebra/mod.rs
ExtAlgebra owns ring data and caches. ExtModule owns module data and shares the ring algebra.
Massey and secondary Ext integration
ext/src/ext_algebra/massey.rs, ext/src/ext_algebra/secondary.rs
Massey and secondary computations use ExtModule, intrinsic algebra access, and shared product maps.
Resolution homomorphism combination
ext/src/resolution_homomorphism.rs
linear_combination validates compatible summands and combines their generator images within a bounded bidegree.

Examples

Layer / File(s) Summary
ExtModule example migration
ext/examples/massey.rs, ext/examples/product.rs, ext/examples/secondary*.rs
Examples use ExtModule constructors and updated algebra, resolution, and secondary accessors.
Example module implementations
ext/examples/bruner.rs, ext/examples/ext_m_n.rs, ext/examples/lift_hom.rs, ext/examples/resolution_size.rs, ext/examples/sq0.rs, ext/examples/steenrod.rs
Examples use ModuleExt and implement multidegree module methods where required.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: hoodmane

Poem

A rabbit sorts degrees in rows,
While ExtAlgebra’s ring cache glows.
ExtModule hops into place,
Massey maps run with shared grace.
Multidegrees guide each trail.
“Hop!” says the rabbit. “Build and prevail!”

🚥 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 primary change: splitting ExtAlgebra into ring and module types.
Linked Issues check ✅ Passed The changes implement issue #259 by separating ring and module responsibilities, updating Massey computations, caches, consumers, and supporting APIs.
Out of Scope Changes check ✅ Passed The multigrading, linear-combination, API, example, and consumer changes directly support the ExtAlgebra and ExtModule split.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 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.

@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: 4

🤖 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/sq0.rs`:
- Around line 287-288: Update element_to_string_multi to pass the halved degree
to the inner module, matching basis_element_to_string_multi and DoubleModule’s
2n representation; use the converted degree value when calling the inner
formatter so it receives n rather than 2n.

In `@ext/src/ext_algebra/massey.rs`:
- Around line 63-72: The documentation for massey_b_hom in
ext/src/ext_algebra/massey.rs lines 63-72 must qualify the caching behavior:
generator product maps are cached and shared, while multi-generator classes
assemble a fresh combined map per call. Update the related documentation at
ext/src/ext_algebra/massey.rs lines 294-296 with the same qualification; no code
changes are required.

In `@ext/src/ext_algebra/secondary.rs`:
- Around line 43-49: Rename the public type SecondaryExtAlgebra to
SecondaryExtModule throughout the implementation, including both impl blocks,
its module documentation, all constructors, and every construction site such as
examples and SecondaryExtAlgebra::new. Preserve the existing behavior and ensure
references use the renamed type consistently.

In `@ext/src/resolution_homomorphism.rs`:
- Around line 174-177: Enforce the documented empty-name precondition at the
start of linear_combination by asserting that name is empty, so invalid callers
fail immediately before maps or save state are populated.
🪄 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 Plus

Run ID: 1edd1ab1-6e85-4d4c-933f-12dd25c5be8e

📥 Commits

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

📒 Files selected for processing (46)
  • ext/crates/algebra/Cargo.toml
  • 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_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

Comment thread ext/examples/sq0.rs
Comment thread ext/src/ext_algebra/massey.rs
Comment thread ext/src/ext_algebra/secondary.rs
Comment thread ext/src/resolution_homomorphism.rs
@JoeyBF
JoeyBF force-pushed the claude/259-phase2-ext-split branch from 5adc5f1 to 8ef36da Compare August 1, 2026 06:28
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-phase2-ext-split branch from 8ef36da to 4c96f46 Compare August 1, 2026 07:10
claude added 2 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
@JoeyBF
JoeyBF force-pushed the claude/259-phase2-ext-split branch from 4c96f46 to 285e8bc Compare August 1, 2026 07:31
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.

Split ExtAlgebra into a ring ExtAlgebra (over k) and an ExtModule (over M)

2 participants