Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
169 commits
Select commit Hold shift + click to select a range
093640b
Add `ext_py` subproject
JoeyBF Jul 28, 2025
5e27ef6
Update flake to use uv for frontend
JoeyBF Jul 31, 2025
b7fad78
Set up for test-driven development, first pass
JoeyBF Jul 31, 2025
15598bd
Format ext_py examples
JoeyBF Aug 1, 2025
24dd20e
Progress!
JoeyBF Aug 1, 2025
4530bb5
Activate logging in ext_py
JoeyBF Aug 1, 2025
c333d39
Fix API proposal
JoeyBF Aug 14, 2025
aca7ff3
Refine proposal
JoeyBF Jun 23, 2026
095273d
Update pyo3
hoodmane Jun 23, 2026
7bf2d4b
Bind ValidPrime in ext_py
hoodmane Jun 23, 2026
e23b2eb
Use int boundary for primes in ext_py
hoodmane Jun 23, 2026
042468d
Harden prime boundary checks
hoodmane Jun 23, 2026
71e093b
Bind fp field types in ext_py
hoodmane Jun 23, 2026
67cd771
Tighten fp field bindings
hoodmane Jun 23, 2026
ee5d4d6
Bind FpVector in ext_py
hoodmane Jun 23, 2026
7f13903
Bind FpVector slices in ext_py
hoodmane Jun 23, 2026
ef795f8
Tighten FpVector slice bindings
hoodmane Jun 23, 2026
f551bf6
Bind Matrix in ext_py
hoodmane Jun 23, 2026
2f66a93
Unify FpSlice over vectors and matrix rows
hoodmane Jun 23, 2026
b23e3b1
Revalidate slice length and harden slice tests
hoodmane Jun 23, 2026
13938ca
Restore idiomatic PyO3 in slice bindings
hoodmane Jun 23, 2026
22118ed
Bind Subspace in ext_py
hoodmane Jun 23, 2026
2c5d9f9
Regenerate pivots in Subspace.sum binding
hoodmane Jun 23, 2026
1b30360
Fix Subspace binding low-severity issues
hoodmane Jun 23, 2026
30fd7c4
Simplify Subspace.sum to thin wrapper
hoodmane Jun 23, 2026
236638f
Forbid Matrix::trim keep_pivots with nonzero col_start
hoodmane Jun 23, 2026
e335cb1
Bind QuasiInverse in ext_py
hoodmane Jun 23, 2026
12a1e2d
Harden QuasiInverse binding
hoodmane Jun 23, 2026
0ea664d
Bind Subquotient in ext_py
hoodmane Jun 23, 2026
7589682
Expand Subquotient tests
hoodmane Jun 23, 2026
b87803b
Bind AffineSubspace in ext_py
hoodmane Jun 23, 2026
b7cbc82
Fix AffineSubspace test comments and coverage
hoodmane Jun 23, 2026
02f9f00
Bind AugmentedMatrix2/3 in ext_py
hoodmane Jun 23, 2026
503ca57
Guard compute_* against non-row-reduced matrices
hoodmane Jun 23, 2026
132cdcf
Bind MatrixSliceMut in ext_py
hoodmane Jun 23, 2026
9329c2b
Fix add_masked comment and expand MatrixSliceMut tests
hoodmane Jun 23, 2026
21d75b9
Bind MilnorAlgebra Algebra surface in ext_py
hoodmane Jun 23, 2026
0a6aeab
Guard MilnorAlgebra panic paths
hoodmane Jun 23, 2026
917c2c7
Bind AdemAlgebra Algebra surface in ext_py
hoodmane Jun 23, 2026
37c0032
Bind SteenrodAlgebra union in ext_py
hoodmane Jun 23, 2026
8f6d6b1
Guard Milnor generator decompose and fix json taxonomy
hoodmane Jun 23, 2026
bd217db
Bind SteenrodModule, FDModule, FreeModule in ext_py
hoodmane Jun 23, 2026
3331582
Guard FreeModule/FDModule panic paths
hoodmane Jun 24, 2026
3ff8963
Bind Tensor/Suspension/Zero/RP modules in ext_py
hoodmane Jun 24, 2026
fd80c4e
Guard TensorModule.offset empty block
hoodmane Jun 24, 2026
5d6ed61
Bind QuotientModule and HomModule in ext_py
hoodmane Jun 24, 2026
3ae9be2
Pre-extend algebra in QuotientModule::new; guard HomModule overflow
hoodmane Jun 24, 2026
a9deca0
Bind FPModule, BlockStructure, GeneratorBasisEltPair in ext_py
hoodmane Jun 24, 2026
5ff4850
Split FPModule into builder + immutable module
hoodmane Jun 24, 2026
a304243
Add Consumable<T> and borrow input slices instead of cloning
hoodmane Jun 24, 2026
7dbf052
FpSliceMut add/assign no-clone fast path
hoodmane Jun 24, 2026
f9d4192
Consume augmented matrix in into_matrix/compute_quasi_inverses
hoodmane Jun 24, 2026
46196b0
Arc-share FDModule
hoodmane Jun 24, 2026
8b9bfe4
Raise RuntimeError on input/target aliasing; expand clone-removal tests
hoodmane Jun 24, 2026
26cfa0b
Bind FreeModuleHomomorphism in ext_py
hoodmane Jun 24, 2026
80a7a57
Expand FreeModuleHomomorphism tests
hoodmane Jun 24, 2026
8b36fa4
Rename FDModule to FDModuleBuilder with build()
hoodmane Jun 24, 2026
25ff64a
Fix example refs after FDModuleBuilder rename; add example smoke test
hoodmane Jun 24, 2026
fb3c947
Bind FullModuleHomomorphism in ext_py
hoodmane Jun 24, 2026
01be8c7
Guard FullModuleHomomorphism.from_matrices min_degree; expand tests
hoodmane Jun 24, 2026
3ec73e6
Add examples as acceptance gate
JoeyBF Jun 24, 2026
b337628
Bind quotient and generic-zero homomorphisms in ext_py
hoodmane Jun 24, 2026
38e0f28
Guard quotient/zero get_partial_matrix against out-of-range degrees
hoodmane Jun 24, 2026
6accca1
Bind free-to-free FreeModuleHomomorphism in ext_py
hoodmane Jun 24, 2026
de4b539
Bind HomPullback in ext_py
hoodmane Jun 24, 2026
15370e5
Dispatch Nassau vs general resolution at runtime in ext_py
hoodmane Jun 24, 2026
60cfda5
Guard compute_through_stem; reject Nassau-backed secondary; add resol…
hoodmane Jun 24, 2026
4cf24f5
Guard hom_k against out-of-range degrees; test HomPullback shift
hoodmane Jun 24, 2026
9235a53
Fix Matrix.to_vec panic on zero-column matrices
hoodmane Jun 24, 2026
92feefb
Bind Steenrod evaluator/parser in ext_py
hoodmane Jun 24, 2026
0085dfd
Bind Field algebra and wire HomModule.algebra in ext_py
hoodmane Jun 24, 2026
4072b8f
Bind module_gens_from_json and combinatorics functions in ext_py
hoodmane Jun 24, 2026
fa9cd9c
Guard combinatorics/module_gens_from_json against oversized degrees
hoodmane Jun 24, 2026
29bbe1b
Bind sseq coordinates (Bidegree, BidegreeElement/Generator/Range, ite…
hoodmane Jun 24, 2026
c182092
Release GIL around iter_s_t; test callback error paths
hoodmane Jun 24, 2026
5b8d3ed
Update examples KNOWN_UNBOUND allowlist after rebase
hoodmane Jun 24, 2026
da45a96
Bind Sseq spectral sequence in ext_py
hoodmane Jun 24, 2026
044e26d
Guard Sseq.add_differential intermediate degrees; harden leibniz
hoodmane Jun 24, 2026
fcd6018
Bind sseq charting backends and write_to_graph in ext_py
hoodmane Jun 24, 2026
4adb582
Re-raise swallowed write_to_graph close error; tidy charting tests
hoodmane Jun 24, 2026
6495a75
Bind ChainComplex (CCC) in ext_py
hoodmane Jun 24, 2026
0581501
Validate ChainComplex.new inputs; guard has_computed_bidegree
hoodmane Jun 24, 2026
3467524
Bind Resolution FreeChainComplex method set in ext_py
hoodmane Jun 24, 2026
9075e16
Guard filtration_one_product(s) against uncomputed resolution and out…
hoodmane Jun 24, 2026
6786625
Bind ResolutionHomomorphism in ext_py
hoodmane Jun 24, 2026
dfbfd0b
Document get_map shared view; add ResolutionHomomorphism guard tests
hoodmane Jun 24, 2026
756d57d
Bind ChainHomotopy and FiniteAugmentedChainComplex in ext_py
hoodmane Jun 24, 2026
306276a
Add ChainHomotopy.extend_all reject test; strengthen shape test; fix …
hoodmane Jun 24, 2026
373ad9b
Bind SecondaryResolutionHomomorphism and SecondaryChainHomotopy in ex…
hoodmane Jun 24, 2026
b2547ea
Wrap secondary extend_all panic as ValueError; add lambda guard tests…
hoodmane Jun 25, 2026
9530983
Bind yoneda_representative_element in ext_py
hoodmane Jun 25, 2026
728501f
Add Python I/O util layer (query_module*, construct) and save_dir sup…
hoodmane Jun 25, 2026
e5c04b5
Fix save_dir IO error taxonomy; scan from-imports in test_examples
hoodmane Jun 25, 2026
3232f7f
Bind UnstableResolution + unstable construct/query in ext_py
hoodmane Jun 25, 2026
804f34b
Guard unstable construct against cofiber specs; strengthen unstable t…
hoodmane Jun 25, 2026
e2fe62d
Bind non-IO utils: unicode_num, LAMBDA_BIDEGREE, parse_module_name, l…
hoodmane Jun 25, 2026
1041c48
Guard get_unit against interactive non-unit path; harden parse_module…
hoodmane Jun 25, 2026
1570974
Bind UnstableResolutionHomomorphism + UnstableFreeModuleHomomorphism …
hoodmane Jun 25, 2026
851d763
Contrast unstable vs stable S^3 in tests; add act guard tests; gitign…
hoodmane Jun 25, 2026
b99e63a
Bind ExtAlgebra in ext_py
hoodmane Jun 25, 2026
ebb9b09
Strengthen ExtAlgebra product-guard tests
hoodmane Jun 25, 2026
86ccc44
Bind SecondaryExtAlgebra (d2 layer) in ext_py
hoodmane Jun 25, 2026
69ceb3f
Hold ExtAlgebra as Arc to share with SecondaryExtAlgebra; add d2 guar…
hoodmane Jun 25, 2026
ea21c90
Add Pyodide build
hoodmane Jun 15, 2026
f43e8a0
Bind BidegreeGenerator.__format__ in ext_py
hoodmane Jun 25, 2026
674be70
Extend ext_py algebra/module/resolution bindings
hoodmane Jun 25, 2026
bc8ba66
Update ext_py examples to current bindings
hoodmane Jun 25, 2026
2f64ec0
Add test that runs all ext_py Python examples
hoodmane Jun 25, 2026
0556309
Bind UnstableResolution.filtration_one_products
hoodmane Jun 25, 2026
31360a8
Pass a drawing handle to Sseq.write_to_graph header callback
hoodmane Jun 25, 2026
d2df264
Bind FDModuleBuilder.from_tensor_module and settable name
hoodmane Jun 25, 2026
09cd44b
Bind extend_step_raw on resolution homomorphisms
hoodmane Jun 25, 2026
9bf82a0
Bind hom_k on UnstableFreeModuleHomomorphism
hoodmane Jun 25, 2026
8fd5d52
Let ext.construct accept dict and (spec, algebra) tuple specs
hoodmane Jun 26, 2026
d72e232
Move ext.construct to ext.Resolution.construct
hoodmane Jun 26, 2026
ee6197f
Allow negative-t shift in ResolutionHomomorphism
hoodmane Jun 26, 2026
6932e25
Allow negative internal degree t in stable Resolution queries
hoodmane Jun 26, 2026
a0f1a46
Bind Matrix.compute_kernel/compute_image and Subspace.basis
hoodmane Jun 26, 2026
a60629a
Add resolution-type argument to query_module/query_module_only
hoodmane Jun 26, 2026
d776d73
Split query_module into query_resolution + query_n_s
hoodmane Jun 26, 2026
3d53f2e
Make standard-backend examples request the standard backend
hoodmane Jun 26, 2026
91770db
Bind Resolution.differential
hoodmane Jun 26, 2026
0c5e9c7
Bind SteenrodAlgebra.basis_element_from_index
hoodmane Jun 26, 2026
389b69a
Expose prime (and e3_page) as a property instead of a method
hoodmane Jun 26, 2026
19ffa6c
Bind SecondaryResolution prime, e3_page, homotopy accessors
hoodmane Jun 26, 2026
ee6c4f0
Call source()/target() as methods in examples
hoodmane Jun 26, 2026
0bfb74f
Bind ResolutionHomomorphism.extend_step
hoodmane Jun 26, 2026
d2f41da
Replace steenrod_module_from_json with SteenrodModule.from_spec
hoodmane Jun 26, 2026
11824dc
Pass page index to Sseq.page_data in secondary_product example
hoodmane Jun 26, 2026
9f4f550
Bind SecondaryResolutionHomomorphism.hom_k
hoodmane Jun 26, 2026
6731891
Bind AugmentedMatrix.row_mut
hoodmane Jun 26, 2026
96d5837
Complete secondary_massey example args; fix .shift() porting bug
hoodmane Jun 26, 2026
0e26bf6
Deduplicate PyO3 bindings via macros and helpers
hoodmane Jun 26, 2026
9fb57be
Deduplicate algebra pyclass bindings via algebra_pymethods! macro
hoodmane Jun 26, 2026
8806a22
Bind ChainHomotopy.initialize_homotopies
hoodmane Jun 26, 2026
48ac5d7
Bind SecondaryResolutionHomomorphism.product_nullhomotopy
hoodmane Jun 26, 2026
a97f0e8
Use a defined Massey product (h0, h1) in secondary_massey runner args
hoodmane Jun 26, 2026
2bd272f
Bind SecondaryChainHomotopy computation methods
hoodmane Jun 26, 2026
06b93ec
Bind SteenrodAlgebra.p_tilde
hoodmane Jun 26, 2026
f4a3d2c
Pass page index to Sseq.page_data in secondary_massey example
hoodmane Jun 26, 2026
821f642
Bind Matrix.iter_mut
hoodmane Jun 26, 2026
964d755
Accept FpVector operand in FpSliceMut.add
hoodmane Jun 26, 2026
17a3785
Bind FpVector.iter_nonzero
hoodmane Jun 26, 2026
1923ecc
Bind SecondaryResolutionHomomorphism.hom_k_with
hoodmane Jun 26, 2026
b17796a
Accept FpSliceMut operand in Subquotient.reduce_by_quotient
hoodmane Jun 26, 2026
a7f94d7
Bind FpVector.restrict
hoodmane Jun 26, 2026
01f66c9
Add FreeChainComplex::try_number_of_gens_in_bidegree
hoodmane Jun 26, 2026
9bb3ae1
Add GeneratedAlgebra::try_decompose_basis_element
hoodmane Jun 26, 2026
72802da
Add Bialgebra::try_coproduct
hoodmane Jun 26, 2026
def232e
Derive Clone for MilnorProfile
hoodmane Jun 26, 2026
acdd484
Add fp::field::DynFieldElement
hoodmane Jun 26, 2026
208c41f
Add non-panicking Matrix::try_compute_* variants
hoodmane Jun 26, 2026
5cecd1f
Add QuasiInverse::try_new validating constructor
hoodmane Jun 26, 2026
573f4e0
Fix Matrix::to_vec panic on zero-column matrices
hoodmane Jun 26, 2026
0c2c658
Add SmallFq::try_new and AffineSubspace::try_new
hoodmane Jun 26, 2026
d1c12e7
Add owning stem iterators to ChainComplex/FreeChainComplex
hoodmane Jun 26, 2026
84aaed9
Add non-panicking try_* range accessors to ext
hoodmane Jun 26, 2026
0c3689d
Bind Resolution.set_name with a wrapper-level name override
hoodmane Jun 26, 2026
94a0a1c
Bind Resolution.target
hoodmane Jun 26, 2026
fb78835
Complete lift_hom example args in runner
hoodmane Jun 26, 2026
b78790e
Share target N Arc via with_source in ext_m_n example
hoodmane Jun 26, 2026
3e97ea1
Add load_quasi_inverse option to UnstableResolution constructor
hoodmane Jun 26, 2026
a688c54
Bind DoubleChainComplex (port sq0.rs double module)
hoodmane Jun 26, 2026
d1adcc3
Bind Bidegree.zero
hoodmane Jun 26, 2026
8c27bd5
Support DoubleChainComplex target in ResolutionHomomorphism
hoodmane Jun 26, 2026
7a5c0ea
lint
hoodmane Jun 29, 2026
8f5b9e4
Make write_to_graph not require so many args and make them kwonly
hoodmane Jun 30, 2026
ca0e1d4
Fix warnings
hoodmane Jun 30, 2026
76f3eda
Make many things into getters
hoodmane Jul 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/ext.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,6 @@ jobs:
- name: Lint sseq_gui wasm
run: make -C web_ext/sseq_gui lint-wasm

- name: Test worker panic handling
run: make -C web_ext/sseq_gui test-wasm-js

- name: Build wasm
run: make -C web_ext/sseq_gui wasm

Expand Down
4 changes: 3 additions & 1 deletion ext/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }

