From 2e7607371222ea5361f12d0b19a052e4b2099571 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 7 Jun 2019 15:08:05 +0300 Subject: [PATCH 1/5] Initial work on distributions --- .../kotlin/scientifik/kmath/chains/Chain.kt | 32 +++++------ .../kmath/{ => coroutines}/CoroutinesExtra.kt | 17 +++--- .../scientifik/kmath/streaming/BufferFlow.kt | 4 +- .../scientifik/kmath/chains/ChainExt.kt | 2 +- .../kmath/structures/LazyNDStructure.kt | 2 +- .../kmath/streaming/BufferFlowTest.kt | 6 +-- kmath-prob/build.gradle.kts | 3 +- .../scientifik/kmath/prob/Distribution.kt | 28 ++++++++++ .../scientifik/kmath/prob/RandomGenerator.kt | 11 ++++ .../kmath/prob/CommonDistributions.kt | 53 +++++++++++++++++++ .../prob/CommonsRandomProviderWrapper.kt | 18 +++++++ 11 files changed, 144 insertions(+), 32 deletions(-) rename kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/{ => coroutines}/CoroutinesExtra.kt (90%) create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt create mode 100644 kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/CommonDistributions.kt create mode 100644 kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/CommonsRandomProviderWrapper.kt 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..77a4aefc8 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..946b173ff --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt @@ -0,0 +1,28 @@ +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): Chain + //TODO add sample bunch generator +} + +interface UnivariateDistribution> : Distribution { + /** + * Cumulative distribution for ordered parameter + */ + fun cumulative(arg: T): Double +} \ 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..89cbc615c --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt @@ -0,0 +1,11 @@ +package scientifik.kmath.prob + +/** + * A basic generator + */ +interface RandomGenerator { + fun nextDouble(): Double + fun nextInt(): Int + fun nextLong(): Long + fun nextBlock(size: Int): ByteArray +} \ No newline at end of file diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/CommonDistributions.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/CommonDistributions.kt new file mode 100644 index 000000000..c08008c28 --- /dev/null +++ b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/CommonDistributions.kt @@ -0,0 +1,53 @@ +package scientifik.kmath.prob + +import org.apache.commons.rng.sampling.distribution.* +import scientifik.kmath.chains.Chain +import scientifik.kmath.chains.SimpleChain +import kotlin.math.PI +import kotlin.math.exp +import kotlin.math.sqrt + +class NormalDistribution(val mean: Double, val sigma: Double) : UnivariateDistribution { + enum class Sampler { + BoxMuller, + Marsaglia, + Ziggurat + } + + override fun probability(arg: Double): Double { + val d = (arg - mean) / sigma + return 1.0 / sqrt(2.0 * PI * sigma) * exp(-d * d / 2) + } + + override fun cumulative(arg: Double): Double { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + fun sample(generator: RandomGenerator, sampler: Sampler): Chain { + val normalized = when (sampler) { + Sampler.BoxMuller -> BoxMullerNormalizedGaussianSampler(generator.asProvider()) + Sampler.Marsaglia -> MarsagliaNormalizedGaussianSampler(generator.asProvider()) + Sampler.Ziggurat -> ZigguratNormalizedGaussianSampler(generator.asProvider()) + } + val gauss = GaussianSampler(normalized, mean, sigma) + //TODO add generator to chain state to allow stateful forks + return SimpleChain { gauss.sample() } + } + + override fun sample(generator: RandomGenerator): Chain = sample(generator, Sampler.BoxMuller) +} + +class PoissonDistribution(val mean: Double): UnivariateDistribution{ + override fun probability(arg: Int): Double { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun cumulative(arg: Int): Double { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun sample(generator: RandomGenerator): Chain { + val sampler = PoissonSampler(generator.asProvider(), mean) + return SimpleChain{sampler.sample()} + } +} \ No newline at end of file diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/CommonsRandomProviderWrapper.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/CommonsRandomProviderWrapper.kt new file mode 100644 index 000000000..ad408460d --- /dev/null +++ b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/CommonsRandomProviderWrapper.kt @@ -0,0 +1,18 @@ +package scientifik.kmath.prob + +import org.apache.commons.rng.UniformRandomProvider + +inline class CommonsRandomProviderWrapper(val provider: UniformRandomProvider) : RandomGenerator { + override fun nextDouble(): Double = provider.nextDouble() + + override fun nextInt(): Int = provider.nextInt() + + override fun nextLong(): Long = provider.nextLong() + + override fun nextBlock(size: Int): ByteArray = ByteArray(size).also { provider.nextBytes(it) } +} + +fun UniformRandomProvider.asGenerator(): RandomGenerator = CommonsRandomProviderWrapper(this) + +fun RandomGenerator.asProvider(): UniformRandomProvider = + (this as? CommonsRandomProviderWrapper)?.provider ?: TODO("implement reverse wrapper") \ No newline at end of file From 9c2164cd6496f173633854719794fbb58387902c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 8 Jun 2019 14:26:44 +0300 Subject: [PATCH 2/5] Distribution implementations for commons-math --- {benchmarks => examples}/build.gradle | 0 .../kmath/structures/ArrayBenchmark.kt | 0 .../kmath/structures/BufferBenchmark.kt | 0 .../kmath/structures/NDFieldBenchmark.kt | 0 .../kmath/linear/LinearAlgebraBenchmark.kt | 3 + .../kmath/linear/MultiplicationBenchmark.kt | 2 + .../kmath/structures/ComplexNDBenchmark.kt | 0 .../kmath/structures/NDFieldBenchmark.kt | 0 .../structures/StructureReadBenchmark.kt | 0 .../structures/StructureWriteBenchmark.kt | 0 kmath-commons/build.gradle.kts | 1 + .../expressions/DiffExpression.kt | 30 +++++-- .../kmath/{ => commons}/linear/CMMatrix.kt | 18 ++-- .../kmath/{ => commons}/linear/CMSolver.kt | 3 +- .../commons/prob/CMRandomGeneratorWrapper.kt | 18 ++++ .../kmath/commons/prob/CommonsDistribution.kt | 84 +++++++++++++++++++ .../transform/Transformations.kt | 2 +- .../{ => commons}/expressions/AutoDiffTest.kt | 5 +- kmath-prob/build.gradle.kts | 2 +- .../scientifik/kmath/prob/Distribution.kt | 9 +- .../scientifik/kmath/prob/RandomChain.kt | 17 ++++ .../kmath/prob/CommonDistributions.kt | 53 ------------ .../prob/CommonsRandomProviderWrapper.kt | 18 ---- settings.gradle.kts | 2 +- 24 files changed, 174 insertions(+), 93 deletions(-) rename {benchmarks => examples}/build.gradle (100%) rename {benchmarks => examples}/src/jmh/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt (100%) rename {benchmarks => examples}/src/jmh/kotlin/scientifik/kmath/structures/BufferBenchmark.kt (100%) rename {benchmarks => examples}/src/jmh/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt (100%) rename {benchmarks => examples}/src/main/kotlin/scientifik/kmath/linear/LinearAlgebraBenchmark.kt (92%) rename {benchmarks => examples}/src/main/kotlin/scientifik/kmath/linear/MultiplicationBenchmark.kt (92%) rename {benchmarks => examples}/src/main/kotlin/scientifik/kmath/structures/ComplexNDBenchmark.kt (100%) rename {benchmarks => examples}/src/main/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt (100%) rename {benchmarks => examples}/src/main/kotlin/scientifik/kmath/structures/StructureReadBenchmark.kt (100%) rename {benchmarks => examples}/src/main/kotlin/scientifik/kmath/structures/StructureWriteBenchmark.kt (100%) rename kmath-commons/src/main/kotlin/scientifik/kmath/{ => commons}/expressions/DiffExpression.kt (83%) rename kmath-commons/src/main/kotlin/scientifik/kmath/{ => commons}/linear/CMMatrix.kt (83%) rename kmath-commons/src/main/kotlin/scientifik/kmath/{ => commons}/linear/CMSolver.kt (94%) create mode 100644 kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CMRandomGeneratorWrapper.kt create mode 100644 kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CommonsDistribution.kt rename kmath-commons/src/main/kotlin/scientifik/kmath/{ => commons}/transform/Transformations.kt (97%) rename kmath-commons/src/test/kotlin/scientifik/kmath/{ => commons}/expressions/AutoDiffTest.kt (78%) create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt delete mode 100644 kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/CommonDistributions.kt delete mode 100644 kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/CommonsRandomProviderWrapper.kt diff --git a/benchmarks/build.gradle b/examples/build.gradle similarity index 100% rename from benchmarks/build.gradle rename to examples/build.gradle diff --git a/benchmarks/src/jmh/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt b/examples/src/jmh/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt similarity index 100% rename from benchmarks/src/jmh/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt rename to examples/src/jmh/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt diff --git a/benchmarks/src/jmh/kotlin/scientifik/kmath/structures/BufferBenchmark.kt b/examples/src/jmh/kotlin/scientifik/kmath/structures/BufferBenchmark.kt similarity index 100% rename from benchmarks/src/jmh/kotlin/scientifik/kmath/structures/BufferBenchmark.kt rename to examples/src/jmh/kotlin/scientifik/kmath/structures/BufferBenchmark.kt diff --git a/benchmarks/src/jmh/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt b/examples/src/jmh/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt similarity index 100% rename from benchmarks/src/jmh/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt rename to examples/src/jmh/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt 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/benchmarks/src/main/kotlin/scientifik/kmath/structures/ComplexNDBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/structures/ComplexNDBenchmark.kt similarity index 100% rename from benchmarks/src/main/kotlin/scientifik/kmath/structures/ComplexNDBenchmark.kt rename to examples/src/main/kotlin/scientifik/kmath/structures/ComplexNDBenchmark.kt diff --git a/benchmarks/src/main/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt similarity index 100% rename from benchmarks/src/main/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt rename to examples/src/main/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt 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..8267f8c73 --- /dev/null +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CMRandomGeneratorWrapper.kt @@ -0,0 +1,18 @@ +package scientifik.kmath.commons.prob + +import org.apache.commons.math3.random.RandomGenerator + +inline class CMRandomGeneratorWrapper(val generator: RandomGenerator) : scientifik.kmath.prob.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 RandomGenerator.asKmathGenerator() = CMRandomGeneratorWrapper(this) + +fun scientifik.kmath.prob.RandomGenerator.asCMGenerator() = + (this as? CMRandomGeneratorWrapper)?.generator ?: TODO("Implement reverse CM wrapper") \ 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..51de07975 --- /dev/null +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CommonsDistribution.kt @@ -0,0 +1,84 @@ +package scientifik.kmath.commons.prob + +import org.apache.commons.math3.distribution.* +import scientifik.kmath.chains.Chain +import scientifik.kmath.chains.SimpleChain +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, sigma: Double): 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-prob/build.gradle.kts b/kmath-prob/build.gradle.kts index 77a4aefc8..972b9bba0 100644 --- a/kmath-prob/build.gradle.kts +++ b/kmath-prob/build.gradle.kts @@ -13,7 +13,7 @@ kotlin.sourceSets { jvmMain { dependencies { // https://mvnrepository.com/artifact/org.apache.commons/commons-rng-simple - api("org.apache.commons:commons-rng-sampling:1.2") + //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 index 946b173ff..a8614963b 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt @@ -16,8 +16,13 @@ interface Distribution { * Create a chain of samples from this distribution. * The chain is not guaranteed to be stateless. */ - fun sample(generator: RandomGenerator): Chain + 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 { @@ -25,4 +30,4 @@ interface UnivariateDistribution> : Distribution { * Cumulative distribution for ordered parameter */ fun cumulative(arg: T): Double -} \ No newline at end of file +} 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/jvmMain/kotlin/scientifik/kmath/prob/CommonDistributions.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/CommonDistributions.kt deleted file mode 100644 index c08008c28..000000000 --- a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/CommonDistributions.kt +++ /dev/null @@ -1,53 +0,0 @@ -package scientifik.kmath.prob - -import org.apache.commons.rng.sampling.distribution.* -import scientifik.kmath.chains.Chain -import scientifik.kmath.chains.SimpleChain -import kotlin.math.PI -import kotlin.math.exp -import kotlin.math.sqrt - -class NormalDistribution(val mean: Double, val sigma: Double) : UnivariateDistribution { - enum class Sampler { - BoxMuller, - Marsaglia, - Ziggurat - } - - override fun probability(arg: Double): Double { - val d = (arg - mean) / sigma - return 1.0 / sqrt(2.0 * PI * sigma) * exp(-d * d / 2) - } - - override fun cumulative(arg: Double): Double { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - fun sample(generator: RandomGenerator, sampler: Sampler): Chain { - val normalized = when (sampler) { - Sampler.BoxMuller -> BoxMullerNormalizedGaussianSampler(generator.asProvider()) - Sampler.Marsaglia -> MarsagliaNormalizedGaussianSampler(generator.asProvider()) - Sampler.Ziggurat -> ZigguratNormalizedGaussianSampler(generator.asProvider()) - } - val gauss = GaussianSampler(normalized, mean, sigma) - //TODO add generator to chain state to allow stateful forks - return SimpleChain { gauss.sample() } - } - - override fun sample(generator: RandomGenerator): Chain = sample(generator, Sampler.BoxMuller) -} - -class PoissonDistribution(val mean: Double): UnivariateDistribution{ - override fun probability(arg: Int): Double { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun cumulative(arg: Int): Double { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun sample(generator: RandomGenerator): Chain { - val sampler = PoissonSampler(generator.asProvider(), mean) - return SimpleChain{sampler.sample()} - } -} \ No newline at end of file diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/CommonsRandomProviderWrapper.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/CommonsRandomProviderWrapper.kt deleted file mode 100644 index ad408460d..000000000 --- a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/CommonsRandomProviderWrapper.kt +++ /dev/null @@ -1,18 +0,0 @@ -package scientifik.kmath.prob - -import org.apache.commons.rng.UniformRandomProvider - -inline class CommonsRandomProviderWrapper(val provider: UniformRandomProvider) : RandomGenerator { - override fun nextDouble(): Double = provider.nextDouble() - - override fun nextInt(): Int = provider.nextInt() - - override fun nextLong(): Long = provider.nextLong() - - override fun nextBlock(size: Int): ByteArray = ByteArray(size).also { provider.nextBytes(it) } -} - -fun UniformRandomProvider.asGenerator(): RandomGenerator = CommonsRandomProviderWrapper(this) - -fun RandomGenerator.asProvider(): UniformRandomProvider = - (this as? CommonsRandomProviderWrapper)?.provider ?: TODO("implement reverse wrapper") \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 5adf75fa5..efb1bf0d7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -28,5 +28,5 @@ include( ":kmath-commons", ":kmath-koma", ":kmath-prob", - ":benchmarks" + ":examples" ) From f480aa1c5ca1e23dd811f10990ab832554ee1e56 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 8 Jun 2019 16:25:51 +0300 Subject: [PATCH 3/5] Example fix --- build.gradle.kts | 2 +- examples/build.gradle | 39 ----------- examples/build.gradle.kts | 67 +++++++++++++++++++ .../kmath/structures/ArrayBenchmark.kt | 6 +- .../kmath/structures/BufferBenchmark.kt | 2 +- .../kmath/structures/NDFieldBenchmark.kt | 5 +- .../kmath/commons/prob/DistributionDemo.kt | 11 +++ .../kmath/operations/ComplexDemo.kt | 10 +++ .../{ComplexNDBenchmark.kt => ComplexND.kt} | 0 .../kmath/structures/ComplexNDField.kt | 5 +- .../scientifik/kmath/structures/NDAlgebra.kt | 2 + settings.gradle.kts | 1 + 12 files changed, 104 insertions(+), 46 deletions(-) delete mode 100644 examples/build.gradle create mode 100644 examples/build.gradle.kts rename examples/src/{jmh => benchmarks}/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt (95%) rename examples/src/{jmh => benchmarks}/kotlin/scientifik/kmath/structures/BufferBenchmark.kt (96%) rename examples/src/{jmh => benchmarks}/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt (89%) create mode 100644 examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt create mode 100644 examples/src/main/kotlin/scientifik/kmath/operations/ComplexDemo.kt rename examples/src/main/kotlin/scientifik/kmath/structures/{ComplexNDBenchmark.kt => ComplexND.kt} (100%) diff --git a/build.gradle.kts b/build.gradle.kts index 2fe3900ed..f3ea58b8e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ allprojects { jcenter() maven("https://kotlin.bintray.com/kotlinx") } - + group = "scientifik" version = kmathVersion } diff --git a/examples/build.gradle b/examples/build.gradle deleted file mode 100644 index 7c948e219..000000000 --- a/examples/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/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/examples/src/jmh/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt similarity index 95% rename from examples/src/jmh/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt rename to examples/src/benchmarks/kotlin/scientifik/kmath/structures/ArrayBenchmark.kt index 393d7b06a..d605e1b9c 100644 --- a/examples/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/examples/src/jmh/kotlin/scientifik/kmath/structures/BufferBenchmark.kt b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/BufferBenchmark.kt similarity index 96% rename from examples/src/jmh/kotlin/scientifik/kmath/structures/BufferBenchmark.kt rename to examples/src/benchmarks/kotlin/scientifik/kmath/structures/BufferBenchmark.kt index a41ca83d3..9676b5e4a 100644 --- a/examples/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/examples/src/jmh/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt similarity index 89% rename from examples/src/jmh/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt rename to examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt index 3e41323f1..9c2434bda 100644 --- a/examples/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() { 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..1db7643a0 --- /dev/null +++ b/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt @@ -0,0 +1,11 @@ +package scientifik.kmath.commons.prob + +import kotlinx.coroutines.flow.take +import scientifik.kmath.chains.flow +import scientifik.kmath.prob.Distribution +import scientifik.kmath.prob.RandomGenerator + +fun main() { + val normal = Distribution.normal() + normal.sample(RandomGenerator.default) +} \ No newline at end of file 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/examples/src/main/kotlin/scientifik/kmath/structures/ComplexNDBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt similarity index 100% rename from examples/src/main/kotlin/scientifik/kmath/structures/ComplexNDBenchmark.kt rename to examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt 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..9857e4150 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt @@ -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 } /** diff --git a/settings.gradle.kts b/settings.gradle.kts index efb1bf0d7..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 { From cd1c155e7d896a637aedbdb3ae39b751b83c6d5a Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 8 Jun 2019 16:30:06 +0300 Subject: [PATCH 4/5] Examples fix --- doc/nd-structure.md | 15 +++++---------- .../{NDFieldBenchmark.kt => NDField.kt} | 0 2 files changed, 5 insertions(+), 10 deletions(-) rename examples/src/main/kotlin/scientifik/kmath/structures/{NDFieldBenchmark.kt => NDField.kt} (100%) diff --git a/doc/nd-structure.md b/doc/nd-structure.md index b61609870..0b94bbdf8 100644 --- a/doc/nd-structure.md +++ b/doc/nd-structure.md @@ -9,15 +9,10 @@ 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) ``` @@ -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/src/main/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt similarity index 100% rename from examples/src/main/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt rename to examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt From 1b647b60142f630342fe7ea61be3231d77a07b48 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 8 Jun 2019 16:44:49 +0300 Subject: [PATCH 5/5] Random demo --- build.gradle.kts | 2 +- doc/nd-structure.md | 2 +- .../kmath/structures/NDFieldBenchmark.kt | 2 +- .../kmath/commons/prob/DistributionDemo.kt | 26 ++++++++++++++++--- .../scientifik/kmath/structures/NDField.kt | 2 +- .../commons/prob/CMRandomGeneratorWrapper.kt | 20 ++++++++++---- .../kmath/commons/prob/CommonsDistribution.kt | 4 +-- .../scientifik/kmath/structures/NDAlgebra.kt | 11 +++++--- .../scientifik/kmath/prob/RandomGenerator.kt | 2 ++ 9 files changed, 52 insertions(+), 19 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index f3ea58b8e..d7b7ac78a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,4 @@ -val kmathVersion by extra("0.1.2") +val kmathVersion by extra("0.1.3-dev-1") allprojects { repositories { diff --git a/doc/nd-structure.md b/doc/nd-structure.md index 0b94bbdf8..cf13c6a29 100644 --- a/doc/nd-structure.md +++ b/doc/nd-structure.md @@ -14,7 +14,7 @@ Let us consider following contexts: // 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: diff --git a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt index 9c2434bda..46991b2de 100644 --- a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt +++ b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt @@ -53,6 +53,6 @@ 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 index 1db7643a0..831a167bf 100644 --- a/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt +++ b/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt @@ -1,11 +1,31 @@ package scientifik.kmath.commons.prob -import kotlinx.coroutines.flow.take -import scientifik.kmath.chains.flow +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() - normal.sample(RandomGenerator.default) + 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/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt b/examples/src/main/kotlin/scientifik/kmath/structures/NDField.kt index 53a23c2bc..10fad3ecb 100644 --- a/examples/src/main/kotlin/scientifik/kmath/structures/NDField.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/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CMRandomGeneratorWrapper.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CMRandomGeneratorWrapper.kt index 8267f8c73..a32626e0d 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CMRandomGeneratorWrapper.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CMRandomGeneratorWrapper.kt @@ -1,8 +1,10 @@ package scientifik.kmath.commons.prob -import org.apache.commons.math3.random.RandomGenerator +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: RandomGenerator) : scientifik.kmath.prob.RandomGenerator { +inline class CMRandomGeneratorWrapper(val generator: CMRandom) : RandomGenerator { override fun nextDouble(): Double = generator.nextDouble() override fun nextInt(): Int = generator.nextInt() @@ -12,7 +14,15 @@ inline class CMRandomGeneratorWrapper(val generator: RandomGenerator) : scientif override fun nextBlock(size: Int): ByteArray = ByteArray(size).apply { generator.nextBytes(this) } } -fun RandomGenerator.asKmathGenerator() = CMRandomGeneratorWrapper(this) +fun CMRandom.asKmathGenerator(): RandomGenerator = CMRandomGeneratorWrapper(this) -fun scientifik.kmath.prob.RandomGenerator.asCMGenerator() = - (this as? CMRandomGeneratorWrapper)?.generator ?: TODO("Implement reverse CM wrapper") \ No newline at end of file +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 index 51de07975..94f8560a4 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CommonsDistribution.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/prob/CommonsDistribution.kt @@ -1,8 +1,6 @@ package scientifik.kmath.commons.prob import org.apache.commons.math3.distribution.* -import scientifik.kmath.chains.Chain -import scientifik.kmath.chains.SimpleChain import scientifik.kmath.prob.Distribution import scientifik.kmath.prob.RandomChain import scientifik.kmath.prob.RandomGenerator @@ -38,7 +36,7 @@ class CMIntDistributionWrapper(val builder: (CMRandom?) -> IntegerDistribution) } -fun Distribution.Companion.normal(mean: Double, sigma: Double): UnivariateDistribution = +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 -> 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 9857e4150..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 /** @@ -77,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) } @@ -95,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) } } @@ -115,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 { @@ -130,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. @@ -143,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-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt index 89cbc615c..039ee3d2b 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt @@ -8,4 +8,6 @@ interface RandomGenerator { fun nextInt(): Int fun nextLong(): Long fun nextBlock(size: Int): ByteArray + + companion object } \ No newline at end of file