From 2e7607371222ea5361f12d0b19a052e4b2099571 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 7 Jun 2019 15:08:05 +0300 Subject: [PATCH 01/13] 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 02/13] 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 03/13] 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 04/13] 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 05/13] 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 From bf171a2371650f98f32b41569f6e738bdec8279d Mon Sep 17 00:00:00 2001 From: Peter Klimai Date: Sat, 8 Jun 2019 16:52:42 +0300 Subject: [PATCH 06/13] minor doc typos fix --- README.md | 8 ++++---- doc/algebra.md | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c828103e9..c54ea747a 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Actual feature list is [here](doc/features.md) * Complex numbers backed by the `Field` API (meaning that they will be usable in any structure like vectors and N-dimensional arrays). * Advanced linear algebra operations like matrix inversion and LU decomposition. -* **Array-like structures** Full support of many-dimenstional array-like structures +* **Array-like structures** Full support of many-dimensional array-like structures including mixed arithmetic operations and function operations over arrays and numbers (with the added benefit of static type checking). * **Expressions** By writing a single mathematical expression @@ -22,13 +22,13 @@ can be used for a wide variety of purposes from high performance calculations to * **Histograms** Fast multi-dimensional histograms. -* **Streaming** Streaming operations on mathematica objects and objects buffers. +* **Streaming** Streaming operations on mathematical objects and objects buffers. * **Commons-math wrapper** It is planned to gradually wrap most parts of [Apache commons-math](http://commons.apache.org/proper/commons-math/) library in Kotlin code and maybe rewrite some parts to better suit the Kotlin programming paradigm, however there is no fixed roadmap for that. Feel free to submit a feature request if you want something to be done first. -* **Koma wrapper** [Koma](https://github.com/kyonifer/koma) is a well established numerics library in kotlin, specifically linear algebra. +* **Koma wrapper** [Koma](https://github.com/kyonifer/koma) is a well established numerics library in Kotlin, specifically linear algebra. The plan is to have wrappers for koma implementations for compatibility with kmath API. ## Planned features @@ -110,4 +110,4 @@ dependencies{ ## Contributing -The project requires a lot of additional work. Please fill free to contribute in any way and propose new features. +The project requires a lot of additional work. Please feel free to contribute in any way and propose new features. diff --git a/doc/algebra.md b/doc/algebra.md index 437fc3694..015f4fc82 100644 --- a/doc/algebra.md +++ b/doc/algebra.md @@ -32,12 +32,12 @@ Typical case of `Field` is the `RealField` which works on doubles. And typical c In some cases algebra context could hold additional operation like `exp` or `sin`, in this case it inherits appropriate interface. Also a context could have an operation which produces an element outside of its context. For example -`Matrix` `dot` operation produces a matrix with new dimensions which could not be compatible with initial matrix in +`Matrix` `dot` operation produces a matrix with new dimensions which can be incompatible with initial matrix in terms of linear operations. ## Algebra element -In order to achieve more familiar behavior (where you apply operations directly to mathematica objects), without involving contexts +In order to achieve more familiar behavior (where you apply operations directly to mathematical objects), without involving contexts `kmath` introduces special type objects called `MathElement`. A `MathElement` is basically some object coupled to a mathematical context. For example `Complex` is the pair of real numbers representing real and imaginary parts, but it also holds reference to the `ComplexField` singleton which allows to perform direct operations on `Complex` From fe05fc5a140f9e1878c757dd0bcaec8f97d8609b Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 9 Jun 2019 10:06:48 +0300 Subject: [PATCH 07/13] Added chain mappers --- .../kmath/commons/prob/DistributionDemo.kt | 10 +-- .../kotlin/scientifik/kmath/chains/Chain.kt | 90 +++++++++---------- .../scientifik/kmath/chains/ChainExt.kt | 20 ----- 3 files changed, 46 insertions(+), 74 deletions(-) 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 831a167bf..401dde780 100644 --- a/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt +++ b/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt @@ -2,17 +2,17 @@ package scientifik.kmath.commons.prob import kotlinx.coroutines.runBlocking import scientifik.kmath.chains.Chain -import scientifik.kmath.chains.StatefulChain +import scientifik.kmath.chains.mapWithState 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() +fun Chain.mean(): Chain = mapWithState(AveragingChainState(),{it.copy()}){chain-> + val next = chain.next() num++ value += next - return@StatefulChain value / num + return@mapWithState value / num } @@ -23,7 +23,7 @@ fun main() { runBlocking { repeat(10001) { counter -> val mean = chain.next() - if(counter % 1000 ==0){ + if (counter % 1000 == 0) { println("[$counter] Average value is $mean") } } 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 4adc2a9a7..58841e7b8 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt @@ -17,6 +17,7 @@ package scientifik.kmath.chains import kotlinx.atomicfu.atomic +import kotlinx.atomicfu.updateAndGet import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow @@ -26,11 +27,6 @@ import kotlinx.coroutines.flow.Flow * @param R - the chain element type */ interface Chain { - /** - * Last cached value of the chain. Returns null if [next] was not called - */ - fun peek(): R? - /** * Generate next value, changing state if needed */ @@ -53,84 +49,59 @@ val Chain.flow: Flow 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 - */ -fun Chain.map(func: (T) -> R): Chain { - val parent = this; - return object : Chain { - override fun peek(): R? = parent.peek()?.let(func) - - override suspend fun next(): R { - return func(parent.next()) - } - - override fun fork(): Chain { - return parent.fork().map(func) - } - } -} - /** * A simple chain of independent tokens */ class SimpleChain(private val gen: suspend () -> R) : Chain { - private val atomicValue = atomic(null) - override fun peek(): R? = atomicValue.value - - override suspend fun next(): R = gen().also { atomicValue.lazySet(it) } - + override suspend fun next(): R = gen() override fun fork(): Chain = this } -//TODO force forks on mapping operations? - /** * A stateless Markov chain */ -class MarkovChain(private val seed: () -> R, private val gen: suspend (R) -> R) : - Chain { +class MarkovChain(private val seed: suspend () -> R, private val gen: suspend (R) -> R) : Chain { - constructor(seed: R, gen: suspend (R) -> R) : this({ seed }, gen) + constructor(seedValue: R, gen: suspend (R) -> R) : this({ seedValue }, gen) - private val atomicValue = atomic(null) - override fun peek(): R = atomicValue.value ?: seed() + private val value = atomic(null) override suspend fun next(): R { - val newValue = gen(peek()) - atomicValue.lazySet(newValue) - return peek() + return value.updateAndGet { prev -> gen(prev ?: seed()) }!! } override fun fork(): Chain { - return MarkovChain(peek(), gen) + return MarkovChain(seed = { value.value ?: seed() }, gen = gen) } } /** * A chain with possibly mutable state. The state must not be changed outside the chain. Two chins should never share the state * @param S - the state of the chain + * @param forkState - the function to copy current state without modifying it */ class StatefulChain( private val state: S, private val seed: S.() -> R, + private val forkState: ((S) -> S), private val gen: suspend S.(R) -> R ) : Chain { - constructor(state: S, seed: R, gen: suspend S.(R) -> R) : this(state, { seed }, gen) + constructor(state: S, seedValue: R, forkState: ((S) -> S), gen: suspend S.(R) -> R) : this( + state, + { seedValue }, + forkState, + gen + ) private val atomicValue = atomic(null) - override fun peek(): R = atomicValue.value ?: seed(state) override suspend fun next(): R { - val newValue = gen(state, peek()) - atomicValue.lazySet(newValue) - return peek() + return atomicValue.updateAndGet { prev -> state.gen(prev ?: state.seed()) }!! } override fun fork(): Chain { - throw RuntimeException("Fork not supported for stateful chain") + return StatefulChain(forkState(state), seed, forkState, gen) } } @@ -138,11 +109,32 @@ class StatefulChain( * A chain that repeats the same value */ class ConstantChain(val value: T) : Chain { - override fun peek(): T? = value - override suspend fun next(): T = value override fun fork(): Chain { return this } -} \ No newline at end of file +} + +/** + * Map the chain result using suspended transformation. Initial chain result can no longer be safely consumed + * since mapped chain consumes tokens. Accepts regular transformation function + */ +fun Chain.pipe(func: suspend (T) -> R): Chain = object : Chain { + override suspend fun next(): R = func(this@pipe.next()) + override fun fork(): Chain = this@pipe.fork().pipe(func) +} + +/** + * Map the whole chain + */ +fun Chain.map(mapper: suspend (Chain) -> R): Chain = object : Chain { + override suspend fun next(): R = mapper(this@map) + override fun fork(): Chain = this@map.fork().map(mapper) +} + +fun Chain.mapWithState(state: S, stateFork: (S) -> S, mapper: suspend S.(Chain) -> R): Chain = + object : Chain { + override suspend fun next(): R = state.mapper(this@mapWithState) + override fun fork(): Chain = this@mapWithState.fork().mapWithState(stateFork(state), stateFork, mapper) + } \ No newline at end of file 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 b634c5ae2..013ea2922 100644 --- a/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt +++ b/kmath-coroutines/src/jvmMain/kotlin/scientifik/kmath/chains/ChainExt.kt @@ -17,24 +17,4 @@ operator fun Chain.iterator() = object : Iterator { */ fun Chain.asSequence(): Sequence = object : Sequence { override fun iterator(): Iterator = this@asSequence.iterator() -} - - -/** - * Map the chain result using suspended transformation. Initial chain result can no longer be safely consumed - * since mapped chain consumes tokens. Accepts suspending transformation function. - */ -fun Chain.map(func: suspend (T) -> R): Chain { - val parent = this; - return object : Chain { - override fun peek(): R? = runBlocking { parent.peek()?.let { func(it) } } - - override suspend fun next(): R { - return func(parent.next()) - } - - override fun fork(): Chain { - return parent.fork().map(func) - } - } } \ No newline at end of file From 88ee3e5ab789cab5f5bd44ce91a6a494481d9bae Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 9 Jun 2019 10:54:45 +0300 Subject: [PATCH 08/13] Random generator forks --- README.md | 1 + .../commons/expressions/DiffExpression.kt | 7 ++-- .../commons/prob/CMRandomGeneratorWrapper.kt | 4 ++ .../kmath/commons/expressions/AutoDiffTest.kt | 4 +- .../scientifik/kmath/linear/BufferMatrix.kt | 1 + .../scientifik/kmath/prob/Distribution.kt | 41 +++++++++++++++++-- .../scientifik/kmath/prob/RandomChain.kt | 3 +- .../scientifik/kmath/prob/RandomGenerator.kt | 5 +++ 8 files changed, 54 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index c828103e9..4f9ca77b4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ Bintray: [ ![Download](https://api.bintray.com/packages/mipt-npm/scientifik/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/scientifik/kmath-core/_latestVersion) # KMath +Could be pronounced as `key-math`. The Kotlin MATHematics library is intended as a Kotlin-based analog to Python's `numpy` library. In contrast to `numpy` and `scipy` it is modular and has a lightweight core. ## Features diff --git a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt index e687cb2af..d5c038dc4 100644 --- a/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt +++ b/kmath-commons/src/main/kotlin/scientifik/kmath/commons/expressions/DiffExpression.kt @@ -83,13 +83,12 @@ class DerivativeStructureField( /** * A constructs that creates a derivative structure with required order on-demand */ -class DiffExpression(val function: DerivativeStructureField.() -> DerivativeStructure) : - Expression { +class DiffExpression(val function: DerivativeStructureField.() -> DerivativeStructure) : Expression { + override fun invoke(arguments: Map): Double = DerivativeStructureField( 0, arguments - ) - .run(function).value + ).run(function).value /** * Get the derivative expression with given orders 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 a32626e0d..74e035ecb 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 @@ -12,6 +12,10 @@ inline class CMRandomGeneratorWrapper(val generator: CMRandom) : RandomGenerator override fun nextLong(): Long = generator.nextLong() override fun nextBlock(size: Int): ByteArray = ByteArray(size).apply { generator.nextBytes(this) } + + override fun fork(): RandomGenerator { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } } fun CMRandom.asKmathGenerator(): RandomGenerator = CMRandomGeneratorWrapper(this) diff --git a/kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt b/kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt index 37e4e9ff9..100f49948 100644 --- a/kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt +++ b/kmath-commons/src/test/kotlin/scientifik/kmath/commons/expressions/AutoDiffTest.kt @@ -1,9 +1,7 @@ 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 scientifik.kmath.expressions.invoke 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/linear/BufferMatrix.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt index 34d9a6883..624cd44d4 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/linear/BufferMatrix.kt @@ -24,6 +24,7 @@ class BufferMatrixContext>( } } +@Suppress("OVERRIDE_BY_INLINE") object RealMatrixContext : GenericMatrixContext { override val elementContext = RealField 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 a8614963b..d3cac9163 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt @@ -1,11 +1,17 @@ package scientifik.kmath.prob import scientifik.kmath.chains.Chain +import scientifik.kmath.chains.map +import kotlin.jvm.JvmName + +interface Sampler { + fun sample(generator: RandomGenerator): RandomChain +} /** * A distribution of typed objects */ -interface Distribution { +interface Distribution : Sampler { /** * A probability value for given argument [arg]. * For continuous distributions returns PDF @@ -16,8 +22,7 @@ interface Distribution { * 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 + override fun sample(generator: RandomGenerator): RandomChain /** * An empty companion. Distribution factories should be written as its extensions @@ -31,3 +36,33 @@ interface UnivariateDistribution> : Distribution { */ fun cumulative(arg: T): Double } + +/** + * Compute probability integral in an interval + */ +fun > UnivariateDistribution.integral(from: T, to: T): Double { + require(to > from) + return cumulative(to) - cumulative(from) +} + + +/** + * Sample a bunch of values + */ +fun Sampler.sampleBunch(generator: RandomGenerator, size: Int): Chain> { + require(size > 1) + return sample(generator).map{chain -> + List(size){chain.next()} + } +} + +/** + * Generate a bunch of samples from real distributions + */ +@JvmName("realSampleBunch") +fun Sampler.sampleBunch(generator: RandomGenerator, size: Int): Chain { + require(size > 1) + return sample(generator).map{chain -> + DoubleArray(size){chain.next()} + } +} \ 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 index 792b60603..61e0dae4e 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt @@ -9,9 +9,8 @@ import scientifik.kmath.chains.Chain */ 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) + override fun fork(): Chain = RandomChain(generator.fork(), 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 index 039ee3d2b..381bd9736 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt @@ -9,5 +9,10 @@ interface RandomGenerator { fun nextLong(): Long fun nextBlock(size: Int): ByteArray + /** + * Fork the current state of generator + */ + fun fork(): RandomGenerator + companion object } \ No newline at end of file From e077da39c81110ef213f897b3e2c9e169976d474 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 9 Jun 2019 17:17:13 +0300 Subject: [PATCH 09/13] Sampler space --- .../kotlin/scientifik/kmath/chains/Chain.kt | 13 +++++++- .../scientifik/kmath/prob/Distribution.kt | 4 +-- .../scientifik/kmath/prob/SamplerAlgebra.kt | 31 +++++++++++++++++++ 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.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 58841e7b8..317cdcea0 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt @@ -37,6 +37,8 @@ interface Chain { */ fun fork(): Chain + companion object + } /** @@ -137,4 +139,13 @@ fun Chain.mapWithState(state: S, stateFork: (S) -> S, mapper: suspe object : Chain { override suspend fun next(): R = state.mapper(this@mapWithState) override fun fork(): Chain = this@mapWithState.fork().mapWithState(stateFork(state), stateFork, mapper) - } \ No newline at end of file + } + +/** + * Zip two chains together using given transformation + */ +fun Chain.zip(other: Chain, block: suspend (T, U) -> R): Chain = object : Chain { + override suspend fun next(): R = block(this@zip.next(), other.next()) + + override fun fork(): Chain = this@zip.fork().zip(other.fork(), block) +} \ No newline at end of file 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 d3cac9163..487e13876 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt @@ -5,7 +5,7 @@ import scientifik.kmath.chains.map import kotlin.jvm.JvmName interface Sampler { - fun sample(generator: RandomGenerator): RandomChain + fun sample(generator: RandomGenerator): Chain } /** @@ -22,7 +22,7 @@ interface Distribution : Sampler { * Create a chain of samples from this distribution. * The chain is not guaranteed to be stateless. */ - override fun sample(generator: RandomGenerator): RandomChain + override fun sample(generator: RandomGenerator): Chain /** * An empty companion. Distribution factories should be written as its extensions diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt new file mode 100644 index 000000000..ca9d74aab --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/SamplerAlgebra.kt @@ -0,0 +1,31 @@ +package scientifik.kmath.prob + +import scientifik.kmath.chains.Chain +import scientifik.kmath.chains.ConstantChain +import scientifik.kmath.chains.pipe +import scientifik.kmath.chains.zip +import scientifik.kmath.operations.Space + +class BasicSampler(val chainBuilder: (RandomGenerator) -> Chain) : Sampler { + override fun sample(generator: RandomGenerator): Chain = chainBuilder(generator) +} + +class ConstantSampler(val value: T) : Sampler { + override fun sample(generator: RandomGenerator): Chain = ConstantChain(value) +} + +/** + * A space for samplers. Allows to perform simple operations on distributions + */ +class SamplerSpace(val space: Space) : Space> { + + override val zero: Sampler = ConstantSampler(space.zero) + + override fun add(a: Sampler, b: Sampler): Sampler = BasicSampler { generator -> + a.sample(generator).zip(b.sample(generator)) { aValue, bValue -> space.run { aValue + bValue } } + } + + override fun multiply(a: Sampler, k: Number): Sampler = BasicSampler { generator -> + a.sample(generator).pipe { space.run { it * k.toDouble() } } + } +} \ No newline at end of file From 651f8323f3f5c809d5b12d84bb6065fa32ce0cc7 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 9 Jun 2019 22:03:35 +0300 Subject: [PATCH 10/13] Basic support for BigInteger (#69) --- .../kmath/structures/NDFieldBenchmark.kt | 2 +- .../scientifik/kmath/structures/NDField.kt | 2 +- .../kmath/structures/BoxingNDRing.kt | 72 +++++++++++++++++++ .../scientifik/kmath/structures/NDAlgebra.kt | 6 +- .../scientifik/kmath/structures/NDElement.kt | 2 +- .../scientifik/kmath/operations/BigNumers.kt | 45 ++++++++++++ 6 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt create mode 100644 kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumers.kt diff --git a/examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt b/examples/src/benchmarks/kotlin/scientifik/kmath/structures/NDFieldBenchmark.kt index 46991b2de..ae27620f7 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(RealField, dim, dim) + val genericField = NDField.boxing(RealField, dim, dim) } } \ 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 10fad3ecb..7e8c433c0 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(RealField, dim, dim) + val genericField = NDField.boxing(RealField, dim, dim) val autoTime = measureTimeMillis { diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt new file mode 100644 index 000000000..39fc555e8 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/BoxingNDRing.kt @@ -0,0 +1,72 @@ +package scientifik.kmath.structures + +import scientifik.kmath.operations.Ring +import scientifik.kmath.operations.RingElement + +class BoxingNDRing>( + override val shape: IntArray, + override val elementContext: R, + val bufferFactory: BufferFactory +) : BufferedNDRing { + + override val strides: Strides = DefaultStrides(shape) + + fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer = + bufferFactory(size, initializer) + + override fun check(vararg elements: NDBuffer) { + if (!elements.all { it.strides == this.strides }) error("Element strides are not the same as context strides") + } + + override val zero by lazy { produce { zero } } + override val one by lazy { produce { one } } + + override fun produce(initializer: R.(IntArray) -> T) = + BufferedNDRingElement( + this, + buildBuffer(strides.linearSize) { offset -> elementContext.initializer(strides.index(offset)) }) + + override fun map(arg: NDBuffer, transform: R.(T) -> T): BufferedNDRingElement { + check(arg) + return BufferedNDRingElement( + this, + buildBuffer(arg.strides.linearSize) { offset -> elementContext.transform(arg.buffer[offset]) }) + +// val buffer = arg.buffer.transform { _, value -> elementContext.transform(value) } +// return BufferedNDFieldElement(this, buffer) + + } + + override fun mapIndexed( + arg: NDBuffer, + transform: R.(index: IntArray, T) -> T + ): BufferedNDRingElement { + check(arg) + return BufferedNDRingElement( + this, + buildBuffer(arg.strides.linearSize) { offset -> + elementContext.transform( + arg.strides.index(offset), + arg.buffer[offset] + ) + }) + +// val buffer = +// arg.buffer.transform { offset, value -> elementContext.transform(arg.strides.index(offset), value) } +// return BufferedNDFieldElement(this, buffer) + } + + override fun combine( + a: NDBuffer, + b: NDBuffer, + transform: R.(T, T) -> T + ): BufferedNDRingElement { + check(a, b) + return BufferedNDRingElement( + this, + buildBuffer(strides.linearSize) { offset -> elementContext.transform(a.buffer[offset], b.buffer[offset]) }) + } + + override fun NDBuffer.toElement(): RingElement, *, out BufferedNDRing> = + BufferedNDRingElement(this@BoxingNDRing, buffer) +} \ 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 352c05467..c826565cf 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDAlgebra.kt @@ -82,6 +82,8 @@ interface NDSpace, N : NDStructure> : Space, NDAlgebra add(this@plus, value) } operator fun T.minus(arg: N) = map(arg) { value -> add(-this@minus, value) } + + companion object } /** @@ -98,6 +100,8 @@ interface NDRing, N : NDStructure> : Ring, NDSpace 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) } + + companion object } /** @@ -132,7 +136,7 @@ interface NDField, N : NDStructure> : Field, NDRing> buffered( + fun > boxing( field: F, vararg shape: Int, bufferFactory: BufferFactory = Buffer.Companion::boxing diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt index 59cf98df3..a18a03364 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/NDElement.kt @@ -41,7 +41,7 @@ interface NDElement> : NDStructure { /** * Simple boxing NDArray */ - fun > buffered( + fun > boxing( shape: IntArray, field: F, initializer: F.(IntArray) -> T diff --git a/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumers.kt b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumers.kt new file mode 100644 index 000000000..f44d15042 --- /dev/null +++ b/kmath-core/src/jvmMain/kotlin/scientifik/kmath/operations/BigNumers.kt @@ -0,0 +1,45 @@ +package scientifik.kmath.operations + +import scientifik.kmath.structures.* +import java.math.BigDecimal +import java.math.BigInteger +import java.math.MathContext + +object BigIntegerRing : Ring { + override val zero: BigInteger = BigInteger.ZERO + override val one: BigInteger = BigInteger.ONE + + override fun add(a: BigInteger, b: BigInteger): BigInteger = a.add(b) + + override fun multiply(a: BigInteger, k: Number): BigInteger = a.multiply(k.toInt().toBigInteger()) + + override fun multiply(a: BigInteger, b: BigInteger): BigInteger = a.multiply(b) +} + +class BigDecimalField(val mathContext: MathContext = MathContext.DECIMAL64) : Field { + override val zero: BigDecimal = BigDecimal.ZERO + override val one: BigDecimal = BigDecimal.ONE + + override fun add(a: BigDecimal, b: BigDecimal): BigDecimal = a.add(b) + + override fun multiply(a: BigDecimal, k: Number): BigDecimal = + a.multiply(k.toDouble().toBigDecimal(mathContext), mathContext) + + override fun multiply(a: BigDecimal, b: BigDecimal): BigDecimal = a.multiply(b, mathContext) + override fun divide(a: BigDecimal, b: BigDecimal): BigDecimal = a.divide(b, mathContext) +} + +inline fun Buffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInteger): Buffer = + boxing(size, initializer) + +inline fun MutableBuffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInteger): MutableBuffer = + boxing(size, initializer) + +fun NDAlgebra.Companion.bigInt(vararg shape: Int): BoxingNDRing = + BoxingNDRing(shape, BigIntegerRing, Buffer.Companion::bigInt) + +fun NDElement.Companion.bigInt( + vararg shape: Int, + initializer: BigIntegerRing.(IntArray) -> BigInteger +): BufferedNDRingElement = + NDAlgebra.bigInt(*shape).produce(initializer) \ No newline at end of file From 5a8a45cf6ad288cac258e5de723587284ac88f85 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 18 Jun 2019 10:22:47 +0300 Subject: [PATCH 11/13] minor update --- .../kmath/expressions/Expression.kt | 29 ++++++++++--------- kmath-histograms/build.gradle.kts | 1 - .../scientifik/kmath/prob/RandomChain.kt | 5 +--- .../scientifik/kmath/prob/RandomGenerator.kt | 27 +++++++++++++++-- 4 files changed, 42 insertions(+), 20 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt index ad345bc5a..aa7407c0a 100644 --- a/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/scientifik/kmath/expressions/Expression.kt @@ -58,32 +58,35 @@ internal class DivExpession(val context: Field, val expr: Expression, v override fun invoke(arguments: Map): T = context.divide(expr.invoke(arguments), second.invoke(arguments)) } -class ExpressionField(val field: Field) : Field>, ExpressionContext { - - override val zero: Expression = ConstantExpression(field.zero) - - override val one: Expression = ConstantExpression(field.one) +open class ExpressionSpace(val space: Space) : Space>, ExpressionContext { + override val zero: Expression = ConstantExpression(space.zero) override fun const(value: T): Expression = ConstantExpression(value) override fun variable(name: String, default: T?): Expression = VariableExpression(name, default) - override fun add(a: Expression, b: Expression): Expression = SumExpression(field, a, b) + override fun add(a: Expression, b: Expression): Expression = SumExpression(space, a, b) - override fun multiply(a: Expression, k: Number): Expression = ConstProductExpession(field, a, k) - - override fun multiply(a: Expression, b: Expression): Expression = ProductExpression(field, a, b) - - override fun divide(a: Expression, b: Expression): Expression = DivExpession(field, a, b) + override fun multiply(a: Expression, k: Number): Expression = ConstProductExpession(space, a, k) operator fun Expression.plus(arg: T) = this + const(arg) operator fun Expression.minus(arg: T) = this - const(arg) - operator fun Expression.times(arg: T) = this * const(arg) - operator fun Expression.div(arg: T) = this / const(arg) operator fun T.plus(arg: Expression) = arg + this operator fun T.minus(arg: Expression) = arg - this +} + + +class ExpressionField(val field: Field) : Field>, ExpressionSpace(field) { + override val one: Expression = ConstantExpression(field.one) + override fun multiply(a: Expression, b: Expression): Expression = ProductExpression(field, a, b) + + override fun divide(a: Expression, b: Expression): Expression = DivExpession(field, a, b) + + operator fun Expression.times(arg: T) = this * const(arg) + operator fun Expression.div(arg: T) = this / const(arg) + operator fun T.times(arg: Expression) = arg * this operator fun T.div(arg: Expression) = arg / this } \ No newline at end of file diff --git a/kmath-histograms/build.gradle.kts b/kmath-histograms/build.gradle.kts index 21df4c26f..7eaa5e174 100644 --- a/kmath-histograms/build.gradle.kts +++ b/kmath-histograms/build.gradle.kts @@ -2,7 +2,6 @@ plugins { `npm-multiplatform` } -// Just an example how we can collapse nested DSL for simple declarations kotlin.sourceSets.commonMain { dependencies { api(project(":kmath-core")) diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt index 61e0dae4e..d65b9530a 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt @@ -5,12 +5,9 @@ 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 suspend fun next(): R = generator.gen().also { atomicValue.lazySet(it) } + override suspend fun next(): R = generator.gen() override fun fork(): Chain = RandomChain(generator.fork(), 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 index 381bd9736..13983c6b2 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt @@ -1,5 +1,7 @@ package scientifik.kmath.prob +import kotlin.random.Random + /** * A basic generator */ @@ -10,9 +12,30 @@ interface RandomGenerator { fun nextBlock(size: Int): ByteArray /** - * Fork the current state of generator + * Create a new generator which is independent from current generator (operations on new generator do not affect this one + * and vise versa). The statistical properties of new generator should be the same as for this one. + * For pseudo-random generator, the fork is keeping the same sequence of numbers for given call order for each run. + * + * The thread safety of this operation is not guaranteed since it could affect the state of the generator. */ fun fork(): RandomGenerator - companion object + companion object { + val default by lazy { DefaultGenerator(Random.nextLong()) } + } +} + +class DefaultGenerator(seed: Long?) : RandomGenerator { + private val random = seed?.let { Random(it) } ?: Random + + override fun nextDouble(): Double = random.nextDouble() + + override fun nextInt(): Int = random.nextInt() + + override fun nextLong(): Long = random.nextLong() + + override fun nextBlock(size: Int): ByteArray = random.nextBytes(size) + + override fun fork(): RandomGenerator = DefaultGenerator(nextLong()) + } \ No newline at end of file From 88759807ab3b5b015352212c5e2a51b546a2aca3 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 26 Jun 2019 18:51:01 +0300 Subject: [PATCH 12/13] Added complex example for article --- .../scientifik/kmath/structures/ComplexND.kt | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt b/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt index 111b76ff0..b57e9db79 100644 --- a/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt +++ b/examples/src/main/kotlin/scientifik/kmath/structures/ComplexND.kt @@ -1,5 +1,8 @@ package scientifik.kmath.structures +import scientifik.kmath.linear.transpose +import scientifik.kmath.operations.Complex +import scientifik.kmath.operations.toComplex import kotlin.system.measureTimeMillis fun main() { @@ -31,4 +34,24 @@ fun main() { } println("Complex addition completed in $complexTime millis") -} \ No newline at end of file +} + + +fun complexExample() { + //Create a context for 2-d structure with complex values + NDField.complex(4, 8).run { + //a constant real-valued structure + val x = one * 2.5 + operator fun Number.plus(other: Complex) = Complex(this.toDouble() + other.re, other.im) + //a structure generator specific to this context + val matrix = produce { (k, l) -> + k + l*i + } + + //Perform sum + val sum = matrix + x + 1.0 + + //Represent the sum as 2d-structure and transpose + sum.as2D().transpose() + } +} From 11bbdce94bba61570207211f35e97a1a8cb4c0fc Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 26 Jun 2019 18:52:37 +0300 Subject: [PATCH 13/13] Version 0.1.3 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index d7b7ac78a..edc4bed7b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,4 @@ -val kmathVersion by extra("0.1.3-dev-1") +val kmathVersion by extra("0.1.3") allprojects { repositories {