Skip to content

Add trying variants to a bunch of APIs - #243

Merged
hoodmane merged 8 commits into
SpectralSequences:masterfrom
hoodmane:try
Jun 25, 2026
Merged

Add trying variants to a bunch of APIs#243
hoodmane merged 8 commits into
SpectralSequences:masterfrom
hoodmane:try

Conversation

@hoodmane

@hoodmane hoodmane commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

To help with Python bindings.

Summary by CodeRabbit

  • New Features

    • Added several fallible APIs that return errors or None instead of panicking in common edge cases.
    • Improved module loading and parsing with clearer error messages for missing files, invalid shifts, and malformed cofiber inputs.
    • Added safer helpers for algebra basis lookup, module actions, graph output, filtration products, and Yoneda representatives.
  • Bug Fixes

    • Invalid or out-of-range inputs now fail gracefully across algebra, module, chain-complex, and spectral-sequence workflows.
    • Preserved existing behavior for valid inputs while reducing unexpected crashes.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@hoodmane, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 34 minutes and 14 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 @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 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 configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a322d7b8-b14c-4c94-be66-0f135bcc3eef

📥 Commits

Reviewing files that changed from the base of the PR and between 542c075 and e6b3af1.

📒 Files selected for processing (15)
  • ext/crates/algebra/src/algebra/adem_algebra.rs
  • ext/crates/algebra/src/algebra/algebra_trait.rs
  • ext/crates/algebra/src/algebra/milnor_algebra.rs
  • ext/crates/algebra/src/module/finite_dimensional_module.rs
  • ext/crates/algebra/src/module/mod.rs
  • ext/crates/algebra/src/module/module_trait.rs
  • ext/crates/sseq/src/sseq.rs
  • ext/src/chain_complex/mod.rs
  • ext/src/secondary.rs
  • ext/src/utils.rs
  • ext/src/yoneda.rs
  • ext/tests/construct_cofiber_error.rs
  • ext/tests/filtration_one_product.rs
  • ext/tests/parse_module_name.rs
  • ext/tests/try_yoneda.rs
📝 Walkthrough

Walkthrough

The PR adds fallible try_* APIs and explicit error types across algebra lookups, module actions, graph drawing, filtration, homotopy, utility loading/parsing, and Yoneda representative construction. Existing wrappers now delegate to these fallible paths, and tests cover success and failure cases.

Changes

Fallible helpers and explicit errors

Layer / File(s) Summary
Algebra basis lookup helpers
ext/crates/algebra/src/algebra/algebra_trait.rs, ext/crates/algebra/src/algebra/adem_algebra.rs
Algebra adds try_basis_element_to_string; AdemAlgebra adds try_beps_pn, routes basis_element_from_string through fallible lookup, and adds tests for the new Option-based behavior.
Milnor basis parsing
ext/crates/algebra/src/algebra/milnor_algebra.rs
MilnorAlgebra routes basis parsing through fallible lookups, exposes try_beps_pn, and adds tests for missing and profile-excluded basis elements.
Module action errors
ext/crates/algebra/src/module/module_trait.rs, ext/crates/algebra/src/module/mod.rs, ext/crates/algebra/src/module/finite_dimensional_module.rs
Module adds fallible action helpers with ActError, the error type is re-exported, and the finite-dimensional module tests cover the returned error variants.
Filtration product fallback
ext/src/chain_complex/mod.rs, ext/tests/filtration_one_product.rs
filtration_one_product delegates to a fallible implementation that validates the source bidegree and operation index before building products.
Graph drawing precondition
ext/crates/sseq/src/sseq.rs
Sseq<2, P> adds a fallible graph-writing entrypoint that checks the minimum y coordinate before running the existing drawing logic, and tests cover the precondition.
Homotopy step fallback
ext/src/secondary.rs
compute_homotopy_step now wraps a fallible implementation that returns anyhow::Result, uses ensure! for lift checks, and has a regression test for the error case.
Module loading and cofiber errors
ext/src/utils.rs, ext/tests/parse_module_name.rs, ext/tests/construct_cofiber_error.rs
parse_module_name, construct_standard, and load_module_json switch to structured errors, LoadModuleError is added, and tests cover shift parsing, missing modules, and invalid cofiber specs.
Yoneda representative fallback
ext/src/yoneda.rs, ext/tests/try_yoneda.rs
try_yoneda_representative_element adds precondition checks and returns anyhow::Result, while yoneda_representative_element delegates through expect; tests cover valid and invalid inputs.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

A rabbit hops where errors glow,
No panic pits for me below.
try_ paths bloom like clover bright,
And safe small burrows feel just right.
🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is broadly related to the PR, but it is too vague to clearly identify the main changes. Use a more specific title that names the fallible try_ API additions, e.g. "Add fallible try_ variants for algebra and module APIs".
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

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

