Another histogram refactor
This commit is contained in:
parent
0b2e8ff25e
commit
39640498fc
@ -11,7 +11,7 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "space.kscience"
|
group = "space.kscience"
|
||||||
version = "0.3.0-dev-20"
|
version = "0.3.0-dev-21"
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
|
@ -5,4 +5,4 @@
|
|||||||
|
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
|
|
||||||
toolsVersion=0.11.1-kotlin-1.6.10
|
toolsVersion=0.11.2-kotlin-1.6.10
|
||||||
|
@ -7,6 +7,7 @@ package space.kscience.kmath.domains
|
|||||||
import space.kscience.kmath.linear.Point
|
import space.kscience.kmath.linear.Point
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
import space.kscience.kmath.structures.Buffer
|
import space.kscience.kmath.structures.Buffer
|
||||||
|
import space.kscience.kmath.structures.DoubleBuffer
|
||||||
import space.kscience.kmath.structures.indices
|
import space.kscience.kmath.structures.indices
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,9 +17,17 @@ import space.kscience.kmath.structures.indices
|
|||||||
* @author Alexander Nozik
|
* @author Alexander Nozik
|
||||||
*/
|
*/
|
||||||
@UnstableKMathAPI
|
@UnstableKMathAPI
|
||||||
public class HyperSquareDomain(private val lower: Buffer<Double>, private val upper: Buffer<Double>) : DoubleDomain {
|
public class HyperSquareDomain(public val lower: Buffer<Double>, public val upper: Buffer<Double>) : 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
|
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<Double>): Boolean = point.indices.all { i ->
|
override operator fun contains(point: Point<Double>): Boolean = point.indices.all { i ->
|
||||||
point[i] in lower[i]..upper[i]
|
point[i] in lower[i]..upper[i]
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
package space.kscience.kmath.histogram
|
package space.kscience.kmath.histogram
|
||||||
|
|
||||||
import space.kscience.kmath.domains.Domain
|
|
||||||
import space.kscience.kmath.domains.HyperSquareDomain
|
import space.kscience.kmath.domains.HyperSquareDomain
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
import space.kscience.kmath.nd.*
|
import space.kscience.kmath.nd.*
|
||||||
@ -13,6 +12,9 @@ import space.kscience.kmath.operations.DoubleField
|
|||||||
import space.kscience.kmath.structures.*
|
import space.kscience.kmath.structures.*
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multivariate histogram space for hyper-square real-field bins.
|
||||||
|
*/
|
||||||
public class DoubleHistogramSpace(
|
public class DoubleHistogramSpace(
|
||||||
private val lower: Buffer<Double>,
|
private val lower: Buffer<Double>,
|
||||||
private val upper: Buffer<Double>,
|
private val upper: Buffer<Double>,
|
||||||
@ -47,7 +49,7 @@ public class DoubleHistogramSpace(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
@OptIn(UnstableKMathAPI::class)
|
||||||
override fun getDomain(index: IntArray): Domain<Double> {
|
override fun getDomain(index: IntArray): HyperSquareDomain {
|
||||||
val lowerBoundary = index.mapIndexed { axis, i ->
|
val lowerBoundary = index.mapIndexed { axis, i ->
|
||||||
when (i) {
|
when (i) {
|
||||||
0 -> Double.NEGATIVE_INFINITY
|
0 -> Double.NEGATIVE_INFINITY
|
||||||
@ -67,8 +69,13 @@ public class DoubleHistogramSpace(
|
|||||||
return HyperSquareDomain(lowerBoundary, upperBoundary)
|
return HyperSquareDomain(lowerBoundary, upperBoundary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(UnstableKMathAPI::class)
|
||||||
|
public val Bin<Double>.domain: HyperSquareDomain
|
||||||
|
get() = (this as? DomainBin<Double>)?.domain as? HyperSquareDomain
|
||||||
|
?: error("Im a teapot. This is not my bin")
|
||||||
|
|
||||||
override fun produceBin(index: IntArray, value: Double): Bin<Double> {
|
@OptIn(UnstableKMathAPI::class)
|
||||||
|
override fun produceBin(index: IntArray, value: Double): DomainBin<Double> {
|
||||||
val domain = getDomain(index)
|
val domain = getDomain(index)
|
||||||
return DomainBin(domain, value)
|
return DomainBin(domain, value)
|
||||||
}
|
}
|
||||||
@ -96,7 +103,9 @@ public class DoubleHistogramSpace(
|
|||||||
*)
|
*)
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
public fun fromRanges(vararg ranges: ClosedFloatingPointRange<Double>): DoubleHistogramSpace = DoubleHistogramSpace(
|
public fun fromRanges(
|
||||||
|
vararg ranges: ClosedFloatingPointRange<Double>,
|
||||||
|
): DoubleHistogramSpace = DoubleHistogramSpace(
|
||||||
ranges.map(ClosedFloatingPointRange<Double>::start).asBuffer(),
|
ranges.map(ClosedFloatingPointRange<Double>::start).asBuffer(),
|
||||||
ranges.map(ClosedFloatingPointRange<Double>::endInclusive).asBuffer()
|
ranges.map(ClosedFloatingPointRange<Double>::endInclusive).asBuffer()
|
||||||
)
|
)
|
||||||
@ -110,21 +119,22 @@ public class DoubleHistogramSpace(
|
|||||||
*)
|
*)
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
public fun fromRanges(vararg ranges: Pair<ClosedFloatingPointRange<Double>, Int>): DoubleHistogramSpace =
|
public fun fromRanges(
|
||||||
DoubleHistogramSpace(
|
vararg ranges: Pair<ClosedFloatingPointRange<Double>, Int>,
|
||||||
ListBuffer(
|
): DoubleHistogramSpace = DoubleHistogramSpace(
|
||||||
ranges
|
ListBuffer(
|
||||||
.map(Pair<ClosedFloatingPointRange<Double>, Int>::first)
|
ranges
|
||||||
.map(ClosedFloatingPointRange<Double>::start)
|
.map(Pair<ClosedFloatingPointRange<Double>, Int>::first)
|
||||||
),
|
.map(ClosedFloatingPointRange<Double>::start)
|
||||||
|
),
|
||||||
|
|
||||||
ListBuffer(
|
ListBuffer(
|
||||||
ranges
|
ranges
|
||||||
.map(Pair<ClosedFloatingPointRange<Double>, Int>::first)
|
.map(Pair<ClosedFloatingPointRange<Double>, Int>::first)
|
||||||
.map(ClosedFloatingPointRange<Double>::endInclusive)
|
.map(ClosedFloatingPointRange<Double>::endInclusive)
|
||||||
),
|
),
|
||||||
|
|
||||||
ranges.map(Pair<ClosedFloatingPointRange<Double>, Int>::second).toIntArray()
|
ranges.map(Pair<ClosedFloatingPointRange<Double>, Int>::second).toIntArray()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,7 +7,6 @@ package space.kscience.kmath.histogram
|
|||||||
|
|
||||||
import space.kscience.kmath.domains.Domain
|
import space.kscience.kmath.domains.Domain
|
||||||
import space.kscience.kmath.linear.Point
|
import space.kscience.kmath.linear.Point
|
||||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
|
||||||
import space.kscience.kmath.nd.DefaultStrides
|
import space.kscience.kmath.nd.DefaultStrides
|
||||||
import space.kscience.kmath.nd.FieldND
|
import space.kscience.kmath.nd.FieldND
|
||||||
import space.kscience.kmath.nd.Shape
|
import space.kscience.kmath.nd.Shape
|
||||||
@ -24,22 +23,21 @@ public data class DomainBin<in T : Comparable<T>>(
|
|||||||
override val value: Number,
|
override val value: Number,
|
||||||
) : Bin<T>, Domain<T> by domain
|
) : Bin<T>, Domain<T> by domain
|
||||||
|
|
||||||
@OptIn(UnstableKMathAPI::class)
|
|
||||||
public class IndexedHistogram<T : Comparable<T>, V : Any>(
|
public class IndexedHistogram<T : Comparable<T>, V : Any>(
|
||||||
public val context: IndexedHistogramSpace<T, V>,
|
public val histogramSpace: IndexedHistogramSpace<T, V>,
|
||||||
public val values: StructureND<V>,
|
public val values: StructureND<V>,
|
||||||
) : Histogram<T, Bin<T>> {
|
) : Histogram<T, Bin<T>> {
|
||||||
|
|
||||||
override fun get(point: Point<T>): Bin<T>? {
|
override fun get(point: Point<T>): Bin<T>? {
|
||||||
val index = context.getIndex(point) ?: return null
|
val index = histogramSpace.getIndex(point) ?: return null
|
||||||
return context.produceBin(index, values[index])
|
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<Bin<T>>
|
override val bins: Iterable<Bin<T>>
|
||||||
get() = DefaultStrides(context.shape).asSequence().map {
|
get() = DefaultStrides(histogramSpace.shape).asSequence().map {
|
||||||
context.produceBin(it, values[it])
|
histogramSpace.produceBin(it, values[it])
|
||||||
}.asIterable()
|
}.asIterable()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,13 +65,13 @@ public interface IndexedHistogramSpace<T : Comparable<T>, V : Any>
|
|||||||
public fun produce(builder: HistogramBuilder<T>.() -> Unit): IndexedHistogram<T, V>
|
public fun produce(builder: HistogramBuilder<T>.() -> Unit): IndexedHistogram<T, V>
|
||||||
|
|
||||||
override fun add(left: IndexedHistogram<T, V>, right: IndexedHistogram<T, V>): IndexedHistogram<T, V> {
|
override fun add(left: IndexedHistogram<T, V>, right: IndexedHistogram<T, V>): IndexedHistogram<T, V> {
|
||||||
require(left.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.context == 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 })
|
return IndexedHistogram(this, histogramValueSpace { left.values + right.values })
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun scale(a: IndexedHistogram<T, V>, value: Double): IndexedHistogram<T, V> {
|
override fun scale(a: IndexedHistogram<T, V>, value: Double): IndexedHistogram<T, V> {
|
||||||
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 })
|
return IndexedHistogram(this, histogramValueSpace { a.values * value })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,26 +37,6 @@ public class UnivariateBin(
|
|||||||
public interface UnivariateHistogram : Histogram<Double, UnivariateBin> {
|
public interface UnivariateHistogram : Histogram<Double, UnivariateBin> {
|
||||||
public operator fun get(value: Double): UnivariateBin?
|
public operator fun get(value: Double): UnivariateBin?
|
||||||
override operator fun get(point: Buffer<Double>): UnivariateBin? = get(point[0])
|
override operator fun get(point: Buffer<Double>): 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
|
@UnstableKMathAPI
|
@ -49,7 +49,9 @@ internal class TreeHistogramBuilder(val binFactory: (Double) -> UnivariateDomain
|
|||||||
fun createBin(value: Double): BinCounter {
|
fun createBin(value: Double): BinCounter {
|
||||||
val binDefinition = binFactory(value)
|
val binDefinition = binFactory(value)
|
||||||
val newBin = BinCounter(binDefinition)
|
val newBin = BinCounter(binDefinition)
|
||||||
synchronized(this) { bins[binDefinition.center] = newBin }
|
synchronized(this) {
|
||||||
|
bins[binDefinition.center] = newBin
|
||||||
|
}
|
||||||
return newBin
|
return newBin
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,6 +133,24 @@ public class TreeHistogramSpace(
|
|||||||
override val zero: UnivariateHistogram by lazy { fill { } }
|
override val zero: UnivariateHistogram by lazy { fill { } }
|
||||||
|
|
||||||
public companion object {
|
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.
|
* Build and fill a [UnivariateHistogram]. Returns a read-only histogram.
|
||||||
*/
|
*/
|
||||||
|
@ -13,7 +13,7 @@ class TreeHistogramTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun normalFill() {
|
fun normalFill() {
|
||||||
val histogram = UnivariateHistogram.uniform(0.1) {
|
val histogram = TreeHistogramSpace.uniform(0.1) {
|
||||||
repeat(100_000) {
|
repeat(100_000) {
|
||||||
putValue(Random.nextDouble())
|
putValue(Random.nextDouble())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user