zstd = { version = "0.13.3", optional = true }

[target.'cfg(unix)'.dependencies]
# Emscripten reports `target_family = "unix"` but lacks the signal APIs
# (`pthread_sigmask`, …) that `ctrlc`/`nix` require, so exclude wasm targets.
[target.'cfg(all(unix, not(target_arch = "wasm32")))'.dependencies]
ctrlc = { version = "3", features = ["termination"] }

[dev-dependencies]
Expand Down
8 changes: 4 additions & 4 deletions ext/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ test:

lint:
cargo fmt --all -- --check
cargo clippy --workspace --no-default-features --profile test
cargo clippy --workspace --all-targets --profile test
cargo check --workspace --no-default-features --profile test
cargo check --workspace --all-targets --all-features --profile test
cargo clippy --workspace --no-default-features --profile test --fix
cargo clippy --workspace --all-targets --profile test --fix
cargo check --workspace --no-default-features --profile test --fix
cargo check --workspace --all-targets --all-features --profile test --fix

docs:
# Prevent the cached crates.js from confusing the current run
Expand Down
30 changes: 29 additions & 1 deletion ext/crates/algebra/src/algebra/adem_algebra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc_hash::FxHashMap as HashMap;
#[cfg(doc)]
use crate::algebra::SteenrodAlgebra;
use crate::algebra::{
Algebra, Bialgebra, GeneratedAlgebra, UnstableAlgebra,
Algebra, Bialgebra, CoproductError, GeneratedAlgebra, UnstableAlgebra,
combinatorics::{self, MAX_XI_TAU},
};

