DtmRipsSimplexStream

org.appliedtopology.tda4j.streams.DtmRipsSimplexStream
See theDtmRipsSimplexStream companion object
class DtmRipsSimplexStream(val reified: DtmMetricSpace, keepCriterion: PartialFunction[Simplex[Int], Boolean] = ..., maxFiltrationValue: Option[Double] = ..., parallelFiltrationValue: Boolean = ...) extends RipserCofaceSimplexStream

The DTM-based (weighted Rips) filtration of Anai, Chazal, Glisse, Ike, Lecci, Rouvreau, Saulnier & Wasserman, "DTM-based filtrations" (arXiv:1811.04757, Prop. 3.5), checked byte-for-byte against GUDHI's own gudhi.dtm_rips_complex.DTMRipsComplex/gudhi.weighted_rips_complex.WeightedRipsComplex for p = 1 (DtmRipsStreamSpec; see .claude/WORKLOG-dtm-filtrations.md for the fetched source and every oracle value). Values are in the same "doubled"/diameter units every other VR stream in this codebase uses (GUDHI's own choice too, for the identical reason: consistency with plain, unweighted Rips, whose edge filtration is the raw pairwise distance, not half of it).

f(x) is the empirical distance-to-measure per ambient point (streams.DistanceToMeasure); at f = 0 everywhere (in particular at k = 1, DistanceToMeasure's own degenerate case) this reduces EXACTLY to plain Vietoris-Rips, threshold included -- DtmRipsStreamSpec checks this too.

p selects the ball-radius exponent of Def. 3.1 -- NOT DistanceToMeasure's own exponent q, a different knob entirely. Only p = 1 and p = 2 are implemented:

  • p = 1: t(f_x, f_y, d) = max(f_x, f_y, (d + f_x + f_y) / 2). The only variant GUDHI's own Python bindings implement, and the one every published worked example (including this class's own regression oracle) targets.
  • p = 2: closed form t(f_x, f_y, d) = max(f_x, f_y, sqrt(u^2 + f_x^2)) where u = (d^2 + f_y^2 - f_x^2) / (2d), valid (and symmetric in x/y -- verified algebraically, not just numerically) whenever |f_y^2 - f_x^2| = 1 -- the inherited default "max pairwise distance" filtration functional is already exactly the flag-complex extension this construction wants (same relationship WitnessMetricSpace has to LazyWitnessSimplexStream). The only override needed is at dimension 0: unlike plain VR (where every vertex is born at filtration 0, so the base class's hardcoded dim 0.0 and its unsorted, unfiltered case 0 vertex emission are both harmless -- see .claude/WORKLOG-dtm-filtrations.md for why), THIS is the first coface stream in this codebase whose vertices have distinct, nonzero filtration values, so case 0 MUST be sorted by filtrationOrdering.reverse and filtered by threshold like every other dimension (ordering contract rule 2 in CLAUDE.md) -- silently violating that would corrupt Chain's pivot table exactly like the historical "no tie-break" bugs did, and would let vertices past maxFiltrationValue leak into the complex as spurious isolated components.

maxFiltrationValue defaults (via None) to the reified space's own minimumEnclosingRadius, the same convention every other flag-complex VR stream in this codebase uses (not GUDHI's own max_filtration = +Infinity default) -- valid here for a real, checked reason, not just by analogy: t(f_x, f_y, d) >= max(f_x, f_y) by construction for both p = 1 and p = 2 (immediate for p=1's outer max; for p=2, t^2 = u^2 + f_x^2 >= f_x^2 and the symmetric v = d - u form gives t^2 = v^2 + f_y^2 >= f_y^2 too), so taking x* = argmin_x max_y distance(x,y) and R = distance(x*,ยท)'s own max: for every vertex z, 2*f(z) = 2*f_z) -- every vertex's own birth is <= R, so truncating there cannot silently drop a vertex, and beyond R the complex is a cone from x* exactly as in the unweighted case. Refuses engine=ripser in matlab.TDA4j's dispatch (both Ripser engines assume vertex births at 0 and a diameter-only incremental formula); naive/chunks/cohomology all consume this like any other CofaceSimplexStream[Int, Double].

