From 60c7cac89e236a9c53601aa1852f27e7878d699b Mon Sep 17 00:00:00 2001 From: Mikael Vejdemo-Johansson Date: Tue, 13 Feb 2024 18:14:37 +0000 Subject: [PATCH] Rebuilt metric space interface to take lists of DoubleArrays instead. Leaks less of the internals. --- .../tda4j/FiniteMetricSpace.kt | 28 +++++++++---- .../tda4j/FiniteMetricSpaceSpec.kt | 41 ++++++++----------- 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/commonMain/kotlin/org/appliedtopology/tda4j/FiniteMetricSpace.kt b/src/commonMain/kotlin/org/appliedtopology/tda4j/FiniteMetricSpace.kt index 9d4a6654..af120313 100644 --- a/src/commonMain/kotlin/org/appliedtopology/tda4j/FiniteMetricSpace.kt +++ b/src/commonMain/kotlin/org/appliedtopology/tda4j/FiniteMetricSpace.kt @@ -1,7 +1,14 @@ package org.appliedtopology.tda4j -import space.kscience.kmath.tensors.core.DoubleTensor2D -import space.kscience.kmath.tensors.core.DoubleTensorAlgebra +import space.kscience.kmath.linear.Matrix +import space.kscience.kmath.linear.asMatrix +import space.kscience.kmath.linear.linearSpace +import space.kscience.kmath.linear.transpose +import space.kscience.kmath.nd.StructureND +import space.kscience.kmath.nd.as2D +import space.kscience.kmath.operations.algebra +import kotlin.collections.component1 +import kotlin.collections.component2 import kotlin.math.pow interface FiniteMetricSpace { @@ -47,19 +54,26 @@ class ExplicitMetricSpace(val distances: Map, Do override fun contains(x: VertexT): Boolean = elements.contains(x) } -class EuclideanMetricSpace(val points: DoubleTensor2D) : FiniteMetricSpace { +class EuclideanMetricSpace(val points: List) : FiniteMetricSpace { + val pointsND: Matrix = + with(Double.algebra.linearSpace) { + StructureND.auto(points.size, points[0].size) { (i, j) -> + points[i][j] + } + }.as2D() + override fun distance( x: Int, y: Int, ): Double { @Suppress("ktlint:standard:property-naming") - with(DoubleTensorAlgebra) { - val x_y = points.rowsByIndices(intArrayOf(x)) - points.rowsByIndices(intArrayOf(y)) - return x_y.dot(x_y.transposed())[intArrayOf(0, 0)].pow(0.5) + with(Double.algebra.linearSpace) { + val x_y: Matrix = (pointsND.rows[x] - pointsND.rows[y]).asMatrix() + return x_y.dot(x_y.transpose())[0, 0].pow(0.5) } } - override val size: Int = points.shape[0] + override val size: Int = pointsND.shape[0] override val elements: Iterable get() = IntRange(0, size - 1) diff --git a/src/commonTest/kotlin/org/appliedtopology/tda4j/FiniteMetricSpaceSpec.kt b/src/commonTest/kotlin/org/appliedtopology/tda4j/FiniteMetricSpaceSpec.kt index 61dd2f4c..aa4be7b3 100644 --- a/src/commonTest/kotlin/org/appliedtopology/tda4j/FiniteMetricSpaceSpec.kt +++ b/src/commonTest/kotlin/org/appliedtopology/tda4j/FiniteMetricSpaceSpec.kt @@ -6,25 +6,22 @@ import io.kotest.matchers.ints.shouldBeGreaterThan import io.kotest.property.Arb import io.kotest.property.arbitrary.* import io.kotest.property.checkAll -import space.kscience.kmath.nd.ShapeND -import space.kscience.kmath.structures.DoubleBuffer -import space.kscience.kmath.tensors.core.* fun pointCloudArb( dimensions: Collection = (2..10).toList(), sizes: Collection = listOf(5, 10, 25, 50, 100, 500), -): Arb = +): Arb> = arbitrary { val dim = Arb.element(dimensions).bind() val size = Arb.element(sizes).bind() val doubles = - Arb.doubleArray( - Arb.of(listOf(dim * size)), - Arb.double(-100.0, 100.0), - ).bind() - Double.tensorAlgebra.withBroadcast { - fromArray(ShapeND(size, dim), doubles).asDoubleTensor2D() - } + (1..size).map { + Arb.doubleArray( + Arb.of(listOf(dim * size)), + Arb.double(-100.0, 100.0), + ).bind() + } + doubles } class FiniteMetricSpaceSpec : StringSpec({ @@ -33,9 +30,7 @@ class FiniteMetricSpaceSpec : StringSpec({ mapOf(Pair(0, 0) to 0.0, Pair(0, 1) to 1.0, Pair(1, 0) to 1.0, Pair(1, 1) to 0.0), ) val msE: FiniteMetricSpace = - EuclideanMetricSpace( - DoubleTensor2D(2, 2, OffsetDoubleBuffer(DoubleBuffer(0.0, 1.0, 1.0, 0.0), 0, 4)), - ) + EuclideanMetricSpace(listOf(doubleArrayOf(0.0, 1.0), doubleArrayOf(1.0, 0.0))) "An explicit metric space is non-empty" { (msX.size) shouldBeGreaterThan 0 @@ -52,15 +47,15 @@ class FiniteMetricSpaceSpec : StringSpec({ } "Generate random Euclidean metric space" { - checkAll(pointCloudArb()) { - it.colNum shouldBeGreaterThan 0 - it.rowNum shouldBeGreaterThan 0 - collect("${it.colNum} columns") - collect("${it.rowNum} rows") - Double.tensorAlgebra.withBroadcast { - val diff = it.rowsByIndices(intArrayOf(1)) - it.rowsByIndices(intArrayOf(2)) - collect(kotlin.math.floor(kotlin.math.sqrt(diff.dot(diff.transposed())[intArrayOf(0, 0)]) / 10.0)) - } + checkAll(100, pointCloudArb()) { + val space = EuclideanMetricSpace(it) + space.pointsND + + space.pointsND.colNum shouldBeGreaterThan 0 + space.pointsND.rowNum shouldBeGreaterThan 0 + collect("${space.pointsND.colNum} columns") + collect("${space.pointsND.rowNum} rows") + collect(space.distance(1, 2)) } } })