Expand Down Expand Up @@ -1391,6 +1391,34 @@ impl Bialgebra for AdemAlgebra {
}
}

fn try_coproduct(
&self,
op_deg: i32,
op_idx: usize,
) -> Result<Vec<(i32, usize, i32, usize)>, CoproductError> {
// Guards the `assert_eq!(op_deg % q, 0)` (generic) and `assert_eq!(op_idx, 0)`
// (p = 2) preconditions below, plus the out-of-range basis lookup, so
// `coproduct` never panics.
if op_deg < 0 {
return Err(CoproductError::OutOfRange);
}
self.compute_basis(op_deg);
if op_idx >= self.dimension(op_deg) {
return Err(CoproductError::OutOfRange);
}
if self.generic {
if op_deg != 1 {
let q = self.prime() * 2 - 2;
if !(op_deg as u32).is_multiple_of(q) {
return Err(CoproductError::IndivisibleDegree { q, degree: op_deg });
}
}
} else if op_idx != 0 {
return Err(CoproductError::NonzeroIndex);
}
Ok(self.coproduct(op_deg, op_idx))
}

fn coproduct(&self, op_deg: i32, op_idx: usize) -> Vec<(i32, usize, i32, usize)> {
if self.generic {
if op_deg == 1 {
Expand Down
50 changes: 50 additions & 0 deletions ext/crates/algebra/src/algebra/algebra_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,29 @@ impl<A: UnstableAlgebra> MuAlgebra<true> for A {
}
}

/// Why [`GeneratedAlgebra::try_decompose_basis_element`] could not decompose a basis element.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DecomposeError {
/// `degree` is negative, or `idx` is not a valid basis index in that degree.
OutOfRange,
/// The basis element exists but has no decomposition: the degree-0 unit, or an
/// algebra generator.
Indecomposable,
}

impl std::fmt::Display for DecomposeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::OutOfRange => f.write_str("basis element index out of range"),
Self::Indecomposable => {
f.write_str("the unit and algebra generators are indecomposable")
}
}
}
}

