From d7dc23645daa3d2e8febf834f065e26ce812e77f Mon Sep 17 00:00:00 2001 From: Mikael Vejdemo-Johansson Date: Wed, 23 Oct 2024 16:46:00 +0200 Subject: [PATCH] Swapped out to making `Simplex` a type alias for `SortedSet` with topological methods added through self-type style type class implementations. --- .../appliedtopology/tda4j/AlphaShapes.scala | 4 +- .../tda4j/FiniteMetricSpace.scala | 6 +- .../org/appliedtopology/tda4j/Homology.scala | 11 +- .../appliedtopology/tda4j/RipserStream.scala | 38 +++---- .../org/appliedtopology/tda4j/Simplex.scala | 103 ++++++------------ .../appliedtopology/tda4j/SimplexStream.scala | 14 +-- .../appliedtopology/tda4j/SimplicialSet.scala | 4 +- .../appliedtopology/tda4j/SymmetryGroup.scala | 14 +-- .../org/appliedtopology/tda4j/UnionFind.scala | 23 ---- .../appliedtopology/tda4j/VietorisRips.scala | 25 ++--- .../appliedtopology/tda4j/SimplexSpec.scala | 6 +- .../tda4j/SimplexStreamSpec.scala | 10 +- 12 files changed, 99 insertions(+), 159 deletions(-) diff --git a/src/main/scala/org/appliedtopology/tda4j/AlphaShapes.scala b/src/main/scala/org/appliedtopology/tda4j/AlphaShapes.scala index c6bc74ec..8ca8ccd3 100644 --- a/src/main/scala/org/appliedtopology/tda4j/AlphaShapes.scala +++ b/src/main/scala/org/appliedtopology/tda4j/AlphaShapes.scala @@ -33,7 +33,7 @@ class AlphaShapes(val points: Array[Array[Double]]) !points.exists(metricSpace.pointSqDistance(mb.center(), _) < mb.squaredRadius()) def isDelaunaySimplex(spx: Simplex[Int]): Boolean = - isDelaunay(spx.vertices.toArray.map(points(_))) + isDelaunay(spx.toArray.map(points(_))) override def iterateDimension: PartialFunction[Int, Iterator[Simplex[Int]]] = { case d if d == cacheDimension => simplexCache.iterator @@ -52,7 +52,7 @@ class AlphaShapes(val points: Array[Array[Double]]) val newSimplexCache = for spx <- simplexCache i <- metricSpace.elements.takeWhile(_ < spx.vertices.min) - coface = spx.union(Simplex(i)) + i <- metricSpace.elements.takeWhile(_ < spx.min) if isDelaunaySimplex(coface) yield coface simplexCache = newSimplexCache.sortBy(filtrationValue) diff --git a/src/main/scala/org/appliedtopology/tda4j/FiniteMetricSpace.scala b/src/main/scala/org/appliedtopology/tda4j/FiniteMetricSpace.scala index ddd85f9a..f208d871 100644 --- a/src/main/scala/org/appliedtopology/tda4j/FiniteMetricSpace.scala +++ b/src/main/scala/org/appliedtopology/tda4j/FiniteMetricSpace.scala @@ -66,14 +66,14 @@ object FiniteMetricSpace { val metricSpace: FiniteMetricSpace[VertexT] ) extends PartialFunction[Simplex[VertexT], Double] { def isDefinedAt(spx: Simplex[VertexT]): Boolean = - spx.vertices.forall(v => metricSpace.contains(v)) + spx.forall(v => metricSpace.contains(v)) def apply(spx: Simplex[VertexT]): Double = if (spx.dim <= 0) 0.0 else - spx.vertices - .flatMap(v => spx.vertices.filter(_ > v).map(w => metricSpace.distance(v, w))) + spx + .flatMap(v => spx.filter(_ > v).map(w => metricSpace.distance(v, w))) .max } } diff --git a/src/main/scala/org/appliedtopology/tda4j/Homology.scala b/src/main/scala/org/appliedtopology/tda4j/Homology.scala index ad98b18e..5a186064 100644 --- a/src/main/scala/org/appliedtopology/tda4j/Homology.scala +++ b/src/main/scala/org/appliedtopology/tda4j/Homology.scala @@ -5,7 +5,10 @@ import org.appliedtopology.tda4j.barcode.PersistenceBar import collection.{immutable, mutable} import scala.annotation.tailrec -//class ReducedSimplicialHomologyContext[VertexT: Ordering, CoefficientT: Fractional, FiltrationT: Ordering]() +import math.Fractional.Implicits.infixFractionalOps +import math.Ordering.Implicits.sortedSetOrdering + +//class ReducedSimplicialHomologyContext[VertexT: Ordering, CoefficientT: Field, FiltrationT: Ordering]() // extends CellularHomologyContext[Simplex[VertexT], CoefficientT, FiltrationT]() {} class SimplicialHomologyContext[VertexT: Ordering, CoefficientT: Field, FiltrationT: Ordering]() @@ -164,11 +167,11 @@ class SimplicialHomologyByDimensionContext[VertexT: Ordering, CoefficientT: Fiel // secondly, we can read off homology completely from a minimal spanning tree val kruskal = new Kruskal[Simplex[VertexT]]( cycles.keys.toSeq, - { (x: Simplex[VertexT], y: Simplex[VertexT]) => stream.filtrationValue(Simplex.from(x.vertices ++ y.vertices)) } + { (x: Simplex[VertexT], y: Simplex[VertexT]) => stream.filtrationValue(x ++ y) } )(using stream.filtrationOrdering) kruskal.mstIterator.foreach { (src, tgt) => - val edge: Simplex[VertexT] = Simplex.from(src.vertices ++ tgt.vertices) + val edge: Simplex[VertexT] = src ++ tgt // the edge src -- tgt will connect src to tgt thus removing one of the cycles val dEdge = edge.boundary @@ -181,7 +184,7 @@ class SimplicialHomologyByDimensionContext[VertexT: Ordering, CoefficientT: Fiel } kruskal.cyclesIterator.foreach { (src, tgt) => - val edge: Simplex[VertexT] = Simplex.from(src.vertices ++ tgt.vertices) + val edge: Simplex[VertexT] = src ++ tgt // the edge src -- tgt will connect src to tgt thus closing a loop val dEdge = edge.boundary diff --git a/src/main/scala/org/appliedtopology/tda4j/RipserStream.scala b/src/main/scala/org/appliedtopology/tda4j/RipserStream.scala index c606bc31..971fa516 100644 --- a/src/main/scala/org/appliedtopology/tda4j/RipserStream.scala +++ b/src/main/scala/org/appliedtopology/tda4j/RipserStream.scala @@ -28,10 +28,10 @@ class SimplexIndexing(val vertexCount: Int) { } @tailrec - final def apply(n: Int, d: Int, upperAccum: Set[Int] = Set()): Simplex[Int] = { - if (d < 0) return Simplex.from(upperAccum) - if (n <= 0) return Simplex.from(upperAccum ++ (0 until d).toSet) - if (d == 0) return Simplex.from(upperAccum + n) + final def apply(n: Int, d: Int, upperAccum: Simplex[Int] = ∆()): Simplex[Int] = { + if (d < 0) return upperAccum + if (n <= 0) return upperAccum ++ (0 until d).toSet + if (d == 0) return upperAccum + n val searchResult: SearchResult = binomialTable(d).search(n) val id: Int = searchResult match { case Found(foundIndex) => foundIndex @@ -41,9 +41,9 @@ class SimplexIndexing(val vertexCount: Int) { } def cofacetIterator(simplex: Simplex[Int]): Iterator[Int] = - cofacetIterator(apply(simplex), simplex.vertices.size, true) + cofacetIterator(apply(simplex), simplex.size, true) def topCofacetIterator(simplex: Simplex[Int]): Iterator[Int] = - cofacetIterator(apply(simplex), simplex.vertices.size, false) + cofacetIterator(apply(simplex), simplex.size, false) def cofacetIterator( index: Int, size: Int, @@ -61,7 +61,7 @@ class SimplexIndexing(val vertexCount: Int) { ) { (s, iB, iA, k, j) => if (j < 0) { None // end iteration when we're done - } else if (s.vertices.contains(j)) { + } else if (s.contains(j)) { if (!allCofacets) None else @@ -79,7 +79,7 @@ class SimplexIndexing(val vertexCount: Int) { .map((os: Option[Int]) => os.get) def facetIterator(index: Int, size: Int): Iterator[Int] = - Iterator.unfold((apply(index, size).vertices.toSeq.sorted, index, 0, size - 1)) { + Iterator.unfold((apply(index, size).toSeq.sorted, index, 0, size - 1)) { (s: Seq[Int], iB: Int, iA: Int, k: Int) => if (k < 0) None else { @@ -91,8 +91,8 @@ class SimplexIndexing(val vertexCount: Int) { } def apply(simplex: Simplex[Int]): Int = - simplex.vertices.toSeq.sorted.reverse.zipWithIndex.map { (v, i) => - binomial(v, simplex.vertices.size - i) + simplex.toSeq.sorted.reverse.zipWithIndex.map { (v, i) => + binomial(v, simplex.size - i) }.sum } @@ -116,7 +116,7 @@ class RipserStreamSparse( val doubleSimplexPairOrdering: Ordering[(Double, Simplex[Int])] = { (x: (Double, Simplex[Int]), y: (Double, Simplex[Int])) => Ordering.Double.TotalOrdering.compare(x._1, y._1) match { - case 0 => simplexOrdering[Int].compare(x._2, y._2) + case 0 => summon[Ordering[Simplex[Int]]].compare(x._2, y._2) case c: Int => c } } @@ -183,11 +183,11 @@ class RipserStreamSparse( fV <- simplexCache.map(_._1).iterator previousSimplex <- simplexCache.filter(_._1 < fV).iterator.map(_._2) nextVertex <- metricSpace.elements - .filter(!previousSimplex.vertices.contains(_)) - .filter(nV => previousSimplex.vertices.map(oV => metricSpace.distance(oV, nV)).max <= fV) - simplex: Simplex[Int] = Simplex.from(previousSimplex.vertices + nextVertex) - if zeroApparentCofacet(si(simplex), simplex.vertices.size).isEmpty - if zeroApparentFacet(si(simplex), simplex.vertices.size).isEmpty + .filter(!previousSimplex.contains(_)) + .filter(nV => previousSimplex.map(oV => metricSpace.distance(oV, nV)).max <= fV) + simplex: Simplex[Int] = previousSimplex + nextVertex + if zeroApparentCofacet(si(simplex), simplex.size).isEmpty + if zeroApparentFacet(si(simplex), simplex.size).isEmpty // also check if this is cleared? yield simplex }.iterator @@ -296,11 +296,11 @@ class RipserStreamOf[VertexT: Ordering]( RipserStream(intMetricSpace, maxFiltrationValue, maxDimension) override def iterator: Iterator[Simplex[VertexT]] = - rs.iterator.map(s => Simplex.from(s.vertices.map(v => vertices(v)))) + rs.iterator.map(s => s.map(v => vertices(v))) override def filtrationValue: PartialFunction[Simplex[VertexT], Double] = { spx => - val indices: SortedSet[Int] = spx.vertices.map(vertices.indexOf) - rs.filtrationValue(Simplex.from(indices)) + val indices: SortedSet[Int] = spx.map(vertices.indexOf) + rs.filtrationValue(indices) } } diff --git a/src/main/scala/org/appliedtopology/tda4j/Simplex.scala b/src/main/scala/org/appliedtopology/tda4j/Simplex.scala index 82a9a285..19b43f70 100644 --- a/src/main/scala/org/appliedtopology/tda4j/Simplex.scala +++ b/src/main/scala/org/appliedtopology/tda4j/Simplex.scala @@ -1,81 +1,44 @@ package org.appliedtopology.tda4j -import scala.collection.{SortedIterableFactory, SortedSetFactoryDefaults, StrictOptimizedSortedSetOps, mutable} -import scala.collection.immutable.{Set, SortedMap, SortedSet, SortedSetOps, TreeSet} -import scala.math.Ordering.IntOrdering -import scala.math.Ordering.Double.IeeeOrdering +import scala.collection.mutable +import scala.collection.immutable.SortedSet import math.Ordering.Implicits.sortedSetOrdering -/** Class representing an abstract simplex. Abstract simplices are given by sets (of totally ordered vertices) - * and inherit from `Cell` so that the class has a `boundary` and a `dim` method. +/** Simplices really are just sets, outright. We provide an implementation of the [OrderedCell] typeclass + * for simplicial complex structures, to enable their use. * - * You should never have reason to use the constructor directly (...and if you do, you should make sure to give the - * internal `SortedSet` yourself) - instead use the factory method in the companion object. In code this means that - * instead of `new Simplex[Self](a,b,c)` you would write `Simplex[Self](a,b,c)`. - * - * @param vertices - * Vertices of the simplex - * @param ordering - * Ordering of the vertex type - * @tparam VertexT - * Vertex type */ -case class Simplex[VertexT : Ordering] private[tda4j] (vertices : SortedSet[VertexT]) { - override def toString(): String = - vertices.mkString(s"∆(", ",", ")") - - def union(other: Simplex[VertexT]) = - new Simplex(vertices.union(other.vertices)) - - def incl(x : VertexT) = - new Simplex(vertices + x) - - def contains(x : VertexT) = vertices.contains(x) -} -def simplexOrdering[VertexT : Ordering as vtxOrdering]: Ordering[Simplex[VertexT]] = - Ordering.by{(spx: Simplex[VertexT]) => spx.vertices}(sortedSetOrdering[SortedSet, VertexT](vtxOrdering)) +type Simplex[VertexT] = SortedSet[VertexT] -given [VertexT : Ordering] => Ordering[Simplex[VertexT]] = simplexOrdering +extension [VertexT : Ordering](spx : Simplex[VertexT]) + def show : String = spx.mkString(s"∆(", ",", ")") - // Ordering.by{(spx: Simplex[VertexT]) => spx.vertices}(sortedSetOrdering[SortedSet, VertexT](vtxOrdering)) - - -/** Simplex companion object with factory methods - */ -object Simplex { - def apply[VertexT: Ordering](vertices: VertexT*) = - new Simplex[VertexT](SortedSet.from(vertices)) - - def empty[VertexT: Ordering]: Simplex[VertexT] = - new Simplex[VertexT](SortedSet.empty) - - def from[VertexT: Ordering](source: IterableOnce[VertexT]): Simplex[VertexT] = - new Simplex(SortedSet.from(source.iterator)) - - def simplexIsOrderedCell[VertexT](using vtxOrd : Ordering[VertexT])(using ord : Ordering[Simplex[VertexT]]) : Simplex[VertexT] is OrderedCell = - new (Simplex[VertexT] is OrderedCell): - override lazy val ordering = ord - extension (t: Simplex[VertexT]) { - def boundary[CoefficientT](using fr: (CoefficientT is Field)): Chain[Simplex[VertexT], CoefficientT] = - if (t.dim <= 0) Chain()(using ord) - else Chain.from( - t.vertices - .to(Seq) - .zipWithIndex - .map((vtx, i) => Simplex.from(t.vertices.toSeq.patch(i, Seq.empty, 1))) - .zip(Iterator.unfold(fr.one)(s => Some((s, fr.negate(s))))) - )(using ord) - def dim: Int = t.vertices.size - 1 - } - def compare(x: Simplex[VertexT], y: Simplex[VertexT]): Int = ord.compare(x, y) - - given [VertexT : Ordering as vtxOrdering] => (Simplex[VertexT] is OrderedCell) = - simplexIsOrderedCell(using vtxOrdering)(using simplexOrdering[VertexT](using vtxOrdering)) -} +object Simplex: + def from[VertexT : Ordering, T <: Seq[VertexT]](vertices : T) : Simplex[VertexT] = SortedSet.from(vertices) + def apply[VertexT : Ordering](vertices : VertexT*) : Simplex[VertexT] = SortedSet.from(vertices) /** Convenience method for defining simplices - * - * The character ∆ is typed as Alt+J on Mac GB layout, and has unicode code 0x0394. - */ -def ∆[T: Ordering](ts: T*): Simplex[T] = Simplex.from(ts) + * + * The character ∆ is typed as Alt+J on Mac GB layout, and has unicode code 0x0394. + */ +def ∆[VertexT : Ordering](vertices : VertexT*) : Simplex[VertexT] = SortedSet.from(vertices) + +def simplexOrdering[VertexT](using vtxOrd : Ordering[VertexT]) : Ordering[Simplex[VertexT]] = sortedSetOrdering(vtxOrd) +def SortedSet_is_OrderedCell[VertexT](using vtxOrd : Ordering[VertexT])(setOrdering : Ordering[SortedSet[VertexT]] = simplexOrdering(using vtxOrd)): (SortedSet[VertexT] is OrderedCell) = + new(SortedSet[VertexT] is OrderedCell) { + override lazy val ordering = setOrdering + extension (spx: SortedSet[VertexT]) { + override def dim = spx.size - 1 + override def boundary[CoefficientT: Field as fr] = + if (spx.dim <= 0) Chain() + else Chain.from( + spx.to(Seq) + .zipWithIndex + .map((vtx, i) => spx -- spx.slice(i, i + 1)) + .zip(Iterator.unfold(fr.one)(s => Some((s, fr.negate(s))))) + ) + } + } +given default_SortedSet_is_OrderedCell[VertexT : Ordering] : (SortedSet[VertexT] is OrderedCell) = + SortedSet_is_OrderedCell[VertexT]() diff --git a/src/main/scala/org/appliedtopology/tda4j/SimplexStream.scala b/src/main/scala/org/appliedtopology/tda4j/SimplexStream.scala index a8705d38..c1c9668e 100644 --- a/src/main/scala/org/appliedtopology/tda4j/SimplexStream.scala +++ b/src/main/scala/org/appliedtopology/tda4j/SimplexStream.scala @@ -145,7 +145,7 @@ class ExplicitStreamBuilder[VertexT: Ordering, FiltrationT](using given filtrationOrdering: Ordering[(FiltrationT, Simplex[VertexT])] = Ordering .by[(FiltrationT, Simplex[VertexT]), FiltrationT]((f: FiltrationT, s: Simplex[VertexT]) => f)(ordering) - .orElseBy((f: FiltrationT, s: Simplex[VertexT]) => s)(simplexOrdering[VertexT]) + .orElseBy((f: FiltrationT, s: Simplex[VertexT]) => s)(simplexOrdering) simplices.sortInPlace new ExplicitStream(filtrationValues.toMap, simplices.map((_, s) => s).toSeq)(using filterable) @@ -169,21 +169,21 @@ class FilteredSimplexOrdering[VertexT, FiltrationT]( case (x, y) if filtration.filtrationValue.isDefinedAt(x) && filtration.filtrationValue.isDefinedAt(y) => filtrationOrdering.compare(filtration.filtrationValue(x), filtration.filtrationValue(y)) match { case 0 => - if (Ordering.Int.compare(x.vertices.size, y.vertices.size) == 0) + if (Ordering.Int.compare(x.size, y.size) == 0) Ordering.Implicits .sortedSetOrdering[SortedSet, VertexT](vertexOrdering) - .compare(x.vertices, y.vertices) + .compare(x, y) else - Ordering.Int.compare(x.vertices.size, y.vertices.size) + Ordering.Int.compare(x.size, y.size) case cmp if cmp != 0 => cmp } case (x, y) => // at least one does not have a filtration value defined; just go by dimension and lexicographic - if (Ordering.Int.compare(x.vertices.size, y.vertices.size) == 0) + if (Ordering.Int.compare(x.size, y.size) == 0) Ordering.Implicits .sortedSetOrdering[SortedSet, VertexT](vertexOrdering) - .compare(x.vertices, y.vertices) + .compare(x, y) else - Ordering.Int.compare(x.vertices.size, y.vertices.size) + Ordering.Int.compare(x.size, y.size) } } diff --git a/src/main/scala/org/appliedtopology/tda4j/SimplicialSet.scala b/src/main/scala/org/appliedtopology/tda4j/SimplicialSet.scala index 6a58b123..e7ba54ea 100644 --- a/src/main/scala/org/appliedtopology/tda4j/SimplicialSet.scala +++ b/src/main/scala/org/appliedtopology/tda4j/SimplicialSet.scala @@ -361,7 +361,7 @@ class Singular[VertexT: Ordering] private[tda4j](val allSimplices: Seq[Simplex[V generators.map { spx => spx -> (0 to spx.dim).map { i => - SimplicialWrapper(Simplex(spx.wrapped.vertices.drop(i))) + SimplicialWrapper(spx.wrapped.drop(i)) }.toList } ) @@ -385,7 +385,7 @@ object Singular { * @return */ def from[VertexT: Ordering](underlying: Seq[Simplex[VertexT]]): Singular[VertexT] = - fromAll(underlying.toSet.flatMap(spx => spx.vertices.subsets.filter(_.nonEmpty).map(Simplex.from)).toSeq) + fromAll(underlying.toSet.flatMap(spx => spx.subsets.filter(_.nonEmpty)).toSeq) /** * Create a [[Singular]] simplicial set from the simplices in `allSimplices`. This method will trust the diff --git a/src/main/scala/org/appliedtopology/tda4j/SymmetryGroup.scala b/src/main/scala/org/appliedtopology/tda4j/SymmetryGroup.scala index 2f92db94..3648937a 100644 --- a/src/main/scala/org/appliedtopology/tda4j/SymmetryGroup.scala +++ b/src/main/scala/org/appliedtopology/tda4j/SymmetryGroup.scala @@ -56,14 +56,14 @@ trait SymmetryGroup[KeyT, VertexT: Ordering]() { def orbitSeq( simplex: Simplex[VertexT] ): Set[Simplex[VertexT]] = - keys.map(k => Simplex.from(simplex.vertices.map(apply(k)))).toSet + keys.map(k => simplex.map(apply(k))).toSet def orbitPar( simplex: Simplex[VertexT] ): Set[Simplex[VertexT]] = { val futures: Iterable[Future[Simplex[VertexT]]] = for (k <- keys) yield Future { - Simplex.from(simplex.vertices.map(apply(k))) + simplex.map(apply(k)) } val allfutures = Future.sequence(futures) @@ -82,7 +82,7 @@ trait SymmetryGroup[KeyT, VertexT: Ordering]() { def representative( simplex: Simplex[VertexT] ): Simplex[VertexT] = - keys.map(k => Simplex.from(simplex.vertices.map(apply(k)))).min + keys.map(k => simplex.map(apply(k))).min /** Check if `simplex` is the canonical representative of its own orbit. * @@ -291,9 +291,9 @@ class SymmetricZomorodianIncremental[VertexT: Ordering, KeyT]( val task = tasks.pop() val tau = task._1 val N = task._2 - val simplex = Simplex.from(tau) + val simplex : Simplex[VertexT] = tau if (symmetry.isRepresentative(simplex)) { - representatives += Simplex.from(tau) + representatives += tau } if (tau.size <= maxDimension) { N.foreach { v => @@ -490,7 +490,7 @@ class HyperCubeSymmetryGeneratorsBitSet(val bitlength: Int) extends HyperCubeSym if (representatives.contains(simplex)) { simplex == representatives(simplex) } else { - if (generators.forall(g => simplex <= Simplex.from(simplex.vertices.map(s => g(s))))) { + if (generators.forall(g => simplex <= simplex.map(s => g(s)))) { // simplex is a pseudo-minimum // time to check the entire orbit representatives(simplex) = super.representative(simplex) @@ -524,7 +524,7 @@ class HyperCubeSymmetryGenerators(val bitlength: Int) extends HyperCubeSymmetry( if (representatives.contains(simplex)) { simplex == representatives(simplex) } else { - if (generators.par.forall(g => simplex <= Simplex.from(simplex.vertices.map(s => g(s))))) { + if (generators.par.forall(g => simplex <= simplex.map(s => g(s)))) { // simplex is a pseudo-minimum // time to check the entire orbit representatives(simplex) = super.representative(simplex) diff --git a/src/main/scala/org/appliedtopology/tda4j/UnionFind.scala b/src/main/scala/org/appliedtopology/tda4j/UnionFind.scala index 3324b68e..338aa628 100644 --- a/src/main/scala/org/appliedtopology/tda4j/UnionFind.scala +++ b/src/main/scala/org/appliedtopology/tda4j/UnionFind.scala @@ -91,26 +91,3 @@ object Kruskal { new Kruskal(metricSpace.elements.toSeq, metricSpace.distance) } -/* -def KruskalF[Self](metricSpace: FiniteMetricSpace[Self])(using orderingT: Ordering[Self]) = { - val unionFind: UnionFind[Self] = UnionFind(metricSpace.elements) - val sortedEdges: List[(Double, unionFind.UFSet, unionFind.UFSet)] = - (for - x <- unionFind.sets.keysIterator - y <- unionFind.sets.keysIterator - if orderingT.lt(x.label, y.label) - yield (metricSpace.distance(x.label, y.label), x, y)).toList.sortWith { (l,r) => - l._1 < r._1 - } - - def process(dxy: (Double,unionFind.UFSet,unionFind.UFSet)): Either[(Self,Self),(Self,Self)] = - if unionFind.find(dxy._2) != unionFind.find(dxy._3) then { - unionFind.union(dxy._2, dxy._3) - Left[(Self, Self), (Self, Self)]((dxy._2.label, dxy._3.label)) - } else { - Right[(Self, Self), (Self, Self)]((dxy._2.label, dxy._3.label)) - } - - val lrList: (List[(Self,Self)],List[(Self,Self)]) = sortedEdges.partitionMap(process) -} - */ diff --git a/src/main/scala/org/appliedtopology/tda4j/VietorisRips.scala b/src/main/scala/org/appliedtopology/tda4j/VietorisRips.scala index 86a2188b..1ff18f89 100644 --- a/src/main/scala/org/appliedtopology/tda4j/VietorisRips.scala +++ b/src/main/scala/org/appliedtopology/tda4j/VietorisRips.scala @@ -189,7 +189,7 @@ class BronKerbosch[VertexT: Ordering] extends CliqueFinder[VertexT] { val simplices: Seq[Simplex[VertexT]] = cliqueSet .filter(spx => spx.nonEmpty) - .map(spx => Simplex[VertexT](spx.to(Seq)*)) to Seq + .map(spx => SortedSet[VertexT](spx.to(Seq)*)) to Seq val filtration = new MaximumDistanceFiltrationValue[VertexT](metricSpace) val simplexOrdering = @@ -228,7 +228,7 @@ class ZomorodianIncremental[VertexT: Ordering] extends CliqueFinder[VertexT] { val task = tasks.pop() val tau = task._1 val N = task._2 - V += Simplex.from(tau) + V += tau if (tau.size <= maxDimension) { N.foreach { v => val sigma = tau + v @@ -293,7 +293,7 @@ object LazyVietorisRips { val task = tasks.pop() val tau = task._1 val N = task._2 - V += Simplex.from(tau) + V += tau if (tau.size <= maxDimension) { N.foreach { v => val sigma = tau + v @@ -303,10 +303,9 @@ object LazyVietorisRips { } } - val newSimplices: SortedSet[Simplex[VertexT]] = - V.map(spx => spx.vertices) - .map(spx => Simplex.from(spx ++ endpoints)) - .to(SortedSet) + val newSimplices: mutable.SortedSet[Simplex[VertexT]] = + V.map(spx => spx ++ endpoints) + FoldState( g + nextEdge, @@ -318,7 +317,7 @@ object LazyVietorisRips { val simplices = edges.nodes .to(Seq) .map(_.toOuter) - .map(spx => Simplex(spx)) + .map(spx => SortedSet(spx)) .sorted .to(LazyList) #::: { val edgesLL: LazyList[WUnDiEdge[VertexT]] = @@ -388,10 +387,10 @@ object LazyStratifiedVietorisRips { ): LazyList[Simplex[VertexT]] = LazyList .from( - simplex.vertices.tail - .foldRight(neighbors(simplex.vertices.head))((v, N) => N.intersect(neighbors(v))) + simplex.tail + .foldRight(neighbors(simplex.head))((v, N) => N.intersect(neighbors(v))) ) - .map(v => Simplex.from(simplex.vertices + v)) + .map(v => simplex + v) case class FoldState( outputLists: Array[LazyList[Simplex[VertexT]]], @@ -416,12 +415,12 @@ object LazyStratifiedVietorisRips { val newCofacets = for cofacet <- candidateCofacets - others = cofacet.vertices.toList + others = cofacet.toList .combinations(simplex.dim + 1) .filter(spx => filtrationValue(Simplex.from(spx)) == metricSpace.distance(src, tgt)) .toList .sorted(math.Ordering.Implicits.seqOrdering) - if simplex.vertices.toSeq == others.last + if simplex.toSeq == others.last yield cofacet oneStep( diff --git a/src/test/scala/org/appliedtopology/tda4j/SimplexSpec.scala b/src/test/scala/org/appliedtopology/tda4j/SimplexSpec.scala index fba4578b..ff898df8 100644 --- a/src/test/scala/org/appliedtopology/tda4j/SimplexSpec.scala +++ b/src/test/scala/org/appliedtopology/tda4j/SimplexSpec.scala @@ -41,11 +41,11 @@ class SimplexTypeSpec extends mutable.Specification { val s = Simplex(1, 2, 3) val t = Simplex(2, 3, 4) "be the return type of the Simplex constructor" >> { - s must haveClass[Simplex[Int]] + s must haveSuperclass[Simplex[Int]] } } "we can create a simplex, and get the right type" >> { - ∆('a', 'b', 'c') must haveClass[Simplex[Char]] - ∆('a', 'b', 'c') must haveClass[Simplex[Char]] + ∆('a', 'b', 'c') must haveSuperclass[Simplex[Char]] + ∆('a', 'b', 'c') must haveSuperclass[Simplex[Char]] } } diff --git a/src/test/scala/org/appliedtopology/tda4j/SimplexStreamSpec.scala b/src/test/scala/org/appliedtopology/tda4j/SimplexStreamSpec.scala index 54ed74ee..f9512453 100644 --- a/src/test/scala/org/appliedtopology/tda4j/SimplexStreamSpec.scala +++ b/src/test/scala/org/appliedtopology/tda4j/SimplexStreamSpec.scala @@ -43,9 +43,8 @@ class SimplexStreamSpec extends mutable.Specification { cmutable.Set(Simplex()) simplexStream.iterator.foreach { spx => seen += spx - spx.vertices.toSet - .subsets() - .foreach(face => seen must contain(Simplex.from(face))) + spx.subsets() + .foreach(face => seen must contain(face)) } } "have all simplices, in order" >> { @@ -82,9 +81,8 @@ class SimplexStreamSpec extends mutable.Specification { cmutable.Set(Simplex()) sortedSimplexSeq.foreach { spx => seen += spx - spx.vertices.toSet - .subsets() - .foreach(face => seen must contain(Simplex.from(face))) + spx.subsets() + .foreach(face => seen must contain(face)) } } }