org.appliedtopology.tda4j.homology

Members list

Type members

Experimental classlikes

class CellularCohomologyContext[CellT, CoefficientT, FiltrationT]

Persistent cohomology (Bauer's algorithm, arXiv:1908.02518) generic over CellT: OrderedCell -- the cohomology counterpart to CellularHomologyContext, filling in what CLAUDE.md's own architecture notes call a real, previously-unfilled asymmetry: cohomology in this codebase used to mean RipserCohomologyContext/ PackedRipserCohomologyContext only, both hardcoded to Simplex[Int] via SimplexIndexing's combinatorial number system. This class instead works for any CellT: OrderedCell this library has -- Simplex, Cube, FiniteSimplicialSet generators alike -- including complexes that already use Simplex[Int] but aren't flag complexes (Cech, Alpha), which the VR-specialized engines can't serve either way. See .claude/DESIGN-generic-cohomology.md for the full design derivation (including an advisor() review and a later correction dropping apparent pairs from the design entirely); this doc summarizes the load-bearing points, not the exploration.

Persistent cohomology (Bauer's algorithm, arXiv:1908.02518) generic over CellT: OrderedCell -- the cohomology counterpart to CellularHomologyContext, filling in what CLAUDE.md's own architecture notes call a real, previously-unfilled asymmetry: cohomology in this codebase used to mean RipserCohomologyContext/ PackedRipserCohomologyContext only, both hardcoded to Simplex[Int] via SimplexIndexing's combinatorial number system. This class instead works for any CellT: OrderedCell this library has -- Simplex, Cube, FiniteSimplicialSet generators alike -- including complexes that already use Simplex[Int] but aren't flag complexes (Cech, Alpha), which the VR-specialized engines can't serve either way. See .claude/DESIGN-generic-cohomology.md for the full design derivation (including an advisor() review and a later correction dropping apparent pairs from the design entirely); this doc summarizes the load-bearing points, not the exploration.

'''The key idea''': the coboundary matrix persistent cohomology reduces is the transpose of the ordinary boundary matrix, same coefficients -- if tau.boundary contains (sigma, c), sigma's coboundary contains (tau, c). Every stream this class targets (Cube, FiniteSimplicialSet, Cech, Alpha, and even ordinary Simplex[Int] VR complexes at a size where the reference/oracle engines matter more than raw speed) already gets fully materialized before persistence runs, unlike Vietoris-Rips at the scale RipserCohomologyContext targets -- so unlike that class's elaborate SimplexIndexing/insertionDiameter/sparseCofacets apparatus (built specifically to avoid ever materializing a combinatorially-exploding full flag complex), this class builds the coboundary relation directly, by inverting each materialized cell's own already-generic boundary[CoefficientT] call -- no cell-type-specific coboundary formula needed anywhere, and no dual Cocell/OrderedCocell typeclass either (removed from Chain.scala, on the same understanding: coboundary is extrinsic to a cell, not intrinsic the way boundary is, since it depends on which higher-dimensional cells actually exist in the ambient complex).

'''No maxDim parameter''', unlike every other engine in this codebase's history -- deliberately, not by oversight: this class simply computes cohomology up to whatever top dimension the materialized stream actually contains, which deletes the whole "does maxDim mean top built or top reported degree" footgun class (CellularPersistenceInChunksContext, RipserCohomologyContext, and PackedRipserCohomologyContext each had to fix this exact bug once -- see .claude/WORKLOG-maxdim-semantics-fix.md) rather than reimplementing it a fourth time. A caller wanting only H_0..H_k wraps the input stream first -- LimitedCofaceSimplexStream(stream, k + 1), the mechanism RipserCohomologySpec's own oracle and the MATLAB facade's engine=naive path already use for exactly this -- so real (k+1)-dimensional cells exist to correctly resolve whether a k-born class is finite or essential, and drops any dim == k + 1 bars from the returned list itself afterward.

'''No apparent pairs''', also deliberately: Definition 3.2/Proposition 3.9's whole point is avoiding coboundary enumeration for cells that turn out to be trivially paired -- and this class has no enumeration to avoid, because it must materialize the coboundary relation for every cell up front just to have "coboundary" exist at all. What would be left after porting the mutual-pair check (skip one basis write, skip one call into an already-cheap Chain.reduceBy miss) is noise, plausibly a net loss once the pair- detection scan itself is counted, and not worth the extra machinery. See the design doc's "What does NOT carry over" section for the full argument.

'''Representatives''': every bar carries a V-column (tracked exactly the way RipserCohomologyContext.persistentCohomology already does), satisfying this codebase's standing "every engine needs generic Field + real representatives" principle automatically -- this is also this class's actual point, not an afterthought: over a field the cohomology barcode is identical to the homology barcode (the reason Ripser computes cohomology at all -- same answer, cheaper algorithm), so a bars-only version of this class would be entirely redundant with CellularHomologyContext, which already covers every cell type this class does. Only an ''essential'' bar's V-column is a genuine cocycle (d(vcol) = 0) by construction -- Algorithm 1's invariant is d(V_j) = R_j throughout, and R_j is zero exactly when the bar is essential; a finite bar's V-column has coboundary equal to its own nonzero reduced pivot chain instead (still a valid representative -- it witnesses the class on the sub-level set strictly before the bar's death, since every term of that nonzero coboundary is born at or after the death value -- just not a cocycle over the whole complex). coboundaryOfChain exists specifically so a caller (in practice, a test) can verify this directly for essential bars (coboundaryOfChain(rep, ...).isZero()) -- something no engine in this codebase could check for Cube/FiniteSimplicialSet/Cech/Alpha before this class existed, since none of them ever had a cocycle representative to check in the first place.

Attributes

Experimental
true
Supertypes
class Object
trait Matchable
class Any
class CellularHomologyContext[CellT, CoefficientT, FiltrationT]

Naive persistent homology via the standard single-pivot-table reduction algorithm: process cells in filtration order, reduce each cell's boundary against the pivots recorded so far, and every cell either opens a class (reduced boundary is zero) or closes one (reduced boundary is nonzero, and its leading cell -- the pivot -- is necessarily a previously-opened, still-unpaired cell).

Naive persistent homology via the standard single-pivot-table reduction algorithm: process cells in filtration order, reduce each cell's boundary against the pivots recorded so far, and every cell either opens a class (reduced boundary is zero) or closes one (reduced boundary is nonzero, and its leading cell -- the pivot -- is necessarily a previously-opened, still-unpaired cell).

No clearing, no chunking, no cohomology/twist optimization: this is the reference-grade baseline the other algorithm in this file (PersistenceInChunksContext) can be cross-validated against.

Correctness note for future maintainers: the RingModule/Ordering[CellT] instances used for chain arithmetic MUST be summoned inside HomologyState, not at CellularHomologyContext class scope. A given Ordering[CellT] derived from a per-stream filtrationOrdering only exists once a stream is available (i.e. inside HomologyState); summoning Chain[CellT, CoefficientT] is RingModule any earlier silently falls back to the generic, filtration-blind OrderedCell-derived ordering and bakes it into that RingModule instance's closures permanently (Scala resolves a given's own implicit parameters once, at the point the given is constructed, not at each later call to its methods). A real, confirmed bug -- see .claude/WORKLOG-naive-homology.md.

