Lazy structures, structure is no longer iterable, removed copy on read-only buffers.
This commit is contained in:
parent
bdd9dccd4f
commit
cddea1869d
@ -58,11 +58,11 @@ repositories {
|
|||||||
|
|
||||||
Then use regular dependency like
|
Then use regular dependency like
|
||||||
```groovy
|
```groovy
|
||||||
compile(group: 'scientifik', name: 'kmath-core-jvm', version: '0.0.1-SNAPSHOT')
|
compile(group: 'scientifik', name: 'kmath-core', version: '0.0.1-SNAPSHOT')
|
||||||
```
|
```
|
||||||
or in kotlin
|
or in kotlin
|
||||||
```kotlin
|
```kotlin
|
||||||
compile(group = "scientifik", name = "kmath-core-jvm", version = "0.0.1-SNAPSHOT")
|
compile(group = "scientifik", name = "kmath-core", version = "0.0.1-SNAPSHOT")
|
||||||
```
|
```
|
||||||
|
|
||||||
Work builds could be obtained with [![](https://jitpack.io/v/altavir/kmath.svg)](https://jitpack.io/#altavir/kmath).
|
Work builds could be obtained with [![](https://jitpack.io/v/altavir/kmath.svg)](https://jitpack.io/#altavir/kmath).
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
extra["kotlinVersion"] = "1.3.10"
|
extra["kotlinVersion"] = "1.3.11"
|
||||||
|
extra["ioVersion"] = "0.1.2-dev-2"
|
||||||
|
extra["coroutinesVersion"] = "1.0.1"
|
||||||
|
|
||||||
val kotlinVersion: String by extra
|
val kotlinVersion: String by extra
|
||||||
|
val ioVersion: String by extra
|
||||||
|
val coroutinesVersion: String by extra
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
@ -15,6 +19,7 @@ buildscript {
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.jfrog.artifactory") version "4.8.1" apply false
|
id("com.jfrog.artifactory") version "4.8.1" apply false
|
||||||
|
// id("org.jetbrains.kotlin.multiplatform") apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
@ -22,7 +27,7 @@ allprojects {
|
|||||||
apply(plugin = "com.jfrog.artifactory")
|
apply(plugin = "com.jfrog.artifactory")
|
||||||
|
|
||||||
group = "scientifik"
|
group = "scientifik"
|
||||||
version = "0.0.1-SNAPSHOT"
|
version = "0.0.2-dev-1"
|
||||||
}
|
}
|
||||||
|
|
||||||
if(file("artifactory.gradle").exists()){
|
if(file("artifactory.gradle").exists()){
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'kotlin-multiplatform'
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
@ -14,7 +14,7 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
|
api 'org.jetbrains.kotlin:kotlin-stdlib-common'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
commonTest {
|
commonTest {
|
||||||
@ -25,7 +25,7 @@ kotlin {
|
|||||||
}
|
}
|
||||||
jvmMain {
|
jvmMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
api 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jvmTest {
|
jvmTest {
|
||||||
@ -36,7 +36,7 @@ kotlin {
|
|||||||
}
|
}
|
||||||
jsMain {
|
jsMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-js'
|
api 'org.jetbrains.kotlin:kotlin-stdlib-js'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsTest {
|
jsTest {
|
||||||
|
@ -85,7 +85,7 @@ class FastHistogram(
|
|||||||
values[index].increment()
|
values[index].increment()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun iterator(): Iterator<PhantomBin<Double>> = values.asSequence().map { (index, value) ->
|
override fun iterator(): Iterator<PhantomBin<Double>> = values.elements().map { (index, value) ->
|
||||||
PhantomBin(getTemplate(index), value.sum())
|
PhantomBin(getTemplate(index), value.sum())
|
||||||
}.iterator()
|
}.iterator()
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ class FastHistogram(
|
|||||||
* Create a phantom lightweight immutable copy of this histogram
|
* Create a phantom lightweight immutable copy of this histogram
|
||||||
*/
|
*/
|
||||||
fun asPhantomHistogram(): PhantomHistogram<Double> {
|
fun asPhantomHistogram(): PhantomHistogram<Double> {
|
||||||
val binTemplates = values.associate { (index, _) -> getTemplate(index) to index }
|
val binTemplates = values.elements().associate { (index, _) -> getTemplate(index) to index }
|
||||||
return PhantomHistogram(binTemplates, asNDStructure())
|
return PhantomHistogram(binTemplates, asNDStructure())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,8 +98,6 @@ class ArrayVector<T : Any, F : Field<T>> internal constructor(override val conte
|
|||||||
|
|
||||||
override fun iterator(): Iterator<T> = (0 until size).map { element[it] }.iterator()
|
override fun iterator(): Iterator<T> = (0 until size).map { element[it] }.iterator()
|
||||||
|
|
||||||
override fun copy(): ArrayVector<T, F> = ArrayVector(context, element)
|
|
||||||
|
|
||||||
override fun toString(): String = this.joinToString(prefix = "[", postfix = "]", separator = ", ") { it.toString() }
|
override fun toString(): String = this.joinToString(prefix = "[", postfix = "]", separator = ", ") { it.toString() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,4 +38,8 @@ data class Complex(val re: Double, val im: Double) : FieldElement<Complex, Compl
|
|||||||
val abs: Double
|
val abs: Double
|
||||||
get() = kotlin.math.sqrt(square)
|
get() = kotlin.math.sqrt(square)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -49,6 +49,9 @@ inline class Real(val value: Double) : FieldElement<Real, RealField> {
|
|||||||
override val context
|
override val context
|
||||||
get() = RealField
|
get() = RealField
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,3 +75,15 @@ object DoubleField : ExtendedField<Double>, Norm<Double, Double> {
|
|||||||
|
|
||||||
override fun norm(arg: Double): Double = kotlin.math.abs(arg)
|
override fun norm(arg: Double): Double = kotlin.math.abs(arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A field for double without boxing. Does not produce appropriate field element
|
||||||
|
*/
|
||||||
|
object IntField : Field<Int>{
|
||||||
|
override val zero: Int = 0
|
||||||
|
override fun add(a: Int, b: Int): Int = a + b
|
||||||
|
override fun multiply(a: Int, b: Int): Int = a * b
|
||||||
|
override fun multiply(a: Int, k: Double): Int = (k*a).toInt()
|
||||||
|
override val one: Int = 1
|
||||||
|
override fun divide(a: Int, b: Int): Int = a / b
|
||||||
|
}
|
@ -11,11 +11,6 @@ interface Buffer<T> {
|
|||||||
operator fun get(index: Int): T
|
operator fun get(index: Int): T
|
||||||
|
|
||||||
operator fun iterator(): Iterator<T>
|
operator fun iterator(): Iterator<T>
|
||||||
|
|
||||||
/**
|
|
||||||
* A shallow copy of the buffer
|
|
||||||
*/
|
|
||||||
fun copy(): Buffer<T>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> Buffer<T>.asSequence(): Sequence<T> = iterator().asSequence()
|
fun <T> Buffer<T>.asSequence(): Sequence<T> = iterator().asSequence()
|
||||||
@ -26,7 +21,7 @@ interface MutableBuffer<T> : Buffer<T> {
|
|||||||
/**
|
/**
|
||||||
* A shallow copy of the buffer
|
* A shallow copy of the buffer
|
||||||
*/
|
*/
|
||||||
override fun copy(): MutableBuffer<T>
|
fun copy(): MutableBuffer<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -38,8 +33,6 @@ inline class ListBuffer<T>(private val list: List<T>) : Buffer<T> {
|
|||||||
override fun get(index: Int): T = list[index]
|
override fun get(index: Int): T = list[index]
|
||||||
|
|
||||||
override fun iterator(): Iterator<T> = list.iterator()
|
override fun iterator(): Iterator<T> = list.iterator()
|
||||||
|
|
||||||
override fun copy(): ListBuffer<T> = ListBuffer(ArrayList(list))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline class MutableListBuffer<T>(private val list: MutableList<T>) : MutableBuffer<T> {
|
inline class MutableListBuffer<T>(private val list: MutableList<T>) : MutableBuffer<T> {
|
||||||
@ -104,10 +97,43 @@ inline class IntBuffer(private val array: IntArray): MutableBuffer<Int>{
|
|||||||
override fun copy(): MutableBuffer<Int> = IntBuffer(array.copyOf())
|
override fun copy(): MutableBuffer<Int> = IntBuffer(array.copyOf())
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T : Any> buffer(size: Int, noinline initializer: (Int) -> T): Buffer<T> {
|
inline class ReadOnlyBuffer<T>(private val buffer: MutableBuffer<T>) : Buffer<T> {
|
||||||
return ArrayBuffer(Array(size, initializer))
|
override val size: Int get() = buffer.size
|
||||||
|
|
||||||
|
override fun get(index: Int): T = buffer.get(index)
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<T> = buffer.iterator()
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T : Any> mutableBuffer(size: Int, noinline initializer: (Int) -> T): MutableBuffer<T> {
|
/**
|
||||||
return ArrayBuffer(Array(size, initializer))
|
* Convert this buffer to read-only buffer
|
||||||
|
*/
|
||||||
|
fun <T> Buffer<T>.asReadOnly(): Buffer<T> = if (this is MutableBuffer) {
|
||||||
|
ReadOnlyBuffer(this)
|
||||||
|
} else {
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create most appropriate immutable buffer for given type avoiding boxing wherever possible
|
||||||
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
inline fun <reified T : Any> buffer(size: Int, noinline initializer: (Int) -> T): Buffer<T> {
|
||||||
|
return when (T::class) {
|
||||||
|
Double::class -> DoubleBuffer(DoubleArray(size) { initializer(it) as Double }) as Buffer<T>
|
||||||
|
Int::class -> IntBuffer(IntArray(size) { initializer(it) as Int }) as Buffer<T>
|
||||||
|
else -> ArrayBuffer(Array(size, initializer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create most appropriate mutable buffer for given type avoiding boxing wherever possible
|
||||||
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
inline fun <reified T : Any> mutableBuffer(size: Int, noinline initializer: (Int) -> T): MutableBuffer<T> {
|
||||||
|
return when (T::class) {
|
||||||
|
Double::class -> DoubleBuffer(DoubleArray(size) { initializer(it) as Double }) as MutableBuffer<T>
|
||||||
|
Int::class -> IntBuffer(IntArray(size) { initializer(it) as Int }) as MutableBuffer<T>
|
||||||
|
else -> ArrayBuffer(Array(size, initializer))
|
||||||
|
}
|
||||||
}
|
}
|
@ -23,7 +23,7 @@ abstract class NDField<T, F : Field<T>>(val shape: IntArray, val field: F) : Fie
|
|||||||
* 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
|
||||||
*/
|
*/
|
||||||
fun produce(initializer: F.(IntArray) -> T): NDElement<T, F> = NDElement(this, produceStructure(initializer))
|
fun produce(initializer: F.(IntArray) -> T): NDElement<T, F> = NDStructureElement(this, produceStructure(initializer))
|
||||||
|
|
||||||
override val zero: NDElement<T, F> by lazy {
|
override val zero: NDElement<T, F> by lazy {
|
||||||
produce { zero }
|
produce { zero }
|
||||||
@ -83,7 +83,7 @@ abstract class NDField<T, F : Field<T>>(val shape: IntArray, val field: F) : Fie
|
|||||||
// /**
|
// /**
|
||||||
// * Reverse minus operation
|
// * Reverse minus operation
|
||||||
// */
|
// */
|
||||||
// operator fun T.minus(arg: NDElement<T, F>): NDElement<T, F> = arg.transform { _, value ->
|
// operator fun T.minus(arg: NDElement<T, F>): NDElement<T, F> = arg.transformIndexed { _, value ->
|
||||||
// with(arg.context.field) {
|
// with(arg.context.field) {
|
||||||
// this@minus - value
|
// this@minus - value
|
||||||
// }
|
// }
|
||||||
@ -97,38 +97,41 @@ abstract class NDField<T, F : Field<T>>(val shape: IntArray, val field: F) : Fie
|
|||||||
// /**
|
// /**
|
||||||
// * Reverse division operation
|
// * Reverse division operation
|
||||||
// */
|
// */
|
||||||
// operator fun T.div(arg: NDElement<T, F>): NDElement<T, F> = arg.transform { _, value ->
|
// operator fun T.div(arg: NDElement<T, F>): NDElement<T, F> = arg.transformIndexed { _, value ->
|
||||||
// with(arg.context.field) {
|
// with(arg.context.field) {
|
||||||
// this@div / value
|
// this@div / value
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface NDElement<T, F : Field<T>>: FieldElement<NDElement<T, F>, NDField<T, F>>, NDStructure<T>
|
||||||
|
|
||||||
|
inline fun <T, F : Field<T>> NDElement<T, F>.transformIndexed(crossinline action: F.(IntArray, T) -> T): NDElement<T, F> = context.produce { action(it, get(*it)) }
|
||||||
|
inline fun <T, F : Field<T>> NDElement<T, F>.transform(crossinline action: F.(T) -> T): NDElement<T, F> = context.produce { action(get(*it)) }
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Immutable [NDStructure] coupled to the context. Emulates Python ndarray
|
* Read-only [NDStructure] coupled to the context.
|
||||||
*/
|
*/
|
||||||
class NDElement<T, F : Field<T>>(override val context: NDField<T, F>, private val structure: NDStructure<T>) : FieldElement<NDElement<T, F>, NDField<T, F>>, NDStructure<T> by structure {
|
class NDStructureElement<T, F : Field<T>>(override val context: NDField<T, F>, private val structure: NDStructure<T>) : NDElement<T,F>, NDStructure<T> by structure {
|
||||||
|
|
||||||
//TODO ensure structure is immutable
|
//TODO ensure structure is immutable
|
||||||
|
|
||||||
override val self: NDElement<T, F>
|
override val self: NDElement<T, F> get() = this
|
||||||
get() = this
|
|
||||||
|
|
||||||
inline fun transform(crossinline action: (IntArray, T) -> T): NDElement<T, F> = context.produce { action(it, get(*it)) }
|
|
||||||
inline fun transform(crossinline action: (T) -> T): NDElement<T, F> = context.produce { action(get(*it)) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Element by element application of any operation on elements to the whole array. Just like in numpy
|
* Element by element application of any operation on elements to the whole array. Just like in numpy
|
||||||
*/
|
*/
|
||||||
operator fun <T, F : Field<T>> Function1<T, T>.invoke(ndElement: NDElement<T, F>): NDElement<T, F> = ndElement.transform { _, value -> this(value) }
|
operator fun <T, F : Field<T>> Function1<T, T>.invoke(ndElement: NDElement<T, F>): NDElement<T, F> = ndElement.transform {value -> this@invoke(value) }
|
||||||
|
|
||||||
/* plus and minus */
|
/* plus and minus */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Summation operation for [NDElement] and single element
|
* Summation operation for [NDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T, F : Field<T>> NDElement<T, F>.plus(arg: T): NDElement<T, F> = transform { _, value ->
|
operator fun <T, F : Field<T>> NDElement<T, F>.plus(arg: T): NDElement<T, F> = transform {value ->
|
||||||
with(context.field) {
|
with(context.field) {
|
||||||
arg + value
|
arg + value
|
||||||
}
|
}
|
||||||
@ -137,7 +140,7 @@ operator fun <T, F : Field<T>> NDElement<T, F>.plus(arg: T): NDElement<T, F> = t
|
|||||||
/**
|
/**
|
||||||
* Subtraction operation between [NDElement] and single element
|
* Subtraction operation between [NDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T, F : Field<T>> NDElement<T, F>.minus(arg: T): NDElement<T, F> = transform { _, value ->
|
operator fun <T, F : Field<T>> NDElement<T, F>.minus(arg: T): NDElement<T, F> = transform {value ->
|
||||||
with(context.field) {
|
with(context.field) {
|
||||||
arg - value
|
arg - value
|
||||||
}
|
}
|
||||||
@ -148,7 +151,7 @@ operator fun <T, F : Field<T>> NDElement<T, F>.minus(arg: T): NDElement<T, F> =
|
|||||||
/**
|
/**
|
||||||
* Product operation for [NDElement] and single element
|
* Product operation for [NDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T, F : Field<T>> NDElement<T, F>.times(arg: T): NDElement<T, F> = transform { _, value ->
|
operator fun <T, F : Field<T>> NDElement<T, F>.times(arg: T): NDElement<T, F> = transform { value ->
|
||||||
with(context.field) {
|
with(context.field) {
|
||||||
arg * value
|
arg * value
|
||||||
}
|
}
|
||||||
@ -157,7 +160,7 @@ operator fun <T, F : Field<T>> NDElement<T, F>.times(arg: T): NDElement<T, F> =
|
|||||||
/**
|
/**
|
||||||
* Division operation between [NDElement] and single element
|
* Division operation between [NDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T, F : Field<T>> NDElement<T, F>.div(arg: T): NDElement<T, F> = transform { _, value ->
|
operator fun <T, F : Field<T>> NDElement<T, F>.div(arg: T): NDElement<T, F> = transform { value ->
|
||||||
with(context.field) {
|
with(context.field) {
|
||||||
arg / value
|
arg / value
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package scientifik.kmath.structures
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
|
||||||
interface NDStructure<T> : Iterable<Pair<IntArray, T>> {
|
interface NDStructure<T> {
|
||||||
|
|
||||||
val shape: IntArray
|
val shape: IntArray
|
||||||
|
|
||||||
@ -9,6 +9,8 @@ interface NDStructure<T> : Iterable<Pair<IntArray, T>> {
|
|||||||
get() = shape.size
|
get() = shape.size
|
||||||
|
|
||||||
operator fun get(index: IntArray): T
|
operator fun get(index: IntArray): T
|
||||||
|
|
||||||
|
fun elements(): Sequence<Pair<IntArray, T>>
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun <T> NDStructure<T>.get(vararg index: Int): T = get(index)
|
operator fun <T> NDStructure<T>.get(vararg index: Int): T = get(index)
|
||||||
@ -18,7 +20,7 @@ interface MutableNDStructure<T> : NDStructure<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun <T> MutableNDStructure<T>.transformInPlace(action: (IntArray, T) -> T) {
|
fun <T> MutableNDStructure<T>.transformInPlace(action: (IntArray, T) -> T) {
|
||||||
for ((index, oldValue) in this) {
|
elements().forEach { (index, oldValue) ->
|
||||||
this[index] = action(index, oldValue)
|
this[index] = action(index, oldValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,8 +109,8 @@ abstract class GenericNDStructure<T, B : Buffer<T>> : NDStructure<T> {
|
|||||||
override val shape: IntArray
|
override val shape: IntArray
|
||||||
get() = strides.shape
|
get() = strides.shape
|
||||||
|
|
||||||
override fun iterator(): Iterator<Pair<IntArray, T>> =
|
override fun elements()=
|
||||||
strides.indices().map { it to this[it] }.iterator()
|
strides.indices().map { it to this[it] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -169,6 +171,6 @@ fun <T> genericNdStructure(shape: IntArray, initializer: (IntArray) -> T): Mutab
|
|||||||
yield(initializer(it))
|
yield(initializer(it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val buffer = MutableListBuffer<T>(sequence.toMutableList())
|
val buffer = MutableListBuffer(sequence.toMutableList())
|
||||||
return MutableBufferNDStructure(strides, buffer)
|
return MutableBufferNDStructure(strides, buffer)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A specification for serialization and deserialization objects to buffer
|
||||||
|
*/
|
||||||
|
interface BufferSpec<T : Any> {
|
||||||
|
fun fromBuffer(buffer: ByteBuffer): T
|
||||||
|
fun toBuffer(value: T): ByteBuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [BufferSpec] with fixed unit size. Allows storage of any object without boxing.
|
||||||
|
*/
|
||||||
|
interface FixedSizeBufferSpec<T : Any> : BufferSpec<T> {
|
||||||
|
val unitSize: Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read an object from buffer in current position
|
||||||
|
*/
|
||||||
|
fun ByteBuffer.readObject(): T {
|
||||||
|
val buffer = ByteArray(unitSize)
|
||||||
|
get(buffer)
|
||||||
|
return fromBuffer(ByteBuffer.wrap(buffer))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read an object from buffer in given index (not buffer position
|
||||||
|
*/
|
||||||
|
fun ByteBuffer.readObject(index: Int): T {
|
||||||
|
val dup = duplicate()
|
||||||
|
dup.position(index*unitSize)
|
||||||
|
return dup.readObject()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write object to [ByteBuffer] in current buffer position
|
||||||
|
*/
|
||||||
|
fun ByteBuffer.writeObject(obj: T) {
|
||||||
|
val buffer = toBuffer(obj).apply { rewind() }
|
||||||
|
assert(buffer.limit() == unitSize)
|
||||||
|
put(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put an object in given index
|
||||||
|
*/
|
||||||
|
fun ByteBuffer.writeObject(index: Int, obj: T) {
|
||||||
|
val dup = duplicate()
|
||||||
|
dup.position(index*unitSize)
|
||||||
|
dup.writeObject(obj)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
import scientifik.kmath.operations.Complex
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
object ComplexBufferSpec : FixedSizeBufferSpec<Complex> {
|
||||||
|
override val unitSize: Int = 16
|
||||||
|
|
||||||
|
override fun fromBuffer(buffer: ByteBuffer): Complex {
|
||||||
|
val re = buffer.getDouble(0)
|
||||||
|
val im = buffer.getDouble(8)
|
||||||
|
return Complex(re, im)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toBuffer(value: Complex): ByteBuffer = ByteBuffer.allocate(16).apply {
|
||||||
|
putDouble(value.re)
|
||||||
|
putDouble(value.im)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a mutable buffer which ignores boxing
|
||||||
|
*/
|
||||||
|
fun Complex.Companion.createBuffer(size: Int) = ObjectBuffer.create(ComplexBufferSpec, size)
|
||||||
|
|
@ -0,0 +1,28 @@
|
|||||||
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
class ObjectBuffer<T : Any>(private val buffer: ByteBuffer, private val spec: FixedSizeBufferSpec<T>) : MutableBuffer<T> {
|
||||||
|
override val size: Int
|
||||||
|
get() = buffer.limit() / spec.unitSize
|
||||||
|
|
||||||
|
override fun get(index: Int): T = with(spec) { buffer.readObject(index) }
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<T> = (0 until size).asSequence().map { get(it) }.iterator()
|
||||||
|
|
||||||
|
override fun set(index: Int, value: T) = with(spec) { buffer.writeObject(index, value) }
|
||||||
|
|
||||||
|
override fun copy(): MutableBuffer<T> {
|
||||||
|
val dup = buffer.duplicate()
|
||||||
|
val copy = ByteBuffer.allocate(dup.capacity())
|
||||||
|
dup.rewind()
|
||||||
|
copy.put(dup)
|
||||||
|
copy.flip()
|
||||||
|
return ObjectBuffer(copy, spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun <T : Any> create(spec: FixedSizeBufferSpec<T>, size: Int) =
|
||||||
|
ObjectBuffer<T>(ByteBuffer.allocate(size * spec.unitSize), spec)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
import scientifik.kmath.operations.Real
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
object RealBufferSpec : FixedSizeBufferSpec<Real> {
|
||||||
|
override val unitSize: Int = 8
|
||||||
|
|
||||||
|
override fun fromBuffer(buffer: ByteBuffer): Real = Real(buffer.double)
|
||||||
|
|
||||||
|
override fun toBuffer(value: Real): ByteBuffer = ByteBuffer.allocate(8).apply { putDouble(value.value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
object DoubleBufferSpec : FixedSizeBufferSpec<Double> {
|
||||||
|
override val unitSize: Int = 8
|
||||||
|
|
||||||
|
override fun fromBuffer(buffer: ByteBuffer): Double = buffer.double
|
||||||
|
|
||||||
|
override fun toBuffer(value: Double): ByteBuffer = ByteBuffer.allocate(8).apply { putDouble(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Double.Companion.createBuffer(size: Int) = ObjectBuffer.create(DoubleBufferSpec, size)
|
||||||
|
fun Real.Companion.createBuffer(size: Int) = ObjectBuffer.create(RealBufferSpec, size)
|
@ -0,0 +1,17 @@
|
|||||||
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import scientifik.kmath.operations.Complex
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class ComplexBufferSpecTest {
|
||||||
|
@Test
|
||||||
|
fun testComplexBuffer() {
|
||||||
|
val buffer = Complex.createBuffer(20)
|
||||||
|
(0 until 20).forEach {
|
||||||
|
buffer[it] = Complex(it.toDouble(), -it.toDouble())
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(Complex(5.0, -5.0), buffer[5])
|
||||||
|
}
|
||||||
|
}
|
42
kmath-coroutines/build.gradle
Normal file
42
kmath-coroutines/build.gradle
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
plugins {
|
||||||
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
targets {
|
||||||
|
fromPreset(presets.jvm, 'jvm')
|
||||||
|
// For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64
|
||||||
|
// For Linux, preset should be changed to e.g. presets.linuxX64
|
||||||
|
// For MacOS, preset should be changed to e.g. presets.macosX64
|
||||||
|
//fromPreset(presets.mingwX64, 'mingw')
|
||||||
|
}
|
||||||
|
sourceSets {
|
||||||
|
commonMain {
|
||||||
|
dependencies {
|
||||||
|
api project(":kmath-core")
|
||||||
|
api "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutinesVersion"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commonTest {
|
||||||
|
dependencies {
|
||||||
|
api 'org.jetbrains.kotlin:kotlin-test-common'
|
||||||
|
api 'org.jetbrains.kotlin:kotlin-test-annotations-common'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jvmMain {
|
||||||
|
dependencies {
|
||||||
|
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jvmTest {
|
||||||
|
dependencies {
|
||||||
|
implementation 'org.jetbrains.kotlin:kotlin-test'
|
||||||
|
implementation 'org.jetbrains.kotlin:kotlin-test-junit'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// mingwMain {
|
||||||
|
// }
|
||||||
|
// mingwTest {
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
import kotlin.coroutines.EmptyCoroutineContext
|
||||||
|
|
||||||
|
expect fun <R> runBlocking(context: CoroutineContext = EmptyCoroutineContext, function: suspend CoroutineScope.()->R): R
|
||||||
|
|
||||||
|
val Dispatchers.Math: CoroutineDispatcher get() = Dispatchers.Default
|
@ -0,0 +1,79 @@
|
|||||||
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import scientifik.kmath.operations.Field
|
||||||
|
|
||||||
|
class LazyNDField<T, F : Field<T>>(shape: IntArray, field: F, val scope: CoroutineScope = GlobalScope) : NDField<T, F>(shape, field) {
|
||||||
|
|
||||||
|
override fun produceStructure(initializer: F.(IntArray) -> T): NDStructure<T> = LazyNDStructure(this) { initializer(field, it) }
|
||||||
|
|
||||||
|
|
||||||
|
override fun add(a: NDElement<T, F>, b: NDElement<T, F>): NDElement<T, F> {
|
||||||
|
return LazyNDStructure(this) { index ->
|
||||||
|
val aDeferred = a.deferred(index)
|
||||||
|
val bDeferred = b.deferred(index)
|
||||||
|
aDeferred.await() + bDeferred.await()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun multiply(a: NDElement<T, F>, k: Double): NDElement<T, F> {
|
||||||
|
return LazyNDStructure(this) { index -> a.await(index) * k }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun multiply(a: NDElement<T, F>, b: NDElement<T, F>): NDElement<T, F> {
|
||||||
|
return LazyNDStructure(this) { index ->
|
||||||
|
val aDeferred = a.deferred(index)
|
||||||
|
val bDeferred = b.deferred(index)
|
||||||
|
aDeferred.await() * bDeferred.await()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun divide(a: NDElement<T, F>, b: NDElement<T, F>): NDElement<T, F> {
|
||||||
|
return LazyNDStructure(this) { index ->
|
||||||
|
val aDeferred = a.deferred(index)
|
||||||
|
val bDeferred = b.deferred(index)
|
||||||
|
aDeferred.await() / bDeferred.await()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LazyNDStructure<T, F : Field<T>>(override val context: LazyNDField<T, F>, val function: suspend F.(IntArray) -> T) : NDElement<T, F>, NDStructure<T> {
|
||||||
|
override val self: NDElement<T, F> get() = this
|
||||||
|
override val shape: IntArray get() = context.shape
|
||||||
|
|
||||||
|
private val cache = HashMap<IntArray, Deferred<T>>()
|
||||||
|
|
||||||
|
fun deferred(index: IntArray) = cache.getOrPut(index) { context.scope.async(context = Dispatchers.Math) { function.invoke(context.field, index) } }
|
||||||
|
|
||||||
|
suspend fun await(index: IntArray): T = deferred(index).await()
|
||||||
|
|
||||||
|
override fun get(index: IntArray): T = runBlocking {
|
||||||
|
deferred(index).await()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun elements(): Sequence<Pair<IntArray, T>> {
|
||||||
|
val strides = DefaultStrides(shape)
|
||||||
|
return strides.indices().map { index -> index to runBlocking { await(index) } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> NDElement<T, *>.deferred(index: IntArray) = if (this is LazyNDStructure<T, *>) this.deferred(index) else CompletableDeferred(get(index))
|
||||||
|
|
||||||
|
suspend fun <T> NDElement<T, *>.await(index: IntArray) = if (this is LazyNDStructure<T, *>) this.await(index) else get(index)
|
||||||
|
|
||||||
|
fun <T, F : Field<T>> NDElement<T, F>.lazy(scope: CoroutineScope = GlobalScope): LazyNDStructure<T, F> {
|
||||||
|
return if (this is LazyNDStructure<T, F>) {
|
||||||
|
this
|
||||||
|
} else {
|
||||||
|
val context = LazyNDField(context.shape, context.field)
|
||||||
|
LazyNDStructure(context) { get(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T, F : Field<T>> LazyNDStructure<T, F>.transformIndexed(crossinline action: suspend F.(IntArray, T) -> T) = LazyNDStructure(context) { index ->
|
||||||
|
action.invoke(this, index, await(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T, F : Field<T>> LazyNDStructure<T, F>.transform(crossinline action: suspend F.(T) -> T) = LazyNDStructure(context) { index ->
|
||||||
|
action.invoke(this, await(index))
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
import scientifik.kmath.operations.IntField
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
|
||||||
|
class LazyNDFieldTest {
|
||||||
|
@Test
|
||||||
|
fun testLazyStructure() {
|
||||||
|
var counter = 0
|
||||||
|
val regularStructure = NDArrays.create(IntField, intArrayOf(2, 2, 2)) { it[0] + it[1] - it[2] }
|
||||||
|
val result = (regularStructure.lazy() + 2).transform {
|
||||||
|
counter++
|
||||||
|
it * it
|
||||||
|
}
|
||||||
|
assertEquals(4, result[0,0,0])
|
||||||
|
assertEquals(1, counter)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
actual fun <R> runBlocking(context: CoroutineContext, function: suspend CoroutineScope.() -> R): R = kotlinx.coroutines.runBlocking(context, function)
|
@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'kotlin-multiplatform'
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
@ -16,6 +16,7 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
api project(":kmath-core")
|
api project(":kmath-core")
|
||||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
|
implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
|
||||||
|
api "org.jetbrains.kotlinx:kotlinx-io:$ioVersion"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
commonTest {
|
commonTest {
|
||||||
@ -27,6 +28,7 @@ kotlin {
|
|||||||
jvmMain {
|
jvmMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
||||||
|
api "org.jetbrains.kotlinx:kotlinx-io-jvm:$ioVersion"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jvmTest {
|
jvmTest {
|
||||||
|
@ -5,6 +5,6 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':kmath-core')
|
compile project(':kmath-core')
|
||||||
jmh 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
//jmh project(':kmath-core')
|
||||||
}
|
}
|
@ -4,8 +4,7 @@ import org.openjdk.jmh.annotations.*
|
|||||||
import java.nio.IntBuffer
|
import java.nio.IntBuffer
|
||||||
|
|
||||||
|
|
||||||
@Fork(1)
|
@Warmup(iterations = 1)
|
||||||
@Warmup(iterations = 2)
|
|
||||||
@Measurement(iterations = 5)
|
@Measurement(iterations = 5)
|
||||||
@State(Scope.Benchmark)
|
@State(Scope.Benchmark)
|
||||||
open class ArrayBenchmark {
|
open class ArrayBenchmark {
|
||||||
@ -30,7 +29,6 @@ open class ArrayBenchmark {
|
|||||||
for (i in 1..10000) {
|
for (i in 1..10000) {
|
||||||
res += array[10000 - i]
|
res += array[10000 - i]
|
||||||
}
|
}
|
||||||
print(res)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
@ -39,7 +37,6 @@ open class ArrayBenchmark {
|
|||||||
for (i in 1..10000) {
|
for (i in 1..10000) {
|
||||||
res += arrayBuffer.get(10000 - i)
|
res += arrayBuffer.get(10000 - i)
|
||||||
}
|
}
|
||||||
print(res)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
@ -48,6 +45,5 @@ open class ArrayBenchmark {
|
|||||||
for (i in 1..10000) {
|
for (i in 1..10000) {
|
||||||
res += nativeBuffer.get(10000 - i)
|
res += nativeBuffer.get(10000 - i)
|
||||||
}
|
}
|
||||||
print(res)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
import org.openjdk.jmh.annotations.*
|
||||||
|
import scientifik.kmath.operations.Complex
|
||||||
|
|
||||||
|
@Warmup(iterations = 1)
|
||||||
|
@Measurement(iterations = 5)
|
||||||
|
@State(Scope.Benchmark)
|
||||||
|
open class BufferBenchmark {
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
fun genericDoubleBufferReadWrite() {
|
||||||
|
val buffer = Double.createBuffer(size)
|
||||||
|
(0 until size).forEach {
|
||||||
|
buffer[it] = it.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
(0 until size).forEach {
|
||||||
|
buffer[it]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
fun complexBufferReadWrite() {
|
||||||
|
val buffer = Complex.createBuffer(size/2)
|
||||||
|
(0 until size/2).forEach {
|
||||||
|
buffer[it] = Complex(it.toDouble(), -it.toDouble())
|
||||||
|
}
|
||||||
|
|
||||||
|
(0 until size/2).forEach {
|
||||||
|
buffer[it]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val size = 1000
|
||||||
|
}
|
||||||
|
}
|
@ -5,10 +5,12 @@ pluginManagement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enableFeaturePreview("GRADLE_METADATA")
|
//enableFeaturePreview("GRADLE_METADATA")
|
||||||
|
|
||||||
rootProject.name = "kmath"
|
rootProject.name = "kmath"
|
||||||
include(
|
include(
|
||||||
":kmath-core",
|
":kmath-core",
|
||||||
|
":kmath-io",
|
||||||
|
":kmath-coroutines",
|
||||||
":kmath-jmh"
|
":kmath-jmh"
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user