impl std::error::Error for DecomposeError {}

/// An [`Algebra`] equipped with a distinguished presentation.
///
/// These data can be used to specify finite modules as the actions of the distinguished generators.
Expand Down Expand Up @@ -542,6 +565,33 @@ pub trait GeneratedAlgebra: Algebra {
idx: usize,
) -> Vec<(u32, (i32, usize), (i32, usize))>;

/// Non-panicking variant of [`Self::decompose_basis_element`], distinguishing the two
/// reasons it can fail: [`DecomposeError::OutOfRange`] when `degree`/`idx` do not name a
/// basis element, and [`DecomposeError::Indecomposable`] when they name the degree-0 unit
/// or an algebra generator. Computes the basis up to `degree` first.
///
/// These are exactly the inputs for which [`Self::decompose_basis_element`] is invalid and
/// would panic, so the delegation below never reaches the panicking path. An implementation
/// of [`Self::decompose_basis_element`] that can fail for other reasons should override both
/// that method and this one, keeping them consistent.
fn try_decompose_basis_element(
&self,
degree: i32,
idx: usize,
) -> Result<Vec<(u32, (i32, usize), (i32, usize))>, DecomposeError> {
if degree < 0 {
return Err(DecomposeError::OutOfRange);
}
self.compute_basis(degree);
if idx >= self.dimension(degree) {
return Err(DecomposeError::OutOfRange);
}
if degree == 0 || self.generators(degree).contains(&idx) {
return Err(DecomposeError::Indecomposable);
}
Ok(self.decompose_basis_element(degree, idx))
}