Attributes

Experimental
true
Supertypes
class Object
trait Matchable
class Any
Known subtypes
class CubicalHomologyContext[CoefficientT, FiltrationT]
class SimplicialHomologyContext[VertexT, CoefficientT, FiltrationT]
class TDAContext[VertexT, CoefficientT, FiltrationT]
class CellularPersistenceInChunksContext[CellT, CoefficientT](maxDim: Int = ...)(using evidence$1: OrderedCell { type Self = CellT; }, evidence$2: Field { type Self = CoefficientT; })

maxDim means "top homological degree reported," not "top simplex dimension built" -- fixed at the source, the same fix and for the same reason as RipserCohomologyContext's own maxDimension (see .claude/WORKLOG-maxdim-semantics-fix.md). This is homology, not cohomology, so the mirror-image fact holds: correctly determining whether a class BORN at dimension maxDim is essential or killed requires considering real (maxDim + 1)-dimensional cells' own boundaries (a (maxDim+1)-simplex's boundary reduces to a dimension-maxDim pivot exactly when it kills that class) -- without them, every dimension-maxDim class was unconditionally essential, since no cell of the stream was ever considered that could possibly pair against it. Fixed by internally walking 0.to(maxDim + 1) (both in allCells's construction and both loops in advanceAll) instead of 0.to(maxDim), so (maxDim + 1)-cells DO get locally/globally reduced and CAN correctly kill a maxDim-born class -- and filtering diagramAt's essential-bar output back down to sigma.dim <= maxDim (finite bars need no equivalent filter: recordPair's barDim = pivot.dim, and a pivot is always one dimension below its killer, so barDim <= maxDim automatically whenever the killer's own dimension is <= maxDim + 1). (maxDim+1)-cells that themselves end up looking essential (nothing of dimension maxDim + 2 was ever considered to check) are deliberately left in essentialSimplices internally (later pairing logic in recordPair needs an accurate view across all live dimensions) and only excluded at this final reporting boundary, never a filter applied earlier.

