From 15cc4a22e273f8fbff7a88b71ad09ac15d89b223 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 23 Nov 2018 21:44:42 +0300 Subject: [PATCH] Fixed some performance issues with histogram --- .../kmath/histogram/FastHistogram.kt | 56 +++++++++++-------- .../kotlin/scientifik/kmath/linear/Matrix.kt | 22 +++++++- .../kotlin/scientifik/kmath/linear/Vector.kt | 16 ------ .../scientifik/kmath/structures/Buffers.kt | 23 +++++++- .../kmath/structures/NDStructure.kt | 4 +- 5 files changed, 74 insertions(+), 47 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/FastHistogram.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/FastHistogram.kt index 37c4eed2a..f9388f668 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/FastHistogram.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/FastHistogram.kt @@ -1,24 +1,31 @@ package scientifik.kmath.histogram -import scientifik.kmath.linear.RealVector import scientifik.kmath.linear.toVector import scientifik.kmath.structures.Buffer +import scientifik.kmath.structures.ListBuffer import scientifik.kmath.structures.NDStructure import scientifik.kmath.structures.ndStructure import kotlin.math.floor -class MultivariateBin(override val center: RealVector, val sizes: RealVector, val counter: LongCounter = LongCounter()) : Bin { +typealias RealPoint = Point + +private operator fun RealPoint.minus(other: RealPoint) = ListBuffer((0 until size).map { get(it) - other[it] }) + +private inline fun Buffer.mapIndexed(crossinline mapper: (Int, Double) -> T): Sequence = (0 until size).asSequence().map { mapper(it, get(it)) } + + +class MultivariateBin(override val center: RealPoint, val sizes: RealPoint, var counter: Long = 0) : Bin { init { if (center.size != sizes.size) error("Dimension mismatch in bin creation. Expected ${center.size}, but found ${sizes.size}") } override fun contains(vector: Buffer): Boolean { if (vector.size != center.size) error("Dimension mismatch for input vector. Expected ${center.size}, but found ${vector.size}") - return vector.asSequence().mapIndexed { i, value -> value in (center[i] - sizes[i] / 2)..(center[i] + sizes[i] / 2) }.all { it } + return vector.mapIndexed { i, value -> value in (center[i] - sizes[i] / 2)..(center[i] + sizes[i] / 2) }.all { it } } - override val value get() = counter.sum() - internal operator fun inc() = this.also { counter.increment() } + override val value get() = counter + internal operator fun inc() = this.also { counter++ } override val dimension: Int get() = center.size } @@ -28,8 +35,8 @@ class MultivariateBin(override val center: RealVector, val sizes: RealVector, va * The histogram is optimized for speed, but have large size in memory */ class FastHistogram( - private val lower: RealVector, - private val upper: RealVector, + private val lower: RealPoint, + private val upper: RealPoint, private val binNums: IntArray = IntArray(lower.size) { 20 } ) : MutableHistogram { @@ -37,25 +44,27 @@ class FastHistogram( // argument checks if (lower.size != upper.size) error("Dimension mismatch in histogram lower and upper limits.") if (lower.size != binNums.size) error("Dimension mismatch in bin count.") - if ((upper - lower).any { it <= 0 }) error("Range for one of axis is not strictly positive") + if ((upper - lower).asSequence().any { it <= 0 }) error("Range for one of axis is not strictly positive") } override val dimension: Int get() = lower.size //TODO optimize binSize performance if needed - private val binSize = (upper - lower).mapIndexed { index, value -> value / binNums[index] }.toVector() + private val binSize: RealPoint = ListBuffer((upper - lower).mapIndexed { index, value -> value / binNums[index] }.toList()) private val bins: NDStructure by lazy { val actualSizes = IntArray(binNums.size) { binNums[it] + 2 } ndStructure(actualSizes) { indexArray -> - val center = indexArray.mapIndexed { axis, index -> - when (index) { - 0 -> Double.NEGATIVE_INFINITY - actualSizes[axis] - 1 -> Double.POSITIVE_INFINITY - else -> lower[axis] + (index.toDouble() - 0.5) * binSize[axis] - } - }.toVector() + val center = ListBuffer( + indexArray.mapIndexed { axis, index -> + when (index) { + 0 -> Double.NEGATIVE_INFINITY + actualSizes[axis] - 1 -> Double.POSITIVE_INFINITY + else -> lower[axis] + (index.toDouble() - 0.5) * binSize[axis] + } + } + ) MultivariateBin(center, binSize) } } @@ -91,13 +100,14 @@ class FastHistogram( return ndStructure(this.bins.shape) { bins[it].value } } - /** - * Create a phantom lightweight immutable copy of this histogram - */ - fun asPhantom(): PhantomHistogram { - val binTemplates = bins.associate { (index, bin) -> BinTemplate(bin.center, bin.sizes) to index } - return PhantomHistogram(binTemplates, asND()) - } +// /** +// * Create a phantom lightweight immutable copy of this histogram +// */ +// fun asPhantom(): PhantomHistogram { +// val center = +// val binTemplates = bins.associate { (index, bin) -> BinTemplate(bin.center, bin.sizes) to index } +// return PhantomHistogram(binTemplates, asND()) +// } companion object { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Matrix.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Matrix.kt index f7ae04849..243eb2d6c 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Matrix.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Matrix.kt @@ -1,9 +1,7 @@ package scientifik.kmath.linear import scientifik.kmath.operations.* -import scientifik.kmath.structures.ExtendedNDField -import scientifik.kmath.structures.GenericNDField -import scientifik.kmath.structures.NDField +import scientifik.kmath.structures.* /** * The space for linear elements. Supports scalar product alongside with standard linear operations. @@ -169,3 +167,21 @@ class ArrayMatrixSpace>( return ArrayMatrixSpace(rows, columns, field, ndFactory) } } + +/** + * Member of [ArrayMatrixSpace] which wraps 2-D array + */ +class ArrayMatrix> internal constructor(override val context: ArrayMatrixSpace, val element: NDElement) : Matrix { + + constructor(context: ArrayMatrixSpace, initializer: (Int, Int) -> T) : this(context, context.ndField.produce { list -> initializer(list[0], list[1]) }) + + override val rows: Int get() = context.rows + + override val columns: Int get() = context.columns + + override fun get(i: Int, j: Int): T { + return element[i, j] + } + + override val self: ArrayMatrix get() = this +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Vector.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Vector.kt index 3e0f91ebe..38bc54ab6 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Vector.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/Vector.kt @@ -73,23 +73,7 @@ class ArrayVectorSpace>( override fun produce(initializer: (Int) -> T): Vector = ArrayVector(this, initializer) } -/** - * Member of [ArrayMatrixSpace] which wraps 2-D array - */ -class ArrayMatrix> internal constructor(override val context: ArrayMatrixSpace, val element: NDElement) : Matrix { - constructor(context: ArrayMatrixSpace, initializer: (Int, Int) -> T) : this(context, context.ndField.produce { list -> initializer(list[0], list[1]) }) - - override val rows: Int get() = context.rows - - override val columns: Int get() = context.columns - - override fun get(i: Int, j: Int): T { - return element[i, j] - } - - override val self: ArrayMatrix get() = this -} class ArrayVector> internal constructor(override val context: VectorSpace, val element: NDElement) : Vector { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt index 052f9caa4..5b80fa135 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt @@ -4,12 +4,16 @@ package scientifik.kmath.structures /** * A generic random access structure for both primitives and objects */ -interface Buffer : Iterable { +interface Buffer { val size: Int operator fun get(index: Int): T + operator fun iterator(): Iterator + + fun asSequence(): Sequence = iterator().asSequence() + /** * A shallow copy of the buffer */ @@ -25,7 +29,20 @@ interface MutableBuffer : Buffer { override fun copy(): MutableBuffer } -inline class ListBuffer(private val list: MutableList) : MutableBuffer { + +inline class ListBuffer(private val list: List) : Buffer { + + override val size: Int + get() = list.size + + override fun get(index: Int): T = list[index] + + override fun iterator(): Iterator = list.iterator() + + override fun copy(): ListBuffer = ListBuffer(ArrayList(list)) +} + +inline class MutableListBuffer(private val list: MutableList) : MutableBuffer { override val size: Int get() = list.size @@ -38,7 +55,7 @@ inline class ListBuffer(private val list: MutableList) : MutableBuffer override fun iterator(): Iterator = list.iterator() - override fun copy(): MutableBuffer = ListBuffer(ArrayList(list)) + override fun copy(): MutableBuffer = MutableListBuffer(ArrayList(list)) } class ArrayBuffer(private val array: Array) : MutableBuffer { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt index a6880362d..ed05613c5 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt @@ -76,7 +76,7 @@ class DefaultStrides(override val shape: IntArray) : Strides { override fun offset(index: IntArray): Int { return index.mapIndexed { i, value -> if (value < 0 || value >= shape[i]) { - throw RuntimeException("Index $value out of shape bounds: (0,${shape[i]})") + throw RuntimeException("Index $value out of shape bounds: (0,${this.shape[i]})") } value * strides[i] }.sum() @@ -169,6 +169,6 @@ fun genericNdStructure(shape: IntArray, initializer: (IntArray) -> T): Mutab yield(initializer(it)) } } - val buffer = ListBuffer(sequence.toMutableList()) + val buffer = MutableListBuffer(sequence.toMutableList()) return MutableBufferNDStructure(strides, buffer) }