Dev #20
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,5 +1,5 @@
|
|||||||
.gradle
|
.gradle
|
||||||
/build/
|
**/build/
|
||||||
/.idea/
|
/.idea/
|
||||||
|
|
||||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||||
@ -8,4 +8,4 @@
|
|||||||
# Cache of project
|
# Cache of project
|
||||||
.gradletasknamecache
|
.gradletasknamecache
|
||||||
|
|
||||||
gradle.properties
|
artifactory.gradle
|
33
build.gradle
33
build.gradle
@ -1,11 +1,8 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.3.0-rc-146'
|
ext.kotlin_version = '1.3.0'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
maven {
|
|
||||||
url = "http://dl.bintray.com/kotlin/kotlin-eap"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -13,6 +10,7 @@ buildscript {
|
|||||||
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4+"
|
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4+"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
apply plugin: 'maven-publish'
|
apply plugin: 'maven-publish'
|
||||||
apply plugin: "com.jfrog.artifactory"
|
apply plugin: "com.jfrog.artifactory"
|
||||||
@ -21,29 +19,6 @@ allprojects {
|
|||||||
version = '0.0.1-SNAPSHOT'
|
version = '0.0.1-SNAPSHOT'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(file('artifactory.gradle').exists()){
|
||||||
artifactory {
|
apply from: 'artifactory.gradle'
|
||||||
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}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,6 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'kotlin-multiplatform'// version '1.3.0-rc-116'
|
id 'kotlin-multiplatform'
|
||||||
id "me.champeau.gradle.jmh" version "0.4.5"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -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)
|
||||||
|
}
|
@ -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<Double> {
|
||||||
|
init {
|
||||||
|
if (center.size != sizes.size) error("Dimension mismatch in bin creation. Expected ${center.size}, but found ${sizes.size}")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun contains(vector: Buffer<out Double>): 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<Double, MultivariateBin> {
|
||||||
|
|
||||||
|
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<MultivariateBin> 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<out Double>): MultivariateBin? {
|
||||||
|
val index = IntArray(dimension) { getIndex(it, point[it]) }
|
||||||
|
return bins[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun put(point: Buffer<out Double>) {
|
||||||
|
this[point]?.inc() ?: error("Could not find appropriate bin (should not be possible)")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<MultivariateBin> = 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<Double>): 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<ClosedFloatingPointRange<Double>, Int>): FastHistogram {
|
||||||
|
return FastHistogram(
|
||||||
|
ranges.map { it.first.start }.toVector(),
|
||||||
|
ranges.map { it.first.endInclusive }.toVector(),
|
||||||
|
ranges.map { it.second }.toIntArray()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<T: Any> {
|
||||||
|
operator fun contains(vector: Buffer<out T>): Boolean
|
||||||
|
val dimension: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bin in the histogram. The histogram is by definition always done in the real space
|
||||||
|
*/
|
||||||
|
interface Bin<T: Any> : Domain<T> {
|
||||||
|
/**
|
||||||
|
* The value of this bin
|
||||||
|
*/
|
||||||
|
val value: Number
|
||||||
|
val center: Buffer<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Histogram<T: Any, out B : Bin<T>> : Iterable<B> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find existing bin, corresponding to given coordinates
|
||||||
|
*/
|
||||||
|
operator fun get(point: Buffer<out T>): B?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dimension of the histogram
|
||||||
|
*/
|
||||||
|
val dimension: Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment appropriate bin
|
||||||
|
*/
|
||||||
|
fun put(point: Buffer<out T>)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T: Any> Histogram<T,*>.put(vararg point: T) = put(ArrayBuffer(point))
|
||||||
|
|
||||||
|
fun <T: Any> Histogram<T,*>.fill(sequence: Iterable<Buffer<T>>) = sequence.forEach { put(it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass a sequence builder into histogram
|
||||||
|
*/
|
||||||
|
fun <T: Any> Histogram<T, *>.fill(buider: suspend SequenceScope<Buffer<T>>.() -> Unit) = fill(sequence(buider).asIterable())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A space to perform arithmetic operations on histograms
|
||||||
|
*/
|
||||||
|
interface HistogramSpace<T: Any, B : Bin<T>, H : Histogram<T,B>> : Space<H> {
|
||||||
|
/**
|
||||||
|
* Rules for performing operations on bins
|
||||||
|
*/
|
||||||
|
val binSpace: Space<Bin<T>>
|
||||||
|
}
|
@ -1,18 +1,19 @@
|
|||||||
package scientifik.kmath.linear
|
package scientifik.kmath.linear
|
||||||
|
|
||||||
import scientifik.kmath.structures.MutableNDArray
|
import scientifik.kmath.structures.MutableNDStructure
|
||||||
import scientifik.kmath.structures.NDArray
|
import scientifik.kmath.structures.NDStructure
|
||||||
import scientifik.kmath.structures.NDArrays
|
import scientifik.kmath.structures.genericNdStructure
|
||||||
|
import scientifik.kmath.structures.get
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation copier from Apache common-maths
|
* Implementation based on Apache common-maths LU-decomposition
|
||||||
*/
|
*/
|
||||||
abstract class LUDecomposition<T : Comparable<T>>(val matrix: Matrix<T>) {
|
abstract class LUDecomposition<T : Comparable<T>>(val matrix: Matrix<T>) {
|
||||||
|
|
||||||
private val field get() = matrix.context.field
|
private val field get() = matrix.context.field
|
||||||
/** Entries of LU decomposition. */
|
/** Entries of LU decomposition. */
|
||||||
internal val lu: NDArray<T>
|
internal val lu: NDStructure<T>
|
||||||
/** Pivot permutation associated with LU decomposition. */
|
/** Pivot permutation associated with LU decomposition. */
|
||||||
internal val pivot: IntArray
|
internal val pivot: IntArray
|
||||||
/** Parity of the permutation associated with the LU decomposition. */
|
/** Parity of the permutation associated with the LU decomposition. */
|
||||||
@ -85,26 +86,18 @@ abstract class LUDecomposition<T : Comparable<T>>(val matrix: Matrix<T>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
|
||||||
// * 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
|
* In-place transformation for [MutableNDArray], using given transformation for each element
|
||||||
*/
|
*/
|
||||||
operator fun <T> MutableNDArray<T>.set(i: Int, j: Int, value: T) {
|
operator fun <T> MutableNDStructure<T>.set(i: Int, j: Int, value: T) {
|
||||||
this[listOf(i, j)] = value
|
this[intArrayOf(i, j)] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun isSingular(value: T): Boolean
|
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 abs(value: T) = if (value > matrix.context.field.zero) value else with(matrix.context.field) { -value }
|
||||||
|
|
||||||
private fun calculateLU(): Pair<NDArray<T>, IntArray> {
|
private fun calculateLU(): Pair<NDStructure<T>, IntArray> {
|
||||||
if (matrix.rows != matrix.columns) {
|
if (matrix.rows != matrix.columns) {
|
||||||
error("LU decomposition supports only square matrices")
|
error("LU decomposition supports only square matrices")
|
||||||
}
|
}
|
||||||
@ -112,7 +105,7 @@ abstract class LUDecomposition<T : Comparable<T>>(val matrix: Matrix<T>) {
|
|||||||
val m = matrix.columns
|
val m = matrix.columns
|
||||||
val pivot = IntArray(matrix.rows)
|
val pivot = IntArray(matrix.rows)
|
||||||
//TODO fix performance
|
//TODO fix performance
|
||||||
val lu: MutableNDArray<T> = NDArrays.createMutable(matrix.context.field, listOf(matrix.rows, matrix.columns)) { index -> matrix[index[0], index[1]] }
|
val lu: MutableNDStructure<T> = genericNdStructure(intArrayOf(matrix.rows, matrix.columns)) { index -> matrix[index[0], index[1]] }
|
||||||
|
|
||||||
|
|
||||||
with(matrix.context.field) {
|
with(matrix.context.field) {
|
||||||
@ -203,44 +196,6 @@ class RealLUDecomposition(matrix: Matrix<Double>, private val singularityThresho
|
|||||||
/** Specialized solver. */
|
/** Specialized solver. */
|
||||||
object RealLUSolver : LinearSolver<Double> {
|
object RealLUSolver : LinearSolver<Double> {
|
||||||
|
|
||||||
//
|
|
||||||
// /** {@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<Double>, threshold: Double = 1e-11): RealLUDecomposition = RealLUDecomposition(mat, threshold)
|
fun decompose(mat: Matrix<Double>, threshold: Double = 1e-11): RealLUDecomposition = RealLUDecomposition(mat, threshold)
|
||||||
|
|
||||||
|
@ -4,10 +4,7 @@ import scientifik.kmath.operations.DoubleField
|
|||||||
import scientifik.kmath.operations.Field
|
import scientifik.kmath.operations.Field
|
||||||
import scientifik.kmath.operations.Space
|
import scientifik.kmath.operations.Space
|
||||||
import scientifik.kmath.operations.SpaceElement
|
import scientifik.kmath.operations.SpaceElement
|
||||||
import scientifik.kmath.structures.NDArray
|
import scientifik.kmath.structures.*
|
||||||
import scientifik.kmath.structures.NDArrays.createFactory
|
|
||||||
import scientifik.kmath.structures.NDFieldFactory
|
|
||||||
import scientifik.kmath.structures.realNDFieldFactory
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The space for linear elements. Supports scalar product alongside with standard linear operations.
|
* The space for linear elements. Supports scalar product alongside with standard linear operations.
|
||||||
@ -162,11 +159,8 @@ abstract class VectorSpace<T : Any>(val size: Int, val field: Field<T>) : Space<
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface Vector<T : Any> : SpaceElement<Vector<T>, VectorSpace<T>> {
|
interface Vector<T : Any> : SpaceElement<Vector<T>, VectorSpace<T>>, Buffer<T>, Iterable<T> {
|
||||||
val size: Int
|
override val size: Int get() = context.size
|
||||||
get() = context.size
|
|
||||||
|
|
||||||
operator fun get(i: Int): T
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
@ -181,6 +175,7 @@ interface Vector<T : Any> : SpaceElement<Vector<T>, VectorSpace<T>> {
|
|||||||
fun ofReal(size: Int, initializer: (Int) -> Double) =
|
fun ofReal(size: Int, initializer: (Int) -> Double) =
|
||||||
ArrayVector(ArrayVectorSpace(size, DoubleField, realNDFieldFactory), initializer)
|
ArrayVector(ArrayVectorSpace(size, DoubleField, realNDFieldFactory), initializer)
|
||||||
|
|
||||||
|
fun ofReal(vararg point: Double) = point.toVector()
|
||||||
|
|
||||||
fun equals(v1: Vector<*>, v2: Vector<*>): Boolean {
|
fun equals(v1: Vector<*>, v2: Vector<*>): Boolean {
|
||||||
if (v1 === v2) return true
|
if (v1 === v2) return true
|
||||||
@ -193,6 +188,11 @@ interface Vector<T : Any> : SpaceElement<Vector<T>, VectorSpace<T>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typealias NDFieldFactory<T> = (IntArray) -> NDField<T>
|
||||||
|
|
||||||
|
internal fun <T : Any> genericNDFieldFactory(field: Field<T>): NDFieldFactory<T> = { index -> GenericNDField(index, field) }
|
||||||
|
internal val realNDFieldFactory: NDFieldFactory<Double> = { index -> GenericNDField(index, DoubleField) }
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NDArray-based implementation of vector space. By default uses slow [SimpleNDField], but could be overridden with custom [NDField] factory.
|
* 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<T : Any>(
|
|||||||
rows: Int,
|
rows: Int,
|
||||||
columns: Int,
|
columns: Int,
|
||||||
field: Field<T>,
|
field: Field<T>,
|
||||||
val ndFactory: NDFieldFactory<T> = createFactory(field)
|
val ndFactory: NDFieldFactory<T> = genericNDFieldFactory(field)
|
||||||
) : MatrixSpace<T>(rows, columns, field) {
|
) : MatrixSpace<T>(rows, columns, field) {
|
||||||
|
|
||||||
val ndField by lazy {
|
val ndField by lazy {
|
||||||
ndFactory(listOf(rows, columns))
|
ndFactory(intArrayOf(rows, columns))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun produce(initializer: (Int, Int) -> T): Matrix<T> = ArrayMatrix(this, initializer)
|
override fun produce(initializer: (Int, Int) -> T): Matrix<T> = ArrayMatrix(this, initializer)
|
||||||
@ -218,10 +218,10 @@ class ArrayMatrixSpace<T : Any>(
|
|||||||
class ArrayVectorSpace<T : Any>(
|
class ArrayVectorSpace<T : Any>(
|
||||||
size: Int,
|
size: Int,
|
||||||
field: Field<T>,
|
field: Field<T>,
|
||||||
val ndFactory: NDFieldFactory<T> = createFactory(field)
|
val ndFactory: NDFieldFactory<T> = genericNDFieldFactory(field)
|
||||||
) : VectorSpace<T>(size, field) {
|
) : VectorSpace<T>(size, field) {
|
||||||
val ndField by lazy {
|
val ndField by lazy {
|
||||||
ndFactory(listOf(size))
|
ndFactory(intArrayOf(size))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun produce(initializer: (Int) -> T): Vector<T> = ArrayVector(this, initializer)
|
override fun produce(initializer: (Int) -> T): Vector<T> = ArrayVector(this, initializer)
|
||||||
@ -256,13 +256,21 @@ class ArrayVector<T : Any> internal constructor(override val context: ArrayVecto
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun get(i: Int): T {
|
override fun get(index: Int): T {
|
||||||
return array[i]
|
return array[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
override val self: ArrayVector<T> get() = this
|
override val self: ArrayVector<T> get() = this
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<T> = (0 until size).map { array[it] }.iterator()
|
||||||
|
|
||||||
|
override fun copy(): ArrayVector<T> = ArrayVector(context, array)
|
||||||
|
|
||||||
|
override fun toString(): String = this.joinToString(prefix = "[", postfix = "]", separator = ", ") { it.toString() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typealias RealVector = Vector<Double>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A group of methods to resolve equation A dot X = B, where A and B are matrices or vectors
|
* 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<T : Any> {
|
|||||||
fun <T : Any> Array<T>.toVector(field: Field<T>) = Vector.of(size, field) { this[it] }
|
fun <T : Any> Array<T>.toVector(field: Field<T>) = Vector.of(size, field) { this[it] }
|
||||||
|
|
||||||
fun DoubleArray.toVector() = Vector.ofReal(this.size) { this[it] }
|
fun DoubleArray.toVector() = Vector.ofReal(this.size) { this[it] }
|
||||||
|
fun List<Double>.toVector() = Vector.ofReal(this.size) { this[it] }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert matrix to vector if it is possible
|
* Convert matrix to vector if it is possible
|
||||||
@ -306,6 +315,6 @@ fun <T : Any> Vector<T>.toMatrix(): Matrix<T> {
|
|||||||
// //Generic vector
|
// //Generic vector
|
||||||
// matrix(size, 1, context.field) { i, j -> get(i) }
|
// 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) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
package scientifik.kmath.structures
|
|
||||||
|
|
||||||
import scientifik.kmath.operations.Field
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A generic buffer for both primitives and objects
|
|
||||||
*/
|
|
||||||
interface Buffer<T> {
|
|
||||||
operator fun get(index: Int): T
|
|
||||||
operator fun set(index: Int, value: T)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A shallow copy of the buffer
|
|
||||||
*/
|
|
||||||
fun copy(): Buffer<T>
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic implementation of NDField based on continuous buffer
|
|
||||||
*/
|
|
||||||
abstract class BufferNDField<T>(shape: List<Int>, field: Field<T>) : NDField<T>(shape, field) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Strides for memory access
|
|
||||||
*/
|
|
||||||
private val strides: List<Int> by lazy {
|
|
||||||
ArrayList<Int>(shape.size).apply {
|
|
||||||
var current = 1
|
|
||||||
add(1)
|
|
||||||
shape.forEach {
|
|
||||||
current *= it
|
|
||||||
add(current)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun offset(index: List<Int>): 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<Int> {
|
|
||||||
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<T>
|
|
||||||
|
|
||||||
override fun produce(initializer: (List<Int>) -> T): NDArray<T> {
|
|
||||||
val buffer = createBuffer(capacity) { initializer(index(it)) }
|
|
||||||
return BufferNDArray(this, buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produce mutable NDArray instance
|
|
||||||
*/
|
|
||||||
fun produceMutable(initializer: (List<Int>) -> T): MutableNDArray<T> {
|
|
||||||
val buffer = createBuffer(capacity) { initializer(index(it)) }
|
|
||||||
return MutableBufferedNDArray(this, buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private open class BufferNDArray<T>(override val context: BufferNDField<T>, val data: Buffer<T>) : NDArray<T> {
|
|
||||||
|
|
||||||
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<T> get() = this
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MutableBufferedNDArray<T>(context: BufferNDField<T>, data: Buffer<T>): BufferNDArray<T>(context,data), MutableNDArray<T>{
|
|
||||||
override operator fun set(index: List<Int>, value: T){
|
|
||||||
data[context.offset(index)] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
|||||||
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A generic linear buffer for both primitives and objects
|
||||||
|
*/
|
||||||
|
interface Buffer<T> : Iterable<T> {
|
||||||
|
|
||||||
|
val size: Int
|
||||||
|
|
||||||
|
operator fun get(index: Int): T
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A shallow copy of the buffer
|
||||||
|
*/
|
||||||
|
fun copy(): Buffer<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MutableBuffer<T> : Buffer<T> {
|
||||||
|
operator fun set(index: Int, value: T)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A shallow copy of the buffer
|
||||||
|
*/
|
||||||
|
override fun copy(): MutableBuffer<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
inline class ListBuffer<T>(private val list: MutableList<T>) : MutableBuffer<T> {
|
||||||
|
|
||||||
|
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<T> = list.iterator()
|
||||||
|
|
||||||
|
override fun copy(): MutableBuffer<T> = ListBuffer(ArrayList(list))
|
||||||
|
}
|
||||||
|
|
||||||
|
class ArrayBuffer<T>(private val array: Array<T>) : MutableBuffer<T> {
|
||||||
|
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<T> = array.iterator()
|
||||||
|
|
||||||
|
override fun copy(): MutableBuffer<T> = ArrayBuffer(array.copyOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
class DoubleBuffer(private val array: DoubleArray) : MutableBuffer<Double> {
|
||||||
|
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<Double> = array.iterator()
|
||||||
|
|
||||||
|
override fun copy(): MutableBuffer<Double> = DoubleBuffer(array.copyOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Any> buffer(size: Int, noinline initializer: (Int) -> T): Buffer<T> {
|
||||||
|
return ArrayBuffer(Array(size, initializer))
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Any> mutableBuffer(size: Int, noinline initializer: (Int) -> T): MutableBuffer<T> {
|
||||||
|
return ArrayBuffer(Array(size, initializer))
|
||||||
|
}
|
@ -1,72 +0,0 @@
|
|||||||
package scientifik.kmath.structures
|
|
||||||
|
|
||||||
import scientifik.kmath.operations.Field
|
|
||||||
|
|
||||||
typealias NDFieldFactory<T> = (shape: List<Int>) -> NDField<T>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The factory class for fast platform-dependent implementation of NDField of doubles
|
|
||||||
*/
|
|
||||||
expect val realNDFieldFactory: NDFieldFactory<Double>
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleNDField<T : Any>(field: Field<T>, shape: List<Int>) : BufferNDField<T>(shape, field) {
|
|
||||||
override fun createBuffer(capacity: Int, initializer: (Int) -> T): Buffer<T> {
|
|
||||||
val array = ArrayList<T>(capacity)
|
|
||||||
(0 until capacity).forEach {
|
|
||||||
array.add(initializer(it))
|
|
||||||
}
|
|
||||||
|
|
||||||
return BufferOfObjects(array)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class BufferOfObjects<T>(val array: ArrayList<T>) : Buffer<T> {
|
|
||||||
override fun get(index: Int): T = array[index]
|
|
||||||
|
|
||||||
override fun set(index: Int, value: T) {
|
|
||||||
array[index] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun copy(): Buffer<T> = BufferOfObjects(ArrayList(array))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object NDArrays {
|
|
||||||
/**
|
|
||||||
* Create a platform-optimized NDArray of doubles
|
|
||||||
*/
|
|
||||||
fun realNDArray(shape: List<Int>, initializer: (List<Int>) -> Double = { 0.0 }): NDArray<Double> {
|
|
||||||
return realNDFieldFactory(shape).produce(initializer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun real1DArray(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }): NDArray<Double> {
|
|
||||||
return realNDArray(listOf(dim)) { initializer(it[0]) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun real2DArray(dim1: Int, dim2: Int, initializer: (Int, Int) -> Double = { _, _ -> 0.0 }): NDArray<Double> {
|
|
||||||
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<Double> {
|
|
||||||
return realNDArray(listOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple boxing NDField
|
|
||||||
*/
|
|
||||||
fun <T : Any> createFactory(field: Field<T>): NDFieldFactory<T> = { shape -> SimpleNDField(field, shape) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple boxing NDArray
|
|
||||||
*/
|
|
||||||
fun <T : Any> create(field: Field<T>, shape: List<Int>, initializer: (List<Int>) -> T): NDArray<T> {
|
|
||||||
return SimpleNDField(field, shape).produce { initializer(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mutable boxing NDArray
|
|
||||||
*/
|
|
||||||
fun <T : Any> createMutable(field: Field<T>, shape: List<Int>, initializer: (List<Int>) -> T): MutableNDArray<T> {
|
|
||||||
return SimpleNDField(field, shape).produceMutable { initializer(it) }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +1,13 @@
|
|||||||
package scientifik.kmath.structures
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
import scientifik.kmath.operations.DoubleField
|
||||||
import scientifik.kmath.operations.Field
|
import scientifik.kmath.operations.Field
|
||||||
import scientifik.kmath.operations.FieldElement
|
import scientifik.kmath.operations.FieldElement
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An exception is thrown when the expected ans actual shape of NDArray differs
|
* An exception is thrown when the expected ans actual shape of NDArray differs
|
||||||
*/
|
*/
|
||||||
class ShapeMismatchException(val expected: List<Int>, val actual: List<Int>) : RuntimeException()
|
class ShapeMismatchException(val expected: IntArray, val actual: IntArray) : RuntimeException()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Field for n-dimensional arrays.
|
* Field for n-dimensional arrays.
|
||||||
@ -14,13 +15,15 @@ class ShapeMismatchException(val expected: List<Int>, val actual: List<Int>) : R
|
|||||||
* @param field - operations field defined on individual array element
|
* @param field - operations field defined on individual array element
|
||||||
* @param T the type of the element contained in NDArray
|
* @param T the type of the element contained in NDArray
|
||||||
*/
|
*/
|
||||||
abstract class NDField<T>(val shape: List<Int>, val field: Field<T>) : Field<NDArray<T>> {
|
abstract class NDField<T>(val shape: IntArray, val field: Field<T>) : Field<NDArray<T>> {
|
||||||
|
|
||||||
|
abstract fun produceStructure(initializer: (IntArray) -> T): NDStructure<T>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new instance of NDArray using field shape and given initializer
|
* Create new instance of NDArray using field shape and given initializer
|
||||||
* The producer takes list of indices as argument and returns contained value
|
* The producer takes list of indices as argument and returns contained value
|
||||||
*/
|
*/
|
||||||
abstract fun produce(initializer: (List<Int>) -> T): NDArray<T>
|
fun produce(initializer: (IntArray) -> T): NDArray<T> = NDArray(this, produceStructure(initializer))
|
||||||
|
|
||||||
override val zero: NDArray<T> by lazy {
|
override val zero: NDArray<T> by lazy {
|
||||||
produce { this.field.zero }
|
produce { this.field.zero }
|
||||||
@ -31,7 +34,7 @@ abstract class NDField<T>(val shape: List<Int>, val field: Field<T>) : Field<NDA
|
|||||||
*/
|
*/
|
||||||
private fun checkShape(vararg arrays: NDArray<T>) {
|
private fun checkShape(vararg arrays: NDArray<T>) {
|
||||||
arrays.forEach {
|
arrays.forEach {
|
||||||
if (shape != it.shape) {
|
if (!shape.contentEquals(it.shape)) {
|
||||||
throw ShapeMismatchException(shape, it.shape)
|
throw ShapeMismatchException(shape, it.shape)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,76 +74,47 @@ abstract class NDField<T>(val shape: List<Int>, val field: Field<T>) : Field<NDA
|
|||||||
checkShape(a)
|
checkShape(a)
|
||||||
return produce { with(field) { a[it] / b[it] } }
|
return produce { with(field) { a[it] / b[it] } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse sum operation
|
||||||
|
*/
|
||||||
|
operator fun <T> T.plus(arg: NDArray<T>): NDArray<T> = arg + this
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse minus operation
|
||||||
|
*/
|
||||||
|
operator fun <T> T.minus(arg: NDArray<T>): NDArray<T> = arg.transform { _, value ->
|
||||||
|
with(arg.context.field) {
|
||||||
|
this@minus - value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Many-dimensional array
|
* Reverse product operation
|
||||||
*/
|
*/
|
||||||
interface NDArray<T> : FieldElement<NDArray<T>, NDField<T>> {
|
operator fun <T> T.times(arg: NDArray<T>): NDArray<T> = arg * this
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of dimensions of this NDArray
|
* Reverse division operation
|
||||||
*/
|
*/
|
||||||
val shape: List<Int>
|
operator fun <T> T.div(arg: NDArray<T>): NDArray<T> = arg.transform { _, value ->
|
||||||
get() = context.shape
|
with(arg.context.field) {
|
||||||
|
this@div / value
|
||||||
/**
|
|
||||||
* The number of dimentsions for this array
|
|
||||||
*/
|
|
||||||
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<Int>): T {
|
|
||||||
return get(*index.toIntArray())
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun iterator(): Iterator<Pair<List<Int>, T>> {
|
|
||||||
return iterateIndexes(shape).map { Pair(it, this[it]) }.iterator()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate new NDArray, using given transformation for each element
|
|
||||||
*/
|
|
||||||
fun transform(action: (List<Int>, T) -> T): NDArray<T> = context.produce { action(it, this[it]) }
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Iterate over all indexes in the nd-shape
|
|
||||||
*/
|
|
||||||
fun iterateIndexes(shape: List<Int>): Sequence<List<Int>> {
|
|
||||||
return if (shape.size == 1) {
|
|
||||||
(0 until shape[0]).asSequence().map { listOf(it) }
|
|
||||||
} else {
|
|
||||||
val tailShape = ArrayList(shape).apply { removeAt(0) }
|
|
||||||
val tailSequence: List<List<Int>> = 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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In-place mutable [NDArray]
|
* Immutable [NDStructure] coupled to the context. Emulates Python ndarray
|
||||||
*/
|
*/
|
||||||
interface MutableNDArray<T> : NDArray<T> {
|
data class NDArray<T>(override val context: NDField<T>, private val structure: NDStructure<T>) : FieldElement<NDArray<T>, NDField<T>>, NDStructure<T> by structure {
|
||||||
operator fun set(index: List<Int>, value: T)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
//TODO ensure structure is immutable
|
||||||
* In-place transformation for [MutableNDArray], using given transformation for each element
|
|
||||||
*/
|
override val self: NDArray<T>
|
||||||
fun <T> MutableNDArray<T>.transformInPlace(action: (List<Int>, T) -> T) {
|
get() = this
|
||||||
for ((index, oldValue) in this) {
|
|
||||||
this[index] = action(index, oldValue)
|
fun transform(action: (IntArray, T) -> T): NDArray<T> = context.produce { action(it, get(*it)) }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -159,11 +133,6 @@ operator fun <T> NDArray<T>.plus(arg: T): NDArray<T> = transform { _, value ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reverse sum operation
|
|
||||||
*/
|
|
||||||
operator fun <T> T.plus(arg: NDArray<T>): NDArray<T> = arg + this
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subtraction operation between [NDArray] and single element
|
* Subtraction operation between [NDArray] and single element
|
||||||
*/
|
*/
|
||||||
@ -173,15 +142,6 @@ operator fun <T> NDArray<T>.minus(arg: T): NDArray<T> = transform { _, value ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reverse minus operation
|
|
||||||
*/
|
|
||||||
operator fun <T> T.minus(arg: NDArray<T>): NDArray<T> = arg.transform { _, value ->
|
|
||||||
with(arg.context.field) {
|
|
||||||
this@minus - value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* prod and div */
|
/* prod and div */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -193,11 +153,6 @@ operator fun <T> NDArray<T>.times(arg: T): NDArray<T> = transform { _, value ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reverse product operation
|
|
||||||
*/
|
|
||||||
operator fun <T> T.times(arg: NDArray<T>): NDArray<T> = arg * this
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Division operation between [NDArray] and single element
|
* Division operation between [NDArray] and single element
|
||||||
*/
|
*/
|
||||||
@ -207,12 +162,41 @@ operator fun <T> NDArray<T>.div(arg: T): NDArray<T> = transform { _, value ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
class GenericNDField<T : Any>(shape: IntArray, field: Field<T>) : NDField<T>(shape, field) {
|
||||||
* Reverse division operation
|
override fun produceStructure(initializer: (IntArray) -> T): NDStructure<T> = genericNdStructure(shape, initializer)
|
||||||
*/
|
|
||||||
operator fun <T> T.div(arg: NDArray<T>): NDArray<T> = arg.transform { _, value ->
|
|
||||||
with(arg.context.field) {
|
|
||||||
this@div / value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//typealias NDFieldFactory<T> = (IntArray)->NDField<T>
|
||||||
|
|
||||||
|
object NDArrays {
|
||||||
|
/**
|
||||||
|
* Create a platform-optimized NDArray of doubles
|
||||||
|
*/
|
||||||
|
fun realNDArray(shape: IntArray, initializer: (IntArray) -> Double = { 0.0 }): NDArray<Double> {
|
||||||
|
return GenericNDField(shape, DoubleField).produce(initializer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun real1DArray(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }): NDArray<Double> {
|
||||||
|
return realNDArray(intArrayOf(dim)) { initializer(it[0]) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun real2DArray(dim1: Int, dim2: Int, initializer: (Int, Int) -> Double = { _, _ -> 0.0 }): NDArray<Double> {
|
||||||
|
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<Double> {
|
||||||
|
return realNDArray(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Simple boxing NDField
|
||||||
|
// */
|
||||||
|
// fun <T : Any> fieldFactory(field: Field<T>): NDFieldFactory<T> = { shape -> GenericNDField(shape, field) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple boxing NDArray
|
||||||
|
*/
|
||||||
|
fun <T : Any> create(field: Field<T>, shape: IntArray, initializer: (IntArray) -> T): NDArray<T> {
|
||||||
|
return GenericNDField(shape, field).produce { initializer(it) }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,174 @@
|
|||||||
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
|
||||||
|
interface NDStructure<T> : Iterable<Pair<IntArray, T>> {
|
||||||
|
|
||||||
|
val shape: IntArray
|
||||||
|
|
||||||
|
val dimension
|
||||||
|
get() = shape.size
|
||||||
|
|
||||||
|
operator fun get(index: IntArray): T
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun <T> NDStructure<T>.get(vararg index: Int): T = get(index)
|
||||||
|
|
||||||
|
interface MutableNDStructure<T> : NDStructure<T> {
|
||||||
|
operator fun set(index: IntArray, value: T)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> MutableNDStructure<T>.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<Int>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<IntArray> {
|
||||||
|
//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<T, B : Buffer<T>> : NDStructure<T> {
|
||||||
|
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<Pair<IntArray, T>> =
|
||||||
|
strides.indices().map { it to this[it] }.iterator()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boxing generic [NDStructure]
|
||||||
|
*/
|
||||||
|
class BufferNDStructure<T>(
|
||||||
|
override val strides: Strides,
|
||||||
|
override val buffer: Buffer<T>
|
||||||
|
) : GenericNDStructure<T, Buffer<T>>() {
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (strides.linearSize != buffer.size) {
|
||||||
|
error("Expected buffer side of ${strides.linearSize}, but found ${buffer.size}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T: Any> ndStructure(strides: Strides, noinline initializer: (IntArray) -> T) =
|
||||||
|
BufferNDStructure<T>(strides, buffer(strides.linearSize){ i-> initializer(strides.index(i))})
|
||||||
|
|
||||||
|
inline fun <reified T: Any> ndStructure(shape: IntArray, noinline initializer: (IntArray) -> T) =
|
||||||
|
ndStructure(DefaultStrides(shape), initializer)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mutable ND buffer based on linear [Buffer]
|
||||||
|
*/
|
||||||
|
class MutableBufferNDStructure<T>(
|
||||||
|
override val strides: Strides,
|
||||||
|
override val buffer: MutableBuffer<T>
|
||||||
|
) : GenericNDStructure<T, MutableBuffer<T>>(), MutableNDStructure<T> {
|
||||||
|
|
||||||
|
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 <reified T: Any> mutableNdStructure(strides: Strides, noinline initializer: (IntArray) -> T) =
|
||||||
|
MutableBufferNDStructure(strides, mutableBuffer(strides.linearSize) { i -> initializer(strides.index(i)) })
|
||||||
|
|
||||||
|
inline fun <reified T: Any> mutableNdStructure(shape: IntArray, noinline initializer: (IntArray) -> T) =
|
||||||
|
mutableNdStructure(DefaultStrides(shape), initializer)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create universal mutable structure
|
||||||
|
*/
|
||||||
|
fun <T> genericNdStructure(shape: IntArray, initializer: (IntArray) -> T): MutableBufferNDStructure<T>{
|
||||||
|
val strides = DefaultStrides(shape)
|
||||||
|
val sequence = sequence{
|
||||||
|
strides.indices().forEach{
|
||||||
|
yield(initializer(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val buffer = ListBuffer<T>(sequence.toMutableList())
|
||||||
|
return MutableBufferNDStructure(strides, buffer)
|
||||||
|
}
|
@ -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() })
|
||||||
|
}
|
||||||
|
}
|
@ -7,15 +7,15 @@ class ArrayMatrixTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSum() {
|
fun testSum() {
|
||||||
val vector1 = realVector(5) { it.toDouble() }
|
val vector1 = Vector.ofReal(5) { it.toDouble() }
|
||||||
val vector2 = realVector(5) { 5 - it.toDouble() }
|
val vector2 = Vector.ofReal(5) { 5 - it.toDouble() }
|
||||||
val sum = vector1 + vector2
|
val sum = vector1 + vector2
|
||||||
assertEquals(5.0, sum[2])
|
assertEquals(5.0, sum[2])
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testVectorToMatrix() {
|
fun testVectorToMatrix() {
|
||||||
val vector = realVector(5) { it.toDouble() }
|
val vector = Vector.ofReal(5) { it.toDouble() }
|
||||||
val matrix = vector.toMatrix()
|
val matrix = vector.toMatrix()
|
||||||
assertEquals(4.0, matrix[4, 0])
|
assertEquals(4.0, matrix[4, 0])
|
||||||
}
|
}
|
||||||
@ -23,8 +23,8 @@ class ArrayMatrixTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDot() {
|
fun testDot() {
|
||||||
val vector1 = realVector(5) { it.toDouble() }
|
val vector1 = Vector.ofReal(5) { it.toDouble() }
|
||||||
val vector2 = realVector(5) { 5 - it.toDouble() }
|
val vector2 = Vector.ofReal(5) { 5 - it.toDouble() }
|
||||||
val product = vector1.toMatrix() dot (vector2.toMatrix().transpose())
|
val product = vector1.toMatrix() dot (vector2.toMatrix().transpose())
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,10 +6,10 @@ import kotlin.test.Test
|
|||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
|
||||||
class SimpleNDFieldTest{
|
class GenericNDFieldTest{
|
||||||
@Test
|
@Test
|
||||||
fun testStrides(){
|
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)
|
assertEquals(ndArray[5,5], 10.0)
|
||||||
}
|
}
|
||||||
|
|
@ -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}
|
||||||
|
}
|
@ -1,8 +0,0 @@
|
|||||||
package scientifik.kmath.structures
|
|
||||||
|
|
||||||
import scientifik.kmath.operations.DoubleField
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Using boxing implementation for js
|
|
||||||
*/
|
|
||||||
actual val realNDFieldFactory: NDFieldFactory<Double> = NDArrays.createFactory(DoubleField)
|
|
@ -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
|
@ -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<Double> {
|
||||||
|
//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<out Double>): 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<Double,UnivariateBin> {
|
||||||
|
|
||||||
|
private val bins: TreeMap<Double, UnivariateBin> = 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<out Double>): UnivariateBin? = get(point[0])
|
||||||
|
|
||||||
|
override val dimension: Int get() = 1
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<UnivariateBin> = bins.values.iterator()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread safe put operation
|
||||||
|
*/
|
||||||
|
fun put(value: Double) {
|
||||||
|
(get(value) ?: createBin(value)).inc()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun put(point: Buffer<out Double>) = 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<Double>) = sequence.forEach { put(it) }
|
@ -1,26 +0,0 @@
|
|||||||
package scientifik.kmath.structures
|
|
||||||
|
|
||||||
import scientifik.kmath.operations.DoubleField
|
|
||||||
import java.nio.DoubleBuffer
|
|
||||||
|
|
||||||
private class RealNDField(shape: List<Int>) : BufferNDField<Double>(shape, DoubleField) {
|
|
||||||
override fun createBuffer(capacity: Int, initializer: (Int) -> Double): Buffer<Double> {
|
|
||||||
val array = DoubleArray(capacity, initializer)
|
|
||||||
val buffer = DoubleBuffer.wrap(array)
|
|
||||||
return BufferOfDoubles(buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class BufferOfDoubles(val buffer: DoubleBuffer): Buffer<Double>{
|
|
||||||
override fun get(index: Int): Double = buffer.get(index)
|
|
||||||
|
|
||||||
override fun set(index: Int, value: Double) {
|
|
||||||
buffer.put(index, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun copy(): Buffer<Double> {
|
|
||||||
return BufferOfDoubles(buffer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
actual val realNDFieldFactory: NDFieldFactory<Double> = { shape -> RealNDField(shape) }
|
|
15
kmath-jmh/build.gradle
Normal file
15
kmath-jmh/build.gradle
Normal file
@ -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'
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package scietifik.kmath.structures
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
import org.openjdk.jmh.annotations.*
|
import org.openjdk.jmh.annotations.*
|
||||||
import java.nio.IntBuffer
|
import java.nio.IntBuffer
|
||||||
@ -6,7 +6,7 @@ import java.nio.IntBuffer
|
|||||||
|
|
||||||
@Fork(1)
|
@Fork(1)
|
||||||
@Warmup(iterations = 2)
|
@Warmup(iterations = 2)
|
||||||
@Measurement(iterations = 50)
|
@Measurement(iterations = 5)
|
||||||
@State(Scope.Benchmark)
|
@State(Scope.Benchmark)
|
||||||
open class ArrayBenchmark {
|
open class ArrayBenchmark {
|
||||||
|
|
@ -1,6 +1,5 @@
|
|||||||
pluginManagement {
|
pluginManagement {
|
||||||
repositories {
|
repositories {
|
||||||
maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' }
|
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven { url = 'https://plugins.gradle.org/m2/' }
|
maven { url = 'https://plugins.gradle.org/m2/' }
|
||||||
}
|
}
|
||||||
@ -10,4 +9,5 @@ enableFeaturePreview('GRADLE_METADATA')
|
|||||||
|
|
||||||
rootProject.name = 'kmath'
|
rootProject.name = 'kmath'
|
||||||
include ':kmath-core'
|
include ':kmath-core'
|
||||||
|
include ':kmath-jmh'
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user