diff --git a/benchmarks/build.gradle b/benchmarks/build.gradle deleted file mode 100644 index 7c948e219..000000000 --- a/benchmarks/build.gradle +++ /dev/null @@ -1,39 +0,0 @@ -plugins { - id "java" - id "me.champeau.gradle.jmh" version "0.4.8" - id 'org.jetbrains.kotlin.jvm' -} - -repositories { - maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } - maven{ url "http://dl.bintray.com/kyonifer/maven"} - mavenCentral() -} - -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" - implementation "org.jetbrains.kotlinx:kotlinx-io-jvm:0.1.5" - //compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8" - //jmh project(':kmath-core') -} - -jmh { - warmupIterations = 1 -} - -jmhClasses.dependsOn(compileKotlin) - -compileKotlin { - kotlinOptions { - jvmTarget = "1.8" - } -} -compileTestKotlin { - kotlinOptions { - jvmTarget = "1.8" - } -} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 2fe3900ed..d7b7ac78a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,11 +1,11 @@ -val kmathVersion by extra("0.1.2") +val kmathVersion by extra("0.1.3-dev-1") allprojects { repositories { jcenter() maven("https://kotlin.bintray.com/kotlinx") } - + group = "scientifik" version = kmathVersion } diff --git a/doc/nd-structure.md b/doc/nd-structure.md index b61609870..cf13c6a29 100644 --- a/doc/nd-structure.md +++ b/doc/nd-structure.md @@ -9,17 +9,12 @@ structures. In `kmath` performance depends on which particular context was used Let us consider following contexts: ```kotlin - // specialized nd-field for Double. It works as generic Double field as well - val specializedField = NDField.real(intArrayOf(dim, dim)) - // automatically build context most suited for given type. - val autoField = NDField.auto(intArrayOf(dim, dim), RealField) - - //A field implementing lazy computations. All elements are computed on-demand - val lazyField = NDField.lazy(intArrayOf(dim, dim), RealField) - + val autoField = NDField.auto(RealField, dim, dim) + // specialized nd-field for Double. It works as generic Double field as well + val specializedField = NDField.real(dim, dim) //A generic boxing field. It should be used for objects, not primitives. - val genericField = NDField.buffered(intArrayOf(dim, dim), RealField) + val genericField = NDField.buffered(RealField, dim, dim) ``` Now let us perform several tests and see which implementation is best suited for each case: @@ -32,7 +27,7 @@ to it `n = 1000` times. The code to run this looks like: ```kotlin specializedField.run { - var res = one + var res: NDBuffer = one repeat(n) { res += 1.0 } @@ -93,7 +88,7 @@ In this case it completes in about `4x-5x` time due to boxing. The boxing field produced by ```kotlin genericField.run { - var res = one + var res: NDBuffer = one repeat(n) { res += 1.0 } diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts new file mode 100644 index 000000000..7f6bec31f --- /dev/null +++ b/examples/build.gradle.kts @@ -0,0 +1,67 @@ +import org.jetbrains.gradle.benchmarks.JvmBenchmarkTarget +import org.jetbrains.kotlin.allopen.gradle.AllOpenExtension +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + java + kotlin("jvm") + kotlin("plugin.allopen") version "1.3.31" + id("org.jetbrains.gradle.benchmarks.plugin") version "0.1.7-dev-24" +} + +configure { + annotation("org.openjdk.jmh.annotations.State") +} + +repositories { + maven("https://dl.bintray.com/kotlin/kotlin-eap") + maven("http://dl.bintray.com/kyonifer/maven") + maven("https://dl.bintray.com/orangy/maven") + mavenCentral() +} + +sourceSets { + register("benchmarks") +} + +dependencies { + implementation(project(":kmath-core")) + implementation(project(":kmath-coroutines")) + implementation(project(":kmath-commons")) + implementation(project(":kmath-koma")) + implementation("com.kyonifer:koma-core-ejml:0.12") + implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:0.1.5") + + implementation("org.jetbrains.gradle.benchmarks:runtime:0.1.7-dev-24") + + + "benchmarksCompile"(sourceSets.main.get().compileClasspath) +} + +// Configure benchmark +benchmark { + // Setup configurations + targets { + // This one matches sourceSet name above + register("benchmarks") { + this as JvmBenchmarkTarget + jmhVersion = "1.21" + } + } + + configurations { + register("fast") { + warmups = 5 // number of warmup iterations + iterations = 3 // number of iterations + iterationTime = 500 // time in seconds per iteration + iterationTimeUnit = "ms" // time unity for iterationTime, default is seconds + } + } +} + + +tasks.withType { + kotlinOptions { + jvmTarget = "1.8" + } +} \ No newline at end of file diff --git a/benchmarks/src/jmh/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt similarity index 95% rename from benchmarks/src/jmh/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt rename to examples/src/benchmarks/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt index 393d7b06a..d605e1b9c 100644 --- a/benchmarks/src/jmh/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt +++ b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt @@ -7,7 +7,7 @@ import java.nio.IntBuffer @State(Scope.Benchmark) -open class ArrayBenchmark { +class ArrayBenchmark { @Benchmark fun benchmarkArrayRead() { @@ -32,10 +32,10 @@ open class ArrayBenchmark { 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 { diff --git a/benchmarks/src/jmh/kotlin/scientifik/kmath/structures/BufferBenchmark.kt b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/BufferBenchmark.kt similarity index 96% rename from benchmarks/src/jmh/kotlin/scientifik/kmath/structures/BufferBenchmark.kt rename to examples/src/benchmarks/kotlin/scientifik/kmath/structures/BufferBenchmark.kt index a41ca83d3..9676b5e4a 100644 --- a/benchmarks/src/jmh/kotlin/scientifik/kmath/structures/BufferBenchmark.kt +++ b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/BufferBenchmark.kt @@ -7,7 +7,7 @@ import scientifik.kmath.operations.Complex import scientifik.kmath.operations.complex @State(Scope.Benchmark) -open class BufferBenchmark { +class BufferBenchmark { @Benchmark fun genericDoubleBufferReadWrite() { diff --git a/benchmarks/src/jmh/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt similarity index 84% rename from benchmarks/src/jmh/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt rename to examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt index 3e41323f1..46991b2de 100644 --- a/benchmarks/src/jmh/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt +++ b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt @@ -1,9 +1,12 @@ package scientifik.kmath.structures import org.openjdk.jmh.annotations.Benchmark +import org.openjdk.jmh.annotations.Scope +import org.openjdk.jmh.annotations.State import scientifik.kmath.operations.RealField -open class NDFieldBenchmark { +@State(Scope.Benchmark) +class NDFieldBenchmark { @Benchmark fun autoFieldAdd() { @@ -50,6 +53,6 @@ open class NDFieldBenchmark { val bufferedField = NDField.auto(RealField, dim, dim) val specializedField = NDField.real(dim, dim) - val genericField = NDField.buffered(intArrayOf(dim, dim), RealField) + val genericField = NDField.buffered(RealField, dim, dim) } } \ No newline at end of file diff --git a/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt b/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt new file mode 100644 index 000000000..831a167bf --- /dev/null +++ b/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt @@ -0,0 +1,31 @@ +package scientifik.kmath.commons.prob + +import kotlinx.coroutines.runBlocking +import scientifik.kmath.chains.Chain +import scientifik.kmath.chains.StatefulChain +import scientifik.kmath.prob.Distribution +import scientifik.kmath.prob.RandomGenerator + +data class AveragingChainState(var num: Int = 0, var value: Double = 0.0) + +fun Chain.mean(): Chain = StatefulChain(AveragingChainState(), 0.0) { + val next = this@mean.next() + num++ + value += next + return@StatefulChain value / num +} + + +fun main() { + val normal = Distribution.normal() + val chain = normal.sample(RandomGenerator.default).mean() + + runBlocking { + repeat(10001) { counter -> + val mean = chain.next() + if(counter % 1000 ==0){ + println("[$counter] Average value is $mean") + } + } + } +} \ No newline at end of file diff --git a/benchmarks/src/main/kotlin/scientifik/kmath/linear/LinearAlgebraBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/linear/LinearAlgebraBenchmark.kt similarity index 92% rename from benchmarks/src/main/kotlin/scientifik/kmath/linear/LinearAlgebraBenchmark.kt rename to examples/src/main/kotlin/scientifik/kmath/linear/LinearAlgebraBenchmark.kt index c397e9bd0..960f03de3 100644 --- a/benchmarks/src/main/kotlin/scientifik/kmath/linear/LinearAlgebraBenchmark.kt +++ b/examples/src/main/kotlin/scientifik/kmath/linear/LinearAlgebraBenchmark.kt @@ -1,6 +1,9 @@ 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.structures.Matrix import kotlin.contracts.ExperimentalContracts diff --git a/benchmarks/src/main/kotlin/scientifik/kmath/linear/MultiplicationBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/linear/MultiplicationBenchmark.kt similarity index 92% rename from benchmarks/src/main/kotlin/scientifik/kmath/linear/MultiplicationBenchmark.kt rename to examples/src/main/kotlin/scientifik/kmath/linear/MultiplicationBenchmark.kt index 31a890867..03bd0001c 100644 --- a/benchmarks/src/main/kotlin/scientifik/kmath/linear/MultiplicationBenchmark.kt +++ b/examples/src/main/kotlin/scientifik/kmath/linear/MultiplicationBenchmark.kt @@ -1,6 +1,8 @@ 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.structures.Matrix import kotlin.random.Random diff --git a/examples/src/main/kotlin/scientifik/kmath/operations/ComplexDemo.kt b/examples/src/main/kotlin/scientifik/kmath/operations/ComplexDemo.kt new file mode 100644 index 000000000..86b28303d --- /dev/null +++ b/examples/src/main/kotlin/scientifik/kmath/operations/ComplexDemo.kt @@ -0,0 +1,10 @@ +package scientifik.kmath.operations + +import scientifik.kmath.structures.NDElement +import scientifik.kmath.structures.complex + +fun main() { + val element = NDElement.complex(2, 2) { index: IntArray -> + Complex(index[0].toDouble() - index[1].toDouble(), index[0].toDouble() + index[1].toDouble()) + } +} \ No newline at end of file diff --git a/benchmarks/src/main/kotlin/scientifik/kmath/structures/ComplexNDBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt similarity index 100% rename from benchmarks/src/main/kotlin/scientifik/kmath/structures/ComplexNDBenchmark.kt rename to examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt diff --git a/benchmarks/src/main/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt similarity index 96% rename from benchmarks/src/main/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt rename to examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt index 53a23c2bc..10fad3ecb 100644 --- a/benchmarks/src/main/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt +++ b/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt @@ -13,7 +13,7 @@ fun main(args: Array) { // specialized nd-field for Double. It works as generic Double field as well val specializedField = NDField.real(dim, dim) //A generic boxing field. It should be used for objects, not primitives. - val genericField = NDField.buffered(intArrayOf(dim, dim), RealField) + val genericField = NDField.buffered(RealField, dim, dim) val autoTime = measureTimeMillis { diff --git a/benchmarks/src/main/kotlin/scientifik/kmath/structures/StructureReadBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/structures/StructureReadBenchmark.kt similarity index 100% rename from benchmarks/src/main/kotlin/scientifik/kmath/structures/StructureReadBenchmark.kt rename to examples/src/main/kotlin/scientifik/kmath/structures/StructureReadBenchmark.kt diff --git a/benchmarks/src/main/kotlin/scientifik/kmath/structures/StructureWriteBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/structures/StructureWriteBenchmark.kt similarity index 100% rename from benchmarks/src/main/kotlin/scientifik/kmath/structures/StructureWriteBenchmark.kt rename to examples/src/main/kotlin/scientifik/kmath/structures/StructureWriteBenchmark.kt diff --git a/kmath-commons/build.gradle.kts b/kmath-commons/build.gradle.kts index c610749be..ffdc27d43 100644 --- a/kmath-commons/build.gradle.kts +++ b/kmath-commons/build.gradle.kts @@ -8,6 +8,7 @@ description = "Commons math binding for kmath" dependencies { api(project(":kmath-core")) api(project(":kmath-coroutines")) + api(project(":kmath-prob")) api("org.apache.commons:commons-math3:3.6.1") testImplementation("org.jetbrains.kotlin:kotlin-test") testImplementation("org.jetbrains.kotlin:kotlin-test-junit") diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/expressions/DiffExpression.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt similarity index 83% rename from kmath-commons/src/main/kotlin/scientifik/kmath/expressions/DiffExpression.kt rename to kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt index 5f6d0d8fe..e687cb2af 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/expressions/DiffExpression.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt @@ -1,6 +1,8 @@ -package scientifik.kmath.expressions +package scientifik.kmath.commons.expressions import org.apache.commons.math3.analysis.differentiation.DerivativeStructure +import scientifik.kmath.expressions.Expression +import scientifik.kmath.expressions.ExpressionContext import scientifik.kmath.operations.ExtendedField import scientifik.kmath.operations.Field import kotlin.properties.ReadOnlyProperty @@ -81,8 +83,12 @@ class DerivativeStructureField( /** * A constructs that creates a derivative structure with required order on-demand */ -class DiffExpression(val function: DerivativeStructureField.() -> DerivativeStructure) : Expression { - override fun invoke(arguments: Map): Double = DerivativeStructureField(0, arguments) +class DiffExpression(val function: DerivativeStructureField.() -> DerivativeStructure) : + Expression { + override fun invoke(arguments: Map): Double = DerivativeStructureField( + 0, + arguments + ) .run(function).value /** @@ -109,21 +115,27 @@ fun DiffExpression.derivative(name: String) = derivative(name to 1) * A context for [DiffExpression] (not to be confused with [DerivativeStructure]) */ object DiffExpressionContext : ExpressionContext, Field { - override fun variable(name: String, default: Double?) = DiffExpression { variable(name, default?.const()) } + override fun variable(name: String, default: Double?) = + DiffExpression { variable(name, default?.const()) } - override fun const(value: Double): DiffExpression = DiffExpression { value.const() } + override fun const(value: Double): DiffExpression = + DiffExpression { value.const() } - override fun add(a: DiffExpression, b: DiffExpression) = DiffExpression { a.function(this) + b.function(this) } + override fun add(a: DiffExpression, b: DiffExpression) = + DiffExpression { a.function(this) + b.function(this) } override val zero = DiffExpression { 0.0.const() } - override fun multiply(a: DiffExpression, k: Number) = DiffExpression { a.function(this) * k } + override fun multiply(a: DiffExpression, k: Number) = + DiffExpression { a.function(this) * k } override val one = DiffExpression { 1.0.const() } - override fun multiply(a: DiffExpression, b: DiffExpression) = DiffExpression { a.function(this) * b.function(this) } + override fun multiply(a: DiffExpression, b: DiffExpression) = + DiffExpression { a.function(this) * b.function(this) } - override fun divide(a: DiffExpression, b: DiffExpression) = DiffExpression { a.function(this) / b.function(this) } + override fun divide(a: DiffExpression, b: DiffExpression) = + DiffExpression { a.function(this) / b.function(this) } } diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/linear/CMMatrix.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMMatrix.kt similarity index 83% rename from kmath-commons/src/main/kotlin/scientifik/kmath/linear/CMMatrix.kt rename to kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMMatrix.kt index 9103f0fb2..72e5fb95a 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/linear/CMMatrix.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMMatrix.kt @@ -1,11 +1,13 @@ -package scientifik.kmath.linear +package scientifik.kmath.commons.linear import org.apache.commons.math3.linear.* import org.apache.commons.math3.linear.RealMatrix import org.apache.commons.math3.linear.RealVector +import scientifik.kmath.linear.* import scientifik.kmath.structures.Matrix -class CMMatrix(val origin: RealMatrix, features: Set? = null) : FeaturedMatrix { +class CMMatrix(val origin: RealMatrix, features: Set? = null) : + FeaturedMatrix { override val rowNum: Int get() = origin.rowDimension override val colNum: Int get() = origin.columnDimension @@ -70,10 +72,14 @@ object CMMatrixContext : MatrixContext { override fun multiply(a: Matrix, k: Number) = CMMatrix(a.toCM().origin.scalarMultiply(k.toDouble())) - override fun Matrix.times(value: Double): Matrix = produce(rowNum,colNum){i,j-> get(i,j)*value} + override fun Matrix.times(value: Double): Matrix = + produce(rowNum, colNum) { i, j -> get(i, j) * value } } -operator fun CMMatrix.plus(other: CMMatrix): CMMatrix = CMMatrix(this.origin.add(other.origin)) -operator fun CMMatrix.minus(other: CMMatrix): CMMatrix = CMMatrix(this.origin.subtract(other.origin)) +operator fun CMMatrix.plus(other: CMMatrix): CMMatrix = + CMMatrix(this.origin.add(other.origin)) +operator fun CMMatrix.minus(other: CMMatrix): CMMatrix = + CMMatrix(this.origin.subtract(other.origin)) -infix fun CMMatrix.dot(other: CMMatrix): CMMatrix = CMMatrix(this.origin.multiply(other.origin)) \ No newline at end of file +infix fun CMMatrix.dot(other: CMMatrix): CMMatrix = + CMMatrix(this.origin.multiply(other.origin)) \ No newline at end of file diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/linear/CMSolver.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMSolver.kt similarity index 94% rename from kmath-commons/src/main/kotlin/scientifik/kmath/linear/CMSolver.kt rename to kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMSolver.kt index a56e8c93e..77b688e31 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/linear/CMSolver.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/linear/CMSolver.kt @@ -1,6 +1,7 @@ -package scientifik.kmath.linear +package scientifik.kmath.commons.linear import org.apache.commons.math3.linear.* +import scientifik.kmath.linear.Point import scientifik.kmath.structures.Matrix enum class CMDecomposition { diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CMRandomGeneratorWrapper.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CMRandomGeneratorWrapper.kt new file mode 100644 index 000000000..a32626e0d --- /dev/null +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CMRandomGeneratorWrapper.kt @@ -0,0 +1,28 @@ +package scientifik.kmath.commons.prob + +import org.apache.commons.math3.random.JDKRandomGenerator +import scientifik.kmath.prob.RandomGenerator +import org.apache.commons.math3.random.RandomGenerator as CMRandom + +inline class CMRandomGeneratorWrapper(val generator: CMRandom) : RandomGenerator { + override fun nextDouble(): Double = generator.nextDouble() + + override fun nextInt(): Int = generator.nextInt() + + override fun nextLong(): Long = generator.nextLong() + + override fun nextBlock(size: Int): ByteArray = ByteArray(size).apply { generator.nextBytes(this) } +} + +fun CMRandom.asKmathGenerator(): RandomGenerator = CMRandomGeneratorWrapper(this) + +fun RandomGenerator.asCMGenerator(): CMRandom = + (this as? CMRandomGeneratorWrapper)?.generator ?: TODO("Implement reverse CM wrapper") + +val RandomGenerator.Companion.default: RandomGenerator by lazy { JDKRandomGenerator().asKmathGenerator() } + +fun RandomGenerator.Companion.jdk(seed: Int? = null): RandomGenerator = if (seed == null) { + JDKRandomGenerator() +} else { + JDKRandomGenerator(seed) +}.asKmathGenerator() \ No newline at end of file diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CommonsDistribution.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CommonsDistribution.kt new file mode 100644 index 000000000..94f8560a4 --- /dev/null +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CommonsDistribution.kt @@ -0,0 +1,82 @@ +package scientifik.kmath.commons.prob + +import org.apache.commons.math3.distribution.* +import scientifik.kmath.prob.Distribution +import scientifik.kmath.prob.RandomChain +import scientifik.kmath.prob.RandomGenerator +import scientifik.kmath.prob.UnivariateDistribution +import org.apache.commons.math3.random.RandomGenerator as CMRandom + +class CMRealDistributionWrapper(val builder: (CMRandom?) -> RealDistribution) : UnivariateDistribution { + + private val defaultDistribution by lazy { builder(null) } + + override fun probability(arg: Double): Double = defaultDistribution.probability(arg) + + override fun cumulative(arg: Double): Double = defaultDistribution.cumulativeProbability(arg) + + override fun sample(generator: RandomGenerator): RandomChain { + val distribution = builder(generator.asCMGenerator()) + return RandomChain(generator) { distribution.sample() } + } +} + +class CMIntDistributionWrapper(val builder: (CMRandom?) -> IntegerDistribution) : UnivariateDistribution { + + private val defaultDistribution by lazy { builder(null) } + + override fun probability(arg: Int): Double = defaultDistribution.probability(arg) + + override fun cumulative(arg: Int): Double = defaultDistribution.cumulativeProbability(arg) + + override fun sample(generator: RandomGenerator): RandomChain { + val distribution = builder(generator.asCMGenerator()) + return RandomChain(generator) { distribution.sample() } + } +} + + +fun Distribution.Companion.normal(mean: Double = 0.0, sigma: Double = 1.0): UnivariateDistribution = + CMRealDistributionWrapper { generator -> NormalDistribution(generator, mean, sigma) } + +fun Distribution.Companion.poisson(mean: Double): UnivariateDistribution = CMIntDistributionWrapper { generator -> + PoissonDistribution( + generator, + mean, + PoissonDistribution.DEFAULT_EPSILON, + PoissonDistribution.DEFAULT_MAX_ITERATIONS + ) +} + +fun Distribution.Companion.binomial(trials: Int, p: Double): UnivariateDistribution = + CMIntDistributionWrapper { generator -> + BinomialDistribution(generator, trials, p) + } + +fun Distribution.Companion.student(degreesOfFreedom: Double): UnivariateDistribution = + CMRealDistributionWrapper { generator -> + TDistribution(generator, degreesOfFreedom, TDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY) + } + +fun Distribution.Companion.chi2(degreesOfFreedom: Double): UnivariateDistribution = + CMRealDistributionWrapper { generator -> + ChiSquaredDistribution(generator, degreesOfFreedom) + } + +fun Distribution.Companion.fisher( + numeratorDegreesOfFreedom: Double, + denominatorDegreesOfFreedom: Double +): UnivariateDistribution = + CMRealDistributionWrapper { generator -> + FDistribution(generator, numeratorDegreesOfFreedom, denominatorDegreesOfFreedom) + } + +fun Distribution.Companion.exponential(mean: Double): UnivariateDistribution = + CMRealDistributionWrapper { generator -> + ExponentialDistribution(generator, mean) + } + +fun Distribution.Companion.uniform(a: Double, b: Double): UnivariateDistribution = + CMRealDistributionWrapper { generator -> + UniformRealDistribution(generator, a, b) + } \ No newline at end of file diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/transform/Transformations.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/transform/Transformations.kt similarity index 97% rename from kmath-commons/src/main/kotlin/scientifik/kmath/transform/Transformations.kt rename to kmath-commons/src/main/kotlin/scientifik/kmath/commons/transform/Transformations.kt index ac6922262..ce51735fe 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/transform/Transformations.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/transform/Transformations.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.transform +package scientifik.kmath.commons.transform import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow diff --git a/kmath-commons/src/test/kotlin/scientifik/kmath/expressions/AutoDiffTest.kt b/kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt similarity index 78% rename from kmath-commons/src/test/kotlin/scientifik/kmath/expressions/AutoDiffTest.kt rename to kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt index 2695bb918..37e4e9ff9 100644 --- a/kmath-commons/src/test/kotlin/scientifik/kmath/expressions/AutoDiffTest.kt +++ b/kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt @@ -1,6 +1,9 @@ -package scientifik.kmath.expressions +package scientifik.kmath.commons.expressions import org.junit.Test +import scientifik.kmath.commons.expressions.DerivativeStructureField +import scientifik.kmath.commons.expressions.DiffExpression +import scientifik.kmath.commons.expressions.derivative import kotlin.test.assertEquals inline fun diff(order: Int, vararg parameters: Pair, block: DerivativeStructureField.() -> R) = diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt index 6444f667d..e9d518bd5 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/ComplexNDField.kt @@ -131,4 +131,7 @@ operator fun ComplexNDElement.plus(arg: Double) = operator fun ComplexNDElement.minus(arg: Double) = map { it - arg } -fun NDField.Companion.complex(vararg shape: Int) = ComplexNDField(shape) \ No newline at end of file +fun NDField.Companion.complex(vararg shape: Int): ComplexNDField = ComplexNDField(shape) + +fun NDElement.Companion.complex(vararg shape: Int, initializer: ComplexField.(IntArray) -> Complex): ComplexNDElement = + NDField.complex(*shape).produce(initializer) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt index c6f0f7a48..352c05467 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt @@ -1,9 +1,9 @@ package scientifik.kmath.structures +import scientifik.kmath.operations.Complex import scientifik.kmath.operations.Field import scientifik.kmath.operations.Ring import scientifik.kmath.operations.Space -import kotlin.jvm.JvmName /** @@ -57,6 +57,8 @@ interface NDAlgebra> { * element-by-element invoke a function working on [T] on a [NDStructure] */ operator fun Function1.invoke(structure: N) = map(structure) { value -> this@invoke(value) } + + companion object } /** @@ -75,6 +77,7 @@ interface NDSpace, N : NDStructure> : Space, NDAlgebra add(arg, value) } + operator fun N.minus(arg: T) = map(this) { value -> add(arg, -value) } operator fun T.plus(arg: N) = map(arg) { value -> add(this@plus, value) } @@ -93,6 +96,7 @@ interface NDRing, N : NDStructure> : Ring, NDSpace //TODO move to extensions after KEEP-176 operator fun N.times(arg: T) = map(this) { value -> multiply(arg, value) } + operator fun T.times(arg: N) = map(arg) { value -> multiply(this@times, value) } } @@ -113,6 +117,7 @@ interface NDField, N : NDStructure> : Field, NDRing divide(arg, value) } + operator fun T.div(arg: N) = map(arg) { divide(it, this@div) } companion object { @@ -128,11 +133,10 @@ interface NDField, N : NDStructure> : Field, NDRing> buffered( - shape: IntArray, field: F, + vararg shape: Int, bufferFactory: BufferFactory = Buffer.Companion::boxing - ) = - BoxingNDField(shape, field, bufferFactory) + ) = BoxingNDField(shape, field, bufferFactory) /** * Create a most suitable implementation for nd-field using reified class. @@ -141,6 +145,7 @@ interface NDField, N : NDStructure> : Field, NDRing> auto(field: F, vararg shape: Int): BufferedNDField = when { T::class == Double::class -> real(*shape) as BufferedNDField + T::class == Complex::class -> complex(*shape) as BufferedNDField else -> BoxingNDField(shape, field, Buffer.Companion::auto) } } diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt index 4d2f604e1..4adc2a9a7 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt @@ -18,6 +18,7 @@ package scientifik.kmath.chains import kotlinx.atomicfu.atomic import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.Flow /** @@ -28,7 +29,7 @@ interface Chain { /** * Last cached value of the chain. Returns null if [next] was not called */ - val value: R? + fun peek(): R? /** * Generate next value, changing state if needed @@ -46,13 +47,12 @@ interface Chain { * Chain as a coroutine flow. The flow emit affects chain state and vice versa */ @FlowPreview -val Chain.flow +val Chain.flow: Flow get() = kotlinx.coroutines.flow.flow { while (true) emit(next()) } fun Iterator.asChain(): Chain = SimpleChain { next() } fun Sequence.asChain(): Chain = 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 @@ -60,7 +60,7 @@ fun Sequence.asChain(): Chain = iterator().asChain() fun Chain.map(func: (T) -> R): Chain { val parent = this; return object : Chain { - override val value: R? get() = parent.value?.let(func) + override fun peek(): R? = parent.peek()?.let(func) override suspend fun next(): R { return func(parent.next()) @@ -77,7 +77,7 @@ fun Chain.map(func: (T) -> R): Chain { */ class SimpleChain(private val gen: suspend () -> R) : Chain { private val atomicValue = atomic(null) - override val value: R? get() = atomicValue.value + override fun peek(): R? = atomicValue.value override suspend fun next(): R = gen().also { atomicValue.lazySet(it) } @@ -95,16 +95,16 @@ class MarkovChain(private val seed: () -> R, private val gen: suspe constructor(seed: R, gen: suspend (R) -> R) : this({ seed }, gen) private val atomicValue = atomic(null) - override val value: R get() = atomicValue.value ?: seed() + override fun peek(): R = atomicValue.value ?: seed() override suspend fun next(): R { - val newValue = gen(value) + val newValue = gen(peek()) atomicValue.lazySet(newValue) - return value + return peek() } override fun fork(): Chain { - return MarkovChain(value, gen) + return MarkovChain(peek(), gen) } } @@ -121,12 +121,12 @@ class StatefulChain( constructor(state: S, seed: R, gen: suspend S.(R) -> R) : this(state, { seed }, gen) private val atomicValue = atomic(null) - override val value: R get() = atomicValue.value ?: seed(state) + override fun peek(): R = atomicValue.value ?: seed(state) override suspend fun next(): R { - val newValue = gen(state, value) + val newValue = gen(state, peek()) atomicValue.lazySet(newValue) - return value + return peek() } override fun fork(): Chain { @@ -137,10 +137,10 @@ class StatefulChain( /** * A chain that repeats the same value */ -class ConstantChain(override val value: T) : Chain { - override suspend fun next(): T { - return value - } +class ConstantChain(val value: T) : Chain { + override fun peek(): T? = value + + override suspend fun next(): T = value override fun fork(): Chain { return this diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/CoroutinesExtra.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/CoroutinesExtra.kt similarity index 90% rename from kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/CoroutinesExtra.kt rename to kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/CoroutinesExtra.kt index 51cc07511..dc82d1881 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/CoroutinesExtra.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/coroutines/CoroutinesExtra.kt @@ -1,4 +1,4 @@ -package scientifik.kmath +package scientifik.kmath.coroutines import kotlinx.coroutines.* import kotlinx.coroutines.channels.produce @@ -42,13 +42,14 @@ fun Flow.async( } @FlowPreview -fun AsyncFlow.map(action: (T) -> R) = AsyncFlow(deferredFlow.map { input -> - //TODO add function composition - LazyDeferred(input.dispatcher) { - input.start(this) - action(input.await()) - } -}) +fun AsyncFlow.map(action: (T) -> R) = + AsyncFlow(deferredFlow.map { input -> + //TODO add function composition + LazyDeferred(input.dispatcher) { + input.start(this) + action(input.await()) + } + }) @ExperimentalCoroutinesApi @FlowPreview diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt index d5e94fc3b..85ce73c4b 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/streaming/BufferFlow.kt @@ -22,7 +22,7 @@ fun Flow>.spread(): Flow = flatMapConcat { it.asFlow() } * Collect incoming flow into fixed size chunks */ @FlowPreview -fun Flow.chunked(bufferSize: Int, bufferFactory: BufferFactory) = flow { +fun Flow.chunked(bufferSize: Int, bufferFactory: BufferFactory): Flow> = flow { require(bufferSize > 0) { "Resulting chunk size must be more than zero" } val list = ArrayList(bufferSize) var counter = 0 @@ -46,7 +46,7 @@ fun Flow.chunked(bufferSize: Int, bufferFactory: BufferFactory) = flow * Specialized flow chunker for real buffer */ @FlowPreview -fun Flow.chunked(bufferSize: Int) = flow { +fun Flow.chunked(bufferSize: Int): Flow = flow { require(bufferSize > 0) { "Resulting chunk size must be more than zero" } val array = DoubleArray(bufferSize) var counter = 0 diff --git a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt index 4f2c4cb8b..b634c5ae2 100644 --- a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt +++ b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt @@ -27,7 +27,7 @@ fun Chain.asSequence(): Sequence = object : Sequence { fun Chain.map(func: suspend (T) -> R): Chain { val parent = this; return object : Chain { - override val value: R? get() = runBlocking { parent.value?.let { func(it) } } + override fun peek(): R? = runBlocking { parent.peek()?.let { func(it) } } override suspend fun next(): R { return func(parent.next()) diff --git a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt index fa81fc67b..784b7cd10 100644 --- a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt +++ b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/structures/LazyNDStructure.kt @@ -1,7 +1,7 @@ package scientifik.kmath.structures import kotlinx.coroutines.* -import scientifik.kmath.Math +import scientifik.kmath.coroutines.Math class LazyNDStructure( val scope: CoroutineScope, diff --git a/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt b/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt index 5b65c2120..4fd397993 100644 --- a/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt +++ b/kmath-coroutines/src/jvmTest/kotlin/scientifik/kmath/streaming/BufferFlowTest.kt @@ -4,9 +4,9 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.collect import org.junit.Test -import scientifik.kmath.async -import scientifik.kmath.collect -import scientifik.kmath.map +import scientifik.kmath.coroutines.async +import scientifik.kmath.coroutines.collect +import scientifik.kmath.coroutines.map import java.util.concurrent.Executors diff --git a/kmath-prob/build.gradle.kts b/kmath-prob/build.gradle.kts index 28ab2d6c7..972b9bba0 100644 --- a/kmath-prob/build.gradle.kts +++ b/kmath-prob/build.gradle.kts @@ -6,13 +6,14 @@ plugins { kotlin.sourceSets { commonMain { dependencies { - api(project(":kmath-core")) api(project(":kmath-coroutines")) compileOnly("org.jetbrains.kotlinx:atomicfu-common:${Versions.atomicfuVersion}") } } jvmMain { dependencies { + // https://mvnrepository.com/artifact/org.apache.commons/commons-rng-simple + //api("org.apache.commons:commons-rng-sampling:1.2") compileOnly("org.jetbrains.kotlinx:atomicfu:${Versions.atomicfuVersion}") } } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt new file mode 100644 index 000000000..a8614963b --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt @@ -0,0 +1,33 @@ +package scientifik.kmath.prob + +import scientifik.kmath.chains.Chain + +/** + * A distribution of typed objects + */ +interface Distribution { + /** + * A probability value for given argument [arg]. + * For continuous distributions returns PDF + */ + fun probability(arg: T): Double + + /** + * Create a chain of samples from this distribution. + * The chain is not guaranteed to be stateless. + */ + fun sample(generator: RandomGenerator): RandomChain + //TODO add sample bunch generator + + /** + * An empty companion. Distribution factories should be written as its extensions + */ + companion object +} + +interface UnivariateDistribution> : Distribution { + /** + * Cumulative distribution for ordered parameter + */ + fun cumulative(arg: T): Double +} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt new file mode 100644 index 000000000..792b60603 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt @@ -0,0 +1,17 @@ +package scientifik.kmath.prob + +import kotlinx.atomicfu.atomic +import scientifik.kmath.chains.Chain + +/** + * A possibly stateful chain producing random values. + * TODO make random chain properly fork generator + */ +class RandomChain(val generator: RandomGenerator, private val gen: suspend RandomGenerator.() -> R) : Chain { + private val atomicValue = atomic(null) + override fun peek(): R? = atomicValue.value + + override suspend fun next(): R = generator.gen().also { atomicValue.lazySet(it) } + + override fun fork(): Chain = RandomChain(generator, gen) +} \ No newline at end of file diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt new file mode 100644 index 000000000..039ee3d2b --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt @@ -0,0 +1,13 @@ +package scientifik.kmath.prob + +/** + * A basic generator + */ +interface RandomGenerator { + fun nextDouble(): Double + fun nextInt(): Int + fun nextLong(): Long + fun nextBlock(size: Int): ByteArray + + companion object +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 5adf75fa5..004b432fd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,6 +3,7 @@ pluginManagement { jcenter() gradlePluginPortal() maven("https://dl.bintray.com/kotlin/kotlin-eap") + maven("https://dl.bintray.com/orangy/maven") } resolutionStrategy { eachPlugin { @@ -28,5 +29,5 @@ include( ":kmath-commons", ":kmath-koma", ":kmath-prob", - ":benchmarks" + ":examples" )