From d04ee956e5a75189f65f659168508cd34d45d448 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 12 Feb 2019 14:06:37 +0300 Subject: [PATCH] Lazy structures revision --- .../kmath/structures/ComplexNDBenchmark.kt | 4 +- .../kmath/structures/NDFieldBenchmark.kt | 21 ++-- build.gradle.kts | 4 +- examples/build.gradle.kts | 15 +++ .../scientifik/kmath/structures/BufferSpec.kt | 2 +- .../kmath/structures/ComplexNDField.kt | 6 + .../kmath/structures/CoroutinesExtra.kt | 5 - .../kmath/structures/LazyNDField.kt | 113 ------------------ .../kmath/structures/runBlocking.kt | 11 -- .../kmath/structures/LazyNDStructure.kt | 47 ++++++++ .../kmath/structures/runBlocking.kt | 7 -- settings.gradle.kts | 3 +- 12 files changed, 85 insertions(+), 153 deletions(-) create mode 100644 examples/build.gradle.kts delete mode 100644 kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/structures/LazyNDField.kt delete mode 100644 kmath-coroutines/src/jsMain/kotlin/scientifik/kmath/structures/runBlocking.kt create mode 100644 kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt delete mode 100644 kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/runBlocking.kt diff --git a/benchmarks/src/main/kotlin/scientifik/kmath/structures/ComplexNDBenchmark.kt b/benchmarks/src/main/kotlin/scientifik/kmath/structures/ComplexNDBenchmark.kt index 90bc5a9b4..02f19cd55 100644 --- a/benchmarks/src/main/kotlin/scientifik/kmath/structures/ComplexNDBenchmark.kt +++ b/benchmarks/src/main/kotlin/scientifik/kmath/structures/ComplexNDBenchmark.kt @@ -25,9 +25,9 @@ fun main() { val complexTime = measureTimeMillis { complexField.run { - var res: NDBuffer = one + var res: ComplexNDElement = one repeat(n) { - res += 1.0.toComplex() + res += 1.0 } } } diff --git a/benchmarks/src/main/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt b/benchmarks/src/main/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt index 1e139e247..170320d22 100644 --- a/benchmarks/src/main/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt +++ b/benchmarks/src/main/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt @@ -1,5 +1,6 @@ package scientifik.kmath.structures +import kotlinx.coroutines.GlobalScope import scientifik.kmath.operations.RealField import kotlin.system.measureTimeMillis @@ -11,8 +12,6 @@ fun main(args: Array) { val autoField = NDField.auto(intArrayOf(dim, dim), RealField) // specialized nd-field for Double. It works as generic Double field as well val specializedField = NDField.real(intArrayOf(dim, dim)) - //A field implementing lazy computations. All elements are computed on-demand - val lazyField = NDField.lazy(intArrayOf(dim, dim), RealField) //A generic boxing field. It should be used for objects, not primitives. val genericField = NDField.buffered(intArrayOf(dim, dim), RealField) @@ -26,7 +25,7 @@ fun main(args: Array) { } } - println("Buffered addition completed in $autoTime millis") + println("Automatic field addition completed in $autoTime millis") val elementTime = measureTimeMillis { var res = genericField.one @@ -50,17 +49,15 @@ fun main(args: Array) { val lazyTime = measureTimeMillis { - lazyField.run { - val res = one.map { - var c = 0.0 - repeat(n) { - c += 1.0 - } - c + val res = specializedField.one.mapAsync(GlobalScope) { + var c = 0.0 + repeat(n) { + c += 1.0 } - - res.elements().forEach { it.second } + c } + + res.elements().forEach { it.second } } println("Lazy addition completed in $lazyTime millis") diff --git a/build.gradle.kts b/build.gradle.kts index 3c1c669ba..b794956a4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,7 +24,9 @@ plugins { allprojects { apply(plugin = "maven-publish") - apply(plugin = "com.jfrog.artifactory") + if(project.name.startsWith("kmath")) { + apply(plugin = "com.jfrog.artifactory") + } group = "scientifik" version = "0.0.3" diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts new file mode 100644 index 000000000..86ed9ced6 --- /dev/null +++ b/examples/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + kotlin("jvm") +} + +description = "Examples for different kmath features" + +dependencies { + implementation(project(":kmath-core")) + implementation(project(":kmath-coroutines")) + implementation(project(":kmath-commons")) + implementation(project(":kmath-koma")) + implementation(group = "com.kyonifer", name = "koma-core-ejml", version = "0.12") + testImplementation("org.jetbrains.kotlin:kotlin-test") + testImplementation("org.jetbrains.kotlin:kotlin-test-junit") +} diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/BufferSpec.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/BufferSpec.kt index 65a69cbc2..ce87a1298 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/BufferSpec.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/BufferSpec.kt @@ -4,7 +4,7 @@ import java.nio.ByteBuffer /** - * A specification for serialization and deserialization objects to buffer + * A specification for serialization and deserialization objects to buffer (at current buffer position) */ interface BufferSpec { /** diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt index 190d8a46b..a8b31af3e 100644 --- a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt @@ -122,4 +122,10 @@ operator fun ComplexNDElement.plus(arg: Complex) = * Subtraction operation between [BufferedNDElement] and single element */ operator fun ComplexNDElement.minus(arg: Complex) = + map { it - arg } + +operator fun ComplexNDElement.plus(arg: Double) = + map { it + arg } + +operator fun ComplexNDElement.minus(arg: Double) = map { it - arg } \ No newline at end of file diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/structures/CoroutinesExtra.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/structures/CoroutinesExtra.kt index 6bb8682ff..f6f21af09 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/structures/CoroutinesExtra.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/structures/CoroutinesExtra.kt @@ -6,9 +6,4 @@ import kotlinx.coroutines.Dispatchers import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -expect fun runBlocking( - context: CoroutineContext = EmptyCoroutineContext, - function: suspend CoroutineScope.() -> R -): R - val Dispatchers.Math: CoroutineDispatcher get() = Dispatchers.Default \ No newline at end of file diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/structures/LazyNDField.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/structures/LazyNDField.kt deleted file mode 100644 index 924ddc653..000000000 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/structures/LazyNDField.kt +++ /dev/null @@ -1,113 +0,0 @@ -package scientifik.kmath.structures - -import kotlinx.coroutines.* -import scientifik.kmath.operations.Field -import scientifik.kmath.operations.FieldElement - -class LazyNDField>( - override val shape: IntArray, - override val elementContext: F, - val scope: CoroutineScope = GlobalScope -) : NDField> { - - override val zero by lazy { produce { zero } } - - override val one by lazy { produce { one } } - - override fun produce(initializer: F.(IntArray) -> T) = - LazyNDStructure(this) { elementContext.initializer(it) } - - override fun mapIndexed( - arg: NDStructure, - transform: F.(index: IntArray, T) -> T - ): LazyNDStructure { - check(arg) - return if (arg is LazyNDStructure) { - LazyNDStructure(this) { index -> - //FIXME if value of arg is already calculated, it should be used - elementContext.transform(index, arg.function(index)) - } - } else { - LazyNDStructure(this) { elementContext.transform(it, arg.await(it)) } - } -// return LazyNDStructure(this) { elementField.transform(it, arg.await(it)) } - } - - override fun map(arg: NDStructure, transform: F.(T) -> T) = - mapIndexed(arg) { _, t -> transform(t) } - - override fun combine(a: NDStructure, b: NDStructure, transform: F.(T, T) -> T): LazyNDStructure { - check(a, b) - return if (a is LazyNDStructure && b is LazyNDStructure) { - LazyNDStructure(this@LazyNDField) { index -> - elementContext.transform( - a.function(index), - b.function(index) - ) - } - } else { - LazyNDStructure(this@LazyNDField) { elementContext.transform(a.await(it), b.await(it)) } - } -// return LazyNDStructure(this) { elementField.transform(a.await(it), b.await(it)) } - } - - fun NDStructure.lazy(): LazyNDStructure { - check(this) - return if (this is LazyNDStructure) { - LazyNDStructure(this@LazyNDField, function) - } else { - LazyNDStructure(this@LazyNDField) { get(it) } - } - } -} - -class LazyNDStructure>( - override val context: LazyNDField, - val function: suspend (IntArray) -> T -) : FieldElement, LazyNDStructure, LazyNDField>, - NDElement> { - - - override fun unwrap(): NDStructure = this - - override fun NDStructure.wrap(): LazyNDStructure = LazyNDStructure(context) { await(it) } - - override val shape: IntArray get() = context.shape - - private val cache = HashMap>() - - fun deferred(index: IntArray) = cache.getOrPut(index) { - context.scope.async(context = Dispatchers.Math) { - function(index) - } - } - - suspend fun await(index: IntArray): T = deferred(index).await() - - override fun get(index: IntArray): T = runBlocking { - deferred(index).await() - } - - override fun elements(): Sequence> { - val strides = DefaultStrides(shape) - val res = runBlocking { - strides.indices().toList().map { index -> index to await(index) } - } - return res.asSequence() - } -} - -fun NDStructure.deferred(index: IntArray) = - if (this is LazyNDStructure) this.deferred(index) else CompletableDeferred(get(index)) - -suspend fun NDStructure.await(index: IntArray) = - if (this is LazyNDStructure) this.await(index) else get(index) - - -fun > NDField.Companion.lazy(shape: IntArray, field: F, scope: CoroutineScope = GlobalScope) = - LazyNDField(shape, field, scope) - -fun > NDStructure.lazy(field: F, scope: CoroutineScope = GlobalScope): LazyNDStructure { - val context: LazyNDField = LazyNDField(shape, field, scope) - return LazyNDStructure(context) { get(it) } -} \ No newline at end of file diff --git a/kmath-coroutines/src/jsMain/kotlin/scientifik/kmath/structures/runBlocking.kt b/kmath-coroutines/src/jsMain/kotlin/scientifik/kmath/structures/runBlocking.kt deleted file mode 100644 index 7331d1801..000000000 --- a/kmath-coroutines/src/jsMain/kotlin/scientifik/kmath/structures/runBlocking.kt +++ /dev/null @@ -1,11 +0,0 @@ -package scientifik.kmath.structures - -import kotlinx.coroutines.CoroutineScope -import kotlin.coroutines.CoroutineContext - -actual fun runBlocking( - context: CoroutineContext, - function: suspend CoroutineScope.() -> R -): R { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. -} \ No newline at end of file diff --git a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt new file mode 100644 index 000000000..b4832827d --- /dev/null +++ b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt @@ -0,0 +1,47 @@ +package scientifik.kmath.structures + +import kotlinx.coroutines.* + +class LazyNDStructure( + val scope: CoroutineScope, + override val shape: IntArray, + val function: suspend (IntArray) -> T +) : NDStructure { + + private val cache = HashMap>() + + fun deferred(index: IntArray) = cache.getOrPut(index) { + scope.async(context = Dispatchers.Math) { + function(index) + } + } + + suspend fun await(index: IntArray): T = deferred(index).await() + + override fun get(index: IntArray): T = runBlocking { + deferred(index).await() + } + + override fun elements(): Sequence> { + val strides = DefaultStrides(shape) + val res = runBlocking { + strides.indices().toList().map { index -> index to await(index) } + } + return res.asSequence() + } +} + +fun NDStructure.deferred(index: IntArray) = + if (this is LazyNDStructure) this.deferred(index) else CompletableDeferred(get(index)) + +suspend fun NDStructure.await(index: IntArray) = + if (this is LazyNDStructure) this.await(index) else get(index) + +/** + * PENDING would benifit from KEEP-176 + */ +fun NDStructure.mapAsyncIndexed(scope: CoroutineScope, function: suspend (T, index: IntArray) -> R) = + LazyNDStructure(scope, shape) { index -> function(get(index), index) } + +fun NDStructure.mapAsync(scope: CoroutineScope, function: suspend (T) -> R) = + LazyNDStructure(scope, shape) { index -> function(get(index)) } \ No newline at end of file diff --git a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/runBlocking.kt b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/runBlocking.kt deleted file mode 100644 index 0bc3b0dea..000000000 --- a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/runBlocking.kt +++ /dev/null @@ -1,7 +0,0 @@ -package scientifik.kmath.structures - -import kotlinx.coroutines.CoroutineScope -import kotlin.coroutines.CoroutineContext - -actual fun runBlocking(context: CoroutineContext, function: suspend CoroutineScope.() -> R): R = - kotlinx.coroutines.runBlocking(context, function) \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index b306d1c8d..d6fd42594 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -25,5 +25,6 @@ include( ":kmath-commons", ":kmath-koma", ":kmath-sequential", - ":benchmarks" + ":benchmarks", + ":examples" )