Attributes

Companion
object
Experimental
true
Graph
Supertypes
trait CofaceSimplexStream[Int, Double]
trait StratifiedSimplexStream[Int, Double]
trait StratifiedCellStream[Simplex[Int], Double]
trait CellStream[Simplex[Int], Double]
trait IterableOnce[Simplex[Int]]
trait Filtration[Simplex[Int], Double]
trait Filterable[Double]
class Object
trait Matchable
class Any
Show all

Members list

Value members

Concrete methods

def f: IndexedSeq[Double]
override def iterateDimension: PartialFunction[Int, Iterator[Simplex[Int]]]

Contract .iterator below relies on: the domain must be contiguous starting at 0 -- defined for 0, 1, ..., k for some k (or empty, or all of the non-negative integers), never with a gap. .iterator stops at the first dimension this is undefined for, so a non-contiguous domain (defined at d but not at d - 1) would silently truncate iteration instead of skipping the gap. Every implementation in this codebase already satisfies this (a simplicial complex can't have a d-simplex without its (d-1)-dimensional faces, so "no cells at d" implies "no cells at any dimension beyond d" too); a new implementation must preserve it.

Contract .iterator below relies on: the domain must be contiguous starting at 0 -- defined for 0, 1, ..., k for some k (or empty, or all of the non-negative integers), never with a gap. .iterator stops at the first dimension this is undefined for, so a non-contiguous domain (defined at d but not at d - 1) would silently truncate iteration instead of skipping the gap. Every implementation in this codebase already satisfies this (a simplicial complex can't have a d-simplex without its (d-1)-dimensional faces, so "no cells at d" implies "no cells at any dimension beyond d" too); a new implementation must preserve it.

Attributes

Definition Classes
def p: Double

Inherited methods

override def iterator: Iterator[Simplex[Int]]

Dimension-major: all of dimension d before any of dimension d + 1.

Dimension-major: all of dimension d before any of dimension d + 1.

MUST NOT be implemented as Iterator.from(0).filter(iterateDimension.isDefinedAt)....fold(...) (a real, confirmed bug this replaced -- see .claude/WORKLOG-cohomology.md): Iterator.filter on an infinite source can never prove "no more matches ahead", so once past the last dimension iterateDimension is defined for, it spins forever searching for a d that will never come -- and Int silently wrapping from Int.MaxValue to Int.MinValue after ~2^31 iterations can eventually feed a huge negative d straight to iterateDimension instead, surfacing as a BinomialCoefficient range exception rather than a hang. .takeWhile instead stops at the first d this is undefined for and never asks about any d beyond it, relying on exactly the contiguous-domain contract documented on iterateDimension above.

Attributes

Definition Classes
StratifiedCellStream -> IterableOnce
Inherited from:
StratifiedCellStream
def knownSize: Int

The number of elements in this collection, if it can be cheaply computed, -1 otherwise. Cheaply usually means: Not requiring a collection traversal.

The number of elements in this collection, if it can be cheaply computed, -1 otherwise. Cheaply usually means: Not requiring a collection traversal.

Attributes

Inherited from:
IterableOnce
def stepper[S <: Stepper[_]](implicit shape: StepperShape[Simplex[Int], S]): S

Returns a scala.collection.Stepper for the elements of this collection.

Returns a scala.collection.Stepper for the elements of this collection.

The Stepper enables creating a Java stream to operate on the collection, see scala.jdk.StreamConverters. For collections holding primitive values, the Stepper can be used as an iterator which doesn't box the elements.

The implicit scala.collection.StepperShape parameter defines the resulting Stepper type according to the element type of this collection.

  • For collections of Int, Short, Byte or Char, an scala.collection.IntStepper is returned
  • For collections of Double or Float, a scala.collection.DoubleStepper is returned
  • For collections of Long a scala.collection.LongStepper is returned
  • For any other element type, an scala.collection.AnyStepper is returned

Note that this method is overridden in subclasses and the return type is refined to S with EfficientSplit, for example scala.collection.IndexedSeqOps.stepper. For Steppers marked with scala.collection.Stepper.EfficientSplit, the converters in scala.jdk.StreamConverters allow creating parallel streams, whereas bare Steppers can be converted only to sequential streams.

Type parameters

S

the type of the returned Stepper, determined by the implicit StepperShape

Attributes

Inherited from:
IterableOnce

Concrete fields

Inherited fields

var currentDimension: Int

Attributes

Inherited from:
EnumeratingCofaceSimplexStream
var currentDimensionCache: Queue[Simplex[Int]]

Attributes

Inherited from:
EnumeratingCofaceSimplexStream
lazy val edges: Iterable[Simplex[Int]]

Attributes

Inherited from:
EnumeratingCofaceSimplexStream
override val filtrationOrdering: Ordering[Simplex[Int]]

Filtration value, reversed (so smaller-under-this-ordering means YOUNGER, matching SimplexStream's own established convention), then dimension, then COLEXICOGRAPHIC order on the vertex set (via simplexIndexing's own combinatorial-number-system index) -- the "lexicographically refined" tie-break Ripser's own apparent-pairs machinery (Definition 3.2/Proposition 3.9, see RipserCohomologyContext) is defined in terms of, so using it here keeps this stream's ordering consistent with every other Ripser-flavored piece of this codebase, not just internally self-consistent -- deliberately not the plain lexicographic tie-break FilteredSimplexOrdering uses.

Filtration value, reversed (so smaller-under-this-ordering means YOUNGER, matching SimplexStream's own established convention), then dimension, then COLEXICOGRAPHIC order on the vertex set (via simplexIndexing's own combinatorial-number-system index) -- the "lexicographically refined" tie-break Ripser's own apparent-pairs machinery (Definition 3.2/Proposition 3.9, see RipserCohomologyContext) is defined in terms of, so using it here keeps this stream's ordering consistent with every other Ripser-flavored piece of this codebase, not just internally self-consistent -- deliberately not the plain lexicographic tie-break FilteredSimplexOrdering uses.

Fixes a real, previously-confirmed bug (.claude/WORKLOG-cohomology.md): a bare Ordering.by(filtrationValue) has no tie-break at all, so two DIFFERENT simplices tied at the same filtration value compare as equal -- not a total order. This happens by construction on any Vietoris-Rips complex with a triangle, since a triangle's filtration value always equals that of its own longest edge; CellularHomologyContext bakes a stream's filtrationOrdering into Chain.reduceBy's SortedMap, so two cells that compare equal collide as a single map key and the reduction silently garbles pairings for that complex.

iterateDimension sorts each dimension's bucket by filtrationOrdering.reverse -- deliberately .reverse on this SAME Ordering object, not an independently-built "oldest first" comparator: two individually-valid orderings that disagree on tie-break direction let a coface sort before its own tied facet, corrupting Chain.reduceBy's pivot table the same way the no-tie-break bug did. A stream's iteration order and its filtrationOrdering (pivot order) must be THE SAME total order, one the consistent reverse of the other.

Attributes

Inherited from:
EnumeratingCofaceSimplexStream
override val filtrationValue: PartialFunction[Simplex[Int], Double]

Attributes

Inherited from:
EnumeratingCofaceSimplexStream
val largest: Double

Attributes

Inherited from:
DoubleFiltration
var lastDimensionCache: IndexedSeq[Simplex[Int]]

Attributes

Inherited from:
EnumeratingCofaceSimplexStream

Attributes

Inherited from:
EnumeratingCofaceSimplexStream

Attributes

Inherited from:
EnumeratingCofaceSimplexStream
val smallest: Double

Attributes

Inherited from:
DoubleFiltration