/// Returns relations that the algebra wants checked to ensure the consistency of module.
///
/// Relations are encoded as general multi-degree elements which are killed in the quotient:
Expand Down
61 changes: 61 additions & 0 deletions ext/crates/algebra/src/algebra/bialgebra_trait.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,42 @@
use crate::algebra::Algebra;

/// Why [`Bialgebra::try_coproduct`] could not compute a coproduct.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CoproductError {
/// `op_deg` is negative, or `op_idx` is not a valid basis index in that degree.
OutOfRange,
/// The Milnor coproduct is only implemented at the prime 2.
OddPrimeUnsupported,
/// The generic Adem coproduct is only defined when the degree is divisible by `q = 2p - 2`
/// (the degree-1 Bockstein aside).
IndivisibleDegree {
/// The modulus `q = 2p - 2` the degree must be divisible by.
q: u32,
/// The offending degree.
degree: i32,
},
/// The Adem coproduct at the prime 2 is only defined for index 0.
NonzeroIndex,
}

impl std::fmt::Display for CoproductError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::OutOfRange => f.write_str("basis element index out of range"),
Self::OddPrimeUnsupported => f.write_str("coproduct is only supported at p = 2"),
Self::IndivisibleDegree { q, degree } => {
write!(
f,
"coproduct expects a degree divisible by {q}, got {degree}"
)
}
Self::NonzeroIndex => f.write_str("at p = 2 the coproduct expects index 0"),
}
}
}