maxDim means "top homological degree reported," not "top simplex dimension built" -- fixed at the source, the same fix and for the same reason as RipserCohomologyContext's own maxDimension (see .claude/WORKLOG-maxdim-semantics-fix.md). This is homology, not cohomology, so the mirror-image fact holds: correctly determining whether a class BORN at dimension maxDim is essential or killed requires considering real (maxDim + 1)-dimensional cells' own boundaries (a (maxDim+1)-simplex's boundary reduces to a dimension-maxDim pivot exactly when it kills that class) -- without them, every dimension-maxDim class was unconditionally essential, since no cell of the stream was ever considered that could possibly pair against it. Fixed by internally walking 0.to(maxDim + 1) (both in allCells's construction and both loops in advanceAll) instead of 0.to(maxDim), so (maxDim + 1)-cells DO get locally/globally reduced and CAN correctly kill a maxDim-born class -- and filtering diagramAt's essential-bar output back down to sigma.dim <= maxDim (finite bars need no equivalent filter: recordPair's barDim = pivot.dim, and a pivot is always one dimension below its killer, so barDim <= maxDim automatically whenever the killer's own dimension is <= maxDim + 1). (maxDim+1)-cells that themselves end up looking essential (nothing of dimension maxDim + 2 was ever considered to check) are deliberately left in essentialSimplices internally (later pairing logic in recordPair needs an accurate view across all live dimensions) and only excluded at this final reporting boundary, never a filter applied earlier.

Attributes

Experimental
true
Supertypes
class Object
trait Matchable
class Any
Known subtypes
class CubicalPersistenceInChunksContext[CoefficientT]
class PersistenceInChunksContext[VertexT, CoefficientT]

Circular coordinates (de Silva, Morozov, Vejdemo-Johansson, "Persistent Cohomology and Circular Coordinates," Discrete & Computational Geometry 45:737-759, 2011): given a persistent H¹ class of a Vietoris-Rips complex, produce a map from (a connected subset of) the point cloud to the circle R/Z representing that class -- a genuinely topological coordinate capturing periodic/cyclic structure in data. .claude/WORKLOG-mainstream- feature-gap-analysis.md item 2, including the user's own reframing of the original open question (see that worklog for the full derivation this implementation follows) and cross-checked against a real reference implementation (scikit-tda/DREiMac's toroidalcoords.py, fetched directly -- not recalled from memory, matching this codebase's own io-module verification ethos) for the exact harmonic-smoothing linear system and the "coordinate is literally the smoothed potential itself, mod 1" formula, which is less obvious from the paper's own more abstract framing than it looks once seen written out as code.

Circular coordinates (de Silva, Morozov, Vejdemo-Johansson, "Persistent Cohomology and Circular Coordinates," Discrete & Computational Geometry 45:737-759, 2011): given a persistent H¹ class of a Vietoris-Rips complex, produce a map from (a connected subset of) the point cloud to the circle R/Z representing that class -- a genuinely topological coordinate capturing periodic/cyclic structure in data. .claude/WORKLOG-mainstream- feature-gap-analysis.md item 2, including the user's own reframing of the original open question (see that worklog for the full derivation this implementation follows) and cross-checked against a real reference implementation (scikit-tda/DREiMac's toroidalcoords.py, fetched directly -- not recalled from memory, matching this codebase's own io-module verification ethos) for the exact harmonic-smoothing linear system and the "coordinate is literally the smoothed potential itself, mod 1" formula, which is less obvious from the paper's own more abstract framing than it looks once seen written out as code.

