Drop koma support, add more explicit visibility modifiers

This commit is contained in:
Iaroslav Postovalov 2020-09-09 22:31:54 +07:00
parent 8ae9a071ef
commit fc5ec8fed7
No known key found for this signature in database
GPG Key ID: 70D5F4DCB0972F1B
23 changed files with 182 additions and 474 deletions

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
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
* **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)
* Commons math integration
* Koma integration

View File

@ -10,7 +10,6 @@ plugins {
allOpen.annotation("org.openjdk.jmh.annotations.State")
repositories {
maven("http://dl.bintray.com/kyonifer/maven")
maven("https://dl.bintray.com/mipt-npm/scientifik")
maven("https://dl.bintray.com/mipt-npm/dev")
maven("https://dl.bintray.com/kotlin/kotlin-dev/")
@ -25,10 +24,8 @@ dependencies {
implementation(project(":kmath-coroutines"))
implementation(project(":kmath-commons"))
implementation(project(":kmath-prob"))
implementation(project(":kmath-koma"))
implementation(project(":kmath-viktor"))
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.benchmark.runtime:0.2.0-dev-20")
"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 java.nio.IntBuffer
@State(Scope.Benchmark)
class ArrayBenchmark {
@Benchmark
fun benchmarkArrayRead() {
var res = 0
for (i in 1..size) {
res += array[size - i]
}
for (i in 1..size) res += array[size - i]
}
@Benchmark
fun benchmarkBufferRead() {
var res = 0
for (i in 1..size) {
res += arrayBuffer.get(size - i)
}
for (i in 1..size) res += arrayBuffer.get(size - i)
}
@Benchmark
fun nativeBufferRead() {
var res = 0
for (i in 1..size) {
res += nativeBuffer.get(size - i)
}
for (i in 1..size) res += nativeBuffer.get(size - i)
}
companion object {
val size = 1000
val array = IntArray(size) { it }
val arrayBuffer = IntBuffer.wrap(array)
val nativeBuffer = IntBuffer.allocate(size).also {
for (i in 0 until size) {
it.put(i, i)
}
const val size: Int = 1000
val array: IntArray = IntArray(size) { it }
val arrayBuffer: IntBuffer = IntBuffer.wrap(array)
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
import scientifik.kmath.asm.compile
import scientifik.kmath.expressions.Expression
import scientifik.kmath.expressions.expressionInField
import scientifik.kmath.expressions.invoke
import scientifik.kmath.operations.Field
import scientifik.kmath.operations.RealField
import kotlin.random.Random
import kotlin.system.measureTimeMillis
class ExpressionsInterpretersBenchmark {
private val algebra: Field<Double> = RealField
fun functionalExpression() {
val expr = algebra.expressionInField {
variable("x") * const(2.0) + const(2.0) / variable("x") - const(16.0)
}
invokeAndSum(expr)
}
fun mstExpression() {
val expr = algebra.mstInField {
symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0)
}
invokeAndSum(expr)
}
fun asmExpression() {
val expr = algebra.mstInField {
symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0)
}.compile()
invokeAndSum(expr)
}
private fun invokeAndSum(expr: Expression<Double>) {
val random = Random(0)
var sum = 0.0
repeat(1000000) {
sum += expr("x" to random.nextDouble())
}
println(sum)
}
}
fun main() {
val benchmark = ExpressionsInterpretersBenchmark()
val fe = measureTimeMillis {
benchmark.functionalExpression()
}
println("fe=$fe")
val mst = measureTimeMillis {
benchmark.mstExpression()
}
println("mst=$mst")
val asm = measureTimeMillis {
benchmark.asmExpression()
}
println("asm=$asm")
}
//package scientifik.kmath.ast
//
//import scientifik.kmath.asm.compile
//import scientifik.kmath.expressions.Expression
//import scientifik.kmath.expressions.expressionInField
//import scientifik.kmath.expressions.invoke
//import scientifik.kmath.operations.Field
//import scientifik.kmath.operations.RealField
//import kotlin.random.Random
//import kotlin.system.measureTimeMillis
//
//class ExpressionsInterpretersBenchmark {
// private val algebra: Field<Double> = RealField
// fun functionalExpression() {
// val expr = algebra.expressionInField {
// variable("x") * const(2.0) + const(2.0) / variable("x") - const(16.0)
// }
//
// invokeAndSum(expr)
// }
//
// fun mstExpression() {
// val expr = algebra.mstInField {
// symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0)
// }
//
// invokeAndSum(expr)
// }
//
// fun asmExpression() {
// val expr = algebra.mstInField {
// symbol("x") * number(2.0) + number(2.0) / symbol("x") - number(16.0)
// }.compile()
//
// invokeAndSum(expr)
// }
//
// private fun invokeAndSum(expr: Expression<Double>) {
// val random = Random(0)
// var sum = 0.0
//
// repeat(1000000) {
// sum += expr("x" to random.nextDouble())
// }
//
// println(sum)
// }
//}
//
//fun main() {
// val benchmark = ExpressionsInterpretersBenchmark()
//
// val fe = measureTimeMillis {
// benchmark.functionalExpression()
// }
//
// println("fe=$fe")
//
// val mst = measureTimeMillis {
// benchmark.mstExpression()
// }
//
// println("mst=$mst")
//
// val asm = measureTimeMillis {
// benchmark.asmExpression()
// }
//
// 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-coroutines"))
api(project(":kmath-prob"))
api(project(":kmath-functions"))
// api(project(":kmath-functions"))
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 fun <T> Iterator<T>.asChain(): Chain<T> = SimpleChain { next() }
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
*/
public class SimpleChain<out R>(private val gen: suspend () -> R) : Chain<R> {
override suspend fun next(): R = gen()
override fun fork(): Chain<R> = this
public override suspend fun next(): R = gen()
public override fun fork(): Chain<R> = this
}
/**
* A stateless Markov chain
*/
public class MarkovChain<out R : Any>(private val seed: suspend () -> R, private val gen: suspend (R) -> R) : Chain<R> {
private val mutex = Mutex()
private val mutex: Mutex = Mutex()
private var value: R? = null
public fun value(): R? = value
override suspend fun next(): R {
public override suspend fun next(): R {
mutex.withLock {
val newValue = gen(value ?: seed())
value = newValue
@ -74,9 +71,7 @@ public class MarkovChain<out R : Any>(private val seed: suspend () -> R, private
}
}
override fun fork(): Chain<R> {
return MarkovChain(seed = { value ?: seed() }, gen = gen)
}
public override fun fork(): Chain<R> = MarkovChain(seed = { value ?: seed() }, gen = gen)
}
/**
@ -91,12 +86,11 @@ public class StatefulChain<S, out R>(
private val gen: suspend S.(R) -> R
) : Chain<R> {
private val mutex: Mutex = Mutex()
private var value: R? = null
public fun value(): R? = value
override suspend fun next(): R {
public override suspend fun next(): R {
mutex.withLock {
val newValue = state.gen(value ?: state.seed())
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
*/
public class ConstantChain<out T>(public val value: T) : Chain<T> {
override suspend fun next(): T = value
override fun fork(): Chain<T> = this
public override suspend fun next(): T = value
public 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 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

View File

@ -29,7 +29,6 @@ public interface Histogram<T : Any, out B : Bin<T>> : Iterable<B> {
* Dimension of the histogram
*/
public val dimension: Int
}
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 {
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])
@ -87,7 +87,7 @@ public class UnivariateHistogram private constructor(private val factory: (Doubl
)
else -> {
val index = (0 until sorted.size).first { value > sorted[it] }
val index = sorted.indices.first { value > sorted[it] }
val left = sorted[index]
val right = sorted[index + 1]
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
*/
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)

View File

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

View File

@ -5,10 +5,13 @@ import scientifik.kmath.chains.Chain
/**
* 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 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
*/
interface RandomGenerator {
fun nextBoolean(): Boolean
fun nextDouble(): Double
fun nextInt(): Int
fun nextInt(until: Int): Int
fun nextLong(): Long
fun nextLong(until: Long): Long
fun fillBytes(array: ByteArray, fromIndex: Int = 0, toIndex: Int = array.size)
fun nextBytes(size: Int): ByteArray = ByteArray(size).also { fillBytes(it) }
public interface RandomGenerator {
public fun nextBoolean(): Boolean
public fun nextDouble(): Double
public fun nextInt(): Int
public fun nextInt(until: Int): Int
public fun nextLong(): Long
public 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) }
/**
* 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.
*/
fun fork(): RandomGenerator
public fun fork(): RandomGenerator
companion object {
val default by lazy { DefaultGenerator() }
public companion object {
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 {
override fun nextBoolean(): Boolean = random.nextBoolean()
public inline class DefaultGenerator(public val random: Random = Random) : RandomGenerator {
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()
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) {
public override fun fillBytes(array: ByteArray, fromIndex: Int, toIndex: Int) {
random.nextBytes(array, fromIndex, toIndex)
}
override fun nextBytes(size: Int): ByteArray = random.nextBytes(size)
override fun fork(): RandomGenerator = RandomGenerator.default(random.nextLong())
public override fun nextBytes(size: Int): ByteArray = random.nextBytes(size)
public 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.invoke
class BasicSampler<T : Any>(val chainBuilder: (RandomGenerator) -> Chain<T>) : Sampler<T> {
override fun sample(generator: RandomGenerator): Chain<T> = chainBuilder(generator)
public class BasicSampler<T : Any>(public val chainBuilder: (RandomGenerator) -> Chain<T>) : Sampler<T> {
public override fun sample(generator: RandomGenerator): Chain<T> = chainBuilder(generator)
}
class ConstantSampler<T : Any>(val value: T) : Sampler<T> {
override fun sample(generator: RandomGenerator): Chain<T> = ConstantChain(value)
public class ConstantSampler<T : Any>(public val value: T) : Sampler<T> {
public override fun sample(generator: RandomGenerator): Chain<T> = ConstantChain(value)
}
/**
* 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)
override fun add(a: Sampler<T>, b: Sampler<T>): Sampler<T> = BasicSampler { generator ->
public 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 } }
}
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() } }
}
}

View File

@ -6,7 +6,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.scanReduce
import kotlinx.coroutines.flow.runningReduce
import scientifik.kmath.coroutines.mapParallel
import scientifik.kmath.operations.*
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
*/
interface Statistic<T, R> {
suspend operator fun invoke(data: Buffer<T>): R
public interface Statistic<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 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
suspend fun computeIntermediate(data: Buffer<T>): I
public suspend fun computeIntermediate(data: Buffer<T>): I
//Compose two blocks
suspend fun composeIntermediate(first: I, second: I): I
public suspend fun composeIntermediate(first: I, second: I): I
//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
@ -46,7 +46,7 @@ private fun <T, I, R> ComposableStatistic<T, I, R>.flowIntermediate(
dispatcher: CoroutineDispatcher = Dispatchers.Default
): Flow<I> = flow
.mapParallel(dispatcher) { computeIntermediate(it) }
.scanReduce(::composeIntermediate)
.runningReduce(::composeIntermediate)
/**
@ -57,7 +57,7 @@ private fun <T, I, R> ComposableStatistic<T, I, R>.flowIntermediate(
*/
@FlowPreview
@ExperimentalCoroutinesApi
fun <T, I, R> ComposableStatistic<T, I, R>.flow(
public fun <T, I, R> ComposableStatistic<T, I, R>.flow(
flow: Flow<Buffer<T>>,
dispatcher: CoroutineDispatcher = Dispatchers.Default
): Flow<R> = flowIntermediate(flow, dispatcher).map(::toResult)
@ -65,32 +65,32 @@ fun <T, I, R> ComposableStatistic<T, I, R>.flow(
/**
* Arithmetic mean
*/
class Mean<T>(val space: Space<T>) : ComposableStatistic<T, Pair<T, Int>, T> {
override suspend fun computeIntermediate(data: Buffer<T>): Pair<T, Int> =
public class Mean<T>(public val space: Space<T>) : ComposableStatistic<T, Pair<T, Int>, T> {
public override suspend fun computeIntermediate(data: Buffer<T>): Pair<T, Int> =
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)
override suspend fun toResult(intermediate: Pair<T, Int>): T =
public override suspend fun toResult(intermediate: Pair<T, Int>): T =
space { intermediate.first / intermediate.second }
companion object {
public companion object {
//TODO replace with optimized version which respects overflow
val real: Mean<Double> = Mean(RealField)
val int: Mean<Int> = Mean(IntRing)
val long: Mean<Long> = Mean(LongRing)
public val real: Mean<Double> = Mean(RealField)
public val int: Mean<Int> = Mean(IntRing)
public val long: Mean<Long> = Mean(LongRing)
}
}
/**
* Non-composable median
*/
class Median<T>(private val comparator: Comparator<T>) : Statistic<T, T> {
override suspend fun invoke(data: Buffer<T>): T =
public class Median<T>(private val comparator: Comparator<T>) : Statistic<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
companion object {
val real: Median<Double> = Median(Comparator { a: Double, b: Double -> a.compareTo(b) })
public companion object {
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.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 {
return if (arg in range) {
return 1.0 / length
} else {
0.0
}
}
override fun sample(generator: RandomGenerator): Chain<Double> =
SimpleChain { range.start + generator.nextDouble() * length }
override fun sample(generator: RandomGenerator): Chain<Double> {
return SimpleChain {
range.start + generator.nextDouble() * length
}
}
override fun cumulative(arg: Double): Double {
return when {
override fun cumulative(arg: Double): Double = 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)

View File

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

View File

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