From 39640498fc93f4ca5adc4c439c641d5aeb4510cb Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 19 Mar 2022 09:20:32 +0300 Subject: [PATCH] Another histogram refactor --- build.gradle.kts | 2 +- buildSrc/gradle.properties | 2 +- .../kmath/domains/HyperSquareDomain.kt | 11 ++++- .../kmath/histogram/DoubleHistogramSpace.kt | 46 +++++++++++-------- .../kmath/histogram/IndexedHistogramSpace.kt | 20 ++++---- .../kmath/histogram/UnivariateHistogram.kt | 20 -------- .../kmath/histogram/TreeHistogramSpace.kt | 22 ++++++++- .../kmath/histogram/TreeHistogramTest.kt | 2 +- 8 files changed, 71 insertions(+), 54 deletions(-) rename kmath-histograms/src/{jvmMain => commonMain}/kotlin/space/kscience/kmath/histogram/UnivariateHistogram.kt (71%) diff --git a/build.gradle.kts b/build.gradle.kts index 3372d505d..ee77f32df 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,7 +11,7 @@ allprojects { } group = "space.kscience" - version = "0.3.0-dev-20" + version = "0.3.0-dev-21" } subprojects { diff --git a/buildSrc/gradle.properties b/buildSrc/gradle.properties index 05486d4f6..713f9bcd9 100644 --- a/buildSrc/gradle.properties +++ b/buildSrc/gradle.properties @@ -5,4 +5,4 @@ kotlin.code.style=official -toolsVersion=0.11.1-kotlin-1.6.10 +toolsVersion=0.11.2-kotlin-1.6.10 diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/HyperSquareDomain.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/HyperSquareDomain.kt index bd5514623..2fac442e7 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/HyperSquareDomain.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/HyperSquareDomain.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.domains import space.kscience.kmath.linear.Point import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.indices /** @@ -16,9 +17,17 @@ import space.kscience.kmath.structures.indices * @author Alexander Nozik */ @UnstableKMathAPI -public class HyperSquareDomain(private val lower: Buffer, private val upper: Buffer) : DoubleDomain { +public class HyperSquareDomain(public val lower: Buffer, public val upper: Buffer) : DoubleDomain { + init { + require(lower.size == upper.size) { + "Domain borders size mismatch. Lower borders size is ${lower.size}, but upper borders size is ${upper.size}" + } + } + override val dimension: Int get() = lower.size + public val center: DoubleBuffer get() = DoubleBuffer(dimension) { (lower[it] + upper[it]) / 2.0 } + override operator fun contains(point: Point): Boolean = point.indices.all { i -> point[i] in lower[i]..upper[i] } diff --git a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/DoubleHistogramSpace.kt b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/DoubleHistogramSpace.kt index 61d0b9f33..27f04d60b 100644 --- a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/DoubleHistogramSpace.kt +++ b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/DoubleHistogramSpace.kt @@ -5,7 +5,6 @@ package space.kscience.kmath.histogram -import space.kscience.kmath.domains.Domain import space.kscience.kmath.domains.HyperSquareDomain import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.nd.* @@ -13,6 +12,9 @@ import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.structures.* import kotlin.math.floor +/** + * Multivariate histogram space for hyper-square real-field bins. + */ public class DoubleHistogramSpace( private val lower: Buffer, private val upper: Buffer, @@ -47,7 +49,7 @@ public class DoubleHistogramSpace( } @OptIn(UnstableKMathAPI::class) - override fun getDomain(index: IntArray): Domain { + override fun getDomain(index: IntArray): HyperSquareDomain { val lowerBoundary = index.mapIndexed { axis, i -> when (i) { 0 -> Double.NEGATIVE_INFINITY @@ -67,8 +69,13 @@ public class DoubleHistogramSpace( return HyperSquareDomain(lowerBoundary, upperBoundary) } + @OptIn(UnstableKMathAPI::class) + public val Bin.domain: HyperSquareDomain + get() = (this as? DomainBin)?.domain as? HyperSquareDomain + ?: error("Im a teapot. This is not my bin") - override fun produceBin(index: IntArray, value: Double): Bin { + @OptIn(UnstableKMathAPI::class) + override fun produceBin(index: IntArray, value: Double): DomainBin { val domain = getDomain(index) return DomainBin(domain, value) } @@ -96,7 +103,9 @@ public class DoubleHistogramSpace( *) *``` */ - public fun fromRanges(vararg ranges: ClosedFloatingPointRange): DoubleHistogramSpace = DoubleHistogramSpace( + public fun fromRanges( + vararg ranges: ClosedFloatingPointRange, + ): DoubleHistogramSpace = DoubleHistogramSpace( ranges.map(ClosedFloatingPointRange::start).asBuffer(), ranges.map(ClosedFloatingPointRange::endInclusive).asBuffer() ) @@ -110,21 +119,22 @@ public class DoubleHistogramSpace( *) *``` */ - public fun fromRanges(vararg ranges: Pair, Int>): DoubleHistogramSpace = - DoubleHistogramSpace( - ListBuffer( - ranges - .map(Pair, Int>::first) - .map(ClosedFloatingPointRange::start) - ), + public fun fromRanges( + vararg ranges: Pair, Int>, + ): DoubleHistogramSpace = DoubleHistogramSpace( + ListBuffer( + ranges + .map(Pair, Int>::first) + .map(ClosedFloatingPointRange::start) + ), - ListBuffer( - ranges - .map(Pair, Int>::first) - .map(ClosedFloatingPointRange::endInclusive) - ), + ListBuffer( + ranges + .map(Pair, Int>::first) + .map(ClosedFloatingPointRange::endInclusive) + ), - ranges.map(Pair, Int>::second).toIntArray() - ) + ranges.map(Pair, Int>::second).toIntArray() + ) } } \ No newline at end of file diff --git a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/IndexedHistogramSpace.kt b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/IndexedHistogramSpace.kt index 9275c1c5e..bfacebb43 100644 --- a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/IndexedHistogramSpace.kt +++ b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/IndexedHistogramSpace.kt @@ -7,7 +7,6 @@ package space.kscience.kmath.histogram import space.kscience.kmath.domains.Domain import space.kscience.kmath.linear.Point -import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.nd.DefaultStrides import space.kscience.kmath.nd.FieldND import space.kscience.kmath.nd.Shape @@ -24,22 +23,21 @@ public data class DomainBin>( override val value: Number, ) : Bin, Domain by domain -@OptIn(UnstableKMathAPI::class) public class IndexedHistogram, V : Any>( - public val context: IndexedHistogramSpace, + public val histogramSpace: IndexedHistogramSpace, public val values: StructureND, ) : Histogram> { override fun get(point: Point): Bin? { - val index = context.getIndex(point) ?: return null - return context.produceBin(index, values[index]) + val index = histogramSpace.getIndex(point) ?: return null + return histogramSpace.produceBin(index, values[index]) } - override val dimension: Int get() = context.shape.size + override val dimension: Int get() = histogramSpace.shape.size override val bins: Iterable> - get() = DefaultStrides(context.shape).asSequence().map { - context.produceBin(it, values[it]) + get() = DefaultStrides(histogramSpace.shape).asSequence().map { + histogramSpace.produceBin(it, values[it]) }.asIterable() } @@ -67,13 +65,13 @@ public interface IndexedHistogramSpace, V : Any> public fun produce(builder: HistogramBuilder.() -> Unit): IndexedHistogram override fun add(left: IndexedHistogram, right: IndexedHistogram): IndexedHistogram { - require(left.context == this) { "Can't operate on a histogram produced by external space" } - require(right.context == this) { "Can't operate on a histogram produced by external space" } + require(left.histogramSpace == this) { "Can't operate on a histogram produced by external space" } + require(right.histogramSpace == this) { "Can't operate on a histogram produced by external space" } return IndexedHistogram(this, histogramValueSpace { left.values + right.values }) } override fun scale(a: IndexedHistogram, value: Double): IndexedHistogram { - require(a.context == this) { "Can't operate on a histogram produced by external space" } + require(a.histogramSpace == this) { "Can't operate on a histogram produced by external space" } return IndexedHistogram(this, histogramValueSpace { a.values * value }) } diff --git a/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/UnivariateHistogram.kt b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UnivariateHistogram.kt similarity index 71% rename from kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/UnivariateHistogram.kt rename to kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UnivariateHistogram.kt index ac0576a8e..69ea83ae3 100644 --- a/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/UnivariateHistogram.kt +++ b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UnivariateHistogram.kt @@ -37,26 +37,6 @@ public class UnivariateBin( public interface UnivariateHistogram : Histogram { public operator fun get(value: Double): UnivariateBin? override operator fun get(point: Buffer): UnivariateBin? = get(point[0]) - - public companion object { - /** - * Build and fill a [UnivariateHistogram]. Returns a read-only histogram. - */ - public inline fun uniform( - binSize: Double, - start: Double = 0.0, - builder: UnivariateHistogramBuilder.() -> Unit, - ): UnivariateHistogram = TreeHistogramSpace.uniform(binSize, start).fill(builder) - - /** - * Build and fill a histogram with custom borders. Returns a read-only histogram. - */ - public inline fun custom( - borders: DoubleArray, - builder: UnivariateHistogramBuilder.() -> Unit, - ): UnivariateHistogram = TreeHistogramSpace.custom(borders).fill(builder) - - } } @UnstableKMathAPI diff --git a/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/TreeHistogramSpace.kt b/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/TreeHistogramSpace.kt index 0853615e6..1ab49003c 100644 --- a/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/TreeHistogramSpace.kt +++ b/kmath-histograms/src/jvmMain/kotlin/space/kscience/kmath/histogram/TreeHistogramSpace.kt @@ -49,7 +49,9 @@ internal class TreeHistogramBuilder(val binFactory: (Double) -> UnivariateDomain fun createBin(value: Double): BinCounter { val binDefinition = binFactory(value) val newBin = BinCounter(binDefinition) - synchronized(this) { bins[binDefinition.center] = newBin } + synchronized(this) { + bins[binDefinition.center] = newBin + } return newBin } @@ -131,6 +133,24 @@ public class TreeHistogramSpace( override val zero: UnivariateHistogram by lazy { fill { } } public companion object { + /** + * Build and fill a [UnivariateHistogram]. Returns a read-only histogram. + */ + public inline fun uniform( + binSize: Double, + start: Double = 0.0, + builder: UnivariateHistogramBuilder.() -> Unit, + ): UnivariateHistogram = uniform(binSize, start).fill(builder) + + /** + * Build and fill a histogram with custom borders. Returns a read-only histogram. + */ + public inline fun custom( + borders: DoubleArray, + builder: UnivariateHistogramBuilder.() -> Unit, + ): UnivariateHistogram = custom(borders).fill(builder) + + /** * Build and fill a [UnivariateHistogram]. Returns a read-only histogram. */ diff --git a/kmath-histograms/src/jvmTest/kotlin/space/kscience/kmath/histogram/TreeHistogramTest.kt b/kmath-histograms/src/jvmTest/kotlin/space/kscience/kmath/histogram/TreeHistogramTest.kt index 28a1b03cb..f1a8f953b 100644 --- a/kmath-histograms/src/jvmTest/kotlin/space/kscience/kmath/histogram/TreeHistogramTest.kt +++ b/kmath-histograms/src/jvmTest/kotlin/space/kscience/kmath/histogram/TreeHistogramTest.kt @@ -13,7 +13,7 @@ class TreeHistogramTest { @Test fun normalFill() { - val histogram = UnivariateHistogram.uniform(0.1) { + val histogram = TreeHistogramSpace.uniform(0.1) { repeat(100_000) { putValue(Random.nextDouble()) }