'''The reframing''' (this is what makes the construction tractable): rather than asking whether a finite H¹ bar's representative restricts to a nonzero cocycle on some sub-level complex K_r (an open question about an already-computed representative), fix r inside the target bar's own [birth, death) range up front, build the static truncated complex K_r (maxFiltrationValue = Some(r), the same knob that already implements enclosing-radius truncation, plus a cell-dimension cap so CellularCohomologyContext -- which fully materializes its input, no maxDim of its own -- doesn't build cells above what H¹ needs), and compute cohomology of that fixed complex directly. The target class is essential there by construction (nothing survives past r in a view that stops at r) -- the verification question dissolves rather than needing an answer. Matching multiple simultaneously-alive classes at K_r back to a specific full-filtration bar turns out to need only a birth-value comparison, not a more elaborate algorithm: K_r's own persistent cohomology (fed the same filtration values, just cut off at r) assigns every bar the SAME birth it would have in the full computation (truncating the end of a filtration cannot change how early something is born), so an essential bar at K_r with birth b is unambiguously "the same" class as a full-computation bar with that same birth b, found by direct comparison -- no separate matching machinery needed.

'''Harmonic smoothing''': the chosen cocycle z (an integer 1-cochain, lifted from a large-prime field representative -- see prime's own doc) is smoothed by solving min_g ||z - d0 g||^2 for a real-valued vertex function g (d0, the 0-coboundary map, is (d0 g)(edge [i,j]) = g(j) - g(i)), via the normal equations d0^T d0 g = d0^T z -- a sparse SPD least-squares solve, not "optimization" in the LP/QP sense. Solved matrix-free (org.apache.commons.math3.linear.ConjugateGradient against a RealLinearOperator built directly from Simplex.boundary[Double], no dense matrix ever materialized, no new dependency -- commons-math3 is already vendored) over the connected component of K_r's 1-skeleton containing the cocycle's own support (a class is only meaningful there -- other components have no path along which it could be defined at all), with one arbitrarily-chosen vertex in that component anchored at g = 0 to make the reduced system genuinely positive definite, not just semi-definite (the unreduced graph Laplacian is singular on constants, one dimension of null space per connected component -- anchoring one vertex removes exactly that one dimension, rather than disabling ConjugateGradient's own positive-definiteness check and hoping).

The output coordinate is then, remarkably directly, theta(v) = frac(g(v)): no separate path-integration step is needed (confirmed against DREiMac's own code, not derived from the paper's more abstract statement alone).

Attributes

Experimental
true
Supertypes
class Object
trait Matchable
class Any
Self type
class CubicalHomologyContext[CoefficientT, FiltrationT]()(using evidence$1: Field { type Self = CoefficientT; }, evidence$2: Ordering[FiltrationT]) extends CellularHomologyContext[Cube, CoefficientT, FiltrationT]

Thin wrapper mirroring SimplicialHomologyContext's own relationship to CellularHomologyContext -- Cube needed nothing new from the naive engine (it is already generic over CellT: OrderedCell), so this exists purely for the same ergonomic reason SimplicialHomologyContext does: a concrete, easily-discoverable name instead of writing out CellularHomologyContext[Cube, CoefficientT, FiltrationT] at every call site.

Thin wrapper mirroring SimplicialHomologyContext's own relationship to CellularHomologyContext -- Cube needed nothing new from the naive engine (it is already generic over CellT: OrderedCell), so this exists purely for the same ergonomic reason SimplicialHomologyContext does: a concrete, easily-discoverable name instead of writing out CellularHomologyContext[Cube, CoefficientT, FiltrationT] at every call site.

Attributes

Experimental
true
Supertypes
class CellularHomologyContext[Cube, CoefficientT, FiltrationT]
class Object
trait Matchable
class Any
class CubicalPersistenceInChunksContext[CoefficientT](maxDim: Int = ...)(using evidence$1: Field { type Self = CoefficientT; }) extends CellularPersistenceInChunksContext[Cube, CoefficientT]

Thin Cube-specific wrapper, exactly mirroring PersistenceInChunksContext above -- CellularPersistenceInChunksContext[Cube, ...] (including its own unionFindDim01 dimension-0/1 fast path) already has no Cube-specific behavior needed anywhere: Cube is OrderedCell (defaultCubeIsOrderedCell, CubicalOrderedCell.scala) resolves automatically, so this class is a pure ergonomic convenience, not new capability -- CellularPersistenceInChunksContext[Cube, ...] was already cross-validated against CubicalHomologyContext directly (CubicalStreamSpec's own tie-heavy-fixture and random-image cross-validation sections, including the union-find fast path specifically), the exact validation .claude/DESIGN-fast-cubical-engine.md's own Phase 1 called for, just not yet under this name. .claude/WORKLOG-fast-cubical-engine.md.

Thin Cube-specific wrapper, exactly mirroring PersistenceInChunksContext above -- CellularPersistenceInChunksContext[Cube, ...] (including its own unionFindDim01 dimension-0/1 fast path) already has no Cube-specific behavior needed anywhere: Cube is OrderedCell (defaultCubeIsOrderedCell, CubicalOrderedCell.scala) resolves automatically, so this class is a pure ergonomic convenience, not new capability -- CellularPersistenceInChunksContext[Cube, ...] was already cross-validated against CubicalHomologyContext directly (CubicalStreamSpec's own tie-heavy-fixture and random-image cross-validation sections, including the union-find fast path specifically), the exact validation .claude/DESIGN-fast-cubical-engine.md's own Phase 1 called for, just not yet under this name. .claude/WORKLOG-fast-cubical-engine.md.

Attributes

Experimental
true
Supertypes
class Object
trait Matchable
class Any
class FastAlphaHomologyContext[CoefficientT]

The homology.FastCubicalHomologyContext dual-graph union-find, ported to a HelixDelaunay alpha complex (.claude/DESIGN-alpha-dual-unionfind.md, item 7 of .claude/WORKLOG-mainstream-feature-gap-analysis.md, a follow-on to item 6's cubical engine). HelixDelaunay specifically, not AlphaComplexDQP/AlphaShapeDQP -- the dual graph needs the FULL, untruncated triangulation and "every facet has = 2''' (required), same asFastCubicalHomologyContext(which this class mirrors term-for-term):H_0(ordinary primal union-find) plusH_{d-1}(via the dual union-find below) together account for every cell dimension a 2D triangulation has, with no generalChain.reduceByreduction needed at all. Atd >= 3there ared-2"middle" dimensions (1 <= k <= d-2) with no duality shortcut; these are handed toCellularPersistenceInChunksContextrun on aLimitedAlphaShapesStreamview that hides the real top-dimensional simplices entirely -- still a net win, since the (often largest) top dimension never touches generalChainreduction. See.claude/DESIGN-fast-engines-hybrid-middle-dimensions.md` for the full derivation, including why the dual union-find's own correctness doesn't depend on how the middle dimensions get resolved.

The homology.FastCubicalHomologyContext dual-graph union-find, ported to a HelixDelaunay alpha complex (.claude/DESIGN-alpha-dual-unionfind.md, item 7 of .claude/WORKLOG-mainstream-feature-gap-analysis.md, a follow-on to item 6's cubical engine). HelixDelaunay specifically, not AlphaComplexDQP/AlphaShapeDQP -- the dual graph needs the FULL, untruncated triangulation and "every facet has = 2''' (required), same asFastCubicalHomologyContext(which this class mirrors term-for-term):H_0(ordinary primal union-find) plusH_{d-1}(via the dual union-find below) together account for every cell dimension a 2D triangulation has, with no generalChain.reduceByreduction needed at all. Atd >= 3there ared-2"middle" dimensions (1 <= k <= d-2) with no duality shortcut; these are handed toCellularPersistenceInChunksContextrun on aLimitedAlphaShapesStreamview that hides the real top-dimensional simplices entirely -- still a net win, since the (often largest) top dimension never touches generalChainreduction. See.claude/DESIGN-fast-engines-hybrid-middle-dimensions.md` for the full derivation, including why the dual union-find's own correctness doesn't depend on how the middle dimensions get resolved.

'''Unlike the cubical grid, "every facet has 1 or 2 cofaces" is not guaranteed by construction''' -- validated explicitly up front, throwing FastAlphaTriangulationException (a message written for an unsuspecting caller, not just this engine's own developers -- what happened, why it isn't a bug in their data, and the concrete fix) on violation, rather than silently building a wrong dual graph. Measured at roughly 1-in-18700 on random points at ambient dimension 2 (the original measurement) -- but this is a real, genuine HelixDelaunay limitation (a cospherical tiling choice or its own documented frontier-walk incompleteness bug), and it is NOTICEABLY MORE LIKELY at higher ambient dimension and with more points, not a flat rate: roughly 1-in-1666 measured at ambient dimension 3 with 20-30 points (vs. no violations at all in 20000 trials with 6-16 points at the same dimension). See .claude/DESIGN-fast-engines-hybrid-middle-dimensions.md's own measurement and the design note's "new finding" section.

'''A facet's own dual-edge value is helix.filtrationValue(facet) directly, never recomputed as min over its containing top simplices''' -- unlike a cubical grid (where those two quantities are the same by construction), HelixDelaunay.computeFVal's own edgeIsDelaunay shortcut can give a genuinely SMALLER value than either containing triangle's own circumradius; using anything else silently shifts some bars' birth values (see the design note's own worked example for a concrete case where this matters).

See FastCubicalHomologyContext's own doc for the shared parts of the construction (the dual graph itself, the ∞ sentinel and why it must be +Infinity, the birth/death swap, and the representative-tracking orientation-flip scheme) -- identical here, Simplex[Int]'s alternating-sign boundary rule (simplexIsOrderedCell) standing in for Cube's rank-among-non-degenerate-axes rule.

Attributes

Experimental
true
Supertypes
class Object
trait Matchable
class Any
class FastAlphaTriangulationException(message: String) extends RuntimeException

Thrown by FastAlphaHomologyContext when HelixDelaunay's own triangulation does not satisfy the "every facet has 1 or 2 containing top simplices" precondition this engine's dual graph needs (see the class doc's own "new finding" section) -- a real but rare (~1-in-18700 measured, ambient dimension 2) HelixDelaunay limitation, not a sign the input is malformed or that its persistent homology is somehow uncomputable. Deliberately a distinct, named, RuntimeException subtype -- not a bare IllegalStateException -- so a caller (MATLAB/CLI included, where it crosses the bridge the same way NoIntegerCocycleException already does) can catch and handle it specifically, and so its own message can afford to explain the situation in plain language rather than only in this engine's own internal vocabulary (top-cell ids, facet counts).

Thrown by FastAlphaHomologyContext when HelixDelaunay's own triangulation does not satisfy the "every facet has 1 or 2 containing top simplices" precondition this engine's dual graph needs (see the class doc's own "new finding" section) -- a real but rare (~1-in-18700 measured, ambient dimension 2) HelixDelaunay limitation, not a sign the input is malformed or that its persistent homology is somehow uncomputable. Deliberately a distinct, named, RuntimeException subtype -- not a bare IllegalStateException -- so a caller (MATLAB/CLI included, where it crosses the bridge the same way NoIntegerCocycleException already does) can catch and handle it specifically, and so its own message can afford to explain the situation in plain language rather than only in this engine's own internal vocabulary (top-cell ids, facet counts).

Attributes

Experimental
true
Supertypes
class RuntimeException
class Exception
class Throwable
trait Serializable
class Object
trait Matchable
class Any
Show all
class FastCubicalHomologyContext[CoefficientT]

Flash Cubical's dual-graph union-find (Le Breton, Szustakowski, Piraud, arXiv:2606.04801) for H_0 and the TOP homological degree (H_{d-1}, d = ambient dimension) of a CubicalGridStream, generic over Field coefficients and recording real representatives for every bar -- neither of which the source paper's own F2-only, barcode-only treatment provides; both are this codebase's own extension, derived independently (.claude/DESIGN-fast-cubical-engine.md's 2026-09-25 update has the full derivation and a hand-verified worked example -- this session could not reach the paper itself, network-blocked, and no reference implementation exists to port the way streams.EdgeCollapse could port GUDHI's; this is original work built on Alexander duality, not a translation).

Flash Cubical's dual-graph union-find (Le Breton, Szustakowski, Piraud, arXiv:2606.04801) for H_0 and the TOP homological degree (H_{d-1}, d = ambient dimension) of a CubicalGridStream, generic over Field coefficients and recording real representatives for every bar -- neither of which the source paper's own F2-only, barcode-only treatment provides; both are this codebase's own extension, derived independently (.claude/DESIGN-fast-cubical-engine.md's 2026-09-25 update has the full derivation and a hand-verified worked example -- this session could not reach the paper itself, network-blocked, and no reference implementation exists to port the way streams.EdgeCollapse could port GUDHI's; this is original work built on Alexander duality, not a translation).

'''Valid at any ambient dimension >= 2''' (required). At d=2, H_0 (ordinary primal union-find) plus H_1 (= H_{d-1} at d=2, via the dual union-find below) together account for every cell dimension a 2D grid has, with NO general Chain.reduceBy reduction needed at all. At d >= 3 there are d-2 "middle" dimensions (1 = s}, which requires the LARGEST possible value, not the smallest). Primal H_{d-1} of the sublevel filtration equals ordinary H_0 of this dual graph's own SUPERLEVEL filtration (Alexander duality, H_{d-1}(X) ~= H^0(S^d \ X)), computed by the same elder-rule array union-find CellularPersistenceInChunksContext.unionFindDim01 already uses, just processing dual vertices/edges together in DESCENDING order of their own primal value, with every resulting bar's endpoints SWAPPED (a dual merge at value v absorbing a younger dual component born at value b becomes a primal bar (birth = v, death = b)) and ∞'s own component producing no bar at all (it is always the elder/surviving side of every merge it takes part in, by construction, so it never "dies" -- nothing to explicitly filter out).

'''Representatives''': each active dual component tracks its own running signed sum of top cells (a Map[Cube, CoefficientT], cheap to merge -- just a map union with one side's signs flipped as needed), oriented COHERENTLY as unions happen so that shared internal facets cancel in the sum's own boundary; when a component dies (is absorbed into an older one across some facet f), its H_{d-1} representative is boundary(that running sum) -- the internal facets cancel by construction, leaving exactly the (d-1)-cycle bounding the dual component, per the design note's own derivation. The orientation flip needed when merging two components across f is solved directly from f's own boundary coefficients toward its two top cells (both always +-1, from cubeIsOrderedCell's alternating-sign rule) and each side's own already-established sign for its half of f.

Attributes

Experimental
true
Supertypes
class Object
trait Matchable
class Any
class NoIntegerCocycleException(message: String) extends RuntimeException

No valid Z-lift of the chosen cocycle exists for the chosen prime -- either the underlying cohomology class is genuinely torsion (no real/integer lift can exist at any prime -- an RP²-type class is the standard example), or prime was too small relative to the true integer cocycle's own magnitudes for the mod-prime reduction to be injective on the relevant range (retry with a larger prime). Thrown rather than silently coordinatizing against a mod-prime mirage -- see .claude/WORKLOG-mainstream-feature-gap-analysis.md item 2's own framing: "∂(ℤ-lift) = 0 must be a runtime check, not assumed."

No valid Z-lift of the chosen cocycle exists for the chosen prime -- either the underlying cohomology class is genuinely torsion (no real/integer lift can exist at any prime -- an RP²-type class is the standard example), or prime was too small relative to the true integer cocycle's own magnitudes for the mod-prime reduction to be injective on the relevant range (retry with a larger prime). Thrown rather than silently coordinatizing against a mod-prime mirage -- see .claude/WORKLOG-mainstream-feature-gap-analysis.md item 2's own framing: "∂(ℤ-lift) = 0 must be a runtime check, not assumed."

Attributes

Experimental
true
Supertypes
class RuntimeException
class Exception
class Throwable
trait Serializable
class Object
trait Matchable
class Any
Show all
class PackedRipserCohomologyContext[CoefficientT](metricSpace: FiniteMetricSpace[Int], maxDimension: Int, useApparentPairs: Boolean = ..., maxFiltrationValue: Option[Double] = ...)(using evidence$1: Field { type Self = CoefficientT; })

Attributes

Experimental
true
Supertypes
class Object
trait Matchable
class Any

Attributes

Companion
trait
Experimental
true
Supertypes
class Object
trait Matchable
class Any
Self type
trait PersistenceEngine[CellT, C]

A common shape for the three engines that consume an already-built StratifiedCellStream and are generic over CellT: OrderedCell -- CellularHomologyContext (naive), CellularPersistenceInChunksContext (chunks), and CellularCohomologyContext (cohomology). Each has its own incremental API beyond this (advanceTo/diagramAt for naive and chunks), which stays available on the concrete class -- this trait exists only to give callers that just want "the finished barcode" (the MATLAB/CLI facade) one shape to dispatch on, instead of hand-writing each engine's own construct/advance/read dance at every call site.

A common shape for the three engines that consume an already-built StratifiedCellStream and are generic over CellT: OrderedCell -- CellularHomologyContext (naive), CellularPersistenceInChunksContext (chunks), and CellularCohomologyContext (cohomology). Each has its own incremental API beyond this (advanceTo/diagramAt for naive and chunks), which stays available on the concrete class -- this trait exists only to give callers that just want "the finished barcode" (the MATLAB/CLI facade) one shape to dispatch on, instead of hand-writing each engine's own construct/advance/read dance at every call site.

PackedRipserCohomologyContext/RipserCohomologyContext deliberately do NOT implement this: they consume a FiniteMetricSpace[Int] directly (building their own internal sparse-Rips enumeration), not a stream, and are specialized to Simplex[Int]/DiameterIndex rather than generic over CellT -- an honest asymmetry, not a gap (see CLAUDE.md's persistence-engines section). Callers needing Ripser call it directly.

Deliberately does NOT own the "build one dimension higher than requested, then drop it" dance (WORKLOG-maxdim-semantics-fix.md's maxDim-means-top-reported-degree fix): that dance is about how the INPUT stream gets bounded, which is complex-type-specific (LimitedCofaceSimplexStream only wraps a CofaceSimplexStream[Int, Double], so it can truncate a Vietoris-Rips/Cech stream but not a cubical or simplicial-set one -- see CLAUDE.md's LimitedCofaceSimplexStream note), not engine-specific. Callers build the appropriately-bounded stream first (as they already did before this trait existed) and pass it in; this trait only unifies what happens AFTER that -- running the engine and reading back a finished barcode.

Attributes

Companion
object
Experimental
true
Supertypes
class Object
trait Matchable
class Any
class PersistenceInChunksContext[VertexT, CoefficientT](maxDim: Int = ...)(using evidence$1: Ordering[VertexT], evidence$2: Field { type Self = CoefficientT; }) extends CellularPersistenceInChunksContext[Simplex[VertexT], CoefficientT]

Thin Simplex-specific wrapper around CellularPersistenceInChunksContext, exactly mirroring SimplicialHomologyContext's relationship to CellularHomologyContext above -- every existing call site (PersistenceInChunksContext[Int, Double](...) etc.) keeps working unchanged, since the generic engine itself has no Simplex-specific behavior anywhere in its body: everything goes through the generic OrderedCell interface (.dim, .boundary[CoefficientT]), so genericizing was a pure type-annotation change, not a behavior change. Simplex[VertexT] is OrderedCell resolves automatically here from Ordering[VertexT] alone (defaultSimplexIsOrderedCell, SimplexOrderedCell.scala), same as SimplicialHomologyContext already relies on. See .claude/WORKLOG-simplicial-set-filtration.md.

Thin Simplex-specific wrapper around CellularPersistenceInChunksContext, exactly mirroring SimplicialHomologyContext's relationship to CellularHomologyContext above -- every existing call site (PersistenceInChunksContext[Int, Double](...) etc.) keeps working unchanged, since the generic engine itself has no Simplex-specific behavior anywhere in its body: everything goes through the generic OrderedCell interface (.dim, .boundary[CoefficientT]), so genericizing was a pure type-annotation change, not a behavior change. Simplex[VertexT] is OrderedCell resolves automatically here from Ordering[VertexT] alone (defaultSimplexIsOrderedCell, SimplexOrderedCell.scala), same as SimplicialHomologyContext already relies on. See .claude/WORKLOG-simplicial-set-filtration.md.

Attributes

Experimental
true
Supertypes
class CellularPersistenceInChunksContext[Simplex[VertexT], CoefficientT]
class Object
trait Matchable
class Any
class RipserCohomologyContext[CoefficientT](metricSpace: FiniteMetricSpace[Int], maxDimension: Int, useApparentPairs: Boolean = ..., maxFiltrationValue: Option[Double] = ..., memoizeFiltrationValue: Boolean = ...)(using evidence$1: Field { type Self = CoefficientT; })

maxDimension means "top HOMOLOGICAL DEGREE reported," not "top simplex dimension built" -- fixed at the source (previously only worked around at the MATLAB facade layer, which built requestedMaxDimension + 1 internally and filtered the extra dimension back out; see .claude/WORKLOG-maxdim-semantics-fix.md). Before this fix, coboundaryOf/zeroPivotCofacet refused to look past sigma.dim + 1 > maxDimension, i.e. sigma.dim == maxDimension always got a trivially-empty coboundary and therefore always came out essential -- a well-known truncation artifact (H_k needs (k+1)-chains to resolve correctly), not real information about H_maxDimension. Fixed by relaxing that guard to sigma.dim > maxDimension: a real (maxDimension + 1)-simplex is now enumerated on the fly, transiently, whenever needed to resolve a dimension-maxDimension pairing -- never materialized into its own currentLevel/reduced as its own column, so totalSimplexCount and the main loop's own bounds are unchanged; only the two guards moved. Any external caller previously passing maxDimension + 1 and filtering out dim == maxDimension + 1 bars itself should now pass the real requested degree directly and drop that workaround.

maxDimension means "top HOMOLOGICAL DEGREE reported," not "top simplex dimension built" -- fixed at the source (previously only worked around at the MATLAB facade layer, which built requestedMaxDimension + 1 internally and filtered the extra dimension back out; see .claude/WORKLOG-maxdim-semantics-fix.md). Before this fix, coboundaryOf/zeroPivotCofacet refused to look past sigma.dim + 1 > maxDimension, i.e. sigma.dim == maxDimension always got a trivially-empty coboundary and therefore always came out essential -- a well-known truncation artifact (H_k needs (k+1)-chains to resolve correctly), not real information about H_maxDimension. Fixed by relaxing that guard to sigma.dim > maxDimension: a real (maxDimension + 1)-simplex is now enumerated on the fly, transiently, whenever needed to resolve a dimension-maxDimension pairing -- never materialized into its own currentLevel/reduced as its own column, so totalSimplexCount and the main loop's own bounds are unchanged; only the two guards moved. Any external caller previously passing maxDimension + 1 and filtering out dim == maxDimension + 1 bars itself should now pass the real requested degree directly and drop that workaround.

Attributes

Experimental
true
Supertypes
class Object
trait Matchable
class Any
class SimplicialHomologyContext[VertexT, CoefficientT, FiltrationT]()(using evidence$1: Ordering[VertexT], evidence$2: Field { type Self = CoefficientT; }, evidence$3: Ordering[FiltrationT]) extends CellularHomologyContext[Simplex[VertexT], CoefficientT, FiltrationT]

Attributes

Experimental
true
Supertypes
class CellularHomologyContext[Simplex[VertexT], CoefficientT, FiltrationT]
class Object
trait Matchable
class Any
Known subtypes
class TDAContext[VertexT, CoefficientT, FiltrationT]