@hoodmane
hoodmane force-pushed the try branch 2 times, most recently from 96f8f06 to aab655e Compare June 25, 2026 20:47
Comment thread ext/src/chain_complex/mod.rs Outdated
Comment thread ext/crates/algebra/src/algebra/algebra_trait.rs
Comment thread ext/crates/algebra/src/module/finite_dimensional_module.rs
Comment thread ext/crates/sseq/src/sseq.rs Outdated
Comment thread ext/src/yoneda.rs Outdated
Comment thread ext/tests/filtration_one_product.rs

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

🤖 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/algebra/adem_algebra.rs`:
- Around line 1320-1338: The try_beps_pn lookup can still panic because
try_basis_element_to_index reads from basis_element_to_index_map before the
requested degree has been generated. Update try_beps_pn in AdemAlgebra to
compute the target degree first and call self.compute_basis(degree) before
constructing the AdemBasisElement and delegating to try_basis_element_to_index,
so the try_ path remains non-panicking for callers that have not precomputed the
basis.

In `@ext/crates/algebra/src/module/module_trait.rs`:
- Around line 88-89: In `Module::basis` and the related fallible helper that
calls `compute_basis` and `dimension`, validate the incoming
`mod_degree`/`input_degree` against `self.min_degree()` before invoking any
implementor-provided methods. Move the minimum-degree guard ahead of
`compute_basis(...)` and `self.dimension(...)`, and return the existing
below-minimum error early so invalid binding input never reaches
implementation-specific panic paths.

In `@ext/crates/sseq/src/sseq.rs`:
- Around line 465-471: The doc comment on try_write_to_graph is stale: it still
says the method “shifts the sseq horizontally,” but this function only validates
the min-y precondition and performs graph writing. Update the documentation near
try_write_to_graph to remove that sentence and keep the description aligned with
its actual behavior, while preserving the notes about write_to_graph and the
Err(String) precondition check.

In `@ext/src/chain_complex/mod.rs`:
- Around line 120-130: The try_filtration_one_product path can still panic on
invalid negative inputs before the existing computed-bidegree checks protect the
indexing calls. Add early validation in try_filtration_one_product for
source.s() and op_deg so any negative values return an error before calling
self.module(target.s() - 1), self.module(target.s()), or
self.algebra().dimension_unstable. Keep the guard close to the start of the
function, using the existing target/source/bidegree logic to preserve the
current behavior for valid inputs.

In `@ext/src/secondary.rs`:
- Around line 529-532: The try_compute_homotopy_step path currently indexes
self.homotopies()[b.s()] directly, which can panic before the Result-based error
handling is reached. Update try_compute_homotopy_step to use a fallible lookup
with get on the homotopies collection, and return an anyhow error with context
when the homotopy degree is uninitialized or out of bounds so callers receive
Err instead of a panic.

In `@ext/src/utils.rs`:
- Around line 238-246: The cofiber path in `new_output`/`row_mut` can panic
because `Matrix::row_mut` is called with `cofiber.idx()` before checking bounds.
Add a validation step in this cofiber handling logic to ensure the parsed `idx`
is less than
`resolution.module(cofiber.s()).number_of_gens_in_degree(cofiber.t())`, and
return a contextual error if it is out of range instead of proceeding to
`row_mut`.
🪄 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: 9f0afc3a-fd3c-4100-8c20-923af105bb7d

📥 Commits

Reviewing files that changed from the base of the PR and between 9d4837a and 542c075.

📒 Files selected for processing (15)
  • ext/crates/algebra/src/algebra/adem_algebra.rs
  • ext/crates/algebra/src/algebra/algebra_trait.rs
  • ext/crates/algebra/src/algebra/milnor_algebra.rs
  • ext/crates/algebra/src/module/finite_dimensional_module.rs
  • ext/crates/algebra/src/module/mod.rs
  • ext/crates/algebra/src/module/module_trait.rs
  • ext/crates/sseq/src/sseq.rs
  • ext/src/chain_complex/mod.rs
  • ext/src/secondary.rs
  • ext/src/utils.rs
  • ext/src/yoneda.rs
  • ext/tests/construct_cofiber_error.rs
  • ext/tests/filtration_one_product.rs
  • ext/tests/parse_module_name.rs
  • ext/tests/try_yoneda.rs

Comment thread ext/crates/algebra/src/algebra/adem_algebra.rs
Comment on lines +88 to +89
self.compute_basis(mod_degree);
let mod_dim = self.dimension(mod_degree);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Check the module degree before calling implementor methods.

Both fallible helpers still pass arbitrary mod_degree / input_degree into compute_basis and dimension before rejecting below-minimum degrees, so invalid binding input can still reach implementation-specific panic paths. Guard against degree < self.min_degree() first.

Proposed fix
-        self.compute_basis(mod_degree);
+        let min_degree = self.min_degree();
+        if mod_degree < min_degree {
+            return Err(ActError::IndexOutOfRange(format!(
+                "mod_degree {mod_degree} is below module minimum degree {min_degree}"
+            )));
+        }
+        self.compute_basis(mod_degree);
         let mod_dim = self.dimension(mod_degree);
-        self.compute_basis(input_degree);
+        let min_degree = self.min_degree();
+        if input_degree < min_degree {
+            return Err(ActError::IndexOutOfRange(format!(
+                "input_degree {input_degree} is below module minimum degree {min_degree}"
+            )));
+        }
+        self.compute_basis(input_degree);
         let input_dim = self.dimension(input_degree);

Also applies to: 125-126

🤖 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/crates/algebra/src/module/module_trait.rs` around lines 88 - 89, In
`Module::basis` and the related fallible helper that calls `compute_basis` and
`dimension`, validate the incoming `mod_degree`/`input_degree` against
`self.min_degree()` before invoking any implementor-provided methods. Move the
minimum-degree guard ahead of `compute_basis(...)` and `self.dimension(...)`,
and return the existing below-minimum error early so invalid binding input never
reaches implementation-specific panic paths.

