Dev #194

Merged
altavir merged 266 commits from dev into master 2021-01-20 17:32:32 +03:00
23 changed files with 182 additions and 474 deletions
Showing only changes of commit fc5ec8fed7 - Show all commits

View File

@ -54,9 +54,6 @@ can be used for a wide variety of purposes from high performance calculations to
library in Kotlin code and maybe rewrite some parts to better suit the Kotlin programming paradigm, however there is no fixed roadmap for that. Feel free library in Kotlin code and maybe rewrite some parts to better suit the Kotlin programming paradigm, however there is no fixed roadmap for that. Feel free
to submit a feature request if you want something to be done first. to submit a feature request if you want something to be done first.
* **Koma wrapper** [Koma](https://github.com/kyonifer/koma) is a well established numerics library in Kotlin, specifically linear algebra.
The plan is to have wrappers for koma implementations for compatibility with kmath API.
## Planned features ## Planned features
* **Messaging** A mathematical notation to support multi-language and multi-node communication for mathematical tasks. * **Messaging** A mathematical notation to support multi-language and multi-node communication for mathematical tasks.

View File

@ -12,6 +12,3 @@ api and multiple library back-ends.
* [Expressions](./expressions.md) * [Expressions](./expressions.md)
* Commons math integration * Commons math integration
* Koma integration

View File

@ -10,7 +10,6 @@ plugins {
allOpen.annotation("org.openjdk.jmh.annotations.State") allOpen.annotation("org.openjdk.jmh.annotations.State")
repositories { repositories {
maven("http://dl.bintray.com/kyonifer/maven")
maven("https://dl.bintray.com/mipt-npm/scientifik") maven("https://dl.bintray.com/mipt-npm/scientifik")
maven("https://dl.bintray.com/mipt-npm/dev") maven("https://dl.bintray.com/mipt-npm/dev")
maven("https://dl.bintray.com/kotlin/kotlin-dev/") maven("https://dl.bintray.com/kotlin/kotlin-dev/")
@ -25,10 +24,8 @@ dependencies {
implementation(project(":kmath-coroutines")) implementation(project(":kmath-coroutines"))
implementation(project(":kmath-commons")) implementation(project(":kmath-commons"))
implementation(project(":kmath-prob")) implementation(project(":kmath-prob"))
implementation(project(":kmath-koma"))
implementation(project(":kmath-viktor")) implementation(project(":kmath-viktor"))
implementation(project(":kmath-dimensions")) implementation(project(":kmath-dimensions"))
implementation("com.kyonifer:koma-core-ejml:0.12")
implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:0.2.0-npm-dev-6") implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:0.2.0-npm-dev-6")
implementation("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-20") implementation("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-20")
"benchmarksCompile"(sourceSets.main.get().output + sourceSets.main.get().compileClasspath) //sourceSets.main.output + sourceSets.main.runtimeClasspath "benchmarksCompile"(sourceSets.main.get().output + sourceSets.main.get().compileClasspath) //sourceSets.main.output + sourceSets.main.runtimeClasspath

View File

@ -5,44 +5,33 @@ import org.openjdk.jmh.annotations.Scope
import org.openjdk.jmh.annotations.State import org.openjdk.jmh.annotations.State
import java.nio.IntBuffer import java.nio.IntBuffer
@State(Scope.Benchmark) @State(Scope.Benchmark)
class ArrayBenchmark { class ArrayBenchmark {
@Benchmark @Benchmark
fun benchmarkArrayRead() { fun benchmarkArrayRead() {
var res = 0 var res = 0
for (i in 1..size) { for (i in 1..size) res += array[size - i]
res += array[size - i]
}
} }
@Benchmark @Benchmark
fun benchmarkBufferRead() { fun benchmarkBufferRead() {
var res = 0 var res = 0
for (i in 1..size) { for (i in 1..size) res += arrayBuffer.get(size - i)
res += arrayBuffer.get(size - i)
}
} }
@Benchmark @Benchmark
fun nativeBufferRead() { fun nativeBufferRead() {
var res = 0 var res = 0
for (i in 1..size) { for (i in 1..size) res += nativeBuffer.get(size - i)
res += nativeBuffer.get(size - i)
}
} }
companion object { companion object {
val size = 1000 const val size: Int = 1000
val array: IntArray = IntArray(size) { it }
val array = IntArray(size) { it } val arrayBuffer: IntBuffer = IntBuffer.wrap(array)
val arrayBuffer = IntBuffer.wrap(array)
val nativeBuffer = IntBuffer.allocate(size).also {
for (i in 0 until size) {
it.put(i, i)
}
val nativeBuffer: IntBuffer = IntBuffer.allocate(size).also {
for (i in 0 until size) it.put(i, i)
} }
} }
} }

View File

@ -1,70 +1,70 @@
package scientifik.kmath.ast //package scientifik.kmath.ast
//
import scientifik.kmath.asm.compile //import scientifik.kmath.asm.compile
import scientifik.kmath.expressions.Expression //import scientifik.kmath.expressions.Expression
import scientifik.kmath.expressions.expressionInField //import scientifik.kmath.expressions.expressionInField
import scientifik.kmath.expressions.invoke //import scientifik.kmath.expressions.invoke
import scientifik.kmath.operations.Field //import scientifik.kmath.operations.Field
import scientifik.kmath.operations.RealField //import scientifik.kmath.operations.RealField
import kotlin.random.Random //import kotlin.random.Random
import kotlin.system.measureTimeMillis //import kotlin.system.measureTimeMillis
//
class ExpressionsInterpretersBenchmark { //class ExpressionsInterpretersBenchmark {
private val algebra: Field<Double> = RealField // private val algebra: Field<Double> = RealField
fun functionalExpression() { // fun functionalExpression() {
val expr = algebra.expressionInField { // val expr = algebra.expressionInField {
variable("x") * const(2.0) + const(2.0) / variable("x") - const(16.0) // variable("x") * const(2.0) + const(2.0) / variable("x") - const(16.0)
} // }
//
invokeAndSum(expr) // invokeAndSum(expr)
} // }
//
fun mstExpression() { // fun mstExpression() {
val expr = algebra.mstInField { // val expr = algebra.mstInField {
symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0) // symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0)
} // }
//
invokeAndSum(expr) // invokeAndSum(expr)
} // }
//
fun asmExpression() { // fun asmExpression() {
val expr = algebra.mstInField { // val expr = algebra.mstInField {
symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0) // symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0)
}.compile() // }.compile()
//
invokeAndSum(expr) // invokeAndSum(expr)
} // }
//
private fun invokeAndSum(expr: Expression<Double>) { // private fun invokeAndSum(expr: Expression<Double>) {
val random = Random(0) // val random = Random(0)
var sum = 0.0 // var sum = 0.0
//
repeat(1000000) { // repeat(1000000) {
sum += expr("x" to random.nextDouble()) // sum += expr("x" to random.nextDouble())
} // }
//
println(sum) // println(sum)
} // }
} //}
//
fun main() { //fun main() {
val benchmark = ExpressionsInterpretersBenchmark() // val benchmark = ExpressionsInterpretersBenchmark()
//
val fe = measureTimeMillis { // val fe = measureTimeMillis {
benchmark.functionalExpression() // benchmark.functionalExpression()
} // }
//
println("fe=$fe") // println("fe=$fe")
//
val mst = measureTimeMillis { // val mst = measureTimeMillis {
benchmark.mstExpression() // benchmark.mstExpression()
} // }
//
println("mst=$mst") // println("mst=$mst")
//
val asm = measureTimeMillis { // val asm = measureTimeMillis {
benchmark.asmExpression() // benchmark.asmExpression()
} // }
//
println("asm=$asm") // println("asm=$asm")
} //}

View File

@ -1,55 +0,0 @@
package scientifik.kmath.linear
import koma.matrix.ejml.EJMLMatrixFactory
import scientifik.kmath.commons.linear.CMMatrixContext
import scientifik.kmath.commons.linear.inverse
import scientifik.kmath.commons.linear.toCM
import scientifik.kmath.operations.RealField
import scientifik.kmath.operations.invoke
import scientifik.kmath.structures.Matrix
import kotlin.contracts.ExperimentalContracts
import kotlin.random.Random
import kotlin.system.measureTimeMillis
@ExperimentalContracts
fun main() {
val random = Random(1224)
val dim = 100
//creating invertible matrix
val u = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
val l = Matrix.real(dim, dim) { i, j -> if (i >= j) random.nextDouble() else 0.0 }
val matrix = l dot u
val n = 5000 // iterations
MatrixContext.real {
repeat(50) { val res = inverse(matrix) }
val inverseTime = measureTimeMillis { repeat(n) { val res = inverse(matrix) } }
println("[kmath] Inversion of $n matrices $dim x $dim finished in $inverseTime millis")
}
//commons-math
val commonsTime = measureTimeMillis {
CMMatrixContext {
val cm = matrix.toCM() //avoid overhead on conversion
repeat(n) { val res = inverse(cm) }
}
}
println("[commons-math] Inversion of $n matrices $dim x $dim finished in $commonsTime millis")
//koma-ejml
val komaTime = measureTimeMillis {
(KomaMatrixContext(EJMLMatrixFactory(), RealField)) {
val km = matrix.toKoma() //avoid overhead on conversion
repeat(n) {
val res = inverse(km)
}
}
}
println("[koma-ejml] Inversion of $n matrices $dim x $dim finished in $komaTime millis")
}

View File

@ -1,49 +0,0 @@
package scientifik.kmath.linear
import koma.matrix.ejml.EJMLMatrixFactory
import scientifik.kmath.commons.linear.CMMatrixContext
import scientifik.kmath.commons.linear.toCM
import scientifik.kmath.operations.RealField
import scientifik.kmath.operations.invoke
import scientifik.kmath.structures.Matrix
import kotlin.random.Random
import kotlin.system.measureTimeMillis
fun main() {
val random = Random(12224)
val dim = 1000
//creating invertible matrix
val matrix1 = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
val matrix2 = Matrix.real(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
// //warmup
// matrix1 dot matrix2
CMMatrixContext {
val cmMatrix1 = matrix1.toCM()
val cmMatrix2 = matrix2.toCM()
val cmTime = measureTimeMillis {
cmMatrix1 dot cmMatrix2
}
println("CM implementation time: $cmTime")
}
(KomaMatrixContext(EJMLMatrixFactory(), RealField)) {
val komaMatrix1 = matrix1.toKoma()
val komaMatrix2 = matrix2.toKoma()
val komaTime = measureTimeMillis {
komaMatrix1 dot komaMatrix2
}
println("Koma-ejml implementation time: $komaTime")
}
val genericTime = measureTimeMillis {
val res = matrix1 dot matrix2
}
println("Generic implementation time: $genericTime")
}

View File

@ -5,6 +5,6 @@ dependencies {
api(project(":kmath-core")) api(project(":kmath-core"))
api(project(":kmath-coroutines")) api(project(":kmath-coroutines"))
api(project(":kmath-prob")) api(project(":kmath-prob"))
api(project(":kmath-functions")) // api(project(":kmath-functions"))
api("org.apache.commons:commons-math3:3.6.1") api("org.apache.commons:commons-math3:3.6.1")
} }

View File

@ -43,7 +43,6 @@ public interface Chain<out R> : Flow<R> {
public companion object public companion object
} }
public fun <T> Iterator<T>.asChain(): Chain<T> = SimpleChain { next() } public fun <T> Iterator<T>.asChain(): Chain<T> = SimpleChain { next() }
public fun <T> Sequence<T>.asChain(): Chain<T> = iterator().asChain() public fun <T> Sequence<T>.asChain(): Chain<T> = iterator().asChain()
@ -51,22 +50,20 @@ public fun <T> Sequence<T>.asChain(): Chain<T> = iterator().asChain()
* A simple chain of independent tokens * A simple chain of independent tokens
*/ */
public class SimpleChain<out R>(private val gen: suspend () -> R) : Chain<R> { public class SimpleChain<out R>(private val gen: suspend () -> R) : Chain<R> {
override suspend fun next(): R = gen() public override suspend fun next(): R = gen()
override fun fork(): Chain<R> = this public override fun fork(): Chain<R> = this
} }
/** /**
* A stateless Markov chain * A stateless Markov chain
*/ */
public class MarkovChain<out R : Any>(private val seed: suspend () -> R, private val gen: suspend (R) -> R) : Chain<R> { public class MarkovChain<out R : Any>(private val seed: suspend () -> R, private val gen: suspend (R) -> R) : Chain<R> {
private val mutex: Mutex = Mutex()
private val mutex = Mutex()
private var value: R? = null private var value: R? = null
public fun value(): R? = value public fun value(): R? = value
override suspend fun next(): R { public override suspend fun next(): R {
mutex.withLock { mutex.withLock {
val newValue = gen(value ?: seed()) val newValue = gen(value ?: seed())
value = newValue value = newValue
@ -74,9 +71,7 @@ public class MarkovChain<out R : Any>(private val seed: suspend () -> R, private
} }
} }
override fun fork(): Chain<R> { public override fun fork(): Chain<R> = MarkovChain(seed = { value ?: seed() }, gen = gen)
return MarkovChain(seed = { value ?: seed() }, gen = gen)
}
} }
/** /**
@ -91,12 +86,11 @@ public class StatefulChain<S, out R>(
private val gen: suspend S.(R) -> R private val gen: suspend S.(R) -> R
) : Chain<R> { ) : Chain<R> {
private val mutex: Mutex = Mutex() private val mutex: Mutex = Mutex()
private var value: R? = null private var value: R? = null
public fun value(): R? = value public fun value(): R? = value
override suspend fun next(): R { public override suspend fun next(): R {
mutex.withLock { mutex.withLock {
val newValue = state.gen(value ?: state.seed()) val newValue = state.gen(value ?: state.seed())
value = newValue value = newValue
@ -104,16 +98,15 @@ public class StatefulChain<S, out R>(
} }
} }
override fun fork(): Chain<R> = StatefulChain(forkState(state), seed, forkState, gen) public override fun fork(): Chain<R> = StatefulChain(forkState(state), seed, forkState, gen)
} }
/** /**
* A chain that repeats the same value * A chain that repeats the same value
*/ */
public class ConstantChain<out T>(public val value: T) : Chain<T> { public class ConstantChain<out T>(public val value: T) : Chain<T> {
override suspend fun next(): T = value public override suspend fun next(): T = value
public override fun fork(): Chain<T> = this
override fun fork(): Chain<T> = this
} }
/** /**

View File

@ -24,7 +24,7 @@ public interface SuspendableMathFunction<T, C : Algebra<T>, R> {
public suspend operator fun C.invoke(arg: T): R public suspend operator fun C.invoke(arg: T): R
} }
public suspend fun <R> SuspendableMathFunction<Double, RealField, R>.invoke(arg: Double) = RealField.invoke(arg) public suspend fun <R> SuspendableMathFunction<Double, RealField, R>.invoke(arg: Double): R = RealField.invoke(arg)
/** /**
* A parametric function with parameter * A parametric function with parameter

View File

@ -29,7 +29,6 @@ public interface Histogram<T : Any, out B : Bin<T>> : Iterable<B> {
* Dimension of the histogram * Dimension of the histogram
*/ */
public val dimension: Int public val dimension: Int
} }
public interface MutableHistogram<T : Any, out B : Bin<T>> : Histogram<T, B> { public interface MutableHistogram<T : Any, out B : Bin<T>> : Histogram<T, B> {

View File

@ -44,7 +44,7 @@ public class UnivariateHistogram private constructor(private val factory: (Doubl
} }
private fun createBin(value: Double): UnivariateBin = factory(value).also { private fun createBin(value: Double): UnivariateBin = factory(value).also {
synchronized(this) { bins.put(it.position, it) } synchronized(this) { bins[it.position] = it }
} }
public override operator fun get(point: Buffer<out Double>): UnivariateBin? = get(point[0]) public override operator fun get(point: Buffer<out Double>): UnivariateBin? = get(point[0])
@ -87,7 +87,7 @@ public class UnivariateHistogram private constructor(private val factory: (Doubl
) )
else -> { else -> {
val index = (0 until sorted.size).first { value > sorted[it] } val index = sorted.indices.first { value > sorted[it] }
val left = sorted[index] val left = sorted[index]
val right = sorted[index + 1] val right = sorted[index + 1]
UnivariateBin((left + right) / 2, (right - left)) UnivariateBin((left + right) / 2, (right - left))

View File

@ -1,30 +0,0 @@
plugins { id("ru.mipt.npm.mpp") }
repositories.maven("http://dl.bintray.com/kyonifer/maven")
kotlin.sourceSets {
commonMain {
dependencies {
api(project(":kmath-core"))
api("com.kyonifer:koma-core-api-common:0.12")
}
}
jvmMain {
dependencies {
api("com.kyonifer:koma-core-api-jvm:0.12")
}
}
jvmTest {
dependencies {
implementation("com.kyonifer:koma-core-ejml:0.12")
}
}
jsMain {
dependencies {
api("com.kyonifer:koma-core-api-js:0.12")
}
}
}

View File

@ -1,105 +0,0 @@
package scientifik.kmath.linear
import koma.extensions.fill
import koma.matrix.MatrixFactory
import scientifik.kmath.operations.Space
import scientifik.kmath.operations.invoke
import scientifik.kmath.structures.Matrix
import scientifik.kmath.structures.NDStructure
public class KomaMatrixContext<T : Any>(
private val factory: MatrixFactory<koma.matrix.Matrix<T>>,
private val space: Space<T>
) : MatrixContext<T> {
public override fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> T): KomaMatrix<T> =
KomaMatrix(factory.zeros(rows, columns).fill(initializer))
public fun Matrix<T>.toKoma(): KomaMatrix<T> = if (this is KomaMatrix)
this
else
produce(rowNum, colNum) { i, j -> get(i, j) }
public fun Point<T>.toKoma(): KomaVector<T> = if (this is KomaVector)
this
else
KomaVector(factory.zeros(size, 1).fill { i, _ -> get(i) })
public override fun Matrix<T>.dot(other: Matrix<T>): KomaMatrix<T> =
KomaMatrix(toKoma().origin * other.toKoma().origin)
public override fun Matrix<T>.dot(vector: Point<T>): KomaVector<T> =
KomaVector(toKoma().origin * vector.toKoma().origin)
public override operator fun Matrix<T>.unaryMinus(): KomaMatrix<T> =
KomaMatrix(toKoma().origin.unaryMinus())
public override fun add(a: Matrix<T>, b: Matrix<T>): KomaMatrix<T> =
KomaMatrix(a.toKoma().origin + b.toKoma().origin)
public override operator fun Matrix<T>.minus(b: Matrix<T>): KomaMatrix<T> =
KomaMatrix(toKoma().origin - b.toKoma().origin)
public override fun multiply(a: Matrix<T>, k: Number): Matrix<T> =
produce(a.rowNum, a.colNum) { i, j -> space { a[i, j] * k } }
public override operator fun Matrix<T>.times(value: T): KomaMatrix<T> =
KomaMatrix(toKoma().origin * value)
public companion object
}
public fun <T : Any> KomaMatrixContext<T>.solve(a: Matrix<T>, b: Matrix<T>): KomaMatrix<T> =
KomaMatrix(a.toKoma().origin.solve(b.toKoma().origin))
public fun <T : Any> KomaMatrixContext<T>.solve(a: Matrix<T>, b: Point<T>): KomaVector<T> =
KomaVector(a.toKoma().origin.solve(b.toKoma().origin))
public fun <T : Any> KomaMatrixContext<T>.inverse(a: Matrix<T>): KomaMatrix<T> =
KomaMatrix(a.toKoma().origin.inv())
public class KomaMatrix<T : Any>(public val origin: koma.matrix.Matrix<T>, features: Set<MatrixFeature>? = null) :
FeaturedMatrix<T> {
public override val rowNum: Int get() = origin.numRows()
public override val colNum: Int get() = origin.numCols()
public override val shape: IntArray get() = intArrayOf(origin.numRows(), origin.numCols())
public override val features: Set<MatrixFeature> = features ?: hashSetOf(
object : DeterminantFeature<T> {
override val determinant: T get() = origin.det()
},
object : LUPDecompositionFeature<T> {
private val lup by lazy { origin.LU() }
override val l: FeaturedMatrix<T> get() = KomaMatrix(lup.second)
override val u: FeaturedMatrix<T> get() = KomaMatrix(lup.third)
override val p: FeaturedMatrix<T> get() = KomaMatrix(lup.first)
}
)
override fun suggestFeature(vararg features: MatrixFeature): FeaturedMatrix<T> =
KomaMatrix(this.origin, this.features + features)
override operator fun get(i: Int, j: Int): T = origin.getGeneric(i, j)
override fun equals(other: Any?): Boolean {
return NDStructure.equals(this, other as? NDStructure<*> ?: return false)
}
override fun hashCode(): Int {
var result = origin.hashCode()
result = 31 * result + features.hashCode()
return result
}
}
public class KomaVector<T : Any> internal constructor(public val origin: koma.matrix.Matrix<T>) : Point<T> {
override val size: Int get() = origin.numRows()
init {
require(origin.numCols() == 1) { error("Only single column matrices are allowed") }
}
override operator fun get(index: Int): T = origin.getGeneric(index)
override operator fun iterator(): Iterator<T> = origin.toIterable().iterator()
}

View File

@ -73,5 +73,5 @@ public fun <T : Any> Sampler<T>.sampleBuffer(
/** /**
* Generate a bunch of samples from real distributions * Generate a bunch of samples from real distributions
*/ */
public fun Sampler<Double>.sampleBuffer(generator: RandomGenerator, size: Int) = public fun Sampler<Double>.sampleBuffer(generator: RandomGenerator, size: Int): Chain<Buffer<Double>> =
sampleBuffer(generator, size, Buffer.Companion::real) sampleBuffer(generator, size, Buffer.Companion::real)

View File

@ -6,42 +6,38 @@ import scientifik.kmath.chains.SimpleChain
/** /**
* A multivariate distribution which takes a map of parameters * A multivariate distribution which takes a map of parameters
*/ */
interface NamedDistribution<T> : Distribution<Map<String, T>> public interface NamedDistribution<T> : Distribution<Map<String, T>>
/** /**
* A multivariate distribution that has independent distributions for separate axis * A multivariate distribution that has independent distributions for separate axis
*/ */
class FactorizedDistribution<T>(val distributions: Collection<NamedDistribution<T>>) : NamedDistribution<T> { public class FactorizedDistribution<T>(public val distributions: Collection<NamedDistribution<T>>) :
NamedDistribution<T> {
override fun probability(arg: Map<String, T>): Double { override fun probability(arg: Map<String, T>): Double =
return distributions.fold(1.0) { acc, distr -> acc * distr.probability(arg) } distributions.fold(1.0) { acc, distr -> acc * distr.probability(arg) }
}
override fun sample(generator: RandomGenerator): Chain<Map<String, T>> { override fun sample(generator: RandomGenerator): Chain<Map<String, T>> {
val chains = distributions.map { it.sample(generator) } val chains = distributions.map { it.sample(generator) }
return SimpleChain<Map<String, T>> { return SimpleChain { chains.fold(emptyMap()) { acc, chain -> acc + chain.next() } }
chains.fold(emptyMap()) { acc, chain -> acc + chain.next() }
}
} }
} }
class NamedDistributionWrapper<T : Any>(val name: String, val distribution: Distribution<T>) : NamedDistribution<T> { public class NamedDistributionWrapper<T : Any>(public val name: String, public val distribution: Distribution<T>) :
NamedDistribution<T> {
override fun probability(arg: Map<String, T>): Double = distribution.probability( override fun probability(arg: Map<String, T>): Double = distribution.probability(
arg[name] ?: error("Argument with name $name not found in input parameters") arg[name] ?: error("Argument with name $name not found in input parameters")
) )
override fun sample(generator: RandomGenerator): Chain<Map<String, T>> { override fun sample(generator: RandomGenerator): Chain<Map<String, T>> {
val chain = distribution.sample(generator) val chain = distribution.sample(generator)
return SimpleChain { return SimpleChain { mapOf(name to chain.next()) }
mapOf(name to chain.next())
}
} }
} }
class DistributionBuilder<T: Any>{ public class DistributionBuilder<T : Any> {
private val distributions = ArrayList<NamedDistribution<T>>() private val distributions = ArrayList<NamedDistribution<T>>()
infix fun String.to(distribution: Distribution<T>){ public infix fun String.to(distribution: Distribution<T>) {
distributions.add(NamedDistributionWrapper(this,distribution)) distributions.add(NamedDistributionWrapper(this, distribution))
} }
} }

View File

@ -5,10 +5,13 @@ import scientifik.kmath.chains.Chain
/** /**
* A possibly stateful chain producing random values. * A possibly stateful chain producing random values.
*/ */
class RandomChain<out R>(val generator: RandomGenerator, private val gen: suspend RandomGenerator.() -> R) : Chain<R> { public class RandomChain<out R>(
public val generator: RandomGenerator,
private val gen: suspend RandomGenerator.() -> R
) : Chain<R> {
override suspend fun next(): R = generator.gen() override suspend fun next(): R = generator.gen()
override fun fork(): Chain<R> = RandomChain(generator.fork(), gen) override fun fork(): Chain<R> = RandomChain(generator.fork(), gen)
} }
fun <R> RandomGenerator.chain(gen: suspend RandomGenerator.() -> R): RandomChain<R> = RandomChain(this, gen) public fun <R> RandomGenerator.chain(gen: suspend RandomGenerator.() -> R): RandomChain<R> = RandomChain(this, gen)

View File

@ -5,17 +5,15 @@ import kotlin.random.Random
/** /**
* A basic generator * A basic generator
*/ */
interface RandomGenerator { public interface RandomGenerator {
fun nextBoolean(): Boolean public fun nextBoolean(): Boolean
public fun nextDouble(): Double
fun nextDouble(): Double public fun nextInt(): Int
fun nextInt(): Int public fun nextInt(until: Int): Int
fun nextInt(until: Int): Int public fun nextLong(): Long
fun nextLong(): Long public fun nextLong(until: Long): Long
fun nextLong(until: Long): Long public fun fillBytes(array: ByteArray, fromIndex: Int = 0, toIndex: Int = array.size)
public fun nextBytes(size: Int): ByteArray = ByteArray(size).also { fillBytes(it) }
fun fillBytes(array: ByteArray, fromIndex: Int = 0, toIndex: Int = array.size)
fun nextBytes(size: Int): ByteArray = ByteArray(size).also { fillBytes(it) }
/** /**
* Create a new generator which is independent from current generator (operations on new generator do not affect this one * Create a new generator which is independent from current generator (operations on new generator do not affect this one
@ -24,32 +22,27 @@ interface RandomGenerator {
* *
* The thread safety of this operation is not guaranteed since it could affect the state of the generator. * The thread safety of this operation is not guaranteed since it could affect the state of the generator.
*/ */
fun fork(): RandomGenerator public fun fork(): RandomGenerator
companion object { public companion object {
val default by lazy { DefaultGenerator() } public val default: DefaultGenerator by lazy { DefaultGenerator() }
fun default(seed: Long) = DefaultGenerator(Random(seed)) public fun default(seed: Long): DefaultGenerator = DefaultGenerator(Random(seed))
} }
} }
inline class DefaultGenerator(val random: Random = Random) : RandomGenerator { public inline class DefaultGenerator(public val random: Random = Random) : RandomGenerator {
override fun nextBoolean(): Boolean = random.nextBoolean() public override fun nextBoolean(): Boolean = random.nextBoolean()
public override fun nextDouble(): Double = random.nextDouble()
public override fun nextInt(): Int = random.nextInt()
public override fun nextInt(until: Int): Int = random.nextInt(until)
public override fun nextLong(): Long = random.nextLong()
public override fun nextLong(until: Long): Long = random.nextLong(until)
override fun nextDouble(): Double = random.nextDouble() public override fun fillBytes(array: ByteArray, fromIndex: Int, toIndex: Int) {
override fun nextInt(): Int = random.nextInt()
override fun nextInt(until: Int): Int = random.nextInt(until)
override fun nextLong(): Long = random.nextLong()
override fun nextLong(until: Long): Long = random.nextLong(until)
override fun fillBytes(array: ByteArray, fromIndex: Int, toIndex: Int) {
random.nextBytes(array, fromIndex, toIndex) random.nextBytes(array, fromIndex, toIndex)
} }
override fun nextBytes(size: Int): ByteArray = random.nextBytes(size) public override fun nextBytes(size: Int): ByteArray = random.nextBytes(size)
public override fun fork(): RandomGenerator = RandomGenerator.default(random.nextLong())
override fun fork(): RandomGenerator = RandomGenerator.default(random.nextLong())
} }

View File

@ -7,26 +7,25 @@ import scientifik.kmath.chains.zip
import scientifik.kmath.operations.Space import scientifik.kmath.operations.Space
import scientifik.kmath.operations.invoke import scientifik.kmath.operations.invoke
class BasicSampler<T : Any>(val chainBuilder: (RandomGenerator) -> Chain<T>) : Sampler<T> { public class BasicSampler<T : Any>(public val chainBuilder: (RandomGenerator) -> Chain<T>) : Sampler<T> {
override fun sample(generator: RandomGenerator): Chain<T> = chainBuilder(generator) public override fun sample(generator: RandomGenerator): Chain<T> = chainBuilder(generator)
} }
class ConstantSampler<T : Any>(val value: T) : Sampler<T> { public class ConstantSampler<T : Any>(public val value: T) : Sampler<T> {
override fun sample(generator: RandomGenerator): Chain<T> = ConstantChain(value) public override fun sample(generator: RandomGenerator): Chain<T> = ConstantChain(value)
} }
/** /**
* A space for samplers. Allows to perform simple operations on distributions * A space for samplers. Allows to perform simple operations on distributions
*/ */
class SamplerSpace<T : Any>(val space: Space<T>) : Space<Sampler<T>> { public class SamplerSpace<T : Any>(public val space: Space<T>) : Space<Sampler<T>> {
public override val zero: Sampler<T> = ConstantSampler(space.zero)
override val zero: Sampler<T> = ConstantSampler(space.zero) public override fun add(a: Sampler<T>, b: Sampler<T>): Sampler<T> = BasicSampler { generator ->
override fun add(a: Sampler<T>, b: Sampler<T>): Sampler<T> = BasicSampler { generator ->
a.sample(generator).zip(b.sample(generator)) { aValue, bValue -> space { aValue + bValue } } a.sample(generator).zip(b.sample(generator)) { aValue, bValue -> space { aValue + bValue } }
} }
override fun multiply(a: Sampler<T>, k: Number): Sampler<T> = BasicSampler { generator -> public override fun multiply(a: Sampler<T>, k: Number): Sampler<T> = BasicSampler { generator ->
a.sample(generator).map { space { it * k.toDouble() } } a.sample(generator).map { space { it * k.toDouble() } }
} }
} }

View File

@ -6,7 +6,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.scanReduce import kotlinx.coroutines.flow.runningReduce
import scientifik.kmath.coroutines.mapParallel import scientifik.kmath.coroutines.mapParallel
import scientifik.kmath.operations.* import scientifik.kmath.operations.*
import scientifik.kmath.structures.Buffer import scientifik.kmath.structures.Buffer
@ -16,8 +16,8 @@ import scientifik.kmath.structures.asSequence
/** /**
* A function, that transforms a buffer of random quantities to some resulting value * A function, that transforms a buffer of random quantities to some resulting value
*/ */
interface Statistic<T, R> { public interface Statistic<T, R> {
suspend operator fun invoke(data: Buffer<T>): R public suspend operator fun invoke(data: Buffer<T>): R
} }
/** /**
@ -26,17 +26,17 @@ interface Statistic<T, R> {
* @param I - intermediate block type * @param I - intermediate block type
* @param R - result type * @param R - result type
*/ */
interface ComposableStatistic<T, I, R> : Statistic<T, R> { public interface ComposableStatistic<T, I, R> : Statistic<T, R> {
//compute statistic on a single block //compute statistic on a single block
suspend fun computeIntermediate(data: Buffer<T>): I public suspend fun computeIntermediate(data: Buffer<T>): I
//Compose two blocks //Compose two blocks
suspend fun composeIntermediate(first: I, second: I): I public suspend fun composeIntermediate(first: I, second: I): I
//Transform block to result //Transform block to result
suspend fun toResult(intermediate: I): R public suspend fun toResult(intermediate: I): R
override suspend fun invoke(data: Buffer<T>): R = toResult(computeIntermediate(data)) public override suspend fun invoke(data: Buffer<T>): R = toResult(computeIntermediate(data))
} }
@FlowPreview @FlowPreview
@ -46,7 +46,7 @@ private fun <T, I, R> ComposableStatistic<T, I, R>.flowIntermediate(
dispatcher: CoroutineDispatcher = Dispatchers.Default dispatcher: CoroutineDispatcher = Dispatchers.Default
): Flow<I> = flow ): Flow<I> = flow
.mapParallel(dispatcher) { computeIntermediate(it) } .mapParallel(dispatcher) { computeIntermediate(it) }
.scanReduce(::composeIntermediate) .runningReduce(::composeIntermediate)
/** /**
@ -57,7 +57,7 @@ private fun <T, I, R> ComposableStatistic<T, I, R>.flowIntermediate(
*/ */
@FlowPreview @FlowPreview
@ExperimentalCoroutinesApi @ExperimentalCoroutinesApi
fun <T, I, R> ComposableStatistic<T, I, R>.flow( public fun <T, I, R> ComposableStatistic<T, I, R>.flow(
flow: Flow<Buffer<T>>, flow: Flow<Buffer<T>>,
dispatcher: CoroutineDispatcher = Dispatchers.Default dispatcher: CoroutineDispatcher = Dispatchers.Default
): Flow<R> = flowIntermediate(flow, dispatcher).map(::toResult) ): Flow<R> = flowIntermediate(flow, dispatcher).map(::toResult)
@ -65,32 +65,32 @@ fun <T, I, R> ComposableStatistic<T, I, R>.flow(
/** /**
* Arithmetic mean * Arithmetic mean
*/ */
class Mean<T>(val space: Space<T>) : ComposableStatistic<T, Pair<T, Int>, T> { public class Mean<T>(public val space: Space<T>) : ComposableStatistic<T, Pair<T, Int>, T> {
override suspend fun computeIntermediate(data: Buffer<T>): Pair<T, Int> = public override suspend fun computeIntermediate(data: Buffer<T>): Pair<T, Int> =
space { sum(data.asIterable()) } to data.size space { sum(data.asIterable()) } to data.size
override suspend fun composeIntermediate(first: Pair<T, Int>, second: Pair<T, Int>): Pair<T, Int> = public override suspend fun composeIntermediate(first: Pair<T, Int>, second: Pair<T, Int>): Pair<T, Int> =
space { first.first + second.first } to (first.second + second.second) space { first.first + second.first } to (first.second + second.second)
override suspend fun toResult(intermediate: Pair<T, Int>): T = public override suspend fun toResult(intermediate: Pair<T, Int>): T =
space { intermediate.first / intermediate.second } space { intermediate.first / intermediate.second }
companion object { public companion object {
//TODO replace with optimized version which respects overflow //TODO replace with optimized version which respects overflow
val real: Mean<Double> = Mean(RealField) public val real: Mean<Double> = Mean(RealField)
val int: Mean<Int> = Mean(IntRing) public val int: Mean<Int> = Mean(IntRing)
val long: Mean<Long> = Mean(LongRing) public val long: Mean<Long> = Mean(LongRing)
} }
} }
/** /**
* Non-composable median * Non-composable median
*/ */
class Median<T>(private val comparator: Comparator<T>) : Statistic<T, T> { public class Median<T>(private val comparator: Comparator<T>) : Statistic<T, T> {
override suspend fun invoke(data: Buffer<T>): T = public override suspend fun invoke(data: Buffer<T>): T =
data.asSequence().sortedWith(comparator).toList()[data.size / 2] //TODO check if this is correct data.asSequence().sortedWith(comparator).toList()[data.size / 2] //TODO check if this is correct
companion object { public companion object {
val real: Median<Double> = Median(Comparator { a: Double, b: Double -> a.compareTo(b) }) public val real: Median<Double> = Median { a: Double, b: Double -> a.compareTo(b) }
} }
} }

View File

@ -3,32 +3,20 @@ package scientifik.kmath.prob
import scientifik.kmath.chains.Chain import scientifik.kmath.chains.Chain
import scientifik.kmath.chains.SimpleChain import scientifik.kmath.chains.SimpleChain
class UniformDistribution(val range: ClosedFloatingPointRange<Double>) : UnivariateDistribution<Double> { public class UniformDistribution(public val range: ClosedFloatingPointRange<Double>) : UnivariateDistribution<Double> {
private val length: Double = range.endInclusive - range.start
private val length = range.endInclusive - range.start override fun probability(arg: Double): Double = if (arg in range) 1.0 / length else 0.0
override fun probability(arg: Double): Double { override fun sample(generator: RandomGenerator): Chain<Double> =
return if (arg in range) { SimpleChain { range.start + generator.nextDouble() * length }
return 1.0 / length
} else {
0.0
}
}
override fun sample(generator: RandomGenerator): Chain<Double> { override fun cumulative(arg: Double): Double = when {
return SimpleChain { arg < range.start -> 0.0
range.start + generator.nextDouble() * length arg >= range.endInclusive -> 1.0
} else -> (arg - range.start) / length
}
override fun cumulative(arg: Double): Double {
return when {
arg < range.start -> 0.0
arg >= range.endInclusive -> 1.0
else -> (arg - range.start) / length
}
} }
} }
fun Distribution.Companion.uniform(range: ClosedFloatingPointRange<Double>): UniformDistribution = public fun Distribution.Companion.uniform(range: ClosedFloatingPointRange<Double>): UniformDistribution =
UniformDistribution(range) UniformDistribution(range)

View File

@ -15,8 +15,8 @@ public abstract class ContinuousSamplerDistribution : Distribution<Double> {
private inner class ContinuousSamplerChain(val generator: RandomGenerator) : BlockingRealChain() { private inner class ContinuousSamplerChain(val generator: RandomGenerator) : BlockingRealChain() {
private val sampler = buildCMSampler(generator) private val sampler = buildCMSampler(generator)
public override fun nextDouble(): Double = sampler.sample() override fun nextDouble(): Double = sampler.sample()
public override fun fork(): Chain<Double> = ContinuousSamplerChain(generator.fork()) override fun fork(): Chain<Double> = ContinuousSamplerChain(generator.fork())
} }
protected abstract fun buildCMSampler(generator: RandomGenerator): ContinuousSampler protected abstract fun buildCMSampler(generator: RandomGenerator): ContinuousSampler
@ -28,8 +28,8 @@ public abstract class DiscreteSamplerDistribution : Distribution<Int> {
private inner class ContinuousSamplerChain(val generator: RandomGenerator) : BlockingIntChain() { private inner class ContinuousSamplerChain(val generator: RandomGenerator) : BlockingIntChain() {
private val sampler = buildSampler(generator) private val sampler = buildSampler(generator)
public override fun nextInt(): Int = sampler.sample() override fun nextInt(): Int = sampler.sample()
public override fun fork(): Chain<Int> = ContinuousSamplerChain(generator.fork()) override fun fork(): Chain<Int> = ContinuousSamplerChain(generator.fork())
} }
protected abstract fun buildSampler(generator: RandomGenerator): DiscreteSampler protected abstract fun buildSampler(generator: RandomGenerator): DiscreteSampler
@ -58,9 +58,7 @@ public fun Distribution.Companion.normal(
return normalSampler(method, provider) return normalSampler(method, provider)
} }
override fun probability(arg: Double): Double { override fun probability(arg: Double): Double = exp(-arg.pow(2) / 2) / sqrt(PI * 2)
return exp(-arg.pow(2) / 2) / sqrt(PI * 2)
}
} }
public fun Distribution.Companion.normal( public fun Distribution.Companion.normal(

View File

@ -26,14 +26,12 @@ rootProject.name = "kmath"
include( include(
":kmath-memory", ":kmath-memory",
":kmath-core", ":kmath-core",
":kmath-functions", // ":kmath-functions",
":kmath-coroutines", ":kmath-coroutines",
":kmath-histograms", ":kmath-histograms",
":kmath-commons", ":kmath-commons",
":kmath-viktor", ":kmath-viktor",
":kmath-koma",
":kmath-prob", ":kmath-prob",
":kmath-io",
":kmath-dimensions", ":kmath-dimensions",
":kmath-for-real", ":kmath-for-real",
":kmath-geometry", ":kmath-geometry",