diff --git a/.gitignore b/.gitignore index 5b55fc854..e91684008 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .gradle -/build/ +**/build/ /.idea/ # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) @@ -8,4 +8,4 @@ # Cache of project .gradletasknamecache -gradle.properties \ No newline at end of file +artifactory.gradle \ No newline at end of file diff --git a/build.gradle b/build.gradle index 04700f377..51d476ed6 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,8 @@ buildscript { - ext.kotlin_version = '1.3.0-rc-146' + ext.kotlin_version = '1.3.0' repositories { jcenter() - maven { - url = "http://dl.bintray.com/kotlin/kotlin-eap" - } } dependencies { @@ -13,6 +10,7 @@ buildscript { classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4+" } } + allprojects { apply plugin: 'maven-publish' apply plugin: "com.jfrog.artifactory" @@ -21,29 +19,6 @@ allprojects { version = '0.0.1-SNAPSHOT' } - -artifactory { - contextUrl = "${artifactory_contextUrl}" //The base Artifactory URL if not overridden by the publisher/resolver - publish { - repository { - repoKey = 'gradle-dev-local' - username = "${artifactory_user}" - password = "${artifactory_password}" - } - - defaults { - publications('jvm', 'js', 'kotlinMultiplatform', 'metadata') - publishBuildInfo = false - publishArtifacts = true - publishPom = true - publishIvy = false - } - } - resolve { - repository { - repoKey = 'gradle-dev' - username = "${artifactory_user}" - password = "${artifactory_password}" - } - } +if(file('artifactory.gradle').exists()){ + apply from: 'artifactory.gradle' } \ No newline at end of file diff --git a/kmath-core/build.gradle b/kmath-core/build.gradle index 8e0e6ca2e..18be067e6 100644 --- a/kmath-core/build.gradle +++ b/kmath-core/build.gradle @@ -1,6 +1,5 @@ plugins { - id 'kotlin-multiplatform'// version '1.3.0-rc-116' - id "me.champeau.gradle.jmh" version "0.4.5" + id 'kotlin-multiplatform' } repositories { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/Counters.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/Counters.kt new file mode 100644 index 000000000..f6cc1f822 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/Counters.kt @@ -0,0 +1,20 @@ +package scientifik.kmath.histogram + +/* + * Common representation for atomic counters + */ + + +expect class LongCounter(){ + fun decrement() + fun increment() + fun reset() + fun sum(): Long + fun add(l:Long) +} + +expect class DoubleCounter(){ + fun reset() + fun sum(): Double + fun add(d: Double) +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/FastHistogram.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/FastHistogram.kt new file mode 100644 index 000000000..5f69f9d12 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/FastHistogram.kt @@ -0,0 +1,118 @@ +package scientifik.kmath.histogram + +import scientifik.kmath.linear.RealVector +import scientifik.kmath.linear.toVector +import scientifik.kmath.structures.Buffer +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 { + 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 } + } + + override val value: Number get() = counter.sum() + internal operator fun inc() = this.also { counter.increment() } + + override val dimension: Int get() = center.size +} + +/** + * Uniform multivariate histogram with fixed borders. Based on NDStructure implementation with complexity of m for bin search, where m is the number of dimensions + */ +class FastHistogram( + private val lower: RealVector, + private val upper: RealVector, + private val binNums: IntArray = IntArray(lower.size) { 20 } +) : Histogram { + + init { + // 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") + } + + + 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 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] -> Double.POSITIVE_INFINITY + else -> lower[axis] + (index - 1) * binSize[axis] + } + }.toVector() + MultivariateBin(center, binSize) + } + } + + /** + * Get internal [NDStructure] bin index for given axis + */ + private fun getIndex(axis: Int, value: Double): Int { + return when { + value >= upper[axis] -> binNums[axis] + 1 // overflow + value < lower[axis] -> 0 // underflow + else -> floor((value - lower[axis]) / binSize[axis]).toInt() + 1 + } + } + + + override fun get(point: Buffer): MultivariateBin? { + val index = IntArray(dimension) { getIndex(it, point[it]) } + return bins[index] + } + + override fun put(point: Buffer) { + this[point]?.inc() ?: error("Could not find appropriate bin (should not be possible)") + } + + override fun iterator(): Iterator = bins.asSequence().map { it.second }.iterator() + + companion object { + + /** + * Use it like + * ``` + *FastHistogram.fromRanges( + * (-1.0..1.0), + * (-1.0..1.0) + *) + *``` + */ + fun fromRanges(vararg ranges: ClosedFloatingPointRange): FastHistogram { + return FastHistogram(ranges.map { it.start }.toVector(), ranges.map { it.endInclusive }.toVector()) + } + + /** + * Use it like + * ``` + *FastHistogram.fromRanges( + * (-1.0..1.0) to 50, + * (-1.0..1.0) to 32 + *) + *``` + */ + fun fromRanges(vararg ranges: Pair, Int>): FastHistogram { + return FastHistogram( + ranges.map { it.first.start }.toVector(), + ranges.map { it.first.endInclusive }.toVector(), + ranges.map { it.second }.toIntArray() + ) + } + } + +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt new file mode 100644 index 000000000..1b5c231b4 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/histogram/Histogram.kt @@ -0,0 +1,62 @@ +package scientifik.kmath.histogram + +import scientifik.kmath.operations.Space +import scientifik.kmath.structures.ArrayBuffer +import scientifik.kmath.structures.Buffer + +/** + * A simple geometric domain + * TODO move to geometry module + */ +interface Domain { + operator fun contains(vector: Buffer): Boolean + val dimension: Int +} + +/** + * The bin in the histogram. The histogram is by definition always done in the real space + */ +interface Bin : Domain { + /** + * The value of this bin + */ + val value: Number + val center: Buffer +} + +interface Histogram> : Iterable { + + /** + * Find existing bin, corresponding to given coordinates + */ + operator fun get(point: Buffer): B? + + /** + * Dimension of the histogram + */ + val dimension: Int + + /** + * Increment appropriate bin + */ + fun put(point: Buffer) +} + +fun Histogram.put(vararg point: T) = put(ArrayBuffer(point)) + +fun Histogram.fill(sequence: Iterable>) = sequence.forEach { put(it) } + +/** + * Pass a sequence builder into histogram + */ +fun Histogram.fill(buider: suspend SequenceScope>.() -> Unit) = fill(sequence(buider).asIterable()) + +/** + * A space to perform arithmetic operations on histograms + */ +interface HistogramSpace, H : Histogram> : Space { + /** + * Rules for performing operations on bins + */ + val binSpace: Space> +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUDecomposition.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUDecomposition.kt index e51d574f8..0b71405c6 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LUDecomposition.kt @@ -1,18 +1,19 @@ package scientifik.kmath.linear -import scientifik.kmath.structures.MutableNDArray -import scientifik.kmath.structures.NDArray -import scientifik.kmath.structures.NDArrays +import scientifik.kmath.structures.MutableNDStructure +import scientifik.kmath.structures.NDStructure +import scientifik.kmath.structures.genericNdStructure +import scientifik.kmath.structures.get import kotlin.math.absoluteValue /** - * Implementation copier from Apache common-maths + * Implementation based on Apache common-maths LU-decomposition */ abstract class LUDecomposition>(val matrix: Matrix) { private val field get() = matrix.context.field /** Entries of LU decomposition. */ - internal val lu: NDArray + internal val lu: NDStructure /** Pivot permutation associated with LU decomposition. */ internal val pivot: IntArray /** Parity of the permutation associated with the LU decomposition. */ @@ -85,26 +86,18 @@ abstract class LUDecomposition>(val matrix: Matrix) { } } -// /** -// * Get a solver for finding the A X = B solution in exact linear -// * sense. -// * @return a solver -// */ -// val solver: DecompositionSolver -// get() = Solver(lu, pivot, singular) - /** * In-place transformation for [MutableNDArray], using given transformation for each element */ - operator fun MutableNDArray.set(i: Int, j: Int, value: T) { - this[listOf(i, j)] = value + operator fun MutableNDStructure.set(i: Int, j: Int, value: T) { + this[intArrayOf(i, j)] = value } abstract fun isSingular(value: T): Boolean private fun abs(value: T) = if (value > matrix.context.field.zero) value else with(matrix.context.field) { -value } - private fun calculateLU(): Pair, IntArray> { + private fun calculateLU(): Pair, IntArray> { if (matrix.rows != matrix.columns) { error("LU decomposition supports only square matrices") } @@ -112,7 +105,7 @@ abstract class LUDecomposition>(val matrix: Matrix) { val m = matrix.columns val pivot = IntArray(matrix.rows) //TODO fix performance - val lu: MutableNDArray = NDArrays.createMutable(matrix.context.field, listOf(matrix.rows, matrix.columns)) { index -> matrix[index[0], index[1]] } + val lu: MutableNDStructure = genericNdStructure(intArrayOf(matrix.rows, matrix.columns)) { index -> matrix[index[0], index[1]] } with(matrix.context.field) { @@ -203,44 +196,6 @@ class RealLUDecomposition(matrix: Matrix, private val singularityThresho /** Specialized solver. */ object RealLUSolver : LinearSolver { -// -// /** {@inheritDoc} */ -// override fun solve(b: RealVector): RealVector { -// val m = pivot.size -// if (b.getDimension() != m) { -// throw DimensionMismatchException(b.getDimension(), m) -// } -// if (singular) { -// throw SingularMatrixException() -// } -// -// val bp = DoubleArray(m) -// -// // Apply permutations to b -// for (row in 0 until m) { -// bp[row] = b.getEntry(pivot[row]) -// } -// -// // Solve LY = b -// for (col in 0 until m) { -// val bpCol = bp[col] -// for (i in col + 1 until m) { -// bp[i] -= bpCol * lu[i][col] -// } -// } -// -// // Solve UX = Y -// for (col in m - 1 downTo 0) { -// bp[col] /= lu[col][col] -// val bpCol = bp[col] -// for (i in 0 until col) { -// bp[i] -= bpCol * lu[i][col] -// } -// } -// -// return ArrayRealVector(bp, false) -// } - fun decompose(mat: Matrix, threshold: Double = 1e-11): RealLUDecomposition = RealLUDecomposition(mat, threshold) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgrebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgrebra.kt index 68961a2db..e585b884d 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgrebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/LinearAlgrebra.kt @@ -4,10 +4,7 @@ import scientifik.kmath.operations.DoubleField import scientifik.kmath.operations.Field import scientifik.kmath.operations.Space import scientifik.kmath.operations.SpaceElement -import scientifik.kmath.structures.NDArray -import scientifik.kmath.structures.NDArrays.createFactory -import scientifik.kmath.structures.NDFieldFactory -import scientifik.kmath.structures.realNDFieldFactory +import scientifik.kmath.structures.* /** * The space for linear elements. Supports scalar product alongside with standard linear operations. @@ -162,11 +159,8 @@ abstract class VectorSpace(val size: Int, val field: Field) : Space< } -interface Vector : SpaceElement, VectorSpace> { - val size: Int - get() = context.size - - operator fun get(i: Int): T +interface Vector : SpaceElement, VectorSpace>, Buffer, Iterable { + override val size: Int get() = context.size companion object { /** @@ -181,6 +175,7 @@ interface Vector : SpaceElement, VectorSpace> { fun ofReal(size: Int, initializer: (Int) -> Double) = ArrayVector(ArrayVectorSpace(size, DoubleField, realNDFieldFactory), initializer) + fun ofReal(vararg point: Double) = point.toVector() fun equals(v1: Vector<*>, v2: Vector<*>): Boolean { if (v1 === v2) return true @@ -193,6 +188,11 @@ interface Vector : SpaceElement, VectorSpace> { } } +typealias NDFieldFactory = (IntArray) -> NDField + +internal fun genericNDFieldFactory(field: Field): NDFieldFactory = { index -> GenericNDField(index, field) } +internal val realNDFieldFactory: NDFieldFactory = { index -> GenericNDField(index, DoubleField) } + /** * NDArray-based implementation of vector space. By default uses slow [SimpleNDField], but could be overridden with custom [NDField] factory. @@ -201,11 +201,11 @@ class ArrayMatrixSpace( rows: Int, columns: Int, field: Field, - val ndFactory: NDFieldFactory = createFactory(field) + val ndFactory: NDFieldFactory = genericNDFieldFactory(field) ) : MatrixSpace(rows, columns, field) { val ndField by lazy { - ndFactory(listOf(rows, columns)) + ndFactory(intArrayOf(rows, columns)) } override fun produce(initializer: (Int, Int) -> T): Matrix = ArrayMatrix(this, initializer) @@ -218,10 +218,10 @@ class ArrayMatrixSpace( class ArrayVectorSpace( size: Int, field: Field, - val ndFactory: NDFieldFactory = createFactory(field) + val ndFactory: NDFieldFactory = genericNDFieldFactory(field) ) : VectorSpace(size, field) { val ndField by lazy { - ndFactory(listOf(size)) + ndFactory(intArrayOf(size)) } override fun produce(initializer: (Int) -> T): Vector = ArrayVector(this, initializer) @@ -256,13 +256,21 @@ class ArrayVector internal constructor(override val context: ArrayVecto } } - override fun get(i: Int): T { - return array[i] + override fun get(index: Int): T { + return array[index] } override val self: ArrayVector get() = this + + override fun iterator(): Iterator = (0 until size).map { array[it] }.iterator() + + override fun copy(): ArrayVector = ArrayVector(context, array) + + override fun toString(): String = this.joinToString(prefix = "[", postfix = "]", separator = ", ") { it.toString() } } +typealias RealVector = Vector + /** * A group of methods to resolve equation A dot X = B, where A and B are matrices or vectors */ @@ -278,6 +286,7 @@ interface LinearSolver { fun Array.toVector(field: Field) = Vector.of(size, field) { this[it] } fun DoubleArray.toVector() = Vector.ofReal(this.size) { this[it] } +fun List.toVector() = Vector.ofReal(this.size) { this[it] } /** * Convert matrix to vector if it is possible @@ -306,6 +315,6 @@ fun Vector.toMatrix(): Matrix { // //Generic vector // matrix(size, 1, context.field) { i, j -> get(i) } // } - return Matrix.of(size, 1, context.field) { i, j -> get(i) } + return Matrix.of(size, 1, context.field) { i, _ -> get(i) } } diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferNDField.kt deleted file mode 100644 index aab0f1b97..000000000 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BufferNDField.kt +++ /dev/null @@ -1,112 +0,0 @@ -package scientifik.kmath.structures - -import scientifik.kmath.operations.Field - - -/** - * A generic buffer for both primitives and objects - */ -interface Buffer { - operator fun get(index: Int): T - operator fun set(index: Int, value: T) - - /** - * A shallow copy of the buffer - */ - fun copy(): Buffer -} - -/** - * Generic implementation of NDField based on continuous buffer - */ -abstract class BufferNDField(shape: List, field: Field) : NDField(shape, field) { - - /** - * Strides for memory access - */ - private val strides: List by lazy { - ArrayList(shape.size).apply { - var current = 1 - add(1) - shape.forEach { - current *= it - add(current) - } - } - } - - protected fun offset(index: List): Int { - return index.mapIndexed { i, value -> - if (value < 0 || value >= shape[i]) { - throw RuntimeException("Index out of shape bounds: ($i,$value)") - } - value * strides[i] - }.sum() - } - - //TODO introduce a fast way to calculate index of the next element? - protected fun index(offset: Int): List { - return sequence { - var current = offset - var strideIndex = strides.size - 2 - while (strideIndex >= 0) { - yield(current / strides[strideIndex]) - current %= strides[strideIndex] - strideIndex-- - } - }.toList().reversed() - } - - private val capacity: Int - get() = strides[shape.size] - - - protected abstract fun createBuffer(capacity: Int, initializer: (Int) -> T): Buffer - - override fun produce(initializer: (List) -> T): NDArray { - val buffer = createBuffer(capacity) { initializer(index(it)) } - return BufferNDArray(this, buffer) - } - - /** - * Produce mutable NDArray instance - */ - fun produceMutable(initializer: (List) -> T): MutableNDArray { - val buffer = createBuffer(capacity) { initializer(index(it)) } - return MutableBufferedNDArray(this, buffer) - } - - - private open class BufferNDArray(override val context: BufferNDField, val data: Buffer) : NDArray { - - override fun get(vararg index: Int): T { - return data[context.offset(index.asList())] - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is BufferNDArray<*>) return false - - if (context != other.context) return false - if (data != other.data) return false - - return true - } - - override fun hashCode(): Int { - var result = context.hashCode() - result = 31 * result + data.hashCode() - return result - } - - override val self: NDArray get() = this - } - - private class MutableBufferedNDArray(context: BufferNDField, data: Buffer): BufferNDArray(context,data), MutableNDArray{ - override operator fun set(index: List, value: T){ - data[context.offset(index)] = value - } - } -} - - diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt new file mode 100644 index 000000000..04983ef85 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/Buffers.kt @@ -0,0 +1,80 @@ +package scientifik.kmath.structures + + +/** + * A generic linear buffer for both primitives and objects + */ +interface Buffer : Iterable { + + val size: Int + + operator fun get(index: Int): T + + /** + * A shallow copy of the buffer + */ + fun copy(): Buffer +} + +interface MutableBuffer : Buffer { + operator fun set(index: Int, value: T) + + /** + * A shallow copy of the buffer + */ + override fun copy(): MutableBuffer +} + +inline class ListBuffer(private val list: MutableList) : MutableBuffer { + + override val size: Int + get() = list.size + + override fun get(index: Int): T = list[index] + + override fun set(index: Int, value: T) { + list[index] = value + } + + override fun iterator(): Iterator = list.iterator() + + override fun copy(): MutableBuffer = ListBuffer(ArrayList(list)) +} + +class ArrayBuffer(private val array: Array) : MutableBuffer { + override val size: Int + get() = array.size + + override fun get(index: Int): T = array[index] + + override fun set(index: Int, value: T) { + array[index] = value + } + + override fun iterator(): Iterator = array.iterator() + + override fun copy(): MutableBuffer = ArrayBuffer(array.copyOf()) +} + +class DoubleBuffer(private val array: DoubleArray) : MutableBuffer { + override val size: Int + get() = array.size + + override fun get(index: Int): Double = array[index] + + override fun set(index: Int, value: Double) { + array[index] = value + } + + override fun iterator(): Iterator = array.iterator() + + override fun copy(): MutableBuffer = DoubleBuffer(array.copyOf()) +} + +inline fun buffer(size: Int, noinline initializer: (Int) -> T): Buffer { + return ArrayBuffer(Array(size, initializer)) +} + +inline fun mutableBuffer(size: Int, noinline initializer: (Int) -> T): MutableBuffer { + return ArrayBuffer(Array(size, initializer)) +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDArrays.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDArrays.kt deleted file mode 100644 index 739e91832..000000000 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDArrays.kt +++ /dev/null @@ -1,72 +0,0 @@ -package scientifik.kmath.structures - -import scientifik.kmath.operations.Field - -typealias NDFieldFactory = (shape: List) -> NDField - -/** - * The factory class for fast platform-dependent implementation of NDField of doubles - */ -expect val realNDFieldFactory: NDFieldFactory - - -class SimpleNDField(field: Field, shape: List) : BufferNDField(shape, field) { - override fun createBuffer(capacity: Int, initializer: (Int) -> T): Buffer { - val array = ArrayList(capacity) - (0 until capacity).forEach { - array.add(initializer(it)) - } - - return BufferOfObjects(array) - } - - private class BufferOfObjects(val array: ArrayList) : Buffer { - override fun get(index: Int): T = array[index] - - override fun set(index: Int, value: T) { - array[index] = value - } - - override fun copy(): Buffer = BufferOfObjects(ArrayList(array)) - } -} - -object NDArrays { - /** - * Create a platform-optimized NDArray of doubles - */ - fun realNDArray(shape: List, initializer: (List) -> Double = { 0.0 }): NDArray { - return realNDFieldFactory(shape).produce(initializer) - } - - fun real1DArray(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }): NDArray { - return realNDArray(listOf(dim)) { initializer(it[0]) } - } - - fun real2DArray(dim1: Int, dim2: Int, initializer: (Int, Int) -> Double = { _, _ -> 0.0 }): NDArray { - return realNDArray(listOf(dim1, dim2)) { initializer(it[0], it[1]) } - } - - fun real3DArray(dim1: Int, dim2: Int, dim3: Int, initializer: (Int, Int, Int) -> Double = { _, _, _ -> 0.0 }): NDArray { - return realNDArray(listOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) } - } - - /** - * Simple boxing NDField - */ - fun createFactory(field: Field): NDFieldFactory = { shape -> SimpleNDField(field, shape) } - - /** - * Simple boxing NDArray - */ - fun create(field: Field, shape: List, initializer: (List) -> T): NDArray { - return SimpleNDField(field, shape).produce { initializer(it) } - } - - /** - * Mutable boxing NDArray - */ - fun createMutable(field: Field, shape: List, initializer: (List) -> T): MutableNDArray { - return SimpleNDField(field, shape).produceMutable { initializer(it) } - } -} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDArray.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDField.kt similarity index 51% rename from kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDArray.kt rename to kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDField.kt index 85f60704f..9461bee85 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDArray.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDField.kt @@ -1,12 +1,13 @@ package scientifik.kmath.structures +import scientifik.kmath.operations.DoubleField import scientifik.kmath.operations.Field import scientifik.kmath.operations.FieldElement /** * An exception is thrown when the expected ans actual shape of NDArray differs */ -class ShapeMismatchException(val expected: List, val actual: List) : RuntimeException() +class ShapeMismatchException(val expected: IntArray, val actual: IntArray) : RuntimeException() /** * Field for n-dimensional arrays. @@ -14,13 +15,15 @@ class ShapeMismatchException(val expected: List, val actual: List) : R * @param field - operations field defined on individual array element * @param T the type of the element contained in NDArray */ -abstract class NDField(val shape: List, val field: Field) : Field> { +abstract class NDField(val shape: IntArray, val field: Field) : Field> { + + abstract fun produceStructure(initializer: (IntArray) -> T): NDStructure /** * Create new instance of NDArray using field shape and given initializer * The producer takes list of indices as argument and returns contained value */ - abstract fun produce(initializer: (List) -> T): NDArray + fun produce(initializer: (IntArray) -> T): NDArray = NDArray(this, produceStructure(initializer)) override val zero: NDArray by lazy { produce { this.field.zero } @@ -31,7 +34,7 @@ abstract class NDField(val shape: List, val field: Field) : Field) { arrays.forEach { - if (shape != it.shape) { + if (!shape.contentEquals(it.shape)) { throw ShapeMismatchException(shape, it.shape) } } @@ -71,76 +74,47 @@ abstract class NDField(val shape: List, val field: Field) : Field : FieldElement, NDField> { /** - * The list of dimensions of this NDArray + * Reverse sum operation */ - val shape: List - get() = context.shape + operator fun T.plus(arg: NDArray): NDArray = arg + this /** - * The number of dimentsions for this array + * Reverse minus operation */ - val dimension: Int - get() = shape.size - - /** - * Get the element with given indexes. If number of indexes is different from {@link dimension}, throws exception. - */ - operator fun get(vararg index: Int): T - - operator fun get(index: List): T { - return get(*index.toIntArray()) - } - - operator fun iterator(): Iterator, T>> { - return iterateIndexes(shape).map { Pair(it, this[it]) }.iterator() + operator fun T.minus(arg: NDArray): NDArray = arg.transform { _, value -> + with(arg.context.field) { + this@minus - value + } } /** - * Generate new NDArray, using given transformation for each element + * Reverse product operation */ - fun transform(action: (List, T) -> T): NDArray = context.produce { action(it, this[it]) } + operator fun T.times(arg: NDArray): NDArray = arg * this - companion object { - /** - * Iterate over all indexes in the nd-shape - */ - fun iterateIndexes(shape: List): Sequence> { - return if (shape.size == 1) { - (0 until shape[0]).asSequence().map { listOf(it) } - } else { - val tailShape = ArrayList(shape).apply { removeAt(0) } - val tailSequence: List> = iterateIndexes(tailShape).toList() - (0 until shape[0]).asSequence().map { firstIndex -> - //adding first element to each of provided index lists - tailSequence.map { listOf(firstIndex) + it }.asSequence() - }.flatten() - } + /** + * Reverse division operation + */ + operator fun T.div(arg: NDArray): NDArray = arg.transform { _, value -> + with(arg.context.field) { + this@div / value } } } /** - * In-place mutable [NDArray] + * Immutable [NDStructure] coupled to the context. Emulates Python ndarray */ -interface MutableNDArray : NDArray { - operator fun set(index: List, value: T) -} +data class NDArray(override val context: NDField, private val structure: NDStructure) : FieldElement, NDField>, NDStructure by structure { -/** - * In-place transformation for [MutableNDArray], using given transformation for each element - */ -fun MutableNDArray.transformInPlace(action: (List, T) -> T) { - for ((index, oldValue) in this) { - this[index] = action(index, oldValue) - } + //TODO ensure structure is immutable + + override val self: NDArray + get() = this + + fun transform(action: (IntArray, T) -> T): NDArray = context.produce { action(it, get(*it)) } } /** @@ -159,11 +133,6 @@ operator fun NDArray.plus(arg: T): NDArray = transform { _, value -> } } -/** - * Reverse sum operation - */ -operator fun T.plus(arg: NDArray): NDArray = arg + this - /** * Subtraction operation between [NDArray] and single element */ @@ -173,15 +142,6 @@ operator fun NDArray.minus(arg: T): NDArray = transform { _, value -> } } -/** - * Reverse minus operation - */ -operator fun T.minus(arg: NDArray): NDArray = arg.transform { _, value -> - with(arg.context.field) { - this@minus - value - } -} - /* prod and div */ /** @@ -193,11 +153,6 @@ operator fun NDArray.times(arg: T): NDArray = transform { _, value -> } } -/** - * Reverse product operation - */ -operator fun T.times(arg: NDArray): NDArray = arg * this - /** * Division operation between [NDArray] and single element */ @@ -207,12 +162,41 @@ operator fun NDArray.div(arg: T): NDArray = transform { _, value -> } } -/** - * Reverse division operation - */ -operator fun T.div(arg: NDArray): NDArray = arg.transform { _, value -> - with(arg.context.field) { - this@div / value - } +class GenericNDField(shape: IntArray, field: Field) : NDField(shape, field) { + override fun produceStructure(initializer: (IntArray) -> T): NDStructure = genericNdStructure(shape, initializer) } +//typealias NDFieldFactory = (IntArray)->NDField + +object NDArrays { + /** + * Create a platform-optimized NDArray of doubles + */ + fun realNDArray(shape: IntArray, initializer: (IntArray) -> Double = { 0.0 }): NDArray { + return GenericNDField(shape, DoubleField).produce(initializer) + } + + fun real1DArray(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }): NDArray { + return realNDArray(intArrayOf(dim)) { initializer(it[0]) } + } + + fun real2DArray(dim1: Int, dim2: Int, initializer: (Int, Int) -> Double = { _, _ -> 0.0 }): NDArray { + return realNDArray(intArrayOf(dim1, dim2)) { initializer(it[0], it[1]) } + } + + fun real3DArray(dim1: Int, dim2: Int, dim3: Int, initializer: (Int, Int, Int) -> Double = { _, _, _ -> 0.0 }): NDArray { + return realNDArray(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) } + } + +// /** +// * Simple boxing NDField +// */ +// fun fieldFactory(field: Field): NDFieldFactory = { shape -> GenericNDField(shape, field) } + + /** + * Simple boxing NDArray + */ + fun create(field: Field, shape: IntArray, initializer: (IntArray) -> T): NDArray { + return GenericNDField(shape, field).produce { initializer(it) } + } +} diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt new file mode 100644 index 000000000..a6880362d --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDStructure.kt @@ -0,0 +1,174 @@ +package scientifik.kmath.structures + + +interface NDStructure : Iterable> { + + val shape: IntArray + + val dimension + get() = shape.size + + operator fun get(index: IntArray): T +} + +operator fun NDStructure.get(vararg index: Int): T = get(index) + +interface MutableNDStructure : NDStructure { + operator fun set(index: IntArray, value: T) +} + +fun MutableNDStructure.transformInPlace(action: (IntArray, T) -> T) { + for ((index, oldValue) in this) { + this[index] = action(index, oldValue) + } +} + +/** + * A way to convert ND index to linear one and back + */ +interface Strides { + /** + * Shape of NDstructure + */ + val shape: IntArray + + /** + * Array strides + */ + val strides: List + + /** + * Get linear index from multidimensional index + */ + fun offset(index: IntArray): Int + + /** + * Get multidimensional from linear + */ + fun index(offset: Int): IntArray + + val linearSize: Int + + /** + * Iterate over ND indices in a natural order + */ + fun indices(): Sequence { + //TODO introduce a fast way to calculate index of the next element? + return (0 until linearSize).asSequence().map { index(it) } + } +} + +class DefaultStrides(override val shape: IntArray) : Strides { + /** + * Strides for memory access + */ + override val strides by lazy { + sequence { + var current = 1 + yield(1) + shape.forEach { + current *= it + yield(current) + } + }.toList() + } + + 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]})") + } + value * strides[i] + }.sum() + } + + override fun index(offset: Int): IntArray { + return sequence { + var current = offset + var strideIndex = strides.size - 2 + while (strideIndex >= 0) { + yield(current / strides[strideIndex]) + current %= strides[strideIndex] + strideIndex-- + } + }.toList().reversed().toIntArray() + } + + override val linearSize: Int + get() = strides[shape.size] +} + +abstract class GenericNDStructure> : NDStructure { + protected abstract val buffer: B + protected abstract val strides: Strides + + override fun get(index: IntArray): T = buffer[strides.offset(index)] + + override val shape: IntArray + get() = strides.shape + + override fun iterator(): Iterator> = + strides.indices().map { it to this[it] }.iterator() +} + +/** + * Boxing generic [NDStructure] + */ +class BufferNDStructure( + override val strides: Strides, + override val buffer: Buffer +) : GenericNDStructure>() { + + init { + if (strides.linearSize != buffer.size) { + error("Expected buffer side of ${strides.linearSize}, but found ${buffer.size}") + } + } +} + +inline fun ndStructure(strides: Strides, noinline initializer: (IntArray) -> T) = + BufferNDStructure(strides, buffer(strides.linearSize){ i-> initializer(strides.index(i))}) + +inline fun ndStructure(shape: IntArray, noinline initializer: (IntArray) -> T) = + ndStructure(DefaultStrides(shape), initializer) + + +/** + * Mutable ND buffer based on linear [Buffer] + */ +class MutableBufferNDStructure( + override val strides: Strides, + override val buffer: MutableBuffer +) : GenericNDStructure>(), MutableNDStructure { + + init { + if (strides.linearSize != buffer.size) { + error("Expected buffer side of ${strides.linearSize}, but found ${buffer.size}") + } + } + + override fun set(index: IntArray, value: T) = buffer.set(strides.offset(index), value) +} + +/** + * Create optimized mutable structure for given type + */ +inline fun mutableNdStructure(strides: Strides, noinline initializer: (IntArray) -> T) = + MutableBufferNDStructure(strides, mutableBuffer(strides.linearSize) { i -> initializer(strides.index(i)) }) + +inline fun mutableNdStructure(shape: IntArray, noinline initializer: (IntArray) -> T) = + mutableNdStructure(DefaultStrides(shape), initializer) + +/** + * Create universal mutable structure + */ +fun genericNdStructure(shape: IntArray, initializer: (IntArray) -> T): MutableBufferNDStructure{ + val strides = DefaultStrides(shape) + val sequence = sequence{ + strides.indices().forEach{ + yield(initializer(it)) + } + } + val buffer = ListBuffer(sequence.toMutableList()) + return MutableBufferNDStructure(strides, buffer) +} diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/histogram/MultivariateHistogramTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/histogram/MultivariateHistogramTest.kt new file mode 100644 index 000000000..2fbbbc88d --- /dev/null +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/histogram/MultivariateHistogramTest.kt @@ -0,0 +1,43 @@ +package scientifik.kmath.histogram + +import scientifik.kmath.linear.Vector +import kotlin.random.Random +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class MultivariateHistogramTest { + @Test + fun testSinglePutHistogram() { + val histogram = FastHistogram.fromRanges( + (-1.0..1.0), + (-1.0..1.0) + ) + histogram.put(0.6, 0.6) + val bin = histogram.find { it.value.toInt() > 0 }!! + assertTrue { bin.contains(Vector.ofReal(0.6, 0.6)) } + assertFalse { bin.contains(Vector.ofReal(-0.6, 0.6)) } + } + + @Test + fun testSequentialPut(){ + val histogram = FastHistogram.fromRanges( + (-1.0..1.0), + (-1.0..1.0), + (-1.0..1.0) + ) + val random = Random(1234) + + fun nextDouble() = random.nextDouble(-1.0,1.0) + + val n = 10000 + + histogram.fill { + repeat(n){ + yield(Vector.ofReal(nextDouble(),nextDouble(),nextDouble())) + } + } + assertEquals(n, histogram.sumBy { it.value.toInt() }) + } +} \ No newline at end of file diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/ArrayMatrixTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/ArrayMatrixTest.kt index ecc2ca28c..f767d682b 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/ArrayMatrixTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/linear/ArrayMatrixTest.kt @@ -7,15 +7,15 @@ class ArrayMatrixTest { @Test fun testSum() { - val vector1 = realVector(5) { it.toDouble() } - val vector2 = realVector(5) { 5 - it.toDouble() } + val vector1 = Vector.ofReal(5) { it.toDouble() } + val vector2 = Vector.ofReal(5) { 5 - it.toDouble() } val sum = vector1 + vector2 assertEquals(5.0, sum[2]) } @Test fun testVectorToMatrix() { - val vector = realVector(5) { it.toDouble() } + val vector = Vector.ofReal(5) { it.toDouble() } val matrix = vector.toMatrix() assertEquals(4.0, matrix[4, 0]) } @@ -23,8 +23,8 @@ class ArrayMatrixTest { @Test fun testDot() { - val vector1 = realVector(5) { it.toDouble() } - val vector2 = realVector(5) { 5 - it.toDouble() } + val vector1 = Vector.ofReal(5) { it.toDouble() } + val vector2 = Vector.ofReal(5) { 5 - it.toDouble() } val product = vector1.toMatrix() dot (vector2.toMatrix().transpose()) diff --git a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/SimpleNDFieldTest.kt b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/GenericNDFieldTest.kt similarity index 70% rename from kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/SimpleNDFieldTest.kt rename to kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/GenericNDFieldTest.kt index 94c1e15cc..338f5f052 100644 --- a/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/SimpleNDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/scientifik/kmath/structures/GenericNDFieldTest.kt @@ -6,10 +6,10 @@ import kotlin.test.Test import kotlin.test.assertEquals -class SimpleNDFieldTest{ +class GenericNDFieldTest{ @Test fun testStrides(){ - val ndArray = create(DoubleField, listOf(10,10)){(it[0]+it[1]).toDouble()} + val ndArray = create(DoubleField, intArrayOf(10,10)){(it[0]+it[1]).toDouble()} assertEquals(ndArray[5,5], 10.0) } diff --git a/kmath-core/src/jsMain/kotlin/scientifik/kmath/histogram/Counters.kt b/kmath-core/src/jsMain/kotlin/scientifik/kmath/histogram/Counters.kt new file mode 100644 index 000000000..3c2915587 --- /dev/null +++ b/kmath-core/src/jsMain/kotlin/scientifik/kmath/histogram/Counters.kt @@ -0,0 +1,16 @@ +package scientifik.kmath.histogram + +actual class LongCounter{ + private var sum: Long = 0 + actual fun decrement() {sum--} + actual fun increment() {sum++} + actual fun reset() {sum = 0} + actual fun sum(): Long = sum + actual fun add(l: Long) {sum+=l} +} +actual class DoubleCounter{ + private var sum: Double = 0.0 + actual fun reset() {sum = 0.0} + actual fun sum(): Double = sum + actual fun add(d: Double) {sum+=d} +} \ No newline at end of file diff --git a/kmath-core/src/jsMain/kotlin/scientifik/kmath/structures/_NDArrays.kt b/kmath-core/src/jsMain/kotlin/scientifik/kmath/structures/_NDArrays.kt deleted file mode 100644 index 32d66a04b..000000000 --- a/kmath-core/src/jsMain/kotlin/scientifik/kmath/structures/_NDArrays.kt +++ /dev/null @@ -1,8 +0,0 @@ -package scientifik.kmath.structures - -import scientifik.kmath.operations.DoubleField - -/** - * Using boxing implementation for js - */ -actual val realNDFieldFactory: NDFieldFactory = NDArrays.createFactory(DoubleField) \ No newline at end of file diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/histogram/Counters.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/histogram/Counters.kt new file mode 100644 index 000000000..bb3667f7d --- /dev/null +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/histogram/Counters.kt @@ -0,0 +1,7 @@ +package scientifik.kmath.histogram + +import java.util.concurrent.atomic.DoubleAdder +import java.util.concurrent.atomic.LongAdder + +actual typealias LongCounter = LongAdder +actual typealias DoubleCounter = DoubleAdder \ No newline at end of file diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt new file mode 100644 index 000000000..2382c379c --- /dev/null +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/histogram/UnivariateHistogram.kt @@ -0,0 +1,89 @@ +package scientifik.kmath.histogram + +import scientifik.kmath.linear.RealVector +import scientifik.kmath.linear.toVector +import scientifik.kmath.structures.Buffer +import java.util.* +import kotlin.math.floor + +//TODO move to common + +class UnivariateBin(val position: Double, val size: Double, val counter: LongCounter = LongCounter()) : Bin { + //TODO add weighting + override val value: Number get() = counter.sum() + + override val center: RealVector get() = doubleArrayOf(position).toVector() + + operator fun contains(value: Double): Boolean = value in (position - size / 2)..(position + size / 2) + + override fun contains(vector: Buffer): Boolean = contains(vector[0]) + + internal operator fun inc() = this.also { counter.increment()} + + override val dimension: Int get() = 1 +} + +/** + * Univariate histogram with log(n) bin search speed + */ +class UnivariateHistogram private constructor(private val factory: (Double) -> UnivariateBin) : Histogram { + + private val bins: TreeMap = TreeMap() + + private operator fun get(value: Double): UnivariateBin? { + // check ceiling entry and return it if it is what needed + val ceil = bins.ceilingEntry(value)?.value + if (ceil != null && value in ceil) return ceil + //check floor entry + val floor = bins.floorEntry(value)?.value + if (floor != null && value in floor) return floor + //neither is valid, not found + return null + } + + private fun createBin(value: Double): UnivariateBin = factory(value).also { + synchronized(this) { bins.put(it.position, it) } + } + + override fun get(point: Buffer): UnivariateBin? = get(point[0]) + + override val dimension: Int get() = 1 + + override fun iterator(): Iterator = bins.values.iterator() + + /** + * Thread safe put operation + */ + fun put(value: Double) { + (get(value) ?: createBin(value)).inc() + } + + override fun put(point: Buffer) = put(point[0]) + + companion object { + fun uniform(binSize: Double, start: Double = 0.0): UnivariateHistogram { + return UnivariateHistogram { value -> + val center = start + binSize * floor((value - start) / binSize + 0.5) + UnivariateBin(center, binSize) + } + } + + fun custom(borders: DoubleArray): UnivariateHistogram { + val sorted = borders.sortedArray() + return UnivariateHistogram { value -> + when { + value < sorted.first() -> UnivariateBin(Double.NEGATIVE_INFINITY, Double.MAX_VALUE) + value > sorted.last() -> UnivariateBin(Double.POSITIVE_INFINITY, Double.MAX_VALUE) + else -> { + val index = (0 until sorted.size).first { value > sorted[it] } + val left = sorted[index] + val right = sorted[index + 1] + UnivariateBin((left + right) / 2, (right - left)) + } + } + } + } + } +} + +fun UnivariateHistogram.fill(sequence: Iterable) = sequence.forEach { put(it) } diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/_NDArrays.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/_NDArrays.kt deleted file mode 100644 index 19d6c4041..000000000 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/_NDArrays.kt +++ /dev/null @@ -1,26 +0,0 @@ -package scientifik.kmath.structures - -import scientifik.kmath.operations.DoubleField -import java.nio.DoubleBuffer - -private class RealNDField(shape: List) : BufferNDField(shape, DoubleField) { - override fun createBuffer(capacity: Int, initializer: (Int) -> Double): Buffer { - val array = DoubleArray(capacity, initializer) - val buffer = DoubleBuffer.wrap(array) - return BufferOfDoubles(buffer) - } - - private class BufferOfDoubles(val buffer: DoubleBuffer): Buffer{ - override fun get(index: Int): Double = buffer.get(index) - - override fun set(index: Int, value: Double) { - buffer.put(index, value) - } - - override fun copy(): Buffer { - return BufferOfDoubles(buffer) - } - } -} - -actual val realNDFieldFactory: NDFieldFactory = { shape -> RealNDField(shape) } \ No newline at end of file diff --git a/kmath-jmh/build.gradle b/kmath-jmh/build.gradle new file mode 100644 index 000000000..a16bf3022 --- /dev/null +++ b/kmath-jmh/build.gradle @@ -0,0 +1,15 @@ +plugins { + id "java" + id "kotlin" + id "me.champeau.gradle.jmh" version "0.4.7" +} + +repositories { + maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' } + mavenCentral() +} + +dependencies { + implementation project(':kmath-core') + jmh 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' +} \ No newline at end of file diff --git a/kmath-core/src/jmh/kotlin/scietifik/kmath/structures/ArrayBenchmark.kt b/kmath-jmh/src/jmh/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt similarity index 94% rename from kmath-core/src/jmh/kotlin/scietifik/kmath/structures/ArrayBenchmark.kt rename to kmath-jmh/src/jmh/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt index 3430b14d4..eb40195e0 100644 --- a/kmath-core/src/jmh/kotlin/scietifik/kmath/structures/ArrayBenchmark.kt +++ b/kmath-jmh/src/jmh/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt @@ -1,4 +1,4 @@ -package scietifik.kmath.structures +package scientifik.kmath.structures import org.openjdk.jmh.annotations.* import java.nio.IntBuffer @@ -6,7 +6,7 @@ import java.nio.IntBuffer @Fork(1) @Warmup(iterations = 2) -@Measurement(iterations = 50) +@Measurement(iterations = 5) @State(Scope.Benchmark) open class ArrayBenchmark { diff --git a/settings.gradle b/settings.gradle index 03760221a..df4ccc99e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,5 @@ pluginManagement { repositories { - maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' } mavenCentral() maven { url = 'https://plugins.gradle.org/m2/' } } @@ -10,4 +9,5 @@ enableFeaturePreview('GRADLE_METADATA') rootProject.name = 'kmath' include ':kmath-core' +include ':kmath-jmh'