Comment thread ext/crates/sseq/src/sseq.rs
Comment thread ext/src/chain_complex/mod.rs
Comment thread ext/src/secondary.rs
Comment thread ext/src/utils.rs
Comment thread ext/src/yoneda.rs Outdated
/// - the length of `class` must match the number of generators of `cc` in bidegree `b`.
///
/// (The internal Euler-characteristic / lift sanity checks remain as `assert!`s, as they witness
/// mathematical invariants that cannot be checked without replaying the computation.)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We should replace those asserts by anyhow::bail

hoodmane added 8 commits June 25, 2026 14:27
The op_idx bounds check on filtration_one_product was gated behind `if U`,
so stable resolutions (U == false) skipped it. This let an out-of-range
op_idx flow into FreeModule::operation_generator_to_index and FpVector::entry,
causing a panic for large op_idx or a silent read of a neighbouring
generator's coefficient for moderate op_idx.

Add try_filtration_one_product returning anyhow::Result<Vec<Vec<u32>>>, which
errors (rather than panicking or silently misreading) when the target
bidegree is uncomputed or op_idx is out of range. filtration_one_product is
now try_filtration_one_product(..).ok(), so it returns Some when the product
is defined and None otherwise.
beps_pn is now try_beps_pn(..).unwrap(), so existing callers are
unaffected while a fallible variant is available.
Use try_beps_pn / try_basis_element_to_index instead of the panicking variants, and compute the basis first for Adem, so nonexistent or out-of-profile names yield None instead of aborting the process.
…icking)

Additive default trait methods that guard indices/degrees and delegate, leaving existing methods unchanged. try_act/try_act_on_basis return a typed ActError whose IndexOutOfRange and InvalidInput variants let callers (the Python bindings) map them to distinct exception types (IndexError vs ValueError).
parse_module_name previously panicked on a non-integer shift field; it now surfaces that via anyhow. load_module_json returns a typed LoadModuleError whose NotFound and Read variants let callers (the Python bindings) distinguish a missing module file (FileNotFoundError) from a read/parse failure (RuntimeError) without matching on error strings.
The cofiber-handling path in construct_standard panicked on
malformed/unsupported specs:
- assert!(!U, ...) aborted for unstable (U=true) cofiber specs
- .unwrap() on cofiber["s"/"t"/"idx"] aborted on missing/non-integer fields
- .expect(...) on module.max_degree() aborted for unbounded modules

Convert these to anyhow errors (bail/context + ?) so the public
construct/construct_standard/construct_nassau entry points return
Err(...) for bad specs instead of panicking. Success behaviour is
unchanged.
try_ variants added, each holding the actual logic with the panicking
function reduced to `try_x(..).unwrap()`:
- yoneda::try_yoneda_representative_element -> anyhow::Result<Yoneda<CC>>:
  validates bidegree is computed and class length matches generator count.
  yoneda_representative_element is now try_yoneda_representative_element(..)
  .unwrap(). (Its internal Euler-characteristic / lift asserts remain, as
  they witness invariants that cannot be checked without replaying the
  computation.)
- Sseq::try_write_to_graph -> Result<Result<(), T::Error>, String>:
  validates the min y-coordinate == 0 precondition (sseq crate has no anyhow
  dependency, so a String error is used). write_to_graph is now
  try_write_to_graph(..).unwrap().

Left as residue (must stay catch_unwind in bindings):
- secondary::{extend_all, compute_*}: the panics (quasi-inverse assert and
  "Failed to lift" check) are mathematical failures only detectable while
  replaying the lift computation; no clean up-front guard exists.
- Sseq::update / Sseq::leibniz: panics arise from internal indexing into
  self-consistent data structures and are entangled with internal control
  flow; no cheaply-checkable external precondition.
compute_homotopy_step is now try_compute_homotopy_step(b).unwrap(). The two
invalid-input asserts (quasi-inverse application and the lift check) became
anyhow errors via anyhow::ensure!. The top-level extend_* drivers stay
panicking because they run through the parallel iter_s_t, which would need a
fallible parallel-iteration refactor.
@hoodmane
hoodmane merged commit bcfc804 into SpectralSequences:master Jun 25, 2026
19 of 20 checks passed
@hoodmane
hoodmane deleted the try branch June 25, 2026 21:56
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