Sequential operations
This commit is contained in:
parent
a3e8ffa147
commit
1e99e89c4c
@ -26,7 +26,7 @@ allprojects {
|
|||||||
apply(plugin = "com.jfrog.artifactory")
|
apply(plugin = "com.jfrog.artifactory")
|
||||||
|
|
||||||
group = "scientifik"
|
group = "scientifik"
|
||||||
version = "0.0.3-dev-4"
|
version = "0.0.3-dev-5"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
//maven("https://dl.bintray.com/kotlin/kotlin-eap")
|
//maven("https://dl.bintray.com/kotlin/kotlin-eap")
|
||||||
@ -44,7 +44,6 @@ allprojects {
|
|||||||
targets.all {
|
targets.all {
|
||||||
sourceSets.all {
|
sourceSets.all {
|
||||||
languageSettings.progressiveMode = true
|
languageSettings.progressiveMode = true
|
||||||
languageSettings.enableLanguageFeature("+InlineClasses")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,13 @@
|
|||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("multiplatform")
|
kotlin("multiplatform")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvm {
|
jvm ()
|
||||||
compilations.all {
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "1.8"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
js()
|
js()
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
all {
|
|
||||||
languageSettings.progressiveMode = true
|
|
||||||
}
|
|
||||||
|
|
||||||
val commonMain by getting {
|
val commonMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
api(kotlin("stdlib"))
|
api(kotlin("stdlib"))
|
||||||
|
51
kmath-sequential/build.gradle.kts
Normal file
51
kmath-sequential/build.gradle.kts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
plugins {
|
||||||
|
kotlin("multiplatform")
|
||||||
|
id("kotlinx-atomicfu")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvm ()
|
||||||
|
js()
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
val commonMain by getting {
|
||||||
|
dependencies {
|
||||||
|
api(project(":kmath-core"))
|
||||||
|
api(project(":kmath-coroutines"))
|
||||||
|
compileOnly("org.jetbrains.kotlinx:atomicfu-common:0.12.1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val commonTest by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("test-common"))
|
||||||
|
implementation(kotlin("test-annotations-common"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val jvmMain by getting {
|
||||||
|
dependencies {
|
||||||
|
compileOnly("org.jetbrains.kotlinx:atomicfu:0.12.1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val jvmTest by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("test"))
|
||||||
|
implementation(kotlin("test-junit"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// val jsMain by getting {
|
||||||
|
// dependencies {
|
||||||
|
// api(kotlin("stdlib-js"))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
val jsTest by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("test-js"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// mingwMain {
|
||||||
|
// }
|
||||||
|
// mingwTest {
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package scientifik.kmath.sequential
|
||||||
|
|
||||||
|
import kotlinx.atomicfu.atomic
|
||||||
|
import kotlinx.atomicfu.getAndUpdate
|
||||||
|
import scientifik.kmath.operations.Space
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object with a state that accumulates incoming elements
|
||||||
|
*/
|
||||||
|
interface Accumulator<in T> {
|
||||||
|
//PENDING use suspend operations?
|
||||||
|
fun push(value: T)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> Accumulator<T>.pushAll(values: Iterable<T>) {
|
||||||
|
values.forEach { push(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic thread-safe summator
|
||||||
|
*/
|
||||||
|
class GenericSum<T : Any>(val context: Space<T>) : Accumulator<T> {
|
||||||
|
//TODO add guard against overflow
|
||||||
|
val counter = atomic(0)
|
||||||
|
val sum = atomic(context.zero)
|
||||||
|
|
||||||
|
val value get() = with(context) { sum.value / counter.value }
|
||||||
|
|
||||||
|
override fun push(value: T) {
|
||||||
|
with(context) {
|
||||||
|
counter.incrementAndGet()
|
||||||
|
sum.getAndUpdate { it + value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Alexander Nozik.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package scientifik.kmath.sequential
|
||||||
|
|
||||||
|
import kotlinx.atomicfu.atomic
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.channels.ReceiveChannel
|
||||||
|
import kotlinx.coroutines.channels.produce
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A not-necessary-Markov chain of some type
|
||||||
|
* @param R - the chain element type
|
||||||
|
*/
|
||||||
|
interface Chain<out R> {
|
||||||
|
/**
|
||||||
|
* Last value of the chain. Returns null if [next] was not called
|
||||||
|
*/
|
||||||
|
val value: R?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate next value, changing state if needed
|
||||||
|
*/
|
||||||
|
suspend fun next(): R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a copy of current chain state. Consuming resulting chain does not affect initial chain
|
||||||
|
*/
|
||||||
|
fun fork(): Chain<R>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chain as a coroutine receive channel
|
||||||
|
*/
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
|
fun <R> Chain<R>.asChannel(scope: CoroutineScope): ReceiveChannel<R> = scope.produce { while (isActive) send(next()) }
|
||||||
|
|
||||||
|
fun <T> Iterator<T>.asChain(): Chain<T> = SimpleChain { next() }
|
||||||
|
fun <T> Sequence<T>.asChain(): Chain<T> = iterator().asChain()
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map the chain result using suspended transformation. Initial chain result can no longer be safely consumed
|
||||||
|
* since mapped chain consumes tokens. Accepts regular transformation function
|
||||||
|
*/
|
||||||
|
fun <T, R> Chain<T>.map(func: (T) -> R): Chain<R> {
|
||||||
|
val parent = this;
|
||||||
|
return object : Chain<R> {
|
||||||
|
override val value: R? get() = parent.value?.let(func)
|
||||||
|
|
||||||
|
override suspend fun next(): R {
|
||||||
|
return func(parent.next())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fork(): Chain<R> {
|
||||||
|
return parent.fork().map(func)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple chain of independent tokens
|
||||||
|
*/
|
||||||
|
class SimpleChain<out R>(private val gen: suspend () -> R) : Chain<R> {
|
||||||
|
private val atomicValue = atomic<R?>(null)
|
||||||
|
override val value: R? get() = atomicValue.value
|
||||||
|
|
||||||
|
override suspend fun next(): R = gen().also { atomicValue.lazySet(it) }
|
||||||
|
|
||||||
|
override fun fork(): Chain<R> = this
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO force forks on mapping operations?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A stateless Markov chain
|
||||||
|
*/
|
||||||
|
class MarkovChain<out R : Any>(private val seed: () -> R, private val gen: suspend (R) -> R) :
|
||||||
|
Chain<R> {
|
||||||
|
|
||||||
|
constructor(seed: R, gen: suspend (R) -> R) : this({ seed }, gen)
|
||||||
|
|
||||||
|
private val atomicValue by lazy { atomic(seed()) }
|
||||||
|
override val value: R get() = atomicValue.value
|
||||||
|
|
||||||
|
override suspend fun next(): R {
|
||||||
|
atomicValue.lazySet(gen(value))
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fork(): Chain<R> {
|
||||||
|
return MarkovChain(value, gen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A chain with possibly mutable state. The state must not be changed outside the chain. Two chins should never share the state
|
||||||
|
* @param S - the state of the chain
|
||||||
|
*/
|
||||||
|
class StatefulChain<S, out R>(
|
||||||
|
private val state: S,
|
||||||
|
private val seed: S.() -> R,
|
||||||
|
private val gen: suspend S.(R) -> R
|
||||||
|
) : Chain<R> {
|
||||||
|
|
||||||
|
constructor(state: S, seed: R, gen: suspend S.(R) -> R) : this(state, { seed }, gen)
|
||||||
|
|
||||||
|
private val atomicValue by lazy { atomic(seed(state)) }
|
||||||
|
override val value: R get() = atomicValue.value
|
||||||
|
|
||||||
|
override suspend fun next(): R {
|
||||||
|
atomicValue.lazySet(gen(state, value))
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fork(): Chain<R> {
|
||||||
|
throw RuntimeException("Fork not supported for stateful chain")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A chain that repeats the same value
|
||||||
|
*/
|
||||||
|
class ConstantChain<out T>(override val value: T) : Chain<T> {
|
||||||
|
override suspend fun next(): T {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fork(): Chain<T> {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package scientifik.kmath.misc
|
package scientifik.kmath.sequential
|
||||||
|
|
||||||
import kotlin.jvm.JvmName
|
import kotlin.jvm.JvmName
|
||||||
|
|
@ -0,0 +1,21 @@
|
|||||||
|
package scientifik.kmath.sequential
|
||||||
|
|
||||||
|
import scientifik.kmath.operations.Space
|
||||||
|
|
||||||
|
|
||||||
|
typealias Reducer<T, C, R> = (C, Iterable<T>) -> R
|
||||||
|
|
||||||
|
inline fun <T, C, R> Iterable<T>.reduce(context: C, crossinline reducer: Reducer<T, C, R>) =
|
||||||
|
reducer(context, this@reduce)
|
||||||
|
|
||||||
|
inline fun <T, C, R> Sequence<T>.reduce(context: C, crossinline reducer: Reducer<T, C, R>) =
|
||||||
|
asIterable().reduce(context, reducer)
|
||||||
|
|
||||||
|
inline fun <T, C, R> Array<T>.reduce(context: C, crossinline reducer: Reducer<T, C, R>) =
|
||||||
|
asIterable().reduce(context, reducer)
|
||||||
|
|
||||||
|
object Reducers {
|
||||||
|
fun <T : Any> mean(): Reducer<T, Space<T>, T> = { context, data ->
|
||||||
|
data.fold(GenericSum(context)) { sum, value -> sum.apply { push(value) } }.value
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package scientifik.kmath.chains
|
||||||
|
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import scientifik.kmath.sequential.Chain
|
||||||
|
import kotlin.sequences.Sequence
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent a chain as regular iterator (uses blocking calls)
|
||||||
|
*/
|
||||||
|
operator fun <R> Chain<R>.iterator() = object : Iterator<R> {
|
||||||
|
override fun hasNext(): Boolean = true
|
||||||
|
|
||||||
|
override fun next(): R = runBlocking { next() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent a chain as a sequence
|
||||||
|
*/
|
||||||
|
fun <R> Chain<R>.asSequence(): Sequence<R> = object : Sequence<R> {
|
||||||
|
override fun iterator(): Iterator<R> = this@asSequence.iterator()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map the chain result using suspended transformation. Initial chain result can no longer be safely consumed
|
||||||
|
* since mapped chain consumes tokens. Accepts suspending transformation function.
|
||||||
|
*/
|
||||||
|
fun <T, R> Chain<T>.map(func: suspend (T) -> R): Chain<R> {
|
||||||
|
val parent = this;
|
||||||
|
return object : Chain<R> {
|
||||||
|
override val value: R? get() = runBlocking { parent.value?.let { func(it) } }
|
||||||
|
|
||||||
|
override suspend fun next(): R {
|
||||||
|
return func(parent.next())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fork(): Chain<R> {
|
||||||
|
return parent.fork().map(func)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,28 @@
|
|||||||
|
buildscript {
|
||||||
|
dependencies {
|
||||||
|
classpath("org.jetbrains.kotlinx:atomicfu-gradle-plugin:0.12.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pluginManagement {
|
pluginManagement {
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven("https://plugins.gradle.org/m2/")
|
gradlePluginPortal()
|
||||||
//maven ("https://dl.bintray.com/kotlin/kotlin-eap")
|
//maven ("https://dl.bintray.com/kotlin/kotlin-eap")
|
||||||
}
|
}
|
||||||
|
resolutionStrategy {
|
||||||
|
eachPlugin {
|
||||||
|
when (requested.id.id) {
|
||||||
|
"kotlinx-atomicfu" -> {
|
||||||
|
useModule("org.jetbrains.kotlinx:atomicfu-gradle-plugin:0.12.1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//enableFeaturePreview("GRADLE_METADATA")
|
//enableFeaturePreview("GRADLE_METADATA")
|
||||||
@ -15,5 +34,6 @@ include(
|
|||||||
":kmath-coroutines",
|
":kmath-coroutines",
|
||||||
":kmath-commons",
|
":kmath-commons",
|
||||||
":kmath-koma",
|
":kmath-koma",
|
||||||
|
":kmath-sequential",
|
||||||
":benchmarks"
|
":benchmarks"
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user