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
|
||||
```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
|
||||
```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).
|
||||
|
@ -1,7 +1,11 @@
|
||||
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 ioVersion: String by extra
|
||||
val coroutinesVersion: String by extra
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
@ -15,6 +19,7 @@ buildscript {
|
||||
|
||||
plugins {
|
||||
id("com.jfrog.artifactory") version "4.8.1" apply false
|
||||
// id("org.jetbrains.kotlin.multiplatform") apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
@ -22,7 +27,7 @@ allprojects {
|
||||
apply(plugin = "com.jfrog.artifactory")
|
||||
|
||||
group = "scientifik"
|
||||
version = "0.0.1-SNAPSHOT"
|
||||
version = "0.0.2-dev-1"
|
||||
}
|
||||
|
||||
if(file("artifactory.gradle").exists()){
|
||||
|
@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id 'kotlin-multiplatform'
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
}
|
||||
|
||||
kotlin {
|
||||
@ -14,7 +14,7 @@ kotlin {
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
|
||||
api 'org.jetbrains.kotlin:kotlin-stdlib-common'
|
||||
}
|
||||
}
|
||||
commonTest {
|
||||
@ -25,7 +25,7 @@ kotlin {
|
||||
}
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
||||
api 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
||||
}
|
||||
}
|
||||
jvmTest {
|
||||
@ -36,7 +36,7 @@ kotlin {
|
||||
}
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-js'
|
||||
api 'org.jetbrains.kotlin:kotlin-stdlib-js'
|
||||
}
|
||||
}
|
||||
jsTest {
|
||||
|
@ -85,7 +85,7 @@ class FastHistogram(
|
||||
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())
|
||||
}.iterator()
|
||||
|
||||
@ -100,7 +100,7 @@ class FastHistogram(
|
||||
* Create a phantom lightweight immutable copy of this histogram
|
||||
*/
|
||||
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())
|
||||
}
|
||||
|
||||
|
@ -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 copy(): ArrayVector<T, F> = ArrayVector(context, element)
|
||||
|
||||
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
|
||||
get() = kotlin.math.sqrt(square)
|
||||
|
||||
companion object {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -49,6 +49,9 @@ inline class Real(val value: Double) : FieldElement<Real, RealField> {
|
||||
override val context
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 iterator(): Iterator<T>
|
||||
|
||||
/**
|
||||
* A shallow copy of the buffer
|
||||
*/
|
||||
fun copy(): Buffer<T>
|
||||
}
|
||||
|
||||
fun <T> Buffer<T>.asSequence(): Sequence<T> = iterator().asSequence()
|
||||
@ -26,7 +21,7 @@ interface MutableBuffer<T> : Buffer<T> {
|
||||
/**
|
||||
* 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 iterator(): Iterator<T> = list.iterator()
|
||||
|
||||
override fun copy(): ListBuffer<T> = ListBuffer(ArrayList(list))
|
||||
}
|
||||
|
||||
inline class MutableListBuffer<T>(private val list: MutableList<T>) : MutableBuffer<T> {
|
||||
@ -89,7 +82,7 @@ inline class DoubleBuffer(private val array: DoubleArray) : MutableBuffer<Double
|
||||
override fun copy(): MutableBuffer<Double> = DoubleBuffer(array.copyOf())
|
||||
}
|
||||
|
||||
inline class IntBuffer(private val array: IntArray): MutableBuffer<Int>{
|
||||
inline class IntBuffer(private val array: IntArray) : MutableBuffer<Int> {
|
||||
override val size: Int
|
||||
get() = array.size
|
||||
|
||||
@ -104,10 +97,43 @@ inline class IntBuffer(private val array: IntArray): MutableBuffer<Int>{
|
||||
override fun copy(): MutableBuffer<Int> = IntBuffer(array.copyOf())
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> buffer(size: Int, noinline initializer: (Int) -> T): Buffer<T> {
|
||||
return ArrayBuffer(Array(size, initializer))
|
||||
inline class ReadOnlyBuffer<T>(private val buffer: MutableBuffer<T>) : Buffer<T> {
|
||||
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
|
||||
* 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 {
|
||||
produce { zero }
|
||||
@ -83,7 +83,7 @@ abstract class NDField<T, F : Field<T>>(val shape: IntArray, val field: F) : Fie
|
||||
// /**
|
||||
// * 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) {
|
||||
// this@minus - value
|
||||
// }
|
||||
@ -97,38 +97,41 @@ abstract class NDField<T, F : Field<T>>(val shape: IntArray, val field: F) : Fie
|
||||
// /**
|
||||
// * 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) {
|
||||
// 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
|
||||
|
||||
override val self: NDElement<T, F>
|
||||
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)) }
|
||||
override val self: NDElement<T, F> get() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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
|
||||
*/
|
||||
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) {
|
||||
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
|
||||
*/
|
||||
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) {
|
||||
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
|
||||
*/
|
||||
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) {
|
||||
arg / value
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package scientifik.kmath.structures
|
||||
|
||||
|
||||
interface NDStructure<T> : Iterable<Pair<IntArray, T>> {
|
||||
interface NDStructure<T> {
|
||||
|
||||
val shape: IntArray
|
||||
|
||||
@ -9,6 +9,8 @@ interface NDStructure<T> : Iterable<Pair<IntArray, T>> {
|
||||
get() = shape.size
|
||||
|
||||
operator fun get(index: IntArray): T
|
||||
|
||||
fun elements(): Sequence<Pair<IntArray, T>>
|
||||
}
|
||||
|
||||
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) {
|
||||
for ((index, oldValue) in this) {
|
||||
elements().forEach { (index, oldValue) ->
|
||||
this[index] = action(index, oldValue)
|
||||
}
|
||||
}
|
||||
@ -87,7 +89,7 @@ class DefaultStrides(override val shape: IntArray) : Strides {
|
||||
var current = offset
|
||||
var strideIndex = strides.size - 2
|
||||
while (strideIndex >= 0) {
|
||||
res[ strideIndex] = (current / strides[strideIndex])
|
||||
res[strideIndex] = (current / strides[strideIndex])
|
||||
current %= strides[strideIndex]
|
||||
strideIndex--
|
||||
}
|
||||
@ -107,8 +109,8 @@ abstract class GenericNDStructure<T, B : Buffer<T>> : NDStructure<T> {
|
||||
override val shape: IntArray
|
||||
get() = strides.shape
|
||||
|
||||
override fun iterator(): Iterator<Pair<IntArray, T>> =
|
||||
strides.indices().map { it to this[it] }.iterator()
|
||||
override fun elements()=
|
||||
strides.indices().map { it to this[it] }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -126,10 +128,10 @@ class BufferNDStructure<T>(
|
||||
}
|
||||
}
|
||||
|
||||
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(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) =
|
||||
inline fun <reified T : Any> ndStructure(shape: IntArray, noinline initializer: (IntArray) -> T) =
|
||||
ndStructure(DefaultStrides(shape), initializer)
|
||||
|
||||
|
||||
@ -153,22 +155,22 @@ class MutableBufferNDStructure<T>(
|
||||
/**
|
||||
* Create optimized mutable structure for given type
|
||||
*/
|
||||
inline fun <reified T: Any> mutableNdStructure(strides: Strides, noinline initializer: (IntArray) -> T) =
|
||||
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) =
|
||||
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>{
|
||||
fun <T> genericNdStructure(shape: IntArray, initializer: (IntArray) -> T): MutableBufferNDStructure<T> {
|
||||
val strides = DefaultStrides(shape)
|
||||
val sequence = sequence{
|
||||
strides.indices().forEach{
|
||||
val sequence = sequence {
|
||||
strides.indices().forEach {
|
||||
yield(initializer(it))
|
||||
}
|
||||
}
|
||||
val buffer = MutableListBuffer<T>(sequence.toMutableList())
|
||||
val buffer = MutableListBuffer(sequence.toMutableList())
|
||||
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 {
|
||||
id 'kotlin-multiplatform'
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
}
|
||||
|
||||
kotlin {
|
||||
@ -16,6 +16,7 @@ kotlin {
|
||||
dependencies {
|
||||
api project(":kmath-core")
|
||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
|
||||
api "org.jetbrains.kotlinx:kotlinx-io:$ioVersion"
|
||||
}
|
||||
}
|
||||
commonTest {
|
||||
@ -27,6 +28,7 @@ kotlin {
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
||||
api "org.jetbrains.kotlinx:kotlinx-io-jvm:$ioVersion"
|
||||
}
|
||||
}
|
||||
jvmTest {
|
||||
|
@ -5,6 +5,6 @@ plugins {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':kmath-core')
|
||||
jmh 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
||||
compile project(':kmath-core')
|
||||
//jmh project(':kmath-core')
|
||||
}
|
@ -4,8 +4,7 @@ import org.openjdk.jmh.annotations.*
|
||||
import java.nio.IntBuffer
|
||||
|
||||
|
||||
@Fork(1)
|
||||
@Warmup(iterations = 2)
|
||||
@Warmup(iterations = 1)
|
||||
@Measurement(iterations = 5)
|
||||
@State(Scope.Benchmark)
|
||||
open class ArrayBenchmark {
|
||||
@ -30,7 +29,6 @@ open class ArrayBenchmark {
|
||||
for (i in 1..10000) {
|
||||
res += array[10000 - i]
|
||||
}
|
||||
print(res)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@ -39,7 +37,6 @@ open class ArrayBenchmark {
|
||||
for (i in 1..10000) {
|
||||
res += arrayBuffer.get(10000 - i)
|
||||
}
|
||||
print(res)
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@ -48,6 +45,5 @@ open class ArrayBenchmark {
|
||||
for (i in 1..10000) {
|
||||
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"
|
||||
include(
|
||||
":kmath-core",
|
||||
":kmath-io",
|
||||
":kmath-coroutines",
|
||||
":kmath-jmh"
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user