impl std::error::Error for CoproductError {}

/// An [`Algebra`] equipped with a coproduct operation that makes it into a
/// bialgebra.
#[enum_dispatch::enum_dispatch]
Expand All @@ -13,6 +50,30 @@ pub trait Bialgebra: Algebra {
/// `x` must have been returned by [`Bialgebra::decompose()`].
fn coproduct(&self, op_deg: i32, op_idx: usize) -> Vec<(i32, usize, i32, usize)>;

/// Non-panicking variant of [`Self::coproduct`]. Computes the basis up to `op_deg` first and
/// returns [`CoproductError::OutOfRange`] when `op_deg`/`op_idx` do not name a basis element.
///
/// This default only guards the out-of-range preconditions shared by every implementation, so
/// it is correct for [`Self::coproduct`] implementations that are total on valid basis
/// elements (such as [`crate::algebra::Field`]). Implementations whose `coproduct` is only
/// partially defined — e.g. [`crate::algebra::MilnorAlgebra`] (prime 2 only) and
/// [`crate::algebra::AdemAlgebra`] (degree/index restrictions) — override this method to guard
/// their extra preconditions, so the delegation below never reaches the panicking path.
fn try_coproduct(
&self,
op_deg: i32,
op_idx: usize,
) -> Result<Vec<(i32, usize, i32, usize)>, CoproductError> {
if op_deg < 0 {
return Err(CoproductError::OutOfRange);
}
self.compute_basis(op_deg);
if op_idx >= self.dimension(op_deg) {
return Err(CoproductError::OutOfRange);
}
Ok(self.coproduct(op_deg, op_idx))
}

/// Decomposes an element of the algebra into a product of elements, each of
/// which we can compute a coproduct on efficiently.
///
Expand Down
26 changes: 24 additions & 2 deletions ext/crates/algebra/src/algebra/milnor_algebra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ use once::OnceVec;
use rustc_hash::FxHashMap as HashMap;
use serde::{Deserialize, Serialize};

use crate::algebra::{Algebra, Bialgebra, GeneratedAlgebra, UnstableAlgebra, combinatorics};
use crate::algebra::{
Algebra, Bialgebra, CoproductError, GeneratedAlgebra, UnstableAlgebra, combinatorics,
};

fn q_part_default() -> u32 {
!0
}

#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MilnorProfile {
/// If `true`, unspecified p_part entries will be 0. Otherwise they will be infinity.
pub truncated: bool,
Expand Down Expand Up @@ -1722,6 +1724,26 @@ impl MilnorAlgebra {
}

impl Bialgebra for MilnorAlgebra {
fn try_coproduct(
&self,
op_deg: i32,
op_idx: usize,
) -> Result<Vec<(i32, usize, i32, usize)>, CoproductError> {
// Guards the `assert_eq!(self.prime(), 2, ...)` below and the out-of-range
// `basis_element_from_index` lookup, so `coproduct` never panics.
if self.prime() != 2 {
return Err(CoproductError::OddPrimeUnsupported);
}
if op_deg < 0 {
return Err(CoproductError::OutOfRange);
}
self.compute_basis(op_deg);
if op_idx >= self.dimension(op_deg) {
return Err(CoproductError::OutOfRange);
}
Ok(self.coproduct(op_deg, op_idx))
}

fn coproduct(&self, op_deg: i32, op_idx: usize) -> Vec<(i32, usize, i32, usize)> {
assert_eq!(self.prime(), 2, "Coproduct at odd primes not supported");
if op_deg == 0 {
Expand Down
4 changes: 2 additions & 2 deletions ext/crates/algebra/src/algebra/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ pub mod adem_algebra;
pub use adem_algebra::AdemAlgebra;

mod algebra_trait;
pub use algebra_trait::{Algebra, GeneratedAlgebra, MuAlgebra, UnstableAlgebra};
pub use algebra_trait::{Algebra, DecomposeError, GeneratedAlgebra, MuAlgebra, UnstableAlgebra};

mod bialgebra_trait;
pub use bialgebra_trait::Bialgebra;
pub use bialgebra_trait::{Bialgebra, CoproductError};

pub mod combinatorics;

Expand Down
5 changes: 4 additions & 1 deletion ext/crates/algebra/src/algebra/steenrod_algebra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use serde::Deserialize;
use serde_json::Value;

use crate::{
algebra::{AdemAlgebra, Algebra, Bialgebra, GeneratedAlgebra, MilnorAlgebra, UnstableAlgebra},
algebra::{
AdemAlgebra, Algebra, Bialgebra, CoproductError, DecomposeError, GeneratedAlgebra,
MilnorAlgebra, UnstableAlgebra,
},
pair_algebra::PairAlgebra,
};

Expand Down
4 changes: 2 additions & 2 deletions ext/crates/algebra/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@

pub mod module;
pub mod steenrod_evaluator;
pub(crate) mod steenrod_parser;
pub mod steenrod_parser;

mod algebra;

pub use crate::algebra::*;

pub(crate) fn module_gens_from_json(
pub fn module_gens_from_json(
gens: &serde_json::Value,
) -> (
bivec::BiVec<usize>,
Expand Down
Loading
Loading