From 2de9548c23fe8fe7ad4a88dcbb4f87b2c7068683 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Sun, 7 Jun 2020 22:12:04 +0700 Subject: [PATCH 01/31] Implement commons-rng particle in pure Kotlin --- kmath-commons-rng-part/build.gradle.kts | 2 + .../commons/rng/UniformRandomProvider.kt | 19 ++ .../rng/sampling/SharedStateSampler.kt | 7 + .../AhrensDieterExponentialSampler.kt | 95 +++++++ .../BoxMullerNormalizedGaussianSampler.kt | 50 ++++ .../distribution/ContinuousSampler.kt | 5 + .../sampling/distribution/DiscreteSampler.kt | 5 + .../sampling/distribution/GaussianSampler.kt | 45 ++++ .../sampling/distribution/InternalGamma.kt | 43 ++++ .../sampling/distribution/InternalUtils.kt | 92 +++++++ .../KempSmallMeanPoissonSampler.kt | 56 +++++ .../distribution/LargenMeanPoissonSampler.kt | 233 ++++++++++++++++++ .../MarsagliaNormalizedGaussianSampler.kt | 54 ++++ .../distribution/NormalizedGaussianSampler.kt | 3 + .../sampling/distribution/PoissonSampler.kt | 32 +++ .../rng/sampling/distribution/SamplerBase.kt | 12 + .../SharedStateContinuousSampler.kt | 7 + .../SharedStateDiscreteSampler.kt | 7 + .../distribution/SmallMeanPoissonSampler.kt | 58 +++++ .../ZigguratNormalizedGaussianSampler.kt | 89 +++++++ kmath-prob/build.gradle.kts | 6 +- .../scientifik/kmath/prob/Distributions.kt | 53 ++++ .../kmath/prob/RandomSourceGenerator.kt | 28 +++ .../scientifik/kmath/prob/Distributions.kt | 62 +++++ .../kmath/prob/RandomSourceGenerator.kt | 50 ++-- .../scientifik/kmath/prob/distributions.kt | 109 -------- settings.gradle.kts | 1 + 27 files changed, 1078 insertions(+), 145 deletions(-) create mode 100644 kmath-commons-rng-part/build.gradle.kts create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/UniformRandomProvider.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/SharedStateSampler.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/ContinuousSampler.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/DiscreteSampler.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/GaussianSampler.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/InternalGamma.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/InternalUtils.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/LargenMeanPoissonSampler.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/NormalizedGaussianSampler.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/PoissonSampler.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SamplerBase.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SharedStateContinuousSampler.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SharedStateDiscreteSampler.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SmallMeanPoissonSampler.kt create mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.kt create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distributions.kt create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt create mode 100644 kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/Distributions.kt delete mode 100644 kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/distributions.kt diff --git a/kmath-commons-rng-part/build.gradle.kts b/kmath-commons-rng-part/build.gradle.kts new file mode 100644 index 000000000..9d3cd0e7d --- /dev/null +++ b/kmath-commons-rng-part/build.gradle.kts @@ -0,0 +1,2 @@ +plugins { id("scientifik.mpp") } +kotlin.sourceSets { commonMain.get().dependencies { api(project(":kmath-coroutines")) } } diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/UniformRandomProvider.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/UniformRandomProvider.kt new file mode 100644 index 000000000..2fbf7a0a2 --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/UniformRandomProvider.kt @@ -0,0 +1,19 @@ +package scientifik.commons.rng + +interface UniformRandomProvider { + fun nextBytes(bytes: ByteArray) + + fun nextBytes( + bytes: ByteArray, + start: Int, + len: Int + ) + + fun nextInt(): Int + fun nextInt(n: Int): Int + fun nextLong(): Long + fun nextLong(n: Long): Long + fun nextBoolean(): Boolean + fun nextFloat(): Float + fun nextDouble(): Double +} \ No newline at end of file diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/SharedStateSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/SharedStateSampler.kt new file mode 100644 index 000000000..d32a646c2 --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/SharedStateSampler.kt @@ -0,0 +1,7 @@ +package scientifik.commons.rng.sampling + +import scientifik.commons.rng.UniformRandomProvider + +interface SharedStateSampler { + fun withUniformRandomProvider(rng: UniformRandomProvider): R +} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.kt new file mode 100644 index 000000000..7aa061951 --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.kt @@ -0,0 +1,95 @@ +package scientifik.commons.rng.sampling.distribution + +import scientifik.commons.rng.UniformRandomProvider +import kotlin.math.ln +import kotlin.math.pow + +class AhrensDieterExponentialSampler : SamplerBase, + SharedStateContinuousSampler { + private val mean: Double + private val rng: UniformRandomProvider + + constructor( + rng: UniformRandomProvider, + mean: Double + ) : super(null) { + require(mean > 0) { "mean is not strictly positive: $mean" } + this.rng = rng + this.mean = mean + } + + private constructor( + rng: UniformRandomProvider, + source: AhrensDieterExponentialSampler + ) : super(null) { + this.rng = rng + mean = source.mean + } + + override fun sample(): Double { + // Step 1: + var a = 0.0 + var u: Double = rng.nextDouble() + + // Step 2 and 3: + while (u < 0.5) { + a += EXPONENTIAL_SA_QI.get( + 0 + ) + u *= 2.0 + } + + // Step 4 (now u >= 0.5): + u += u - 1 + + // Step 5: + if (u <= EXPONENTIAL_SA_QI.get( + 0 + ) + ) { + return mean * (a + u) + } + + // Step 6: + var i = 0 // Should be 1, be we iterate before it in while using 0. + var u2: Double = rng.nextDouble() + var umin = u2 + + // Step 7 and 8: + do { + ++i + u2 = rng.nextDouble() + if (u2 < umin) umin = u2 + // Step 8: + } while (u > EXPONENTIAL_SA_QI[i]) // Ensured to exit since EXPONENTIAL_SA_QI[MAX] = 1. + return mean * (a + umin * EXPONENTIAL_SA_QI[0]) + } + + override fun toString(): String = "Ahrens-Dieter Exponential deviate [$rng]" + + override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateContinuousSampler = + AhrensDieterExponentialSampler(rng, this) + + companion object { + private val EXPONENTIAL_SA_QI = DoubleArray(16) + + fun of( + rng: UniformRandomProvider, + mean: Double + ): SharedStateContinuousSampler = AhrensDieterExponentialSampler(rng, mean) + + init { + /** + * Filling EXPONENTIAL_SA_QI table. + * Note that we don't want qi = 0 in the table. + */ + val ln2 = ln(2.0) + var qi = 0.0 + + EXPONENTIAL_SA_QI.indices.forEach { i -> + qi += ln2.pow(i + 1.0) / InternalUtils.factorial(i + 1) + EXPONENTIAL_SA_QI[i] = qi + } + } + } +} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.kt new file mode 100644 index 000000000..91b3314b5 --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.kt @@ -0,0 +1,50 @@ +package scientifik.commons.rng.sampling.distribution + +import scientifik.commons.rng.UniformRandomProvider +import kotlin.math.* + +class BoxMullerNormalizedGaussianSampler( + private val rng: UniformRandomProvider +) : + NormalizedGaussianSampler, + SharedStateContinuousSampler { + private var nextGaussian: Double = Double.NaN + + override fun sample(): Double { + val random: Double + + if (nextGaussian.isNaN()) { + // Generate a pair of Gaussian numbers. + val x = rng.nextDouble() + val y = rng.nextDouble() + val alpha = 2 * PI * x + val r = sqrt(-2 * ln(y)) + + // Return the first element of the generated pair. + random = r * cos(alpha) + + // Keep second element of the pair for next invocation. + nextGaussian = r * sin(alpha) + } else { + // Use the second element of the pair (generated at the + // previous invocation). + random = nextGaussian + + // Both elements of the pair have been used. + nextGaussian = Double.NaN + } + + return random + } + + override fun toString(): String = "Box-Muller normalized Gaussian deviate [$rng]" + + override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateContinuousSampler = + BoxMullerNormalizedGaussianSampler(rng) + + companion object { + @Suppress("UNCHECKED_CAST") + fun of(rng: UniformRandomProvider): S where S : NormalizedGaussianSampler?, S : SharedStateContinuousSampler? = + BoxMullerNormalizedGaussianSampler(rng) as S + } +} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/ContinuousSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/ContinuousSampler.kt new file mode 100644 index 000000000..4841672a2 --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/ContinuousSampler.kt @@ -0,0 +1,5 @@ +package scientifik.commons.rng.sampling.distribution + +interface ContinuousSampler { + fun sample(): Double +} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/DiscreteSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/DiscreteSampler.kt new file mode 100644 index 000000000..8e8bccd18 --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/DiscreteSampler.kt @@ -0,0 +1,5 @@ +package scientifik.commons.rng.sampling.distribution + +interface DiscreteSampler { + fun sample(): Int +} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/GaussianSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/GaussianSampler.kt new file mode 100644 index 000000000..424ea90fe --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/GaussianSampler.kt @@ -0,0 +1,45 @@ +package scientifik.commons.rng.sampling.distribution + +import scientifik.commons.rng.UniformRandomProvider + +class GaussianSampler : SharedStateContinuousSampler { + private val mean: Double + private val standardDeviation: Double + private val normalized: NormalizedGaussianSampler + + constructor( + normalized: NormalizedGaussianSampler, + mean: Double, + standardDeviation: Double + ) { + require(standardDeviation > 0) { "standard deviation is not strictly positive: $standardDeviation" } + this.normalized = normalized + this.mean = mean + this.standardDeviation = standardDeviation + } + + private constructor( + rng: UniformRandomProvider, + source: GaussianSampler + ) { + mean = source.mean + standardDeviation = source.standardDeviation + normalized = InternalUtils.newNormalizedGaussianSampler(source.normalized, rng) + } + + override fun sample(): Double = standardDeviation * normalized.sample() + mean + + override fun toString(): String = "Gaussian deviate [$normalized]" + + override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateContinuousSampler { + return GaussianSampler(rng, this) + } + + companion object { + fun of( + normalized: NormalizedGaussianSampler, + mean: Double, + standardDeviation: Double + ): SharedStateContinuousSampler = GaussianSampler(normalized, mean, standardDeviation) + } +} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/InternalGamma.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/InternalGamma.kt new file mode 100644 index 000000000..a75ba1608 --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/InternalGamma.kt @@ -0,0 +1,43 @@ +package scientifik.commons.rng.sampling.distribution + +import kotlin.math.PI +import kotlin.math.ln + +internal object InternalGamma { + const val LANCZOS_G = 607.0 / 128.0 + + private val LANCZOS_COEFFICIENTS = doubleArrayOf( + 0.99999999999999709182, + 57.156235665862923517, + -59.597960355475491248, + 14.136097974741747174, + -0.49191381609762019978, + .33994649984811888699e-4, + .46523628927048575665e-4, + -.98374475304879564677e-4, + .15808870322491248884e-3, + -.21026444172410488319e-3, + .21743961811521264320e-3, + -.16431810653676389022e-3, + .84418223983852743293e-4, + -.26190838401581408670e-4, + .36899182659531622704e-5 + ) + + private val HALF_LOG_2_PI: Double = 0.5 * ln(2.0 * PI) + + fun logGamma(x: Double): Double { + // Stripped-down version of the same method defined in "Commons Math": + // Unused "if" branches (for when x < 8) have been removed here since + // this method is only used (by class "InternalUtils") in order to + // compute log(n!) for x > 20. + val sum = lanczos(x) + val tmp = x + LANCZOS_G + 0.5 + return (x + 0.5) * ln(tmp) - tmp + HALF_LOG_2_PI + ln(sum / x) + } + + private fun lanczos(x: Double): Double { + val sum = (LANCZOS_COEFFICIENTS.size - 1 downTo 1).sumByDouble { LANCZOS_COEFFICIENTS[it] / (x + it) } + return sum + LANCZOS_COEFFICIENTS[0] + } +} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/InternalUtils.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/InternalUtils.kt new file mode 100644 index 000000000..8134252aa --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/InternalUtils.kt @@ -0,0 +1,92 @@ +package scientifik.commons.rng.sampling.distribution + +import scientifik.commons.rng.UniformRandomProvider +import scientifik.commons.rng.sampling.SharedStateSampler +import kotlin.math.ln +import kotlin.math.min + +internal object InternalUtils { + private val FACTORIALS = longArrayOf( + 1L, 1L, 2L, + 6L, 24L, 120L, + 720L, 5040L, 40320L, + 362880L, 3628800L, 39916800L, + 479001600L, 6227020800L, 87178291200L, + 1307674368000L, 20922789888000L, 355687428096000L, + 6402373705728000L, 121645100408832000L, 2432902008176640000L + ) + + private const val BEGIN_LOG_FACTORIALS = 2 + + fun factorial(n: Int): Long = FACTORIALS[n] + + fun validateProbabilities(probabilities: DoubleArray?): Double { + require(!(probabilities == null || probabilities.isEmpty())) { "Probabilities must not be empty." } + var sumProb = 0.0 + + probabilities.forEach { prob -> + validateProbability(prob) + sumProb += prob + } + + require(!(sumProb.isInfinite() || sumProb <= 0)) { "Invalid sum of probabilities: $sumProb" } + return sumProb + } + + fun validateProbability(probability: Double): Unit = + require(!(probability < 0 || probability.isInfinite() || probability.isNaN())) { "Invalid probability: $probability" } + + fun newNormalizedGaussianSampler( + sampler: NormalizedGaussianSampler, + rng: UniformRandomProvider + ): NormalizedGaussianSampler { + if (sampler !is SharedStateSampler<*>) throw UnsupportedOperationException("The underlying sampler cannot share state") + + val newSampler: Any = + (sampler as SharedStateSampler<*>).withUniformRandomProvider(rng) as? NormalizedGaussianSampler + ?: throw UnsupportedOperationException( + "The underlying sampler did not create a normalized Gaussian sampler" + ) + + return newSampler as NormalizedGaussianSampler + } + + class FactorialLog private constructor( + numValues: Int, + cache: DoubleArray? + ) { + private val logFactorials: DoubleArray = DoubleArray(numValues) + + init { + val endCopy: Int + + if (cache != null && cache.size > BEGIN_LOG_FACTORIALS) { + // Copy available values. + endCopy = min(cache.size, numValues) + cache.copyInto(logFactorials, BEGIN_LOG_FACTORIALS, BEGIN_LOG_FACTORIALS, endCopy) + } + // All values to be computed + else + endCopy = BEGIN_LOG_FACTORIALS + + // Compute remaining values. + (endCopy until numValues).forEach { i -> + if (i < FACTORIALS.size) logFactorials[i] = ln(FACTORIALS[i].toDouble()) else logFactorials[i] = + logFactorials[i - 1] + ln(i.toDouble()) + } + } + + fun withCache(cacheSize: Int): FactorialLog = FactorialLog(cacheSize, logFactorials) + + fun value(n: Int): Double { + if (n < logFactorials.size) + return logFactorials[n] + + return if (n < FACTORIALS.size) ln(FACTORIALS[n].toDouble()) else InternalGamma.logGamma(n + 1.0) + } + + companion object { + fun create(): FactorialLog = FactorialLog(0, null) + } + } +} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.kt new file mode 100644 index 000000000..6501089b2 --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.kt @@ -0,0 +1,56 @@ +package scientifik.commons.rng.sampling.distribution + +import scientifik.commons.rng.UniformRandomProvider +import kotlin.math.exp + +class KempSmallMeanPoissonSampler private constructor( + private val rng: UniformRandomProvider, + private val p0: Double, + private val mean: Double +) : SharedStateDiscreteSampler { + override fun sample(): Int { + // Note on the algorithm: + // - X is the unknown sample deviate (the output of the algorithm) + // - x is the current value from the distribution + // - p is the probability of the current value x, p(X=x) + // - u is effectively the cumulative probability that the sample X + // is equal or above the current value x, p(X>=x) + // So if p(X>=x) > p(X=x) the sample must be above x, otherwise it is x + var u = rng.nextDouble() + var x = 0 + var p = p0 + + while (u > p) { + u -= p + // Compute the next probability using a recurrence relation. + // p(x+1) = p(x) * mean / (x+1) + p *= mean / ++x + // The algorithm listed in Kemp (1981) does not check that the rolling probability + // is positive. This check is added to ensure no errors when the limit of the summation + // 1 - sum(p(x)) is above 0 due to cumulative error in floating point arithmetic. + if (p == 0.0) return x + } + + return x + } + + override fun toString(): String = "Kemp Small Mean Poisson deviate [$rng]" + + override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateDiscreteSampler = + KempSmallMeanPoissonSampler(rng, p0, mean) + + companion object { + fun of( + rng: UniformRandomProvider, + mean: Double + ): SharedStateDiscreteSampler { + require(mean > 0) { "Mean is not strictly positive: $mean" } + val p0: Double = exp(-mean) + + // Probability must be positive. As mean increases then p(0) decreases. + if (p0 > 0) return KempSmallMeanPoissonSampler(rng, p0, mean) + throw IllegalArgumentException("No probability for mean: $mean") + } + } +} + diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/LargenMeanPoissonSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/LargenMeanPoissonSampler.kt new file mode 100644 index 000000000..ce1e1e3b0 --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/LargenMeanPoissonSampler.kt @@ -0,0 +1,233 @@ +package scientifik.commons.rng.sampling.distribution + +import scientifik.commons.rng.UniformRandomProvider +import kotlin.math.* + +class LargeMeanPoissonSampler : SharedStateDiscreteSampler { + private val rng: UniformRandomProvider + private val exponential: SharedStateContinuousSampler + private val gaussian: SharedStateContinuousSampler + private val factorialLog: InternalUtils.FactorialLog + private val lambda: Double + private val logLambda: Double + private val logLambdaFactorial: Double + private val delta: Double + private val halfDelta: Double + private val twolpd: Double + private val p1: Double + private val p2: Double + private val c1: Double + private val smallMeanPoissonSampler: SharedStateDiscreteSampler + + constructor( + rng: UniformRandomProvider, + mean: Double + ) { + require(mean >= 1) { "mean is not >= 1: $mean" } + // The algorithm is not valid if Math.floor(mean) is not an integer. + require(mean <= MAX_MEAN) { "mean $mean > $MAX_MEAN" } + this.rng = rng + gaussian = ZigguratNormalizedGaussianSampler(rng) + exponential = AhrensDieterExponentialSampler.of(rng, 1.0) + // Plain constructor uses the uncached function. + factorialLog = NO_CACHE_FACTORIAL_LOG!! + // Cache values used in the algorithm + lambda = floor(mean) + logLambda = ln(lambda) + logLambdaFactorial = getFactorialLog(lambda.toInt()) + delta = sqrt(lambda * ln(32 * lambda / PI + 1)) + halfDelta = delta / 2 + twolpd = 2 * lambda + delta + c1 = 1 / (8 * lambda) + val a1: Double = sqrt(PI * twolpd) * exp(c1) + val a2: Double = twolpd / delta * exp(-delta * (1 + delta) / twolpd) + val aSum = a1 + a2 + 1 + p1 = a1 / aSum + p2 = a2 / aSum + + // The algorithm requires a Poisson sample from the remaining lambda fraction. + val lambdaFractional = mean - lambda + smallMeanPoissonSampler = + if (lambdaFractional < Double.MIN_VALUE) NO_SMALL_MEAN_POISSON_SAMPLER else // Not used. + KempSmallMeanPoissonSampler.of(rng, lambdaFractional) + } + + internal constructor( + rng: UniformRandomProvider, + state: LargeMeanPoissonSamplerState, + lambdaFractional: Double + ) { + require(!(lambdaFractional < 0 || lambdaFractional >= 1)) { "lambdaFractional must be in the range 0 (inclusive) to 1 (exclusive): $lambdaFractional" } + this.rng = rng + gaussian = ZigguratNormalizedGaussianSampler(rng) + exponential = AhrensDieterExponentialSampler.of(rng, 1.0) + // Plain constructor uses the uncached function. + factorialLog = NO_CACHE_FACTORIAL_LOG!! + // Use the state to initialise the algorithm + lambda = state.lambdaRaw + logLambda = state.logLambda + logLambdaFactorial = state.logLambdaFactorial + delta = state.delta + halfDelta = state.halfDelta + twolpd = state.twolpd + p1 = state.p1 + p2 = state.p2 + c1 = state.c1 + + // The algorithm requires a Poisson sample from the remaining lambda fraction. + smallMeanPoissonSampler = + if (lambdaFractional < Double.MIN_VALUE) + NO_SMALL_MEAN_POISSON_SAMPLER + else // Not used. + KempSmallMeanPoissonSampler.of(rng, lambdaFractional) + } + + /** + * @param rng Generator of uniformly distributed random numbers. + * @param source Source to copy. + */ + private constructor( + rng: UniformRandomProvider, + source: LargeMeanPoissonSampler + ) { + this.rng = rng + gaussian = source.gaussian.withUniformRandomProvider(rng)!! + exponential = source.exponential.withUniformRandomProvider(rng)!! + // Reuse the cache + factorialLog = source.factorialLog + lambda = source.lambda + logLambda = source.logLambda + logLambdaFactorial = source.logLambdaFactorial + delta = source.delta + halfDelta = source.halfDelta + twolpd = source.twolpd + p1 = source.p1 + p2 = source.p2 + c1 = source.c1 + + // Share the state of the small sampler + smallMeanPoissonSampler = source.smallMeanPoissonSampler.withUniformRandomProvider(rng)!! + } + + /** {@inheritDoc} */ + override fun sample(): Int { + // This will never be null. It may be a no-op delegate that returns zero. + val y2: Int = smallMeanPoissonSampler.sample() + var x: Double + var y: Double + var v: Double + var a: Int + var t: Double + var qr: Double + var qa: Double + while (true) { + // Step 1: + val u = rng.nextDouble() + + if (u <= p1) { + // Step 2: + val n = gaussian.sample() + x = n * sqrt(lambda + halfDelta) - 0.5 + if (x > delta || x < -lambda) continue + y = if (x < 0) floor(x) else ceil(x) + val e = exponential.sample() + v = -e - 0.5 * n * n + c1 + } else { + // Step 3: + if (u > p1 + p2) { + y = lambda + break + } + + x = delta + twolpd / delta * exponential.sample() + y = ceil(x) + v = -exponential.sample() - delta * (x + 1) / twolpd + } + // The Squeeze Principle + // Step 4.1: + a = if (x < 0) 1 else 0 + t = y * (y + 1) / (2 * lambda) + + // Step 4.2 + if (v < -t && a == 0) { + y += lambda + break + } + + // Step 4.3: + qr = t * ((2 * y + 1) / (6 * lambda) - 1) + qa = qr - t * t / (3 * (lambda + a * (y + 1))) + + // Step 4.4: + if (v < qa) { + y += lambda + break + } + + // Step 4.5: + if (v > qr) continue + + // Step 4.6: + if (v < y * logLambda - getFactorialLog((y + lambda).toInt()) + logLambdaFactorial) { + y += lambda + break + } + } + + return min(y2 + y.toLong(), Int.MAX_VALUE.toLong()).toInt() + } + + + private fun getFactorialLog(n: Int): Double = factorialLog.value(n) + + override fun toString(): String = "Large Mean Poisson deviate [$rng]" + + override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateDiscreteSampler = + LargeMeanPoissonSampler(rng, this) + + val state: LargeMeanPoissonSamplerState + get() = LargeMeanPoissonSamplerState( + lambda, logLambda, logLambdaFactorial, + delta, halfDelta, twolpd, p1, p2, c1 + ) + + class LargeMeanPoissonSamplerState( + val lambdaRaw: Double, + val logLambda: Double, + val logLambdaFactorial: Double, + val delta: Double, + val halfDelta: Double, + val twolpd: Double, + val p1: Double, + val p2: Double, + val c1: Double + ) { + fun getLambda(): Int = lambdaRaw.toInt() + } + + companion object { + private const val MAX_MEAN = 0.5 * Int.MAX_VALUE + private var NO_CACHE_FACTORIAL_LOG: InternalUtils.FactorialLog? = null + + private val NO_SMALL_MEAN_POISSON_SAMPLER: SharedStateDiscreteSampler = + object : SharedStateDiscreteSampler { + override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateDiscreteSampler = +// No requirement for RNG + this + + override fun sample(): Int =// No Poisson sample + 0 + } + + fun of( + rng: UniformRandomProvider, + mean: Double + ): SharedStateDiscreteSampler = LargeMeanPoissonSampler(rng, mean) + + init { + // Create without a cache. + NO_CACHE_FACTORIAL_LOG = + InternalUtils.FactorialLog.create() + } + } +} \ No newline at end of file diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.kt new file mode 100644 index 000000000..26ee9ece9 --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.kt @@ -0,0 +1,54 @@ +package scientifik.commons.rng.sampling.distribution + +import scientifik.commons.rng.UniformRandomProvider +import kotlin.math.ln +import kotlin.math.sqrt + +class MarsagliaNormalizedGaussianSampler(private val rng: UniformRandomProvider) : + NormalizedGaussianSampler, + SharedStateContinuousSampler { + private var nextGaussian = Double.NaN + + override fun sample(): Double { + if (nextGaussian.isNaN()) { + // Rejection scheme for selecting a pair that lies within the unit circle. + while (true) { + // Generate a pair of numbers within [-1 , 1). + val x = 2.0 * rng.nextDouble() - 1.0 + val y = 2.0 * rng.nextDouble() - 1.0 + val r2 = x * x + y * y + if (r2 < 1 && r2 > 0) { + // Pair (x, y) is within unit circle. + val alpha = sqrt(-2 * ln(r2) / r2) + + // Keep second element of the pair for next invocation. + nextGaussian = alpha * y + + // Return the first element of the generated pair. + return alpha * x + } + + // Pair is not within the unit circle: Generate another one. + } + } else { + // Use the second element of the pair (generated at the + // previous invocation). + val r = nextGaussian + + // Both elements of the pair have been used. + nextGaussian = Double.NaN + return r + } + } + + override fun toString(): String = "Box-Muller (with rejection) normalized Gaussian deviate [$rng]" + + override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateContinuousSampler = + MarsagliaNormalizedGaussianSampler(rng) + + companion object { + @Suppress("UNCHECKED_CAST") + fun of(rng: UniformRandomProvider): S where S : NormalizedGaussianSampler?, S : SharedStateContinuousSampler? = + MarsagliaNormalizedGaussianSampler(rng) as S + } +} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/NormalizedGaussianSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/NormalizedGaussianSampler.kt new file mode 100644 index 000000000..feff4d954 --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/NormalizedGaussianSampler.kt @@ -0,0 +1,3 @@ +package scientifik.commons.rng.sampling.distribution + +interface NormalizedGaussianSampler : ContinuousSampler diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/PoissonSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/PoissonSampler.kt new file mode 100644 index 000000000..4a7f56f60 --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/PoissonSampler.kt @@ -0,0 +1,32 @@ +package scientifik.commons.rng.sampling.distribution + +import scientifik.commons.rng.UniformRandomProvider + +class PoissonSampler( + rng: UniformRandomProvider, + mean: Double +) : SamplerBase(null), SharedStateDiscreteSampler { + private val poissonSamplerDelegate: SharedStateDiscreteSampler + + override fun sample(): Int = poissonSamplerDelegate.sample() + override fun toString(): String = poissonSamplerDelegate.toString() + + override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateDiscreteSampler? = +// Direct return of the optimised sampler + poissonSamplerDelegate.withUniformRandomProvider(rng) + + companion object { + const val PIVOT = 40.0 + + fun of( + rng: UniformRandomProvider, + mean: Double + ): SharedStateDiscreteSampler =// Each sampler should check the input arguments. + if (mean < PIVOT) SmallMeanPoissonSampler.of(rng, mean) else LargeMeanPoissonSampler.of(rng, mean) + } + + init { + // Delegate all work to specialised samplers. + poissonSamplerDelegate = of(rng, mean) + } +} \ No newline at end of file diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SamplerBase.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SamplerBase.kt new file mode 100644 index 000000000..38df9eb4e --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SamplerBase.kt @@ -0,0 +1,12 @@ +package scientifik.commons.rng.sampling.distribution + +import scientifik.commons.rng.UniformRandomProvider + +@Deprecated("Since version 1.1. Class intended for internal use only.") +open class SamplerBase protected constructor(private val rng: UniformRandomProvider?) { + protected fun nextDouble(): Double = rng!!.nextDouble() + protected fun nextInt(): Int = rng!!.nextInt() + protected fun nextInt(max: Int): Int = rng!!.nextInt(max) + protected fun nextLong(): Long = rng!!.nextLong() + override fun toString(): String = "rng=$rng" +} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SharedStateContinuousSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SharedStateContinuousSampler.kt new file mode 100644 index 000000000..ddac4b7a7 --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SharedStateContinuousSampler.kt @@ -0,0 +1,7 @@ +package scientifik.commons.rng.sampling.distribution + +import scientifik.commons.rng.sampling.SharedStateSampler + +interface SharedStateContinuousSampler : ContinuousSampler, + SharedStateSampler { +} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SharedStateDiscreteSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SharedStateDiscreteSampler.kt new file mode 100644 index 000000000..2d8adada6 --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SharedStateDiscreteSampler.kt @@ -0,0 +1,7 @@ +package scientifik.commons.rng.sampling.distribution + +import scientifik.commons.rng.sampling.SharedStateSampler + +interface SharedStateDiscreteSampler : DiscreteSampler, + SharedStateSampler { // Composite interface +} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SmallMeanPoissonSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SmallMeanPoissonSampler.kt new file mode 100644 index 000000000..a3188620f --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SmallMeanPoissonSampler.kt @@ -0,0 +1,58 @@ +package scientifik.commons.rng.sampling.distribution + +import scientifik.commons.rng.UniformRandomProvider +import kotlin.math.ceil +import kotlin.math.exp + +class SmallMeanPoissonSampler : SharedStateDiscreteSampler { + private val p0: Double + private val limit: Int + private val rng: UniformRandomProvider + + constructor( + rng: UniformRandomProvider, + mean: Double + ) { + this.rng = rng + require(mean > 0) { "mean is not strictly positive: $mean" } + p0 = exp(-mean) + + limit = (if (p0 > 0) ceil(1000 * mean) else throw IllegalArgumentException("No p(x=0) probability for mean: $mean")).toInt() + // This excludes NaN values for the mean + // else + // The returned sample is bounded by 1000 * mean + } + + private constructor( + rng: UniformRandomProvider, + source: SmallMeanPoissonSampler + ) { + this.rng = rng + p0 = source.p0 + limit = source.limit + } + + override fun sample(): Int { + var n = 0 + var r = 1.0 + + while (n < limit) { + r *= rng.nextDouble() + if (r >= p0) n++ else break + } + + return n + } + + override fun toString(): String = "Small Mean Poisson deviate [$rng]" + + override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateDiscreteSampler = + SmallMeanPoissonSampler(rng, this) + + companion object { + fun of( + rng: UniformRandomProvider, + mean: Double + ): SharedStateDiscreteSampler = SmallMeanPoissonSampler(rng, mean) + } +} \ No newline at end of file diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.kt new file mode 100644 index 000000000..a916121db --- /dev/null +++ b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.kt @@ -0,0 +1,89 @@ +package scientifik.commons.rng.sampling.distribution + +import scientifik.commons.rng.UniformRandomProvider +import kotlin.math.* + +class ZigguratNormalizedGaussianSampler(private val rng: UniformRandomProvider) : + NormalizedGaussianSampler, + SharedStateContinuousSampler { + + companion object { + private const val R = 3.442619855899 + private const val ONE_OVER_R: Double = 1 / R + private const val V = 9.91256303526217e-3 + private val MAX: Double = 2.0.pow(63.0) + private val ONE_OVER_MAX: Double = 1.0 / MAX + private const val LEN = 128 + private const val LAST: Int = LEN - 1 + private val K = LongArray(LEN) + private val W = DoubleArray(LEN) + private val F = DoubleArray(LEN) + private fun gauss(x: Double): Double = exp(-0.5 * x * x) + + @Suppress("UNCHECKED_CAST") + fun of(rng: UniformRandomProvider): S where S : NormalizedGaussianSampler?, S : SharedStateContinuousSampler? = + ZigguratNormalizedGaussianSampler(rng) as S + + init { + // Filling the tables. + var d = R + var t = d + var fd = gauss(d) + val q = V / fd + K[0] = (d / q * MAX).toLong() + K[1] = 0 + W[0] = q * ONE_OVER_MAX + W[LAST] = d * ONE_OVER_MAX + F[0] = 1.0 + F[LAST] = fd + + (LAST - 1 downTo 1).forEach { i -> + d = sqrt(-2 * ln(V / d + fd)) + fd = gauss(d) + K[i + 1] = (d / t * MAX).toLong() + t = d + F[i] = fd + W[i] = d * ONE_OVER_MAX + } + } + } + + override fun sample(): Double { + val j = rng.nextLong() + val i = (j and LAST.toLong()).toInt() + return if (abs(j) < K[i]) j * W[i] else fix(j, i) + } + + override fun toString(): String = "Ziggurat normalized Gaussian deviate [$rng]" + + private fun fix( + hz: Long, + iz: Int + ): Double { + var x: Double + var y: Double + x = hz * W[iz] + + return if (iz == 0) { + // Base strip. + // This branch is called about 5.7624515E-4 times per sample. + do { + y = -ln(rng.nextDouble()) + x = -ln(rng.nextDouble()) * ONE_OVER_R + } while (y + y < x * x) + + val out = R + x + if (hz > 0) out else -out + } else { + // Wedge of other strips. + // This branch is called about 0.027323 times per sample. + // else + // Try again. + // This branch is called about 0.012362 times per sample. + if (F[iz] + rng.nextDouble() * (F[iz - 1] - F[iz]) < gauss(x)) x else sample() + } + } + + override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateContinuousSampler = + ZigguratNormalizedGaussianSampler(rng) +} diff --git a/kmath-prob/build.gradle.kts b/kmath-prob/build.gradle.kts index a69d61b73..4ec254e82 100644 --- a/kmath-prob/build.gradle.kts +++ b/kmath-prob/build.gradle.kts @@ -6,10 +6,12 @@ kotlin.sourceSets { commonMain { dependencies { api(project(":kmath-coroutines")) + api(project(":kmath-commons-rng-part")) } } - jvmMain{ - dependencies{ + + jvmMain { + dependencies { api("org.apache.commons:commons-rng-sampling:1.3") api("org.apache.commons:commons-rng-simple:1.3") } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distributions.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distributions.kt new file mode 100644 index 000000000..fd8cc4116 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distributions.kt @@ -0,0 +1,53 @@ +package scientifik.kmath.prob + +import scientifik.commons.rng.UniformRandomProvider +import scientifik.commons.rng.sampling.distribution.* +import scientifik.kmath.chains.BlockingIntChain +import scientifik.kmath.chains.BlockingRealChain +import scientifik.kmath.chains.Chain + +abstract class ContinuousSamplerDistribution : Distribution { + + private inner class ContinuousSamplerChain(val generator: RandomGenerator) : BlockingRealChain() { + private val sampler = buildCMSampler(generator) + + override fun nextDouble(): Double = sampler.sample() + + override fun fork(): Chain = ContinuousSamplerChain(generator.fork()) + } + + protected abstract fun buildCMSampler(generator: RandomGenerator): ContinuousSampler + + override fun sample(generator: RandomGenerator): BlockingRealChain = ContinuousSamplerChain(generator) +} + +abstract class DiscreteSamplerDistribution : Distribution { + + private inner class ContinuousSamplerChain(val generator: RandomGenerator) : BlockingIntChain() { + private val sampler = buildSampler(generator) + + override fun nextInt(): Int = sampler.sample() + + override fun fork(): Chain = ContinuousSamplerChain(generator.fork()) + } + + protected abstract fun buildSampler(generator: RandomGenerator): DiscreteSampler + + override fun sample(generator: RandomGenerator): BlockingIntChain = ContinuousSamplerChain(generator) +} + +enum class NormalSamplerMethod { + BoxMuller, + Marsaglia, + Ziggurat +} + +fun normalSampler(method: NormalSamplerMethod, provider: UniformRandomProvider): NormalizedGaussianSampler = + when (method) { + NormalSamplerMethod.BoxMuller -> BoxMullerNormalizedGaussianSampler( + provider + ) + NormalSamplerMethod.Marsaglia -> MarsagliaNormalizedGaussianSampler(provider) + NormalSamplerMethod.Ziggurat -> ZigguratNormalizedGaussianSampler(provider) + } + diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt new file mode 100644 index 000000000..7be8276f3 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt @@ -0,0 +1,28 @@ +package scientifik.kmath.prob + +import scientifik.commons.rng.UniformRandomProvider + + +inline class RandomGeneratorProvider(val generator: RandomGenerator) : UniformRandomProvider { + override fun nextBoolean(): Boolean = generator.nextBoolean() + + override fun nextFloat(): Float = generator.nextDouble().toFloat() + + override fun nextBytes(bytes: ByteArray) { + generator.fillBytes(bytes) + } + + override fun nextBytes(bytes: ByteArray, start: Int, len: Int) { + generator.fillBytes(bytes, start, start + len) + } + + override fun nextInt(): Int = generator.nextInt() + + override fun nextInt(n: Int): Int = generator.nextInt(n) + + override fun nextDouble(): Double = generator.nextDouble() + + override fun nextLong(): Long = generator.nextLong() + + override fun nextLong(n: Long): Long = generator.nextLong(n) +} diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/Distributions.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/Distributions.kt new file mode 100644 index 000000000..aaeeb1b26 --- /dev/null +++ b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/Distributions.kt @@ -0,0 +1,62 @@ +package scientifik.kmath.prob + +import scientifik.commons.rng.sampling.distribution.ContinuousSampler +import scientifik.commons.rng.sampling.distribution.DiscreteSampler +import scientifik.commons.rng.sampling.distribution.GaussianSampler +import scientifik.commons.rng.sampling.distribution.PoissonSampler +import kotlin.math.PI +import kotlin.math.exp +import kotlin.math.pow +import kotlin.math.sqrt + +fun Distribution.Companion.normal( + method: NormalSamplerMethod = NormalSamplerMethod.Ziggurat +): Distribution = object : ContinuousSamplerDistribution() { + override fun buildCMSampler(generator: RandomGenerator): ContinuousSampler { + val provider = generator.asUniformRandomProvider() + return normalSampler(method, provider) + } + + override fun probability(arg: Double): Double { + return exp(-arg.pow(2) / 2) / sqrt(PI * 2) + } +} + +fun Distribution.Companion.normal( + mean: Double, + sigma: Double, + method: NormalSamplerMethod = NormalSamplerMethod.Ziggurat +): ContinuousSamplerDistribution = object : ContinuousSamplerDistribution() { + private val sigma2 = sigma.pow(2) + private val norm = sigma * sqrt(PI * 2) + + override fun buildCMSampler(generator: RandomGenerator): ContinuousSampler { + val provider = generator.asUniformRandomProvider() + val normalizedSampler = normalSampler(method, provider) + return GaussianSampler(normalizedSampler, mean, sigma) + } + + override fun probability(arg: Double): Double { + return exp(-(arg - mean).pow(2) / 2 / sigma2) / norm + } +} + +fun Distribution.Companion.poisson( + lambda: Double +): DiscreteSamplerDistribution = object : DiscreteSamplerDistribution() { + + override fun buildSampler(generator: RandomGenerator): DiscreteSampler { + return PoissonSampler.of(generator.asUniformRandomProvider(), lambda) + } + + private val computedProb: HashMap = hashMapOf(0 to exp(-lambda)) + + override fun probability(arg: Int): Double { + require(arg >= 0) { "The argument must be >= 0" } + + return if (arg > 40) + exp(-(arg - lambda).pow(2) / 2 / lambda) / sqrt(2 * PI * lambda) + else + computedProb.getOrPut(arg) { probability(arg - 1) * lambda / arg } + } +} diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt index f5a73a08b..8c4b59abb 100644 --- a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt +++ b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt @@ -1,20 +1,18 @@ package scientifik.kmath.prob -import org.apache.commons.rng.UniformRandomProvider import org.apache.commons.rng.simple.RandomSource +import scientifik.commons.rng.UniformRandomProvider -class RandomSourceGenerator(val source: RandomSource, seed: Long?) : RandomGenerator { - internal val random: UniformRandomProvider = seed?.let { +class RandomSourceGenerator(val source: RandomSource, seed: Long?) : + RandomGenerator { + internal val random = seed?.let { RandomSource.create(source, seed) } ?: RandomSource.create(source) override fun nextBoolean(): Boolean = random.nextBoolean() - override fun nextDouble(): Double = random.nextDouble() - override fun nextInt(): Int = random.nextInt() override fun nextInt(until: Int): Int = random.nextInt(until) - override fun nextLong(): Long = random.nextLong() override fun nextLong(until: Long): Long = random.nextLong(until) @@ -26,39 +24,23 @@ class RandomSourceGenerator(val source: RandomSource, seed: Long?) : RandomGener override fun fork(): RandomGenerator = RandomSourceGenerator(source, nextLong()) } -inline class RandomGeneratorProvider(val generator: RandomGenerator) : UniformRandomProvider { - override fun nextBoolean(): Boolean = generator.nextBoolean() - - override fun nextFloat(): Float = generator.nextDouble().toFloat() - - override fun nextBytes(bytes: ByteArray) { - generator.fillBytes(bytes) - } - - override fun nextBytes(bytes: ByteArray, start: Int, len: Int) { - generator.fillBytes(bytes, start, start + len) - } - - override fun nextInt(): Int = generator.nextInt() - - override fun nextInt(n: Int): Int = generator.nextInt(n) - - override fun nextDouble(): Double = generator.nextDouble() - - override fun nextLong(): Long = generator.nextLong() - - override fun nextLong(n: Long): Long = generator.nextLong(n) -} - /** * Represent this [RandomGenerator] as commons-rng [UniformRandomProvider] preserving and mirroring its current state. * Getting new value from one of those changes the state of another. */ fun RandomGenerator.asUniformRandomProvider(): UniformRandomProvider = if (this is RandomSourceGenerator) { - random -} else { - RandomGeneratorProvider(this) -} + object : UniformRandomProvider { + override fun nextBytes(bytes: ByteArray) = random.nextBytes(bytes) + override fun nextBytes(bytes: ByteArray, start: Int, len: Int) = random.nextBytes(bytes, start, len) + override fun nextInt(): Int = random.nextInt() + override fun nextInt(n: Int): Int = random.nextInt(n) + override fun nextLong(): Long = random.nextLong() + override fun nextLong(n: Long): Long = random.nextLong(n) + override fun nextBoolean(): Boolean = random.nextBoolean() + override fun nextFloat(): Float = random.nextFloat() + override fun nextDouble(): Double = random.nextDouble() + } +} else RandomGeneratorProvider(this) fun RandomGenerator.Companion.fromSource(source: RandomSource, seed: Long? = null): RandomSourceGenerator = RandomSourceGenerator(source, seed) diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/distributions.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/distributions.kt deleted file mode 100644 index 412454994..000000000 --- a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/distributions.kt +++ /dev/null @@ -1,109 +0,0 @@ -package scientifik.kmath.prob - -import org.apache.commons.rng.UniformRandomProvider -import org.apache.commons.rng.sampling.distribution.* -import scientifik.kmath.chains.BlockingIntChain -import scientifik.kmath.chains.BlockingRealChain -import scientifik.kmath.chains.Chain -import java.util.* -import kotlin.math.PI -import kotlin.math.exp -import kotlin.math.pow -import kotlin.math.sqrt - -abstract class ContinuousSamplerDistribution : Distribution { - - private inner class ContinuousSamplerChain(val generator: RandomGenerator) : BlockingRealChain() { - private val sampler = buildCMSampler(generator) - - override fun nextDouble(): Double = sampler.sample() - - override fun fork(): Chain = ContinuousSamplerChain(generator.fork()) - } - - protected abstract fun buildCMSampler(generator: RandomGenerator): ContinuousSampler - - override fun sample(generator: RandomGenerator): BlockingRealChain = ContinuousSamplerChain(generator) -} - -abstract class DiscreteSamplerDistribution : Distribution { - - private inner class ContinuousSamplerChain(val generator: RandomGenerator) : BlockingIntChain() { - private val sampler = buildSampler(generator) - - override fun nextInt(): Int = sampler.sample() - - override fun fork(): Chain = ContinuousSamplerChain(generator.fork()) - } - - protected abstract fun buildSampler(generator: RandomGenerator): DiscreteSampler - - override fun sample(generator: RandomGenerator): BlockingIntChain = ContinuousSamplerChain(generator) -} - -enum class NormalSamplerMethod { - BoxMuller, - Marsaglia, - Ziggurat -} - -private fun normalSampler(method: NormalSamplerMethod, provider: UniformRandomProvider): NormalizedGaussianSampler = - when (method) { - NormalSamplerMethod.BoxMuller -> BoxMullerNormalizedGaussianSampler(provider) - NormalSamplerMethod.Marsaglia -> MarsagliaNormalizedGaussianSampler(provider) - NormalSamplerMethod.Ziggurat -> ZigguratNormalizedGaussianSampler(provider) - } - -fun Distribution.Companion.normal( - method: NormalSamplerMethod = NormalSamplerMethod.Ziggurat -): Distribution = object : ContinuousSamplerDistribution() { - override fun buildCMSampler(generator: RandomGenerator): ContinuousSampler { - val provider: UniformRandomProvider = generator.asUniformRandomProvider() - return normalSampler(method, provider) - } - - override fun probability(arg: Double): Double { - return exp(-arg.pow(2) / 2) / sqrt(PI * 2) - } -} - -fun Distribution.Companion.normal( - mean: Double, - sigma: Double, - method: NormalSamplerMethod = NormalSamplerMethod.Ziggurat -): ContinuousSamplerDistribution = object : ContinuousSamplerDistribution() { - private val sigma2 = sigma.pow(2) - private val norm = sigma * sqrt(PI * 2) - - override fun buildCMSampler(generator: RandomGenerator): ContinuousSampler { - val provider: UniformRandomProvider = generator.asUniformRandomProvider() - val normalizedSampler = normalSampler(method, provider) - return GaussianSampler(normalizedSampler, mean, sigma) - } - - override fun probability(arg: Double): Double { - return exp(-(arg - mean).pow(2) / 2 / sigma2) / norm - } -} - -fun Distribution.Companion.poisson( - lambda: Double -): DiscreteSamplerDistribution = object : DiscreteSamplerDistribution() { - - override fun buildSampler(generator: RandomGenerator): DiscreteSampler { - return PoissonSampler.of(generator.asUniformRandomProvider(), lambda) - } - - private val computedProb: HashMap = hashMapOf(0 to exp(-lambda)) - - override fun probability(arg: Int): Double { - require(arg >= 0) { "The argument must be >= 0" } - return if (arg > 40) { - exp(-(arg - lambda).pow(2) / 2 / lambda) / sqrt(2 * PI * lambda) - } else { - computedProb.getOrPut(arg) { - probability(arg - 1) * lambda / arg - } - } - } -} diff --git a/settings.gradle.kts b/settings.gradle.kts index 57173250b..f73a80994 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -35,6 +35,7 @@ include( ":kmath-functions", // ":kmath-io", ":kmath-coroutines", + "kmath-commons-rng-part", ":kmath-histograms", ":kmath-commons", ":kmath-viktor", From bc59f8b2875e5d3fd0e93b77d45f923a79db844f Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 15:13:54 +0700 Subject: [PATCH 02/31] Merger kmath-prob and kmath-commons-rng-part --- kmath-commons-rng-part/build.gradle.kts | 2 - .../rng/sampling/SharedStateSampler.kt | 7 --- .../distribution/ContinuousSampler.kt | 5 -- .../sampling/distribution/DiscreteSampler.kt | 5 -- .../distribution/NormalizedGaussianSampler.kt | 3 -- .../SharedStateContinuousSampler.kt | 7 --- kmath-prob/build.gradle.kts | 15 ++---- .../commons/rng/UniformRandomProvider.kt | 2 +- .../rng/sampling/SharedStateSampler.kt | 7 +++ .../AhrensDieterExponentialSampler.kt | 14 +++-- .../BoxMullerNormalizedGaussianSampler.kt | 4 +- .../distribution/ContinuousSampler.kt | 5 ++ .../sampling/distribution/DiscreteSampler.kt | 5 ++ .../sampling/distribution/GaussianSampler.kt | 20 +++++-- .../sampling/distribution/InternalGamma.kt | 2 +- .../sampling/distribution/InternalUtils.kt | 33 ++++++++---- .../KempSmallMeanPoissonSampler.kt | 10 ++-- .../distribution/LargenMeanPoissonSampler.kt | 38 +++++++++---- .../MarsagliaNormalizedGaussianSampler.kt | 4 +- .../distribution/NormalizedGaussianSampler.kt | 4 ++ .../sampling/distribution/PoissonSampler.kt | 18 +++++-- .../rng/sampling/distribution/SamplerBase.kt | 4 +- .../SharedStateContinuousSampler.kt | 7 +++ .../SharedStateDiscreteSampler.kt | 4 +- .../distribution/SmallMeanPoissonSampler.kt | 10 ++-- .../ZigguratNormalizedGaussianSampler.kt | 22 +++++--- .../scientifik/kmath/prob/Distributions.kt | 53 ------------------- .../kmath/prob/RandomSourceGenerator.kt | 5 +- .../{Distributions.kt => DistributionsJVM.kt} | 14 +++-- .../kmath/prob/RandomSourceGenerator.kt | 2 +- settings.gradle.kts | 2 +- 31 files changed, 175 insertions(+), 158 deletions(-) delete mode 100644 kmath-commons-rng-part/build.gradle.kts delete mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/SharedStateSampler.kt delete mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/ContinuousSampler.kt delete mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/DiscreteSampler.kt delete mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/NormalizedGaussianSampler.kt delete mode 100644 kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SharedStateContinuousSampler.kt rename {kmath-commons-rng-part/src/commonMain/kotlin/scientifik => kmath-prob/src/commonMain/kotlin/scientifik/kmath}/commons/rng/UniformRandomProvider.kt (90%) create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/SharedStateSampler.kt rename {kmath-commons-rng-part/src/commonMain/kotlin/scientifik => kmath-prob/src/commonMain/kotlin/scientifik/kmath}/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.kt (88%) rename {kmath-commons-rng-part/src/commonMain/kotlin/scientifik => kmath-prob/src/commonMain/kotlin/scientifik/kmath}/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.kt (92%) create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/ContinuousSampler.kt create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/DiscreteSampler.kt rename {kmath-commons-rng-part/src/commonMain/kotlin/scientifik => kmath-prob/src/commonMain/kotlin/scientifik/kmath}/commons/rng/sampling/distribution/GaussianSampler.kt (69%) rename {kmath-commons-rng-part/src/commonMain/kotlin/scientifik => kmath-prob/src/commonMain/kotlin/scientifik/kmath}/commons/rng/sampling/distribution/InternalGamma.kt (95%) rename {kmath-commons-rng-part/src/commonMain/kotlin/scientifik => kmath-prob/src/commonMain/kotlin/scientifik/kmath}/commons/rng/sampling/distribution/InternalUtils.kt (72%) rename {kmath-commons-rng-part/src/commonMain/kotlin/scientifik => kmath-prob/src/commonMain/kotlin/scientifik/kmath}/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.kt (88%) rename {kmath-commons-rng-part/src/commonMain/kotlin/scientifik => kmath-prob/src/commonMain/kotlin/scientifik/kmath}/commons/rng/sampling/distribution/LargenMeanPoissonSampler.kt (88%) rename {kmath-commons-rng-part/src/commonMain/kotlin/scientifik => kmath-prob/src/commonMain/kotlin/scientifik/kmath}/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.kt (94%) create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/NormalizedGaussianSampler.kt rename {kmath-commons-rng-part/src/commonMain/kotlin/scientifik => kmath-prob/src/commonMain/kotlin/scientifik/kmath}/commons/rng/sampling/distribution/PoissonSampler.kt (64%) rename {kmath-commons-rng-part/src/commonMain/kotlin/scientifik => kmath-prob/src/commonMain/kotlin/scientifik/kmath}/commons/rng/sampling/distribution/SamplerBase.kt (78%) create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SharedStateContinuousSampler.kt rename {kmath-commons-rng-part/src/commonMain/kotlin/scientifik => kmath-prob/src/commonMain/kotlin/scientifik/kmath}/commons/rng/sampling/distribution/SharedStateDiscreteSampler.kt (52%) rename {kmath-commons-rng-part/src/commonMain/kotlin/scientifik => kmath-prob/src/commonMain/kotlin/scientifik/kmath}/commons/rng/sampling/distribution/SmallMeanPoissonSampler.kt (83%) rename {kmath-commons-rng-part/src/commonMain/kotlin/scientifik => kmath-prob/src/commonMain/kotlin/scientifik/kmath}/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.kt (86%) delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distributions.kt rename kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/{Distributions.kt => DistributionsJVM.kt} (82%) diff --git a/kmath-commons-rng-part/build.gradle.kts b/kmath-commons-rng-part/build.gradle.kts deleted file mode 100644 index 9d3cd0e7d..000000000 --- a/kmath-commons-rng-part/build.gradle.kts +++ /dev/null @@ -1,2 +0,0 @@ -plugins { id("scientifik.mpp") } -kotlin.sourceSets { commonMain.get().dependencies { api(project(":kmath-coroutines")) } } diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/SharedStateSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/SharedStateSampler.kt deleted file mode 100644 index d32a646c2..000000000 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/SharedStateSampler.kt +++ /dev/null @@ -1,7 +0,0 @@ -package scientifik.commons.rng.sampling - -import scientifik.commons.rng.UniformRandomProvider - -interface SharedStateSampler { - fun withUniformRandomProvider(rng: UniformRandomProvider): R -} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/ContinuousSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/ContinuousSampler.kt deleted file mode 100644 index 4841672a2..000000000 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/ContinuousSampler.kt +++ /dev/null @@ -1,5 +0,0 @@ -package scientifik.commons.rng.sampling.distribution - -interface ContinuousSampler { - fun sample(): Double -} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/DiscreteSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/DiscreteSampler.kt deleted file mode 100644 index 8e8bccd18..000000000 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/DiscreteSampler.kt +++ /dev/null @@ -1,5 +0,0 @@ -package scientifik.commons.rng.sampling.distribution - -interface DiscreteSampler { - fun sample(): Int -} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/NormalizedGaussianSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/NormalizedGaussianSampler.kt deleted file mode 100644 index feff4d954..000000000 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/NormalizedGaussianSampler.kt +++ /dev/null @@ -1,3 +0,0 @@ -package scientifik.commons.rng.sampling.distribution - -interface NormalizedGaussianSampler : ContinuousSampler diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SharedStateContinuousSampler.kt b/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SharedStateContinuousSampler.kt deleted file mode 100644 index ddac4b7a7..000000000 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SharedStateContinuousSampler.kt +++ /dev/null @@ -1,7 +0,0 @@ -package scientifik.commons.rng.sampling.distribution - -import scientifik.commons.rng.sampling.SharedStateSampler - -interface SharedStateContinuousSampler : ContinuousSampler, - SharedStateSampler { -} diff --git a/kmath-prob/build.gradle.kts b/kmath-prob/build.gradle.kts index 4ec254e82..4c1d7f949 100644 --- a/kmath-prob/build.gradle.kts +++ b/kmath-prob/build.gradle.kts @@ -3,17 +3,10 @@ plugins { } kotlin.sourceSets { - commonMain { - dependencies { - api(project(":kmath-coroutines")) - api(project(":kmath-commons-rng-part")) - } - } + commonMain.get().dependencies { api(project(":kmath-coroutines")) } - jvmMain { - dependencies { - api("org.apache.commons:commons-rng-sampling:1.3") - api("org.apache.commons:commons-rng-simple:1.3") - } + jvmMain.get().dependencies { + api("org.apache.commons:commons-rng-sampling:1.3") + api("org.apache.commons:commons-rng-simple:1.3") } } \ No newline at end of file diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/UniformRandomProvider.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/UniformRandomProvider.kt similarity index 90% rename from kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/UniformRandomProvider.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/UniformRandomProvider.kt index 2fbf7a0a2..bd6d30124 100644 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/UniformRandomProvider.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/UniformRandomProvider.kt @@ -1,4 +1,4 @@ -package scientifik.commons.rng +package scientifik.kmath.commons.rng interface UniformRandomProvider { fun nextBytes(bytes: ByteArray) diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/SharedStateSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/SharedStateSampler.kt new file mode 100644 index 000000000..30ccefb54 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/SharedStateSampler.kt @@ -0,0 +1,7 @@ +package scientifik.kmath.commons.rng.sampling + +import scientifik.kmath.commons.rng.UniformRandomProvider + +interface SharedStateSampler { + fun withUniformRandomProvider(rng: UniformRandomProvider): R +} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.kt similarity index 88% rename from kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.kt index 7aa061951..714cf2164 100644 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.kt @@ -1,6 +1,6 @@ -package scientifik.commons.rng.sampling.distribution +package scientifik.kmath.commons.rng.sampling.distribution -import scientifik.commons.rng.UniformRandomProvider +import scientifik.kmath.commons.rng.UniformRandomProvider import kotlin.math.ln import kotlin.math.pow @@ -76,7 +76,11 @@ class AhrensDieterExponentialSampler : SamplerBase, fun of( rng: UniformRandomProvider, mean: Double - ): SharedStateContinuousSampler = AhrensDieterExponentialSampler(rng, mean) + ): SharedStateContinuousSampler = + AhrensDieterExponentialSampler( + rng, + mean + ) init { /** @@ -87,7 +91,9 @@ class AhrensDieterExponentialSampler : SamplerBase, var qi = 0.0 EXPONENTIAL_SA_QI.indices.forEach { i -> - qi += ln2.pow(i + 1.0) / InternalUtils.factorial(i + 1) + qi += ln2.pow(i + 1.0) / InternalUtils.factorial( + i + 1 + ) EXPONENTIAL_SA_QI[i] = qi } } diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.kt similarity index 92% rename from kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.kt index 91b3314b5..0e927bb32 100644 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.kt @@ -1,6 +1,6 @@ -package scientifik.commons.rng.sampling.distribution +package scientifik.kmath.commons.rng.sampling.distribution -import scientifik.commons.rng.UniformRandomProvider +import scientifik.kmath.commons.rng.UniformRandomProvider import kotlin.math.* class BoxMullerNormalizedGaussianSampler( diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/ContinuousSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/ContinuousSampler.kt new file mode 100644 index 000000000..aea81cf92 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/ContinuousSampler.kt @@ -0,0 +1,5 @@ +package scientifik.kmath.commons.rng.sampling.distribution + +interface ContinuousSampler { + fun sample(): Double +} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/DiscreteSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/DiscreteSampler.kt new file mode 100644 index 000000000..4de780424 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/DiscreteSampler.kt @@ -0,0 +1,5 @@ +package scientifik.kmath.commons.rng.sampling.distribution + +interface DiscreteSampler { + fun sample(): Int +} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/GaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/GaussianSampler.kt similarity index 69% rename from kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/GaussianSampler.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/GaussianSampler.kt index 424ea90fe..0b54a0ad1 100644 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/GaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/GaussianSampler.kt @@ -1,8 +1,9 @@ -package scientifik.commons.rng.sampling.distribution +package scientifik.kmath.commons.rng.sampling.distribution -import scientifik.commons.rng.UniformRandomProvider +import scientifik.kmath.commons.rng.UniformRandomProvider -class GaussianSampler : SharedStateContinuousSampler { +class GaussianSampler : + SharedStateContinuousSampler { private val mean: Double private val standardDeviation: Double private val normalized: NormalizedGaussianSampler @@ -24,7 +25,11 @@ class GaussianSampler : SharedStateContinuousSampler { ) { mean = source.mean standardDeviation = source.standardDeviation - normalized = InternalUtils.newNormalizedGaussianSampler(source.normalized, rng) + normalized = + InternalUtils.newNormalizedGaussianSampler( + source.normalized, + rng + ) } override fun sample(): Double = standardDeviation * normalized.sample() + mean @@ -40,6 +45,11 @@ class GaussianSampler : SharedStateContinuousSampler { normalized: NormalizedGaussianSampler, mean: Double, standardDeviation: Double - ): SharedStateContinuousSampler = GaussianSampler(normalized, mean, standardDeviation) + ): SharedStateContinuousSampler = + GaussianSampler( + normalized, + mean, + standardDeviation + ) } } diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/InternalGamma.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/InternalGamma.kt similarity index 95% rename from kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/InternalGamma.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/InternalGamma.kt index a75ba1608..79426d7ae 100644 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/InternalGamma.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/InternalGamma.kt @@ -1,4 +1,4 @@ -package scientifik.commons.rng.sampling.distribution +package scientifik.kmath.commons.rng.sampling.distribution import kotlin.math.PI import kotlin.math.ln diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/InternalUtils.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/InternalUtils.kt similarity index 72% rename from kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/InternalUtils.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/InternalUtils.kt index 8134252aa..0f563b956 100644 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/InternalUtils.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/InternalUtils.kt @@ -1,7 +1,7 @@ -package scientifik.commons.rng.sampling.distribution +package scientifik.kmath.commons.rng.sampling.distribution -import scientifik.commons.rng.UniformRandomProvider -import scientifik.commons.rng.sampling.SharedStateSampler +import scientifik.kmath.commons.rng.UniformRandomProvider +import scientifik.kmath.commons.rng.sampling.SharedStateSampler import kotlin.math.ln import kotlin.math.min @@ -63,30 +63,45 @@ internal object InternalUtils { if (cache != null && cache.size > BEGIN_LOG_FACTORIALS) { // Copy available values. endCopy = min(cache.size, numValues) - cache.copyInto(logFactorials, BEGIN_LOG_FACTORIALS, BEGIN_LOG_FACTORIALS, endCopy) + cache.copyInto(logFactorials, + BEGIN_LOG_FACTORIALS, + BEGIN_LOG_FACTORIALS, endCopy) } // All values to be computed else - endCopy = BEGIN_LOG_FACTORIALS + endCopy = + BEGIN_LOG_FACTORIALS // Compute remaining values. (endCopy until numValues).forEach { i -> - if (i < FACTORIALS.size) logFactorials[i] = ln(FACTORIALS[i].toDouble()) else logFactorials[i] = + if (i < FACTORIALS.size) logFactorials[i] = ln( + FACTORIALS[i].toDouble()) else logFactorials[i] = logFactorials[i - 1] + ln(i.toDouble()) } } - fun withCache(cacheSize: Int): FactorialLog = FactorialLog(cacheSize, logFactorials) + fun withCache(cacheSize: Int): FactorialLog = + FactorialLog( + cacheSize, + logFactorials + ) fun value(n: Int): Double { if (n < logFactorials.size) return logFactorials[n] - return if (n < FACTORIALS.size) ln(FACTORIALS[n].toDouble()) else InternalGamma.logGamma(n + 1.0) + return if (n < FACTORIALS.size) ln( + FACTORIALS[n].toDouble()) else InternalGamma.logGamma( + n + 1.0 + ) } companion object { - fun create(): FactorialLog = FactorialLog(0, null) + fun create(): FactorialLog = + FactorialLog( + 0, + null + ) } } } diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.kt similarity index 88% rename from kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.kt index 6501089b2..3072cf020 100644 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.kt @@ -1,6 +1,6 @@ -package scientifik.commons.rng.sampling.distribution +package scientifik.kmath.commons.rng.sampling.distribution -import scientifik.commons.rng.UniformRandomProvider +import scientifik.kmath.commons.rng.UniformRandomProvider import kotlin.math.exp class KempSmallMeanPoissonSampler private constructor( @@ -48,7 +48,11 @@ class KempSmallMeanPoissonSampler private constructor( val p0: Double = exp(-mean) // Probability must be positive. As mean increases then p(0) decreases. - if (p0 > 0) return KempSmallMeanPoissonSampler(rng, p0, mean) + if (p0 > 0) return KempSmallMeanPoissonSampler( + rng, + p0, + mean + ) throw IllegalArgumentException("No probability for mean: $mean") } } diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/LargenMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/LargenMeanPoissonSampler.kt similarity index 88% rename from kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/LargenMeanPoissonSampler.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/LargenMeanPoissonSampler.kt index ce1e1e3b0..ae7d3f079 100644 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/LargenMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/LargenMeanPoissonSampler.kt @@ -1,9 +1,10 @@ -package scientifik.commons.rng.sampling.distribution +package scientifik.kmath.commons.rng.sampling.distribution -import scientifik.commons.rng.UniformRandomProvider +import scientifik.kmath.commons.rng.UniformRandomProvider import kotlin.math.* -class LargeMeanPoissonSampler : SharedStateDiscreteSampler { +class LargeMeanPoissonSampler : + SharedStateDiscreteSampler { private val rng: UniformRandomProvider private val exponential: SharedStateContinuousSampler private val gaussian: SharedStateContinuousSampler @@ -27,8 +28,13 @@ class LargeMeanPoissonSampler : SharedStateDiscreteSampler { // The algorithm is not valid if Math.floor(mean) is not an integer. require(mean <= MAX_MEAN) { "mean $mean > $MAX_MEAN" } this.rng = rng - gaussian = ZigguratNormalizedGaussianSampler(rng) - exponential = AhrensDieterExponentialSampler.of(rng, 1.0) + gaussian = + ZigguratNormalizedGaussianSampler(rng) + exponential = + AhrensDieterExponentialSampler.of( + rng, + 1.0 + ) // Plain constructor uses the uncached function. factorialLog = NO_CACHE_FACTORIAL_LOG!! // Cache values used in the algorithm @@ -49,7 +55,10 @@ class LargeMeanPoissonSampler : SharedStateDiscreteSampler { val lambdaFractional = mean - lambda smallMeanPoissonSampler = if (lambdaFractional < Double.MIN_VALUE) NO_SMALL_MEAN_POISSON_SAMPLER else // Not used. - KempSmallMeanPoissonSampler.of(rng, lambdaFractional) + KempSmallMeanPoissonSampler.of( + rng, + lambdaFractional + ) } internal constructor( @@ -59,8 +68,13 @@ class LargeMeanPoissonSampler : SharedStateDiscreteSampler { ) { require(!(lambdaFractional < 0 || lambdaFractional >= 1)) { "lambdaFractional must be in the range 0 (inclusive) to 1 (exclusive): $lambdaFractional" } this.rng = rng - gaussian = ZigguratNormalizedGaussianSampler(rng) - exponential = AhrensDieterExponentialSampler.of(rng, 1.0) + gaussian = + ZigguratNormalizedGaussianSampler(rng) + exponential = + AhrensDieterExponentialSampler.of( + rng, + 1.0 + ) // Plain constructor uses the uncached function. factorialLog = NO_CACHE_FACTORIAL_LOG!! // Use the state to initialise the algorithm @@ -79,7 +93,10 @@ class LargeMeanPoissonSampler : SharedStateDiscreteSampler { if (lambdaFractional < Double.MIN_VALUE) NO_SMALL_MEAN_POISSON_SAMPLER else // Not used. - KempSmallMeanPoissonSampler.of(rng, lambdaFractional) + KempSmallMeanPoissonSampler.of( + rng, + lambdaFractional + ) } /** @@ -222,7 +239,8 @@ class LargeMeanPoissonSampler : SharedStateDiscreteSampler { fun of( rng: UniformRandomProvider, mean: Double - ): SharedStateDiscreteSampler = LargeMeanPoissonSampler(rng, mean) + ): SharedStateDiscreteSampler = + LargeMeanPoissonSampler(rng, mean) init { // Create without a cache. diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.kt similarity index 94% rename from kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.kt index 26ee9ece9..ee346718e 100644 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.kt @@ -1,6 +1,6 @@ -package scientifik.commons.rng.sampling.distribution +package scientifik.kmath.commons.rng.sampling.distribution -import scientifik.commons.rng.UniformRandomProvider +import scientifik.kmath.commons.rng.UniformRandomProvider import kotlin.math.ln import kotlin.math.sqrt diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/NormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/NormalizedGaussianSampler.kt new file mode 100644 index 000000000..c47b9b4e9 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/NormalizedGaussianSampler.kt @@ -0,0 +1,4 @@ +package scientifik.kmath.commons.rng.sampling.distribution + +interface NormalizedGaussianSampler : + ContinuousSampler diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/PoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/PoissonSampler.kt similarity index 64% rename from kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/PoissonSampler.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/PoissonSampler.kt index 4a7f56f60..a440c3a7d 100644 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/PoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/PoissonSampler.kt @@ -1,11 +1,12 @@ -package scientifik.commons.rng.sampling.distribution +package scientifik.kmath.commons.rng.sampling.distribution -import scientifik.commons.rng.UniformRandomProvider +import scientifik.kmath.commons.rng.UniformRandomProvider class PoissonSampler( rng: UniformRandomProvider, mean: Double -) : SamplerBase(null), SharedStateDiscreteSampler { +) : SamplerBase(null), + SharedStateDiscreteSampler { private val poissonSamplerDelegate: SharedStateDiscreteSampler override fun sample(): Int = poissonSamplerDelegate.sample() @@ -22,11 +23,18 @@ class PoissonSampler( rng: UniformRandomProvider, mean: Double ): SharedStateDiscreteSampler =// Each sampler should check the input arguments. - if (mean < PIVOT) SmallMeanPoissonSampler.of(rng, mean) else LargeMeanPoissonSampler.of(rng, mean) + if (mean < PIVOT) SmallMeanPoissonSampler.of( + rng, + mean + ) else LargeMeanPoissonSampler.of( + rng, + mean + ) } init { // Delegate all work to specialised samplers. - poissonSamplerDelegate = of(rng, mean) + poissonSamplerDelegate = + of(rng, mean) } } \ No newline at end of file diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SamplerBase.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SamplerBase.kt similarity index 78% rename from kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SamplerBase.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SamplerBase.kt index 38df9eb4e..4170e9db3 100644 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SamplerBase.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SamplerBase.kt @@ -1,6 +1,6 @@ -package scientifik.commons.rng.sampling.distribution +package scientifik.kmath.commons.rng.sampling.distribution -import scientifik.commons.rng.UniformRandomProvider +import scientifik.kmath.commons.rng.UniformRandomProvider @Deprecated("Since version 1.1. Class intended for internal use only.") open class SamplerBase protected constructor(private val rng: UniformRandomProvider?) { diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SharedStateContinuousSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SharedStateContinuousSampler.kt new file mode 100644 index 000000000..c833ce8de --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SharedStateContinuousSampler.kt @@ -0,0 +1,7 @@ +package scientifik.kmath.commons.rng.sampling.distribution + +import scientifik.kmath.commons.rng.sampling.SharedStateSampler + +interface SharedStateContinuousSampler : ContinuousSampler, + SharedStateSampler { +} diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SharedStateDiscreteSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SharedStateDiscreteSampler.kt similarity index 52% rename from kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SharedStateDiscreteSampler.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SharedStateDiscreteSampler.kt index 2d8adada6..7b17e4670 100644 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SharedStateDiscreteSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SharedStateDiscreteSampler.kt @@ -1,6 +1,6 @@ -package scientifik.commons.rng.sampling.distribution +package scientifik.kmath.commons.rng.sampling.distribution -import scientifik.commons.rng.sampling.SharedStateSampler +import scientifik.kmath.commons.rng.sampling.SharedStateSampler interface SharedStateDiscreteSampler : DiscreteSampler, SharedStateSampler { // Composite interface diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SmallMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SmallMeanPoissonSampler.kt similarity index 83% rename from kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SmallMeanPoissonSampler.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SmallMeanPoissonSampler.kt index a3188620f..a35dd277c 100644 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/SmallMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SmallMeanPoissonSampler.kt @@ -1,10 +1,11 @@ -package scientifik.commons.rng.sampling.distribution +package scientifik.kmath.commons.rng.sampling.distribution -import scientifik.commons.rng.UniformRandomProvider +import scientifik.kmath.commons.rng.UniformRandomProvider import kotlin.math.ceil import kotlin.math.exp -class SmallMeanPoissonSampler : SharedStateDiscreteSampler { +class SmallMeanPoissonSampler : + SharedStateDiscreteSampler { private val p0: Double private val limit: Int private val rng: UniformRandomProvider @@ -53,6 +54,7 @@ class SmallMeanPoissonSampler : SharedStateDiscreteSampler { fun of( rng: UniformRandomProvider, mean: Double - ): SharedStateDiscreteSampler = SmallMeanPoissonSampler(rng, mean) + ): SharedStateDiscreteSampler = + SmallMeanPoissonSampler(rng, mean) } } \ No newline at end of file diff --git a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.kt similarity index 86% rename from kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.kt index a916121db..26e089599 100644 --- a/kmath-commons-rng-part/src/commonMain/kotlin/scientifik/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.kt @@ -1,6 +1,6 @@ -package scientifik.commons.rng.sampling.distribution +package scientifik.kmath.commons.rng.sampling.distribution -import scientifik.commons.rng.UniformRandomProvider +import scientifik.kmath.commons.rng.UniformRandomProvider import kotlin.math.* class ZigguratNormalizedGaussianSampler(private val rng: UniformRandomProvider) : @@ -26,9 +26,13 @@ class ZigguratNormalizedGaussianSampler(private val rng: UniformRandomProvider) init { // Filling the tables. - var d = R + var d = + R var t = d - var fd = gauss(d) + var fd = + gauss( + d + ) val q = V / fd K[0] = (d / q * MAX).toLong() K[1] = 0 @@ -39,7 +43,10 @@ class ZigguratNormalizedGaussianSampler(private val rng: UniformRandomProvider) (LAST - 1 downTo 1).forEach { i -> d = sqrt(-2 * ln(V / d + fd)) - fd = gauss(d) + fd = + gauss( + d + ) K[i + 1] = (d / t * MAX).toLong() t = d F[i] = fd @@ -80,7 +87,10 @@ class ZigguratNormalizedGaussianSampler(private val rng: UniformRandomProvider) // else // Try again. // This branch is called about 0.012362 times per sample. - if (F[iz] + rng.nextDouble() * (F[iz - 1] - F[iz]) < gauss(x)) x else sample() + if (F[iz] + rng.nextDouble() * (F[iz - 1] - F[iz]) < gauss( + x + ) + ) x else sample() } } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distributions.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distributions.kt deleted file mode 100644 index fd8cc4116..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distributions.kt +++ /dev/null @@ -1,53 +0,0 @@ -package scientifik.kmath.prob - -import scientifik.commons.rng.UniformRandomProvider -import scientifik.commons.rng.sampling.distribution.* -import scientifik.kmath.chains.BlockingIntChain -import scientifik.kmath.chains.BlockingRealChain -import scientifik.kmath.chains.Chain - -abstract class ContinuousSamplerDistribution : Distribution { - - private inner class ContinuousSamplerChain(val generator: RandomGenerator) : BlockingRealChain() { - private val sampler = buildCMSampler(generator) - - override fun nextDouble(): Double = sampler.sample() - - override fun fork(): Chain = ContinuousSamplerChain(generator.fork()) - } - - protected abstract fun buildCMSampler(generator: RandomGenerator): ContinuousSampler - - override fun sample(generator: RandomGenerator): BlockingRealChain = ContinuousSamplerChain(generator) -} - -abstract class DiscreteSamplerDistribution : Distribution { - - private inner class ContinuousSamplerChain(val generator: RandomGenerator) : BlockingIntChain() { - private val sampler = buildSampler(generator) - - override fun nextInt(): Int = sampler.sample() - - override fun fork(): Chain = ContinuousSamplerChain(generator.fork()) - } - - protected abstract fun buildSampler(generator: RandomGenerator): DiscreteSampler - - override fun sample(generator: RandomGenerator): BlockingIntChain = ContinuousSamplerChain(generator) -} - -enum class NormalSamplerMethod { - BoxMuller, - Marsaglia, - Ziggurat -} - -fun normalSampler(method: NormalSamplerMethod, provider: UniformRandomProvider): NormalizedGaussianSampler = - when (method) { - NormalSamplerMethod.BoxMuller -> BoxMullerNormalizedGaussianSampler( - provider - ) - NormalSamplerMethod.Marsaglia -> MarsagliaNormalizedGaussianSampler(provider) - NormalSamplerMethod.Ziggurat -> ZigguratNormalizedGaussianSampler(provider) - } - diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt index 7be8276f3..b4056cabc 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt @@ -1,9 +1,10 @@ package scientifik.kmath.prob -import scientifik.commons.rng.UniformRandomProvider +import scientifik.kmath.commons.rng.UniformRandomProvider -inline class RandomGeneratorProvider(val generator: RandomGenerator) : UniformRandomProvider { +inline class RandomGeneratorProvider(val generator: RandomGenerator) : + UniformRandomProvider { override fun nextBoolean(): Boolean = generator.nextBoolean() override fun nextFloat(): Float = generator.nextDouble().toFloat() diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/Distributions.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/DistributionsJVM.kt similarity index 82% rename from kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/Distributions.kt rename to kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/DistributionsJVM.kt index aaeeb1b26..649cae961 100644 --- a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/Distributions.kt +++ b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/DistributionsJVM.kt @@ -1,9 +1,9 @@ package scientifik.kmath.prob -import scientifik.commons.rng.sampling.distribution.ContinuousSampler -import scientifik.commons.rng.sampling.distribution.DiscreteSampler -import scientifik.commons.rng.sampling.distribution.GaussianSampler -import scientifik.commons.rng.sampling.distribution.PoissonSampler +import scientifik.kmath.commons.rng.sampling.distribution.ContinuousSampler +import scientifik.kmath.commons.rng.sampling.distribution.DiscreteSampler +import scientifik.kmath.commons.rng.sampling.distribution.GaussianSampler +import scientifik.kmath.commons.rng.sampling.distribution.PoissonSampler import kotlin.math.PI import kotlin.math.exp import kotlin.math.pow @@ -33,7 +33,11 @@ fun Distribution.Companion.normal( override fun buildCMSampler(generator: RandomGenerator): ContinuousSampler { val provider = generator.asUniformRandomProvider() val normalizedSampler = normalSampler(method, provider) - return GaussianSampler(normalizedSampler, mean, sigma) + return GaussianSampler( + normalizedSampler, + mean, + sigma + ) } override fun probability(arg: Double): Double { diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt index 8c4b59abb..4c8d630ae 100644 --- a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt +++ b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt @@ -1,7 +1,7 @@ package scientifik.kmath.prob import org.apache.commons.rng.simple.RandomSource -import scientifik.commons.rng.UniformRandomProvider +import scientifik.kmath.commons.rng.UniformRandomProvider class RandomSourceGenerator(val source: RandomSource, seed: Long?) : RandomGenerator { diff --git a/settings.gradle.kts b/settings.gradle.kts index f73a80994..5e584dc58 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -29,13 +29,13 @@ pluginManagement { } rootProject.name = "kmath" + include( ":kmath-memory", ":kmath-core", ":kmath-functions", // ":kmath-io", ":kmath-coroutines", - "kmath-commons-rng-part", ":kmath-histograms", ":kmath-commons", ":kmath-viktor", From 28062cb09637801b7628a6b31c1a02cb1674531d Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 17:16:57 +0700 Subject: [PATCH 03/31] Minimal refactor of existing random API, move samplers implementations to samplers package, implement Sampler by all the Samplers --- .../kotlin/scientifik/kmath/chains/Chain.kt | 65 ++--- .../commons/rng/UniformRandomProvider.kt | 19 -- .../rng/sampling/SharedStateSampler.kt | 7 - .../AhrensDieterExponentialSampler.kt | 101 ------- .../BoxMullerNormalizedGaussianSampler.kt | 50 ---- .../distribution/ContinuousSampler.kt | 5 - .../sampling/distribution/DiscreteSampler.kt | 5 - .../sampling/distribution/GaussianSampler.kt | 55 ---- .../distribution/LargenMeanPoissonSampler.kt | 251 ------------------ .../distribution/NormalizedGaussianSampler.kt | 4 - .../sampling/distribution/PoissonSampler.kt | 40 --- .../rng/sampling/distribution/SamplerBase.kt | 12 - .../SharedStateContinuousSampler.kt | 7 - .../SharedStateDiscreteSampler.kt | 7 - .../distribution/SmallMeanPoissonSampler.kt | 60 ----- .../scientifik/kmath/prob/Distribution.kt | 3 + .../kmath/prob/FactorizedDistribution.kt | 1 - .../scientifik/kmath/prob/RandomGenerator.kt | 10 +- .../AhrensDieterExponentialSampler.kt | 68 +++++ .../BoxMullerNormalizedGaussianSampler.kt | 37 +++ .../kmath/prob/samplers/GaussianSampler.kt | 34 +++ .../samplers}/InternalGamma.kt | 4 +- .../samplers}/InternalUtils.kt | 50 +--- .../samplers}/KempSmallMeanPoissonSampler.kt | 35 +-- .../prob/samplers/LargeMeanPoissonSampler.kt | 132 +++++++++ .../MarsagliaNormalizedGaussianSampler.kt | 42 ++- .../samplers/NormalizedGaussianSampler.kt | 5 + .../kmath/prob/samplers/PoissonSampler.kt | 29 ++ .../prob/samplers/SmallMeanPoissonSampler.kt | 43 +++ .../ZigguratNormalizedGaussianSampler.kt | 118 ++++---- .../scientifik/kmath/prob/DistributionsJVM.kt | 66 ----- .../kmath/prob/RandomSourceGenerator.kt | 23 +- 32 files changed, 485 insertions(+), 903 deletions(-) delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/UniformRandomProvider.kt delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/SharedStateSampler.kt delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.kt delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.kt delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/ContinuousSampler.kt delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/DiscreteSampler.kt delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/GaussianSampler.kt delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/LargenMeanPoissonSampler.kt delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/NormalizedGaussianSampler.kt delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/PoissonSampler.kt delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SamplerBase.kt delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SharedStateContinuousSampler.kt delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SharedStateDiscreteSampler.kt delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SmallMeanPoissonSampler.kt create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterExponentialSampler.kt create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/GaussianSampler.kt rename kmath-prob/src/commonMain/kotlin/scientifik/kmath/{commons/rng/sampling/distribution => prob/samplers}/InternalGamma.kt (93%) rename kmath-prob/src/commonMain/kotlin/scientifik/kmath/{commons/rng/sampling/distribution => prob/samplers}/InternalUtils.kt (55%) rename kmath-prob/src/commonMain/kotlin/scientifik/kmath/{commons/rng/sampling/distribution => prob/samplers}/KempSmallMeanPoissonSampler.kt (66%) create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt rename kmath-prob/src/commonMain/kotlin/scientifik/kmath/{commons/rng/sampling/distribution => prob/samplers}/MarsagliaNormalizedGaussianSampler.kt (53%) create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/NormalizedGaussianSampler.kt create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/PoissonSampler.kt create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt rename kmath-prob/src/commonMain/kotlin/scientifik/kmath/{commons/rng/sampling/distribution => prob/samplers}/ZigguratNormalizedGaussianSampler.kt (58%) delete mode 100644 kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/DistributionsJVM.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 5635499e5..6457c74fd 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/Chain.kt @@ -19,6 +19,7 @@ package scientifik.kmath.chains import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -27,7 +28,7 @@ import kotlinx.coroutines.sync.withLock * A not-necessary-Markov chain of some type * @param R - the chain element type */ -interface Chain: Flow { +interface Chain : Flow { /** * Generate next value, changing state if needed */ @@ -39,13 +40,11 @@ interface Chain: Flow { fun fork(): Chain @OptIn(InternalCoroutinesApi::class) - override suspend fun collect(collector: FlowCollector) { - kotlinx.coroutines.flow.flow { - while (true){ - emit(next()) - } - }.collect(collector) - } + override suspend fun collect(collector: FlowCollector): Unit = flow { + while (true) + emit(next()) + + }.collect(collector) companion object } @@ -66,24 +65,18 @@ class SimpleChain(private val gen: suspend () -> R) : Chain { * A stateless Markov chain */ class MarkovChain(private val seed: suspend () -> R, private val gen: suspend (R) -> R) : Chain { - - private val mutex = Mutex() - + private val mutex: Mutex = Mutex() private var value: R? = null fun value() = value - override suspend fun next(): R { - mutex.withLock { - val newValue = gen(value ?: seed()) - value = newValue - return newValue - } + override suspend fun next(): R = mutex.withLock { + val newValue = gen(value ?: seed()) + value = newValue + return newValue } - override fun fork(): Chain { - return MarkovChain(seed = { value ?: seed() }, gen = gen) - } + override fun fork(): Chain = MarkovChain(seed = { value ?: seed() }, gen = gen) } /** @@ -97,24 +90,18 @@ class StatefulChain( private val forkState: ((S) -> S), private val gen: suspend S.(R) -> R ) : Chain { - private val mutex = Mutex() - private var value: R? = null fun value() = value - override suspend fun next(): R { - mutex.withLock { - val newValue = state.gen(value ?: state.seed()) - value = newValue - return newValue - } + override suspend fun next(): R = mutex.withLock { + val newValue = state.gen(value ?: state.seed()) + value = newValue + return newValue } - override fun fork(): Chain { - return StatefulChain(forkState(state), seed, forkState, gen) - } + override fun fork(): Chain = StatefulChain(forkState(state), seed, forkState, gen) } /** @@ -123,9 +110,7 @@ class StatefulChain( class ConstantChain(val value: T) : Chain { override suspend fun next(): T = value - override fun fork(): Chain { - return this - } + override fun fork(): Chain = this } /** @@ -143,9 +128,8 @@ fun Chain.map(func: suspend (T) -> R): Chain = object : Chain { fun Chain.filter(block: (T) -> Boolean): Chain = object : Chain { override suspend fun next(): T { var next: T - do { - next = this@filter.next() - } while (!block(next)) + do next = this@filter.next() + while (!block(next)) return next } @@ -163,7 +147,9 @@ fun Chain.collect(mapper: suspend (Chain) -> R): Chain = object fun Chain.collectWithState(state: S, stateFork: (S) -> S, mapper: suspend S.(Chain) -> R): Chain = object : Chain { override suspend fun next(): R = state.mapper(this@collectWithState) - override fun fork(): Chain = this@collectWithState.fork().collectWithState(stateFork(state), stateFork, mapper) + + override fun fork(): Chain = + this@collectWithState.fork().collectWithState(stateFork(state), stateFork, mapper) } /** @@ -171,6 +157,5 @@ fun Chain.collectWithState(state: S, stateFork: (S) -> S, mapper: s */ 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/commons/rng/UniformRandomProvider.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/UniformRandomProvider.kt deleted file mode 100644 index bd6d30124..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/UniformRandomProvider.kt +++ /dev/null @@ -1,19 +0,0 @@ -package scientifik.kmath.commons.rng - -interface UniformRandomProvider { - fun nextBytes(bytes: ByteArray) - - fun nextBytes( - bytes: ByteArray, - start: Int, - len: Int - ) - - fun nextInt(): Int - fun nextInt(n: Int): Int - fun nextLong(): Long - fun nextLong(n: Long): Long - fun nextBoolean(): Boolean - fun nextFloat(): Float - fun nextDouble(): Double -} \ No newline at end of file diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/SharedStateSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/SharedStateSampler.kt deleted file mode 100644 index 30ccefb54..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/SharedStateSampler.kt +++ /dev/null @@ -1,7 +0,0 @@ -package scientifik.kmath.commons.rng.sampling - -import scientifik.kmath.commons.rng.UniformRandomProvider - -interface SharedStateSampler { - fun withUniformRandomProvider(rng: UniformRandomProvider): R -} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.kt deleted file mode 100644 index 714cf2164..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.kt +++ /dev/null @@ -1,101 +0,0 @@ -package scientifik.kmath.commons.rng.sampling.distribution - -import scientifik.kmath.commons.rng.UniformRandomProvider -import kotlin.math.ln -import kotlin.math.pow - -class AhrensDieterExponentialSampler : SamplerBase, - SharedStateContinuousSampler { - private val mean: Double - private val rng: UniformRandomProvider - - constructor( - rng: UniformRandomProvider, - mean: Double - ) : super(null) { - require(mean > 0) { "mean is not strictly positive: $mean" } - this.rng = rng - this.mean = mean - } - - private constructor( - rng: UniformRandomProvider, - source: AhrensDieterExponentialSampler - ) : super(null) { - this.rng = rng - mean = source.mean - } - - override fun sample(): Double { - // Step 1: - var a = 0.0 - var u: Double = rng.nextDouble() - - // Step 2 and 3: - while (u < 0.5) { - a += EXPONENTIAL_SA_QI.get( - 0 - ) - u *= 2.0 - } - - // Step 4 (now u >= 0.5): - u += u - 1 - - // Step 5: - if (u <= EXPONENTIAL_SA_QI.get( - 0 - ) - ) { - return mean * (a + u) - } - - // Step 6: - var i = 0 // Should be 1, be we iterate before it in while using 0. - var u2: Double = rng.nextDouble() - var umin = u2 - - // Step 7 and 8: - do { - ++i - u2 = rng.nextDouble() - if (u2 < umin) umin = u2 - // Step 8: - } while (u > EXPONENTIAL_SA_QI[i]) // Ensured to exit since EXPONENTIAL_SA_QI[MAX] = 1. - return mean * (a + umin * EXPONENTIAL_SA_QI[0]) - } - - override fun toString(): String = "Ahrens-Dieter Exponential deviate [$rng]" - - override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateContinuousSampler = - AhrensDieterExponentialSampler(rng, this) - - companion object { - private val EXPONENTIAL_SA_QI = DoubleArray(16) - - fun of( - rng: UniformRandomProvider, - mean: Double - ): SharedStateContinuousSampler = - AhrensDieterExponentialSampler( - rng, - mean - ) - - init { - /** - * Filling EXPONENTIAL_SA_QI table. - * Note that we don't want qi = 0 in the table. - */ - val ln2 = ln(2.0) - var qi = 0.0 - - EXPONENTIAL_SA_QI.indices.forEach { i -> - qi += ln2.pow(i + 1.0) / InternalUtils.factorial( - i + 1 - ) - EXPONENTIAL_SA_QI[i] = qi - } - } - } -} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.kt deleted file mode 100644 index 0e927bb32..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.kt +++ /dev/null @@ -1,50 +0,0 @@ -package scientifik.kmath.commons.rng.sampling.distribution - -import scientifik.kmath.commons.rng.UniformRandomProvider -import kotlin.math.* - -class BoxMullerNormalizedGaussianSampler( - private val rng: UniformRandomProvider -) : - NormalizedGaussianSampler, - SharedStateContinuousSampler { - private var nextGaussian: Double = Double.NaN - - override fun sample(): Double { - val random: Double - - if (nextGaussian.isNaN()) { - // Generate a pair of Gaussian numbers. - val x = rng.nextDouble() - val y = rng.nextDouble() - val alpha = 2 * PI * x - val r = sqrt(-2 * ln(y)) - - // Return the first element of the generated pair. - random = r * cos(alpha) - - // Keep second element of the pair for next invocation. - nextGaussian = r * sin(alpha) - } else { - // Use the second element of the pair (generated at the - // previous invocation). - random = nextGaussian - - // Both elements of the pair have been used. - nextGaussian = Double.NaN - } - - return random - } - - override fun toString(): String = "Box-Muller normalized Gaussian deviate [$rng]" - - override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateContinuousSampler = - BoxMullerNormalizedGaussianSampler(rng) - - companion object { - @Suppress("UNCHECKED_CAST") - fun of(rng: UniformRandomProvider): S where S : NormalizedGaussianSampler?, S : SharedStateContinuousSampler? = - BoxMullerNormalizedGaussianSampler(rng) as S - } -} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/ContinuousSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/ContinuousSampler.kt deleted file mode 100644 index aea81cf92..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/ContinuousSampler.kt +++ /dev/null @@ -1,5 +0,0 @@ -package scientifik.kmath.commons.rng.sampling.distribution - -interface ContinuousSampler { - fun sample(): Double -} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/DiscreteSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/DiscreteSampler.kt deleted file mode 100644 index 4de780424..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/DiscreteSampler.kt +++ /dev/null @@ -1,5 +0,0 @@ -package scientifik.kmath.commons.rng.sampling.distribution - -interface DiscreteSampler { - fun sample(): Int -} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/GaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/GaussianSampler.kt deleted file mode 100644 index 0b54a0ad1..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/GaussianSampler.kt +++ /dev/null @@ -1,55 +0,0 @@ -package scientifik.kmath.commons.rng.sampling.distribution - -import scientifik.kmath.commons.rng.UniformRandomProvider - -class GaussianSampler : - SharedStateContinuousSampler { - private val mean: Double - private val standardDeviation: Double - private val normalized: NormalizedGaussianSampler - - constructor( - normalized: NormalizedGaussianSampler, - mean: Double, - standardDeviation: Double - ) { - require(standardDeviation > 0) { "standard deviation is not strictly positive: $standardDeviation" } - this.normalized = normalized - this.mean = mean - this.standardDeviation = standardDeviation - } - - private constructor( - rng: UniformRandomProvider, - source: GaussianSampler - ) { - mean = source.mean - standardDeviation = source.standardDeviation - normalized = - InternalUtils.newNormalizedGaussianSampler( - source.normalized, - rng - ) - } - - override fun sample(): Double = standardDeviation * normalized.sample() + mean - - override fun toString(): String = "Gaussian deviate [$normalized]" - - override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateContinuousSampler { - return GaussianSampler(rng, this) - } - - companion object { - fun of( - normalized: NormalizedGaussianSampler, - mean: Double, - standardDeviation: Double - ): SharedStateContinuousSampler = - GaussianSampler( - normalized, - mean, - standardDeviation - ) - } -} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/LargenMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/LargenMeanPoissonSampler.kt deleted file mode 100644 index ae7d3f079..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/LargenMeanPoissonSampler.kt +++ /dev/null @@ -1,251 +0,0 @@ -package scientifik.kmath.commons.rng.sampling.distribution - -import scientifik.kmath.commons.rng.UniformRandomProvider -import kotlin.math.* - -class LargeMeanPoissonSampler : - SharedStateDiscreteSampler { - private val rng: UniformRandomProvider - private val exponential: SharedStateContinuousSampler - private val gaussian: SharedStateContinuousSampler - private val factorialLog: InternalUtils.FactorialLog - private val lambda: Double - private val logLambda: Double - private val logLambdaFactorial: Double - private val delta: Double - private val halfDelta: Double - private val twolpd: Double - private val p1: Double - private val p2: Double - private val c1: Double - private val smallMeanPoissonSampler: SharedStateDiscreteSampler - - constructor( - rng: UniformRandomProvider, - mean: Double - ) { - require(mean >= 1) { "mean is not >= 1: $mean" } - // The algorithm is not valid if Math.floor(mean) is not an integer. - require(mean <= MAX_MEAN) { "mean $mean > $MAX_MEAN" } - this.rng = rng - gaussian = - ZigguratNormalizedGaussianSampler(rng) - exponential = - AhrensDieterExponentialSampler.of( - rng, - 1.0 - ) - // Plain constructor uses the uncached function. - factorialLog = NO_CACHE_FACTORIAL_LOG!! - // Cache values used in the algorithm - lambda = floor(mean) - logLambda = ln(lambda) - logLambdaFactorial = getFactorialLog(lambda.toInt()) - delta = sqrt(lambda * ln(32 * lambda / PI + 1)) - halfDelta = delta / 2 - twolpd = 2 * lambda + delta - c1 = 1 / (8 * lambda) - val a1: Double = sqrt(PI * twolpd) * exp(c1) - val a2: Double = twolpd / delta * exp(-delta * (1 + delta) / twolpd) - val aSum = a1 + a2 + 1 - p1 = a1 / aSum - p2 = a2 / aSum - - // The algorithm requires a Poisson sample from the remaining lambda fraction. - val lambdaFractional = mean - lambda - smallMeanPoissonSampler = - if (lambdaFractional < Double.MIN_VALUE) NO_SMALL_MEAN_POISSON_SAMPLER else // Not used. - KempSmallMeanPoissonSampler.of( - rng, - lambdaFractional - ) - } - - internal constructor( - rng: UniformRandomProvider, - state: LargeMeanPoissonSamplerState, - lambdaFractional: Double - ) { - require(!(lambdaFractional < 0 || lambdaFractional >= 1)) { "lambdaFractional must be in the range 0 (inclusive) to 1 (exclusive): $lambdaFractional" } - this.rng = rng - gaussian = - ZigguratNormalizedGaussianSampler(rng) - exponential = - AhrensDieterExponentialSampler.of( - rng, - 1.0 - ) - // Plain constructor uses the uncached function. - factorialLog = NO_CACHE_FACTORIAL_LOG!! - // Use the state to initialise the algorithm - lambda = state.lambdaRaw - logLambda = state.logLambda - logLambdaFactorial = state.logLambdaFactorial - delta = state.delta - halfDelta = state.halfDelta - twolpd = state.twolpd - p1 = state.p1 - p2 = state.p2 - c1 = state.c1 - - // The algorithm requires a Poisson sample from the remaining lambda fraction. - smallMeanPoissonSampler = - if (lambdaFractional < Double.MIN_VALUE) - NO_SMALL_MEAN_POISSON_SAMPLER - else // Not used. - KempSmallMeanPoissonSampler.of( - rng, - lambdaFractional - ) - } - - /** - * @param rng Generator of uniformly distributed random numbers. - * @param source Source to copy. - */ - private constructor( - rng: UniformRandomProvider, - source: LargeMeanPoissonSampler - ) { - this.rng = rng - gaussian = source.gaussian.withUniformRandomProvider(rng)!! - exponential = source.exponential.withUniformRandomProvider(rng)!! - // Reuse the cache - factorialLog = source.factorialLog - lambda = source.lambda - logLambda = source.logLambda - logLambdaFactorial = source.logLambdaFactorial - delta = source.delta - halfDelta = source.halfDelta - twolpd = source.twolpd - p1 = source.p1 - p2 = source.p2 - c1 = source.c1 - - // Share the state of the small sampler - smallMeanPoissonSampler = source.smallMeanPoissonSampler.withUniformRandomProvider(rng)!! - } - - /** {@inheritDoc} */ - override fun sample(): Int { - // This will never be null. It may be a no-op delegate that returns zero. - val y2: Int = smallMeanPoissonSampler.sample() - var x: Double - var y: Double - var v: Double - var a: Int - var t: Double - var qr: Double - var qa: Double - while (true) { - // Step 1: - val u = rng.nextDouble() - - if (u <= p1) { - // Step 2: - val n = gaussian.sample() - x = n * sqrt(lambda + halfDelta) - 0.5 - if (x > delta || x < -lambda) continue - y = if (x < 0) floor(x) else ceil(x) - val e = exponential.sample() - v = -e - 0.5 * n * n + c1 - } else { - // Step 3: - if (u > p1 + p2) { - y = lambda - break - } - - x = delta + twolpd / delta * exponential.sample() - y = ceil(x) - v = -exponential.sample() - delta * (x + 1) / twolpd - } - // The Squeeze Principle - // Step 4.1: - a = if (x < 0) 1 else 0 - t = y * (y + 1) / (2 * lambda) - - // Step 4.2 - if (v < -t && a == 0) { - y += lambda - break - } - - // Step 4.3: - qr = t * ((2 * y + 1) / (6 * lambda) - 1) - qa = qr - t * t / (3 * (lambda + a * (y + 1))) - - // Step 4.4: - if (v < qa) { - y += lambda - break - } - - // Step 4.5: - if (v > qr) continue - - // Step 4.6: - if (v < y * logLambda - getFactorialLog((y + lambda).toInt()) + logLambdaFactorial) { - y += lambda - break - } - } - - return min(y2 + y.toLong(), Int.MAX_VALUE.toLong()).toInt() - } - - - private fun getFactorialLog(n: Int): Double = factorialLog.value(n) - - override fun toString(): String = "Large Mean Poisson deviate [$rng]" - - override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateDiscreteSampler = - LargeMeanPoissonSampler(rng, this) - - val state: LargeMeanPoissonSamplerState - get() = LargeMeanPoissonSamplerState( - lambda, logLambda, logLambdaFactorial, - delta, halfDelta, twolpd, p1, p2, c1 - ) - - class LargeMeanPoissonSamplerState( - val lambdaRaw: Double, - val logLambda: Double, - val logLambdaFactorial: Double, - val delta: Double, - val halfDelta: Double, - val twolpd: Double, - val p1: Double, - val p2: Double, - val c1: Double - ) { - fun getLambda(): Int = lambdaRaw.toInt() - } - - companion object { - private const val MAX_MEAN = 0.5 * Int.MAX_VALUE - private var NO_CACHE_FACTORIAL_LOG: InternalUtils.FactorialLog? = null - - private val NO_SMALL_MEAN_POISSON_SAMPLER: SharedStateDiscreteSampler = - object : SharedStateDiscreteSampler { - override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateDiscreteSampler = -// No requirement for RNG - this - - override fun sample(): Int =// No Poisson sample - 0 - } - - fun of( - rng: UniformRandomProvider, - mean: Double - ): SharedStateDiscreteSampler = - LargeMeanPoissonSampler(rng, mean) - - init { - // Create without a cache. - NO_CACHE_FACTORIAL_LOG = - InternalUtils.FactorialLog.create() - } - } -} \ No newline at end of file diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/NormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/NormalizedGaussianSampler.kt deleted file mode 100644 index c47b9b4e9..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/NormalizedGaussianSampler.kt +++ /dev/null @@ -1,4 +0,0 @@ -package scientifik.kmath.commons.rng.sampling.distribution - -interface NormalizedGaussianSampler : - ContinuousSampler diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/PoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/PoissonSampler.kt deleted file mode 100644 index a440c3a7d..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/PoissonSampler.kt +++ /dev/null @@ -1,40 +0,0 @@ -package scientifik.kmath.commons.rng.sampling.distribution - -import scientifik.kmath.commons.rng.UniformRandomProvider - -class PoissonSampler( - rng: UniformRandomProvider, - mean: Double -) : SamplerBase(null), - SharedStateDiscreteSampler { - private val poissonSamplerDelegate: SharedStateDiscreteSampler - - override fun sample(): Int = poissonSamplerDelegate.sample() - override fun toString(): String = poissonSamplerDelegate.toString() - - override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateDiscreteSampler? = -// Direct return of the optimised sampler - poissonSamplerDelegate.withUniformRandomProvider(rng) - - companion object { - const val PIVOT = 40.0 - - fun of( - rng: UniformRandomProvider, - mean: Double - ): SharedStateDiscreteSampler =// Each sampler should check the input arguments. - if (mean < PIVOT) SmallMeanPoissonSampler.of( - rng, - mean - ) else LargeMeanPoissonSampler.of( - rng, - mean - ) - } - - init { - // Delegate all work to specialised samplers. - poissonSamplerDelegate = - of(rng, mean) - } -} \ No newline at end of file diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SamplerBase.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SamplerBase.kt deleted file mode 100644 index 4170e9db3..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SamplerBase.kt +++ /dev/null @@ -1,12 +0,0 @@ -package scientifik.kmath.commons.rng.sampling.distribution - -import scientifik.kmath.commons.rng.UniformRandomProvider - -@Deprecated("Since version 1.1. Class intended for internal use only.") -open class SamplerBase protected constructor(private val rng: UniformRandomProvider?) { - protected fun nextDouble(): Double = rng!!.nextDouble() - protected fun nextInt(): Int = rng!!.nextInt() - protected fun nextInt(max: Int): Int = rng!!.nextInt(max) - protected fun nextLong(): Long = rng!!.nextLong() - override fun toString(): String = "rng=$rng" -} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SharedStateContinuousSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SharedStateContinuousSampler.kt deleted file mode 100644 index c833ce8de..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SharedStateContinuousSampler.kt +++ /dev/null @@ -1,7 +0,0 @@ -package scientifik.kmath.commons.rng.sampling.distribution - -import scientifik.kmath.commons.rng.sampling.SharedStateSampler - -interface SharedStateContinuousSampler : ContinuousSampler, - SharedStateSampler { -} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SharedStateDiscreteSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SharedStateDiscreteSampler.kt deleted file mode 100644 index 7b17e4670..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SharedStateDiscreteSampler.kt +++ /dev/null @@ -1,7 +0,0 @@ -package scientifik.kmath.commons.rng.sampling.distribution - -import scientifik.kmath.commons.rng.sampling.SharedStateSampler - -interface SharedStateDiscreteSampler : DiscreteSampler, - SharedStateSampler { // Composite interface -} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SmallMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SmallMeanPoissonSampler.kt deleted file mode 100644 index a35dd277c..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/SmallMeanPoissonSampler.kt +++ /dev/null @@ -1,60 +0,0 @@ -package scientifik.kmath.commons.rng.sampling.distribution - -import scientifik.kmath.commons.rng.UniformRandomProvider -import kotlin.math.ceil -import kotlin.math.exp - -class SmallMeanPoissonSampler : - SharedStateDiscreteSampler { - private val p0: Double - private val limit: Int - private val rng: UniformRandomProvider - - constructor( - rng: UniformRandomProvider, - mean: Double - ) { - this.rng = rng - require(mean > 0) { "mean is not strictly positive: $mean" } - p0 = exp(-mean) - - limit = (if (p0 > 0) ceil(1000 * mean) else throw IllegalArgumentException("No p(x=0) probability for mean: $mean")).toInt() - // This excludes NaN values for the mean - // else - // The returned sample is bounded by 1000 * mean - } - - private constructor( - rng: UniformRandomProvider, - source: SmallMeanPoissonSampler - ) { - this.rng = rng - p0 = source.p0 - limit = source.limit - } - - override fun sample(): Int { - var n = 0 - var r = 1.0 - - while (n < limit) { - r *= rng.nextDouble() - if (r >= p0) n++ else break - } - - return n - } - - override fun toString(): String = "Small Mean Poisson deviate [$rng]" - - override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateDiscreteSampler = - SmallMeanPoissonSampler(rng, this) - - companion object { - fun of( - rng: UniformRandomProvider, - mean: Double - ): SharedStateDiscreteSampler = - SmallMeanPoissonSampler(rng, mean) - } -} \ 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 3b874adaa..a99052171 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/Distribution.kt @@ -1,5 +1,6 @@ package scientifik.kmath.prob +import kotlinx.coroutines.flow.first import scientifik.kmath.chains.Chain import scientifik.kmath.chains.collect import scientifik.kmath.structures.Buffer @@ -69,6 +70,8 @@ fun Sampler.sampleBuffer( } } +suspend fun Sampler.next(generator: RandomGenerator) = sample(generator).first() + /** * Generate a bunch of samples from real distributions */ diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt index ea526c058..ae3f918ff 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt @@ -12,7 +12,6 @@ interface NamedDistribution : Distribution> * A multivariate distribution that has independent distributions for separate axis */ class FactorizedDistribution(val distributions: Collection>) : NamedDistribution { - override fun probability(arg: Map): Double { return distributions.fold(1.0) { acc, distr -> acc * distr.probability(arg) } } 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 2a225fe47..4b42db927 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt @@ -7,13 +7,11 @@ import kotlin.random.Random */ interface RandomGenerator { fun nextBoolean(): Boolean - fun nextDouble(): Double fun nextInt(): Int fun nextInt(until: Int): Int fun nextLong(): Long fun nextLong(until: Long): Long - fun fillBytes(array: ByteArray, fromIndex: Int = 0, toIndex: Int = array.size) fun nextBytes(size: Int): ByteArray = ByteArray(size).also { fillBytes(it) } @@ -28,21 +26,16 @@ interface RandomGenerator { companion object { val default by lazy { DefaultGenerator() } - fun default(seed: Long) = DefaultGenerator(Random(seed)) } } -inline class DefaultGenerator(val random: Random = Random) : RandomGenerator { +inline class DefaultGenerator(private val random: Random = Random) : RandomGenerator { override fun nextBoolean(): Boolean = random.nextBoolean() - override fun nextDouble(): Double = random.nextDouble() - override fun nextInt(): Int = random.nextInt() override fun nextInt(until: Int): Int = random.nextInt(until) - override fun nextLong(): Long = random.nextLong() - override fun nextLong(until: Long): Long = random.nextLong(until) override fun fillBytes(array: ByteArray, fromIndex: Int, toIndex: Int) { @@ -50,6 +43,5 @@ inline class DefaultGenerator(val random: Random = Random) : RandomGenerator { } override fun nextBytes(size: Int): ByteArray = random.nextBytes(size) - override fun fork(): RandomGenerator = RandomGenerator.default(random.nextLong()) } \ No newline at end of file diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterExponentialSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterExponentialSampler.kt new file mode 100644 index 000000000..5dec2b641 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterExponentialSampler.kt @@ -0,0 +1,68 @@ +package scientifik.kmath.prob.samplers + +import scientifik.kmath.chains.Chain +import scientifik.kmath.prob.RandomGenerator +import scientifik.kmath.prob.Sampler +import scientifik.kmath.prob.chain +import kotlin.math.ln +import kotlin.math.pow + +class AhrensDieterExponentialSampler(val mean: Double) : Sampler { + init { + require(mean > 0) { "mean is not strictly positive: $mean" } + } + + override fun sample(generator: RandomGenerator): Chain = generator.chain { + // Step 1: + var a = 0.0 + var u = nextDouble() + + // Step 2 and 3: + while (u < 0.5) { + a += EXPONENTIAL_SA_QI[0] + u *= 2.0 + } + + // Step 4 (now u >= 0.5): + u += u - 1 + // Step 5: + if (u <= EXPONENTIAL_SA_QI[0]) return@chain mean * (a + u) + // Step 6: + var i = 0 // Should be 1, be we iterate before it in while using 0. + var u2 = nextDouble() + var umin = u2 + + // Step 7 and 8: + do { + ++i + u2 = nextDouble() + if (u2 < umin) umin = u2 + // Step 8: + } while (u > EXPONENTIAL_SA_QI[i]) // Ensured to exit since EXPONENTIAL_SA_QI[MAX] = 1. + + mean * (a + umin * EXPONENTIAL_SA_QI[0]) + } + + override fun toString(): String = "Ahrens-Dieter Exponential deviate" + + companion object { + private val EXPONENTIAL_SA_QI = DoubleArray(16) + + fun of(mean: Double): Sampler = + AhrensDieterExponentialSampler(mean) + + init { + /** + * Filling EXPONENTIAL_SA_QI table. + * Note that we don't want qi = 0 in the table. + */ + val ln2 = ln(2.0) + var qi = 0.0 + + EXPONENTIAL_SA_QI.indices.forEach { i -> + qi += ln2.pow(i + 1.0) / InternalUtils.factorial(i + 1) + EXPONENTIAL_SA_QI[i] = qi + } + } + } +} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt new file mode 100644 index 000000000..3235b06f5 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt @@ -0,0 +1,37 @@ +package scientifik.kmath.prob.samplers + +import scientifik.kmath.chains.Chain +import scientifik.kmath.prob.RandomGenerator +import scientifik.kmath.prob.Sampler +import scientifik.kmath.prob.chain +import kotlin.math.* + +class BoxMullerNormalizedGaussianSampler : NormalizedGaussianSampler, Sampler { + private var nextGaussian: Double = Double.NaN + + override fun sample(generator: RandomGenerator): Chain = generator.chain { + val random: Double + + if (nextGaussian.isNaN()) { + // Generate a pair of Gaussian numbers. + val x = nextDouble() + val y = nextDouble() + val alpha = 2 * PI * x + val r = sqrt(-2 * ln(y)) + // Return the first element of the generated pair. + random = r * cos(alpha) + // Keep second element of the pair for next invocation. + nextGaussian = r * sin(alpha) + } else { + // Use the second element of the pair (generated at the + // previous invocation). + random = nextGaussian + // Both elements of the pair have been used. + nextGaussian = Double.NaN + } + + random + } + + override fun toString(): String = "Box-Muller normalized Gaussian deviate" +} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/GaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/GaussianSampler.kt new file mode 100644 index 000000000..0c984ab6a --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/GaussianSampler.kt @@ -0,0 +1,34 @@ +package scientifik.kmath.prob.samplers + +import scientifik.kmath.chains.Chain +import scientifik.kmath.chains.map +import scientifik.kmath.prob.RandomGenerator +import scientifik.kmath.prob.Sampler + +class GaussianSampler( + private val mean: Double, + private val standardDeviation: Double, + private val normalized: NormalizedGaussianSampler +) : Sampler { + + init { + require(standardDeviation > 0) { "standard deviation is not strictly positive: $standardDeviation" } + } + + override fun sample(generator: RandomGenerator): Chain = + normalized.sample(generator).map { standardDeviation * it + mean } + + override fun toString(): String = "Gaussian deviate [$normalized]" + + companion object { + fun of( + normalized: NormalizedGaussianSampler, + mean: Double, + standardDeviation: Double + ) = GaussianSampler( + mean, + standardDeviation, + normalized + ) + } +} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/InternalGamma.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/InternalGamma.kt similarity index 93% rename from kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/InternalGamma.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/InternalGamma.kt index 79426d7ae..50c5f4ce0 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/InternalGamma.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/InternalGamma.kt @@ -1,10 +1,10 @@ -package scientifik.kmath.commons.rng.sampling.distribution +package scientifik.kmath.prob.samplers import kotlin.math.PI import kotlin.math.ln internal object InternalGamma { - const val LANCZOS_G = 607.0 / 128.0 + private const val LANCZOS_G = 607.0 / 128.0 private val LANCZOS_COEFFICIENTS = doubleArrayOf( 0.99999999999999709182, diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/InternalUtils.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/InternalUtils.kt similarity index 55% rename from kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/InternalUtils.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/InternalUtils.kt index 0f563b956..f2f1b3865 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/InternalUtils.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/InternalUtils.kt @@ -1,7 +1,5 @@ -package scientifik.kmath.commons.rng.sampling.distribution +package scientifik.kmath.prob.samplers -import scientifik.kmath.commons.rng.UniformRandomProvider -import scientifik.kmath.commons.rng.sampling.SharedStateSampler import kotlin.math.ln import kotlin.math.min @@ -20,8 +18,8 @@ internal object InternalUtils { fun factorial(n: Int): Long = FACTORIALS[n] - fun validateProbabilities(probabilities: DoubleArray?): Double { - require(!(probabilities == null || probabilities.isEmpty())) { "Probabilities must not be empty." } + fun validateProbabilities(probabilities: DoubleArray): Double { + require(probabilities.isNotEmpty()) { "Probabilities must not be empty." } var sumProb = 0.0 probabilities.forEach { prob -> @@ -33,23 +31,10 @@ internal object InternalUtils { return sumProb } - fun validateProbability(probability: Double): Unit = - require(!(probability < 0 || probability.isInfinite() || probability.isNaN())) { "Invalid probability: $probability" } - - fun newNormalizedGaussianSampler( - sampler: NormalizedGaussianSampler, - rng: UniformRandomProvider - ): NormalizedGaussianSampler { - if (sampler !is SharedStateSampler<*>) throw UnsupportedOperationException("The underlying sampler cannot share state") - - val newSampler: Any = - (sampler as SharedStateSampler<*>).withUniformRandomProvider(rng) as? NormalizedGaussianSampler - ?: throw UnsupportedOperationException( - "The underlying sampler did not create a normalized Gaussian sampler" - ) - - return newSampler as NormalizedGaussianSampler - } + private fun validateProbability(probability: Double): Unit = + require(!(probability < 0 || probability.isInfinite() || probability.isNaN())) { + "Invalid probability: $probability" + } class FactorialLog private constructor( numValues: Int, @@ -68,23 +53,19 @@ internal object InternalUtils { BEGIN_LOG_FACTORIALS, endCopy) } // All values to be computed - else - endCopy = - BEGIN_LOG_FACTORIALS + else endCopy = BEGIN_LOG_FACTORIALS // Compute remaining values. (endCopy until numValues).forEach { i -> - if (i < FACTORIALS.size) logFactorials[i] = ln( - FACTORIALS[i].toDouble()) else logFactorials[i] = - logFactorials[i - 1] + ln(i.toDouble()) + if (i < FACTORIALS.size) + logFactorials[i] = ln(FACTORIALS[i].toDouble()) + else + logFactorials[i] = logFactorials[i - 1] + ln(i.toDouble()) } } fun withCache(cacheSize: Int): FactorialLog = - FactorialLog( - cacheSize, - logFactorials - ) + FactorialLog(cacheSize, logFactorials) fun value(n: Int): Double { if (n < logFactorials.size) @@ -98,10 +79,7 @@ internal object InternalUtils { companion object { fun create(): FactorialLog = - FactorialLog( - 0, - null - ) + FactorialLog(0, null) } } } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt similarity index 66% rename from kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt index 3072cf020..cdd4c4cd1 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt @@ -1,14 +1,16 @@ -package scientifik.kmath.commons.rng.sampling.distribution +package scientifik.kmath.prob.samplers -import scientifik.kmath.commons.rng.UniformRandomProvider +import scientifik.kmath.chains.Chain +import scientifik.kmath.prob.RandomGenerator +import scientifik.kmath.prob.Sampler +import scientifik.kmath.prob.chain import kotlin.math.exp class KempSmallMeanPoissonSampler private constructor( - private val rng: UniformRandomProvider, private val p0: Double, private val mean: Double -) : SharedStateDiscreteSampler { - override fun sample(): Int { +) : Sampler { + override fun sample(generator: RandomGenerator): Chain = generator.chain { // Note on the algorithm: // - X is the unknown sample deviate (the output of the algorithm) // - x is the current value from the distribution @@ -16,7 +18,7 @@ class KempSmallMeanPoissonSampler private constructor( // - u is effectively the cumulative probability that the sample X // is equal or above the current value x, p(X>=x) // So if p(X>=x) > p(X=x) the sample must be above x, otherwise it is x - var u = rng.nextDouble() + var u = nextDouble() var x = 0 var p = p0 @@ -28,31 +30,20 @@ class KempSmallMeanPoissonSampler private constructor( // The algorithm listed in Kemp (1981) does not check that the rolling probability // is positive. This check is added to ensure no errors when the limit of the summation // 1 - sum(p(x)) is above 0 due to cumulative error in floating point arithmetic. - if (p == 0.0) return x + if (p == 0.0) return@chain x } - return x + x } - override fun toString(): String = "Kemp Small Mean Poisson deviate [$rng]" - - override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateDiscreteSampler = - KempSmallMeanPoissonSampler(rng, p0, mean) + override fun toString(): String = "Kemp Small Mean Poisson deviate" companion object { - fun of( - rng: UniformRandomProvider, - mean: Double - ): SharedStateDiscreteSampler { + fun of(mean: Double): KempSmallMeanPoissonSampler { require(mean > 0) { "Mean is not strictly positive: $mean" } val p0: Double = exp(-mean) - // Probability must be positive. As mean increases then p(0) decreases. - if (p0 > 0) return KempSmallMeanPoissonSampler( - rng, - p0, - mean - ) + if (p0 > 0) return KempSmallMeanPoissonSampler(p0, mean) throw IllegalArgumentException("No probability for mean: $mean") } } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt new file mode 100644 index 000000000..33b1b3f8a --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt @@ -0,0 +1,132 @@ +package scientifik.kmath.prob.samplers + +import kotlinx.coroutines.flow.first +import scientifik.kmath.chains.Chain +import scientifik.kmath.chains.ConstantChain +import scientifik.kmath.chains.SimpleChain +import scientifik.kmath.prob.RandomGenerator +import scientifik.kmath.prob.Sampler +import scientifik.kmath.prob.next +import kotlin.math.* + +class LargeMeanPoissonSampler(val mean: Double) : Sampler { + private val exponential: Sampler = + AhrensDieterExponentialSampler.of(1.0) + private val gaussian: Sampler = + ZigguratNormalizedGaussianSampler() + private val factorialLog: InternalUtils.FactorialLog = NO_CACHE_FACTORIAL_LOG!! + private val lambda: Double = floor(mean) + private val logLambda: Double = ln(lambda) + private val logLambdaFactorial: Double = getFactorialLog(lambda.toInt()) + private val delta: Double = sqrt(lambda * ln(32 * lambda / PI + 1)) + private val halfDelta: Double = delta / 2 + private val twolpd: Double = 2 * lambda + delta + private val c1: Double = 1 / (8 * lambda) + private val a1: Double = sqrt(PI * twolpd) * exp(c1) + private val a2: Double = twolpd / delta * exp(-delta * (1 + delta) / twolpd) + private val aSum: Double = a1 + a2 + 1 + private val p1: Double = a1 / aSum + private val p2: Double = a2 / aSum + + private val smallMeanPoissonSampler: Sampler = if (mean - lambda < Double.MIN_VALUE) + NO_SMALL_MEAN_POISSON_SAMPLER + else // Not used. + KempSmallMeanPoissonSampler.of(mean - lambda) + + init { + require(mean >= 1) { "mean is not >= 1: $mean" } + // The algorithm is not valid if Math.floor(mean) is not an integer. + require(mean <= MAX_MEAN) { "mean $mean > $MAX_MEAN" } + } + + override fun sample(generator: RandomGenerator): Chain = SimpleChain { + // This will never be null. It may be a no-op delegate that returns zero. + val y2 = smallMeanPoissonSampler.next(generator) + var x: Double + var y: Double + var v: Double + var a: Int + var t: Double + var qr: Double + var qa: Double + + while (true) { + // Step 1: + val u = generator.nextDouble() + + if (u <= p1) { + // Step 2: + val n = gaussian.next(generator) + x = n * sqrt(lambda + halfDelta) - 0.5 + if (x > delta || x < -lambda) continue + y = if (x < 0) floor(x) else ceil(x) + val e = exponential.next(generator) + v = -e - 0.5 * n * n + c1 + } else { + // Step 3: + if (u > p1 + p2) { + y = lambda + break + } + + x = delta + twolpd / delta * exponential.next(generator) + y = ceil(x) + v = -exponential.next(generator) - delta * (x + 1) / twolpd + } + + // The Squeeze Principle + // Step 4.1: + a = if (x < 0) 1 else 0 + t = y * (y + 1) / (2 * lambda) + + // Step 4.2 + if (v < -t && a == 0) { + y += lambda + break + } + + // Step 4.3: + qr = t * ((2 * y + 1) / (6 * lambda) - 1) + qa = qr - t * t / (3 * (lambda + a * (y + 1))) + + // Step 4.4: + if (v < qa) { + y += lambda + break + } + + // Step 4.5: + if (v > qr) continue + + // Step 4.6: + if (v < y * logLambda - getFactorialLog((y + lambda).toInt()) + logLambdaFactorial) { + y += lambda + break + } + } + + min(y2 + y.toLong(), Int.MAX_VALUE.toLong()).toInt() + } + + private fun getFactorialLog(n: Int): Double = factorialLog.value(n) + + override fun toString(): String = "Large Mean Poisson deviate" + + companion object { + private const val MAX_MEAN = 0.5 * Int.MAX_VALUE + private var NO_CACHE_FACTORIAL_LOG: InternalUtils.FactorialLog? = null + + private val NO_SMALL_MEAN_POISSON_SAMPLER = object : Sampler { + override fun sample(generator: RandomGenerator): Chain = ConstantChain(0) + } + + fun of(mean: Double): LargeMeanPoissonSampler = + LargeMeanPoissonSampler(mean) + + init { + // Create without a cache. + NO_CACHE_FACTORIAL_LOG = + InternalUtils.FactorialLog.create() + } + } +} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt similarity index 53% rename from kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt index ee346718e..c44943056 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt @@ -1,35 +1,42 @@ -package scientifik.kmath.commons.rng.sampling.distribution +package scientifik.kmath.prob.samplers -import scientifik.kmath.commons.rng.UniformRandomProvider +import scientifik.kmath.chains.Chain +import scientifik.kmath.prob.RandomGenerator +import scientifik.kmath.prob.Sampler +import scientifik.kmath.prob.chain import kotlin.math.ln import kotlin.math.sqrt -class MarsagliaNormalizedGaussianSampler(private val rng: UniformRandomProvider) : - NormalizedGaussianSampler, - SharedStateContinuousSampler { +class MarsagliaNormalizedGaussianSampler : NormalizedGaussianSampler, Sampler { private var nextGaussian = Double.NaN - override fun sample(): Double { + override fun sample(generator: RandomGenerator): Chain = generator.chain { if (nextGaussian.isNaN()) { + val alpha: Double + var x: Double + // Rejection scheme for selecting a pair that lies within the unit circle. while (true) { // Generate a pair of numbers within [-1 , 1). - val x = 2.0 * rng.nextDouble() - 1.0 - val y = 2.0 * rng.nextDouble() - 1.0 + x = 2.0 * generator.nextDouble() - 1.0 + val y = 2.0 * generator.nextDouble() - 1.0 val r2 = x * x + y * y + if (r2 < 1 && r2 > 0) { // Pair (x, y) is within unit circle. - val alpha = sqrt(-2 * ln(r2) / r2) + alpha = sqrt(-2 * ln(r2) / r2) // Keep second element of the pair for next invocation. nextGaussian = alpha * y // Return the first element of the generated pair. - return alpha * x + break } - // Pair is not within the unit circle: Generate another one. } + + // Return the first element of the generated pair. + alpha * x } else { // Use the second element of the pair (generated at the // previous invocation). @@ -37,18 +44,9 @@ class MarsagliaNormalizedGaussianSampler(private val rng: UniformRandomProvider) // Both elements of the pair have been used. nextGaussian = Double.NaN - return r + r } } - override fun toString(): String = "Box-Muller (with rejection) normalized Gaussian deviate [$rng]" - - override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateContinuousSampler = - MarsagliaNormalizedGaussianSampler(rng) - - companion object { - @Suppress("UNCHECKED_CAST") - fun of(rng: UniformRandomProvider): S where S : NormalizedGaussianSampler?, S : SharedStateContinuousSampler? = - MarsagliaNormalizedGaussianSampler(rng) as S - } + override fun toString(): String = "Box-Muller (with rejection) normalized Gaussian deviate" } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/NormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/NormalizedGaussianSampler.kt new file mode 100644 index 000000000..0e5d6db59 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/NormalizedGaussianSampler.kt @@ -0,0 +1,5 @@ +package scientifik.kmath.prob.samplers + +import scientifik.kmath.prob.Sampler + +interface NormalizedGaussianSampler : Sampler diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/PoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/PoissonSampler.kt new file mode 100644 index 000000000..d3c53fbb4 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/PoissonSampler.kt @@ -0,0 +1,29 @@ +package scientifik.kmath.prob.samplers + +import scientifik.kmath.chains.Chain +import scientifik.kmath.prob.RandomGenerator +import scientifik.kmath.prob.Sampler + + +class PoissonSampler( + mean: Double +) : Sampler { + private val poissonSamplerDelegate: Sampler + + init { + // Delegate all work to specialised samplers. + poissonSamplerDelegate = of(mean) + } + + override fun sample(generator: RandomGenerator): Chain = poissonSamplerDelegate.sample(generator) + override fun toString(): String = poissonSamplerDelegate.toString() + + companion object { + private const val PIVOT = 40.0 + + fun of(mean: Double) =// Each sampler should check the input arguments. + if (mean < PIVOT) SmallMeanPoissonSampler.of( + mean + ) else LargeMeanPoissonSampler.of(mean) + } +} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt new file mode 100644 index 000000000..6509563dd --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt @@ -0,0 +1,43 @@ +package scientifik.kmath.prob.samplers + +import scientifik.kmath.chains.Chain +import scientifik.kmath.chains.SimpleChain +import scientifik.kmath.prob.RandomGenerator +import scientifik.kmath.prob.Sampler +import scientifik.kmath.prob.chain +import kotlin.math.ceil +import kotlin.math.exp + +class SmallMeanPoissonSampler(mean: Double) : Sampler { + private val p0: Double + private val limit: Int + + init { + require(mean > 0) { "mean is not strictly positive: $mean" } + p0 = exp(-mean) + + limit = (if (p0 > 0) + ceil(1000 * mean) + else + throw IllegalArgumentException("No p(x=0) probability for mean: $mean")).toInt() + } + + override fun sample(generator: RandomGenerator): Chain = generator.chain { + var n = 0 + var r = 1.0 + + while (n < limit) { + r *= nextDouble() + if (r >= p0) n++ else break + } + + n + } + + override fun toString(): String = "Small Mean Poisson deviate" + + companion object { + fun of(mean: Double): SmallMeanPoissonSampler = + SmallMeanPoissonSampler(mean) + } +} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt similarity index 58% rename from kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.kt rename to kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt index 26e089599..57e7fc47e 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt @@ -1,38 +1,76 @@ -package scientifik.kmath.commons.rng.sampling.distribution +package scientifik.kmath.prob.samplers -import scientifik.kmath.commons.rng.UniformRandomProvider +import scientifik.kmath.chains.Chain +import scientifik.kmath.chains.SimpleChain +import scientifik.kmath.prob.RandomGenerator +import scientifik.kmath.prob.Sampler +import scientifik.kmath.prob.chain import kotlin.math.* -class ZigguratNormalizedGaussianSampler(private val rng: UniformRandomProvider) : - NormalizedGaussianSampler, - SharedStateContinuousSampler { +class ZigguratNormalizedGaussianSampler() : + NormalizedGaussianSampler, Sampler { + + private fun sampleOne(generator: RandomGenerator): Double { + val j = generator.nextLong() + val i = (j and LAST.toLong()).toInt() + return if (abs(j) < K[i]) j * W[i] else fix(generator, j, i) + } + + override fun sample(generator: RandomGenerator): Chain = generator.chain { sampleOne(this) } + + override fun toString(): String = "Ziggurat normalized Gaussian deviate" + + private fun fix( + generator: RandomGenerator, + hz: Long, + iz: Int + ): Double { + var x: Double + var y: Double + x = hz * W[iz] + + return if (iz == 0) { + // Base strip. + // This branch is called about 5.7624515E-4 times per sample. + do { + y = -ln(generator.nextDouble()) + x = -ln(generator.nextDouble()) * ONE_OVER_R + } while (y + y < x * x) + + val out = R + x + if (hz > 0) out else -out + } else { + // Wedge of other strips. + // This branch is called about 0.027323 times per sample. + // else + // Try again. + // This branch is called about 0.012362 times per sample. + if (F[iz] + generator.nextDouble() * (F[iz - 1] - F[iz]) < gauss( + x + ) + ) x else sampleOne(generator) + } + } companion object { - private const val R = 3.442619855899 + private const val R: Double = 3.442619855899 private const val ONE_OVER_R: Double = 1 / R private const val V = 9.91256303526217e-3 private val MAX: Double = 2.0.pow(63.0) private val ONE_OVER_MAX: Double = 1.0 / MAX - private const val LEN = 128 + private const val LEN: Int = 128 private const val LAST: Int = LEN - 1 - private val K = LongArray(LEN) - private val W = DoubleArray(LEN) - private val F = DoubleArray(LEN) + private val K: LongArray = LongArray(LEN) + private val W: DoubleArray = DoubleArray(LEN) + private val F: DoubleArray = DoubleArray(LEN) private fun gauss(x: Double): Double = exp(-0.5 * x * x) - @Suppress("UNCHECKED_CAST") - fun of(rng: UniformRandomProvider): S where S : NormalizedGaussianSampler?, S : SharedStateContinuousSampler? = - ZigguratNormalizedGaussianSampler(rng) as S - init { // Filling the tables. - var d = - R + var d = R var t = d var fd = - gauss( - d - ) + gauss(d) val q = V / fd K[0] = (d / q * MAX).toLong() K[1] = 0 @@ -54,46 +92,4 @@ class ZigguratNormalizedGaussianSampler(private val rng: UniformRandomProvider) } } } - - override fun sample(): Double { - val j = rng.nextLong() - val i = (j and LAST.toLong()).toInt() - return if (abs(j) < K[i]) j * W[i] else fix(j, i) - } - - override fun toString(): String = "Ziggurat normalized Gaussian deviate [$rng]" - - private fun fix( - hz: Long, - iz: Int - ): Double { - var x: Double - var y: Double - x = hz * W[iz] - - return if (iz == 0) { - // Base strip. - // This branch is called about 5.7624515E-4 times per sample. - do { - y = -ln(rng.nextDouble()) - x = -ln(rng.nextDouble()) * ONE_OVER_R - } while (y + y < x * x) - - val out = R + x - if (hz > 0) out else -out - } else { - // Wedge of other strips. - // This branch is called about 0.027323 times per sample. - // else - // Try again. - // This branch is called about 0.012362 times per sample. - if (F[iz] + rng.nextDouble() * (F[iz - 1] - F[iz]) < gauss( - x - ) - ) x else sample() - } - } - - override fun withUniformRandomProvider(rng: UniformRandomProvider): SharedStateContinuousSampler = - ZigguratNormalizedGaussianSampler(rng) } diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/DistributionsJVM.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/DistributionsJVM.kt deleted file mode 100644 index 649cae961..000000000 --- a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/DistributionsJVM.kt +++ /dev/null @@ -1,66 +0,0 @@ -package scientifik.kmath.prob - -import scientifik.kmath.commons.rng.sampling.distribution.ContinuousSampler -import scientifik.kmath.commons.rng.sampling.distribution.DiscreteSampler -import scientifik.kmath.commons.rng.sampling.distribution.GaussianSampler -import scientifik.kmath.commons.rng.sampling.distribution.PoissonSampler -import kotlin.math.PI -import kotlin.math.exp -import kotlin.math.pow -import kotlin.math.sqrt - -fun Distribution.Companion.normal( - method: NormalSamplerMethod = NormalSamplerMethod.Ziggurat -): Distribution = object : ContinuousSamplerDistribution() { - override fun buildCMSampler(generator: RandomGenerator): ContinuousSampler { - val provider = generator.asUniformRandomProvider() - return normalSampler(method, provider) - } - - override fun probability(arg: Double): Double { - return exp(-arg.pow(2) / 2) / sqrt(PI * 2) - } -} - -fun Distribution.Companion.normal( - mean: Double, - sigma: Double, - method: NormalSamplerMethod = NormalSamplerMethod.Ziggurat -): ContinuousSamplerDistribution = object : ContinuousSamplerDistribution() { - private val sigma2 = sigma.pow(2) - private val norm = sigma * sqrt(PI * 2) - - override fun buildCMSampler(generator: RandomGenerator): ContinuousSampler { - val provider = generator.asUniformRandomProvider() - val normalizedSampler = normalSampler(method, provider) - return GaussianSampler( - normalizedSampler, - mean, - sigma - ) - } - - override fun probability(arg: Double): Double { - return exp(-(arg - mean).pow(2) / 2 / sigma2) / norm - } -} - -fun Distribution.Companion.poisson( - lambda: Double -): DiscreteSamplerDistribution = object : DiscreteSamplerDistribution() { - - override fun buildSampler(generator: RandomGenerator): DiscreteSampler { - return PoissonSampler.of(generator.asUniformRandomProvider(), lambda) - } - - private val computedProb: HashMap = hashMapOf(0 to exp(-lambda)) - - override fun probability(arg: Int): Double { - require(arg >= 0) { "The argument must be >= 0" } - - return if (arg > 40) - exp(-(arg - lambda).pow(2) / 2 / lambda) / sqrt(2 * PI * lambda) - else - computedProb.getOrPut(arg) { probability(arg - 1) * lambda / arg } - } -} diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt index 4c8d630ae..797c932ad 100644 --- a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt +++ b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt @@ -1,11 +1,10 @@ package scientifik.kmath.prob import org.apache.commons.rng.simple.RandomSource -import scientifik.kmath.commons.rng.UniformRandomProvider -class RandomSourceGenerator(val source: RandomSource, seed: Long?) : +class RandomSourceGenerator(private val source: RandomSource, seed: Long?) : RandomGenerator { - internal val random = seed?.let { + private val random = seed?.let { RandomSource.create(source, seed) } ?: RandomSource.create(source) @@ -24,24 +23,6 @@ class RandomSourceGenerator(val source: RandomSource, seed: Long?) : override fun fork(): RandomGenerator = RandomSourceGenerator(source, nextLong()) } -/** - * Represent this [RandomGenerator] as commons-rng [UniformRandomProvider] preserving and mirroring its current state. - * Getting new value from one of those changes the state of another. - */ -fun RandomGenerator.asUniformRandomProvider(): UniformRandomProvider = if (this is RandomSourceGenerator) { - object : UniformRandomProvider { - override fun nextBytes(bytes: ByteArray) = random.nextBytes(bytes) - override fun nextBytes(bytes: ByteArray, start: Int, len: Int) = random.nextBytes(bytes, start, len) - override fun nextInt(): Int = random.nextInt() - override fun nextInt(n: Int): Int = random.nextInt(n) - override fun nextLong(): Long = random.nextLong() - override fun nextLong(n: Long): Long = random.nextLong(n) - override fun nextBoolean(): Boolean = random.nextBoolean() - override fun nextFloat(): Float = random.nextFloat() - override fun nextDouble(): Double = random.nextDouble() - } -} else RandomGeneratorProvider(this) - fun RandomGenerator.Companion.fromSource(source: RandomSource, seed: Long? = null): RandomSourceGenerator = RandomSourceGenerator(source, seed) From e7b1203c2ddca46225c9714d86856365d906b029 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 17:29:57 +0700 Subject: [PATCH 04/31] Move init blocks checks to factory method of and make all the samplers' constructor private --- .../AhrensDieterExponentialSampler.kt | 16 ++++----- .../BoxMullerNormalizedGaussianSampler.kt | 6 +++- .../kmath/prob/samplers/GaussianSampler.kt | 25 +++++++------- .../samplers/KempSmallMeanPoissonSampler.kt | 6 ++-- .../prob/samplers/LargeMeanPoissonSampler.kt | 33 +++++++------------ .../MarsagliaNormalizedGaussianSampler.kt | 6 +++- .../kmath/prob/samplers/PoissonSampler.kt | 14 ++------ .../prob/samplers/SmallMeanPoissonSampler.kt | 24 ++++++-------- .../ZigguratNormalizedGaussianSampler.kt | 6 ++-- 9 files changed, 60 insertions(+), 76 deletions(-) diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterExponentialSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterExponentialSampler.kt index 5dec2b641..794c1010d 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterExponentialSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterExponentialSampler.kt @@ -7,11 +7,7 @@ import scientifik.kmath.prob.chain import kotlin.math.ln import kotlin.math.pow -class AhrensDieterExponentialSampler(val mean: Double) : Sampler { - init { - require(mean > 0) { "mean is not strictly positive: $mean" } - } - +class AhrensDieterExponentialSampler private constructor(val mean: Double) : Sampler { override fun sample(generator: RandomGenerator): Chain = generator.chain { // Step 1: var a = 0.0 @@ -46,10 +42,7 @@ class AhrensDieterExponentialSampler(val mean: Double) : Sampler { override fun toString(): String = "Ahrens-Dieter Exponential deviate" companion object { - private val EXPONENTIAL_SA_QI = DoubleArray(16) - - fun of(mean: Double): Sampler = - AhrensDieterExponentialSampler(mean) + private val EXPONENTIAL_SA_QI by lazy { DoubleArray(16) } init { /** @@ -64,5 +57,10 @@ class AhrensDieterExponentialSampler(val mean: Double) : Sampler { EXPONENTIAL_SA_QI[i] = qi } } + + fun of(mean: Double): AhrensDieterExponentialSampler { + require(mean > 0) { "mean is not strictly positive: $mean" } + return AhrensDieterExponentialSampler(mean) + } } } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt index 3235b06f5..57c38d6b7 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt @@ -6,7 +6,7 @@ import scientifik.kmath.prob.Sampler import scientifik.kmath.prob.chain import kotlin.math.* -class BoxMullerNormalizedGaussianSampler : NormalizedGaussianSampler, Sampler { +class BoxMullerNormalizedGaussianSampler private constructor() : NormalizedGaussianSampler, Sampler { private var nextGaussian: Double = Double.NaN override fun sample(generator: RandomGenerator): Chain = generator.chain { @@ -34,4 +34,8 @@ class BoxMullerNormalizedGaussianSampler : NormalizedGaussianSampler, Sampler { - - init { - require(standardDeviation > 0) { "standard deviation is not strictly positive: $standardDeviation" } - } - override fun sample(generator: RandomGenerator): Chain = normalized.sample(generator).map { standardDeviation * it + mean } @@ -22,13 +17,17 @@ class GaussianSampler( companion object { fun of( - normalized: NormalizedGaussianSampler, mean: Double, - standardDeviation: Double - ) = GaussianSampler( - mean, - standardDeviation, - normalized - ) + standardDeviation: Double, + normalized: NormalizedGaussianSampler + ): GaussianSampler { + require(standardDeviation > 0) { "standard deviation is not strictly positive: $standardDeviation" } + + return GaussianSampler( + mean, + standardDeviation, + normalized + ) + } } } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt index cdd4c4cd1..65e762b68 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt @@ -41,10 +41,10 @@ class KempSmallMeanPoissonSampler private constructor( companion object { fun of(mean: Double): KempSmallMeanPoissonSampler { require(mean > 0) { "Mean is not strictly positive: $mean" } - val p0: Double = exp(-mean) + val p0 = exp(-mean) // Probability must be positive. As mean increases then p(0) decreases. - if (p0 > 0) return KempSmallMeanPoissonSampler(p0, mean) - throw IllegalArgumentException("No probability for mean: $mean") + require(p0 > 0) { "No probability for mean: $mean" } + return KempSmallMeanPoissonSampler(p0, mean) } } } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt index 33b1b3f8a..39a3dc168 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt @@ -1,6 +1,5 @@ package scientifik.kmath.prob.samplers -import kotlinx.coroutines.flow.first import scientifik.kmath.chains.Chain import scientifik.kmath.chains.ConstantChain import scientifik.kmath.chains.SimpleChain @@ -9,12 +8,10 @@ import scientifik.kmath.prob.Sampler import scientifik.kmath.prob.next import kotlin.math.* -class LargeMeanPoissonSampler(val mean: Double) : Sampler { - private val exponential: Sampler = - AhrensDieterExponentialSampler.of(1.0) - private val gaussian: Sampler = - ZigguratNormalizedGaussianSampler() - private val factorialLog: InternalUtils.FactorialLog = NO_CACHE_FACTORIAL_LOG!! +class LargeMeanPoissonSampler private constructor(val mean: Double) : Sampler { + private val exponential: Sampler = AhrensDieterExponentialSampler.of(1.0) + private val gaussian: Sampler = ZigguratNormalizedGaussianSampler() + private val factorialLog: InternalUtils.FactorialLog = NO_CACHE_FACTORIAL_LOG private val lambda: Double = floor(mean) private val logLambda: Double = ln(lambda) private val logLambdaFactorial: Double = getFactorialLog(lambda.toInt()) @@ -33,12 +30,6 @@ class LargeMeanPoissonSampler(val mean: Double) : Sampler { else // Not used. KempSmallMeanPoissonSampler.of(mean - lambda) - init { - require(mean >= 1) { "mean is not >= 1: $mean" } - // The algorithm is not valid if Math.floor(mean) is not an integer. - require(mean <= MAX_MEAN) { "mean $mean > $MAX_MEAN" } - } - override fun sample(generator: RandomGenerator): Chain = SimpleChain { // This will never be null. It may be a no-op delegate that returns zero. val y2 = smallMeanPoissonSampler.next(generator) @@ -113,20 +104,18 @@ class LargeMeanPoissonSampler(val mean: Double) : Sampler { override fun toString(): String = "Large Mean Poisson deviate" companion object { - private const val MAX_MEAN = 0.5 * Int.MAX_VALUE - private var NO_CACHE_FACTORIAL_LOG: InternalUtils.FactorialLog? = null + private const val MAX_MEAN: Double = 0.5 * Int.MAX_VALUE + private val NO_CACHE_FACTORIAL_LOG: InternalUtils.FactorialLog = InternalUtils.FactorialLog.create() private val NO_SMALL_MEAN_POISSON_SAMPLER = object : Sampler { override fun sample(generator: RandomGenerator): Chain = ConstantChain(0) } - fun of(mean: Double): LargeMeanPoissonSampler = - LargeMeanPoissonSampler(mean) - - init { - // Create without a cache. - NO_CACHE_FACTORIAL_LOG = - InternalUtils.FactorialLog.create() + fun of(mean: Double): LargeMeanPoissonSampler { + require(mean >= 1) { "mean is not >= 1: $mean" } + // The algorithm is not valid if Math.floor(mean) is not an integer. + require(mean <= MAX_MEAN) { "mean $mean > $MAX_MEAN" } + return LargeMeanPoissonSampler(mean) } } } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt index c44943056..dd8f82a2c 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt @@ -7,7 +7,7 @@ import scientifik.kmath.prob.chain import kotlin.math.ln import kotlin.math.sqrt -class MarsagliaNormalizedGaussianSampler : NormalizedGaussianSampler, Sampler { +class MarsagliaNormalizedGaussianSampler private constructor(): NormalizedGaussianSampler, Sampler { private var nextGaussian = Double.NaN override fun sample(generator: RandomGenerator): Chain = generator.chain { @@ -49,4 +49,8 @@ class MarsagliaNormalizedGaussianSampler : NormalizedGaussianSampler, Sampler { - private val poissonSamplerDelegate: Sampler - - init { - // Delegate all work to specialised samplers. - poissonSamplerDelegate = of(mean) - } - + private val poissonSamplerDelegate: Sampler = of(mean) override fun sample(generator: RandomGenerator): Chain = poissonSamplerDelegate.sample(generator) override fun toString(): String = poissonSamplerDelegate.toString() @@ -22,8 +16,6 @@ class PoissonSampler( private const val PIVOT = 40.0 fun of(mean: Double) =// Each sampler should check the input arguments. - if (mean < PIVOT) SmallMeanPoissonSampler.of( - mean - ) else LargeMeanPoissonSampler.of(mean) + if (mean < PIVOT) SmallMeanPoissonSampler.of(mean) else LargeMeanPoissonSampler.of(mean) } } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt index 6509563dd..7022b0f3a 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt @@ -8,19 +8,13 @@ import scientifik.kmath.prob.chain import kotlin.math.ceil import kotlin.math.exp -class SmallMeanPoissonSampler(mean: Double) : Sampler { - private val p0: Double - private val limit: Int +class SmallMeanPoissonSampler private constructor(mean: Double) : Sampler { + private val p0: Double = exp(-mean) - init { - require(mean > 0) { "mean is not strictly positive: $mean" } - p0 = exp(-mean) - - limit = (if (p0 > 0) - ceil(1000 * mean) - else - throw IllegalArgumentException("No p(x=0) probability for mean: $mean")).toInt() - } + private val limit: Int = (if (p0 > 0) + ceil(1000 * mean) + else + throw IllegalArgumentException("No p(x=0) probability for mean: $mean")).toInt() override fun sample(generator: RandomGenerator): Chain = generator.chain { var n = 0 @@ -37,7 +31,9 @@ class SmallMeanPoissonSampler(mean: Double) : Sampler { override fun toString(): String = "Small Mean Poisson deviate" companion object { - fun of(mean: Double): SmallMeanPoissonSampler = - SmallMeanPoissonSampler(mean) + fun of(mean: Double): SmallMeanPoissonSampler { + require(mean > 0) { "mean is not strictly positive: $mean" } + return SmallMeanPoissonSampler(mean) + } } } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt index 57e7fc47e..3d22c38a5 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt @@ -7,7 +7,7 @@ import scientifik.kmath.prob.Sampler import scientifik.kmath.prob.chain import kotlin.math.* -class ZigguratNormalizedGaussianSampler() : +class ZigguratNormalizedGaussianSampler private constructor() : NormalizedGaussianSampler, Sampler { private fun sampleOne(generator: RandomGenerator): Double { @@ -63,7 +63,6 @@ class ZigguratNormalizedGaussianSampler() : private val K: LongArray = LongArray(LEN) private val W: DoubleArray = DoubleArray(LEN) private val F: DoubleArray = DoubleArray(LEN) - private fun gauss(x: Double): Double = exp(-0.5 * x * x) init { // Filling the tables. @@ -91,5 +90,8 @@ class ZigguratNormalizedGaussianSampler() : W[i] = d * ONE_OVER_MAX } } + + fun of(): ZigguratNormalizedGaussianSampler = ZigguratNormalizedGaussianSampler() + private fun gauss(x: Double): Double = exp(-0.5 * x * x) } } From 2b24bd979e282d35f3f8e15fd52ef3e5904af97e Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 17:36:14 +0700 Subject: [PATCH 05/31] Add Apache Javadocs references --- .../kmath/prob/samplers/AhrensDieterExponentialSampler.kt | 5 +++++ .../prob/samplers/BoxMullerNormalizedGaussianSampler.kt | 5 +++++ .../scientifik/kmath/prob/samplers/GaussianSampler.kt | 5 +++++ .../kmath/prob/samplers/KempSmallMeanPoissonSampler.kt | 5 +++++ .../kmath/prob/samplers/LargeMeanPoissonSampler.kt | 7 ++++++- .../prob/samplers/MarsagliaNormalizedGaussianSampler.kt | 5 +++++ .../scientifik/kmath/prob/samplers/PoissonSampler.kt | 6 +++++- .../kmath/prob/samplers/SmallMeanPoissonSampler.kt | 5 +++++ .../prob/samplers/ZigguratNormalizedGaussianSampler.kt | 5 +++++ 9 files changed, 46 insertions(+), 2 deletions(-) diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterExponentialSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterExponentialSampler.kt index 794c1010d..a83bf6e12 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterExponentialSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterExponentialSampler.kt @@ -7,6 +7,11 @@ import scientifik.kmath.prob.chain import kotlin.math.ln import kotlin.math.pow +/** + * Based on commons-rng implementation. + * + * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.html + */ class AhrensDieterExponentialSampler private constructor(val mean: Double) : Sampler { override fun sample(generator: RandomGenerator): Chain = generator.chain { // Step 1: diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt index 57c38d6b7..ef5b36289 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt @@ -6,6 +6,11 @@ import scientifik.kmath.prob.Sampler import scientifik.kmath.prob.chain import kotlin.math.* +/** + * Based on commons-rng implementation. + * + * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.html + */ class BoxMullerNormalizedGaussianSampler private constructor() : NormalizedGaussianSampler, Sampler { private var nextGaussian: Double = Double.NaN diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/GaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/GaussianSampler.kt index 9af468858..2a6bd6a09 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/GaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/GaussianSampler.kt @@ -5,6 +5,11 @@ import scientifik.kmath.chains.map import scientifik.kmath.prob.RandomGenerator import scientifik.kmath.prob.Sampler +/** + * Based on commons-rng implementation. + * + * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/GaussianSampler.html + */ class GaussianSampler private constructor( private val mean: Double, private val standardDeviation: Double, diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt index 65e762b68..45afaa179 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt @@ -6,6 +6,11 @@ import scientifik.kmath.prob.Sampler import scientifik.kmath.prob.chain import kotlin.math.exp +/** + * Based on commons-rng implementation. + * + * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.html + */ class KempSmallMeanPoissonSampler private constructor( private val p0: Double, private val mean: Double diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt index 39a3dc168..f14aaa8ab 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt @@ -8,9 +8,14 @@ import scientifik.kmath.prob.Sampler import scientifik.kmath.prob.next import kotlin.math.* +/** + * Based on commons-rng implementation. + * + * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.html + */ class LargeMeanPoissonSampler private constructor(val mean: Double) : Sampler { private val exponential: Sampler = AhrensDieterExponentialSampler.of(1.0) - private val gaussian: Sampler = ZigguratNormalizedGaussianSampler() + private val gaussian: Sampler = ZigguratNormalizedGaussianSampler.of() private val factorialLog: InternalUtils.FactorialLog = NO_CACHE_FACTORIAL_LOG private val lambda: Double = floor(mean) private val logLambda: Double = ln(lambda) diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt index dd8f82a2c..a0711ec24 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt @@ -7,6 +7,11 @@ import scientifik.kmath.prob.chain import kotlin.math.ln import kotlin.math.sqrt +/** + * Based on commons-rng implementation. + * + * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.html + */ class MarsagliaNormalizedGaussianSampler private constructor(): NormalizedGaussianSampler, Sampler { private var nextGaussian = Double.NaN diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/PoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/PoissonSampler.kt index 7b5dedc5b..3a85e6992 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/PoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/PoissonSampler.kt @@ -4,7 +4,11 @@ import scientifik.kmath.chains.Chain import scientifik.kmath.prob.RandomGenerator import scientifik.kmath.prob.Sampler - +/** + * Based on commons-rng implementation. + * + * https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/PoissonSampler.html + */ class PoissonSampler private constructor( mean: Double ) : Sampler { diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt index 7022b0f3a..966d7db9a 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt @@ -8,6 +8,11 @@ import scientifik.kmath.prob.chain import kotlin.math.ceil import kotlin.math.exp +/** + * Based on commons-rng implementation. + * + * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSampler.html + */ class SmallMeanPoissonSampler private constructor(mean: Double) : Sampler { private val p0: Double = exp(-mean) diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt index 3d22c38a5..b1680d54a 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt @@ -7,6 +7,11 @@ import scientifik.kmath.prob.Sampler import scientifik.kmath.prob.chain import kotlin.math.* +/** + * Based on commons-rng implementation. + * + * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.html + */ class ZigguratNormalizedGaussianSampler private constructor() : NormalizedGaussianSampler, Sampler { From 5ff76209aac70ab0e17fdcdf7b8f8d24a337d15d Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 17:37:35 +0700 Subject: [PATCH 06/31] Specify type explicitly --- .../kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt index b1680d54a..979209e79 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt @@ -60,7 +60,7 @@ class ZigguratNormalizedGaussianSampler private constructor() : companion object { private const val R: Double = 3.442619855899 private const val ONE_OVER_R: Double = 1 / R - private const val V = 9.91256303526217e-3 + private const val V: Double = 9.91256303526217e-3 private val MAX: Double = 2.0.pow(63.0) private val ONE_OVER_MAX: Double = 1.0 / MAX private const val LEN: Int = 128 From d4226b7e7d7587f6b48bcb3b6624f57e8da5167e Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 17:38:48 +0700 Subject: [PATCH 07/31] Reformat --- .../scientifik/kmath/prob/samplers/GaussianSampler.kt | 5 +++-- .../prob/samplers/MarsagliaNormalizedGaussianSampler.kt | 3 --- .../prob/samplers/ZigguratNormalizedGaussianSampler.kt | 8 ++------ 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/GaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/GaussianSampler.kt index 2a6bd6a09..755c73df3 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/GaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/GaussianSampler.kt @@ -15,8 +15,9 @@ class GaussianSampler private constructor( private val standardDeviation: Double, private val normalized: NormalizedGaussianSampler ) : Sampler { - override fun sample(generator: RandomGenerator): Chain = - normalized.sample(generator).map { standardDeviation * it + mean } + override fun sample(generator: RandomGenerator): Chain = normalized + .sample(generator) + .map { standardDeviation * it + mean } override fun toString(): String = "Gaussian deviate [$normalized]" diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt index a0711ec24..80d93055d 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt @@ -30,10 +30,8 @@ class MarsagliaNormalizedGaussianSampler private constructor(): NormalizedGaussi if (r2 < 1 && r2 > 0) { // Pair (x, y) is within unit circle. alpha = sqrt(-2 * ln(r2) / r2) - // Keep second element of the pair for next invocation. nextGaussian = alpha * y - // Return the first element of the generated pair. break } @@ -46,7 +44,6 @@ class MarsagliaNormalizedGaussianSampler private constructor(): NormalizedGaussi // Use the second element of the pair (generated at the // previous invocation). val r = nextGaussian - // Both elements of the pair have been used. nextGaussian = Double.NaN r diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt index 979209e79..de593a811 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt @@ -73,8 +73,7 @@ class ZigguratNormalizedGaussianSampler private constructor() : // Filling the tables. var d = R var t = d - var fd = - gauss(d) + var fd = gauss(d) val q = V / fd K[0] = (d / q * MAX).toLong() K[1] = 0 @@ -85,10 +84,7 @@ class ZigguratNormalizedGaussianSampler private constructor() : (LAST - 1 downTo 1).forEach { i -> d = sqrt(-2 * ln(V / d + fd)) - fd = - gauss( - d - ) + fd = gauss(d) K[i + 1] = (d / t * MAX).toLong() t = d F[i] = fd From 46649a1ddf7e479f9ddedd09585f49dbb6690420 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 18:02:15 +0700 Subject: [PATCH 08/31] Delete unused InternalUtils functions --- .../kmath/prob/samplers/InternalUtils.kt | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/InternalUtils.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/InternalUtils.kt index f2f1b3865..c83f35d32 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/InternalUtils.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/InternalUtils.kt @@ -18,24 +18,6 @@ internal object InternalUtils { fun factorial(n: Int): Long = FACTORIALS[n] - fun validateProbabilities(probabilities: DoubleArray): Double { - require(probabilities.isNotEmpty()) { "Probabilities must not be empty." } - var sumProb = 0.0 - - probabilities.forEach { prob -> - validateProbability(prob) - sumProb += prob - } - - require(!(sumProb.isInfinite() || sumProb <= 0)) { "Invalid sum of probabilities: $sumProb" } - return sumProb - } - - private fun validateProbability(probability: Double): Unit = - require(!(probability < 0 || probability.isInfinite() || probability.isNaN())) { - "Invalid probability: $probability" - } - class FactorialLog private constructor( numValues: Int, cache: DoubleArray? @@ -64,9 +46,6 @@ internal object InternalUtils { } } - fun withCache(cacheSize: Int): FactorialLog = - FactorialLog(cacheSize, logFactorials) - fun value(n: Int): Double { if (n < logFactorials.size) return logFactorials[n] From 246feacd72140597f4a94bcaa920b0afe30a058e Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 18:05:56 +0700 Subject: [PATCH 09/31] Delete unused RandomGenerator-to-URP adapter --- .../kmath/prob/RandomSourceGenerator.kt | 29 ------------------- 1 file changed, 29 deletions(-) delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt deleted file mode 100644 index b4056cabc..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt +++ /dev/null @@ -1,29 +0,0 @@ -package scientifik.kmath.prob - -import scientifik.kmath.commons.rng.UniformRandomProvider - - -inline class RandomGeneratorProvider(val generator: RandomGenerator) : - UniformRandomProvider { - override fun nextBoolean(): Boolean = generator.nextBoolean() - - override fun nextFloat(): Float = generator.nextDouble().toFloat() - - override fun nextBytes(bytes: ByteArray) { - generator.fillBytes(bytes) - } - - override fun nextBytes(bytes: ByteArray, start: Int, len: Int) { - generator.fillBytes(bytes, start, start + len) - } - - override fun nextInt(): Int = generator.nextInt() - - override fun nextInt(n: Int): Int = generator.nextInt(n) - - override fun nextDouble(): Double = generator.nextDouble() - - override fun nextLong(): Long = generator.nextLong() - - override fun nextLong(n: Long): Long = generator.nextLong(n) -} From 822f960e9c31bced450088e8f1721fd3a79f83c1 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Mon, 8 Jun 2020 18:19:18 +0700 Subject: [PATCH 10/31] Fix broken demos, add newlines at the end of files --- .../commons/prob/DistributionBenchmark.kt | 44 ++++++++----------- .../kmath/commons/prob/DistributionDemo.kt | 5 +-- .../scientifik/kmath/prob/RandomChain.kt | 2 +- .../scientifik/kmath/prob/RandomGenerator.kt | 2 +- 4 files changed, 22 insertions(+), 31 deletions(-) diff --git a/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionBenchmark.kt b/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionBenchmark.kt index b060cddb6..34549710c 100644 --- a/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionBenchmark.kt +++ b/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionBenchmark.kt @@ -3,25 +3,24 @@ package scientifik.kmath.commons.prob import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking -import org.apache.commons.rng.sampling.distribution.ZigguratNormalizedGaussianSampler import org.apache.commons.rng.simple.RandomSource -import scientifik.kmath.chains.BlockingRealChain -import scientifik.kmath.prob.* +import scientifik.kmath.prob.RandomChain +import scientifik.kmath.prob.RandomGenerator +import scientifik.kmath.prob.fromSource import java.time.Duration import java.time.Instant +import org.apache.commons.rng.sampling.distribution.ZigguratNormalizedGaussianSampler as ApacheZigguratNormalizedGaussianSampler +import scientifik.kmath.prob.samplers.ZigguratNormalizedGaussianSampler as KMathZigguratNormalizedGaussianSampler - -private suspend fun runChain(): Duration { +private suspend fun runKMathChained(): Duration { val generator = RandomGenerator.fromSource(RandomSource.MT, 123L) - - val normal = Distribution.normal(NormalSamplerMethod.Ziggurat) - val chain = normal.sample(generator) as BlockingRealChain - + val normal = KMathZigguratNormalizedGaussianSampler.of() + val chain = normal.sample(generator) as RandomChain val startTime = Instant.now() var sum = 0.0 - repeat(10000001) { counter -> - sum += chain.nextDouble() + repeat(10000001) { counter -> + sum += chain.next() if (counter % 100000 == 0) { val duration = Duration.between(startTime, Instant.now()) @@ -32,9 +31,9 @@ private suspend fun runChain(): Duration { return Duration.between(startTime, Instant.now()) } -private fun runDirect(): Duration { - val provider = RandomSource.create(RandomSource.MT, 123L) - val sampler = ZigguratNormalizedGaussianSampler(provider) +private fun runApacheDirect(): Duration { + val rng = RandomSource.create(RandomSource.MT, 123L) + val sampler = ApacheZigguratNormalizedGaussianSampler.of(rng) val startTime = Instant.now() var sum = 0.0 @@ -56,16 +55,9 @@ private fun runDirect(): Duration { */ fun main() { runBlocking(Dispatchers.Default) { - val chainJob = async { - runChain() - } - - val directJob = async { - runDirect() - } - - println("Chain: ${chainJob.await()}") - println("Direct: ${directJob.await()}") + val chainJob = async { runKMathChained() } + val directJob = async { runApacheDirect() } + println("KMath Chained: ${chainJob.await()}") + println("Apache Direct: ${directJob.await()}") } - -} \ 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 e059415dc..88fc38e94 100644 --- a/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt +++ b/examples/src/main/kotlin/scientifik/kmath/commons/prob/DistributionDemo.kt @@ -3,9 +3,8 @@ package scientifik.kmath.commons.prob import kotlinx.coroutines.runBlocking import scientifik.kmath.chains.Chain import scientifik.kmath.chains.collectWithState -import scientifik.kmath.prob.Distribution import scientifik.kmath.prob.RandomGenerator -import scientifik.kmath.prob.normal +import scientifik.kmath.prob.samplers.ZigguratNormalizedGaussianSampler data class AveragingChainState(var num: Int = 0, var value: Double = 0.0) @@ -18,7 +17,7 @@ fun Chain.mean(): Chain = collectWithState(AveragingChainState() fun main() { - val normal = Distribution.normal() + val normal = ZigguratNormalizedGaussianSampler.of() val chain = normal.sample(RandomGenerator.default).mean() runBlocking { 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 47fc6e4c5..49163c701 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt @@ -11,4 +11,4 @@ class RandomChain(val generator: RandomGenerator, private val gen: suspen override fun fork(): Chain = RandomChain(generator.fork(), gen) } -fun RandomGenerator.chain(gen: suspend RandomGenerator.() -> R): RandomChain = RandomChain(this, gen) \ No newline at end of file +fun RandomGenerator.chain(gen: suspend RandomGenerator.() -> R): RandomChain = RandomChain(this, gen) 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 4b42db927..6bdabed9d 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt @@ -44,4 +44,4 @@ inline class DefaultGenerator(private val random: Random = Random) : RandomGener override fun nextBytes(size: Int): ByteArray = random.nextBytes(size) override fun fork(): RandomGenerator = RandomGenerator.default(random.nextLong()) -} \ No newline at end of file +} From 46f6d57fd944bc8719c8b99f296f151ca9eb4b85 Mon Sep 17 00:00:00 2001 From: Commander Tvis Date: Fri, 12 Jun 2020 01:13:15 +0700 Subject: [PATCH 11/31] Add 2 more samplers, replace SimpleChain with generator.chain --- .../AhrensDieterMarsagliaTsangGammaSampler.kt | 112 ++++++++ .../samplers/AliasMethodDiscreteSampler.kt | 260 ++++++++++++++++++ .../kmath/prob/samplers/InternalUtils.kt | 30 +- .../prob/samplers/LargeMeanPoissonSampler.kt | 4 +- .../prob/samplers/SmallMeanPoissonSampler.kt | 1 - .../ZigguratNormalizedGaussianSampler.kt | 1 - 6 files changed, 396 insertions(+), 12 deletions(-) create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt create mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AliasMethodDiscreteSampler.kt diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt new file mode 100644 index 000000000..57bc778ba --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt @@ -0,0 +1,112 @@ +package scientifik.kmath.prob.samplers + +import scientifik.kmath.chains.Chain +import scientifik.kmath.prob.RandomGenerator +import scientifik.kmath.prob.Sampler +import scientifik.kmath.prob.chain +import scientifik.kmath.prob.next +import kotlin.math.* + +class AhrensDieterMarsagliaTsangGammaSampler private constructor( + alpha: Double, + theta: Double +) : Sampler { + private val delegate: BaseGammaSampler = + if (alpha < 1) AhrensDieterGammaSampler(alpha, theta) else MarsagliaTsangGammaSampler( + alpha, + theta + ) + + private abstract class BaseGammaSampler internal constructor( + protected val alpha: Double, + protected val theta: Double + ) : Sampler { + init { + require(alpha > 0) { "alpha is not strictly positive: $alpha" } + require(theta > 0) { "theta is not strictly positive: $theta" } + } + + override fun toString(): String = "Ahrens-Dieter-Marsaglia-Tsang Gamma deviate" + } + + private class AhrensDieterGammaSampler internal constructor(alpha: Double, theta: Double) : + BaseGammaSampler(alpha, theta) { + private val oneOverAlpha: Double = 1.0 / alpha + private val bGSOptim: Double = 1.0 + alpha / E + + override fun sample(generator: RandomGenerator): Chain = generator.chain { + var x: Double + + // [1]: p. 228, Algorithm GS. + while (true) { + // Step 1: + val u = generator.nextDouble() + val p = bGSOptim * u + + if (p <= 1) { + // Step 2: + x = p.pow(oneOverAlpha) + val u2 = generator.nextDouble() + + if (u2 > exp(-x)) // Reject. + continue + + break + } + + // Step 3: + x = -ln((bGSOptim - p) * oneOverAlpha) + val u2: Double = generator.nextDouble() + if (u2 <= x.pow(alpha - 1.0)) break + // Reject and continue. + } + + x * theta + } + } + + private class MarsagliaTsangGammaSampler internal constructor(alpha: Double, theta: Double) : + BaseGammaSampler(alpha, theta) { + private val dOptim: Double + private val cOptim: Double + private val gaussian: NormalizedGaussianSampler + + init { + gaussian = ZigguratNormalizedGaussianSampler.of() + dOptim = alpha - ONE_THIRD + cOptim = ONE_THIRD / sqrt(dOptim) + } + + override fun sample(generator: RandomGenerator): Chain = generator.chain { + var v: Double + + while (true) { + val x = gaussian.next(generator) + val oPcTx = 1 + cOptim * x + v = oPcTx * oPcTx * oPcTx + if (v <= 0) continue + val x2 = x * x + val u = generator.nextDouble() + // Squeeze. + if (u < 1 - 0.0331 * x2 * x2) break + if (ln(u) < 0.5 * x2 + dOptim * (1 - v + ln(v))) break + } + + theta * dOptim * v + } + + companion object { + private const val ONE_THIRD = 1.0 / 3.0 + } + } + + override fun sample(generator: RandomGenerator): Chain = delegate.sample(generator) + override fun toString(): String = delegate.toString() + + companion object { + fun of( + alpha: Double, + theta: Double + ): Sampler = AhrensDieterMarsagliaTsangGammaSampler(alpha, theta) + } +} \ No newline at end of file diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AliasMethodDiscreteSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AliasMethodDiscreteSampler.kt new file mode 100644 index 000000000..6b514a40b --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AliasMethodDiscreteSampler.kt @@ -0,0 +1,260 @@ +package scientifik.kmath.prob.samplers + +import scientifik.kmath.chains.Chain +import scientifik.kmath.prob.RandomGenerator +import scientifik.kmath.prob.Sampler +import scientifik.kmath.prob.chain +import kotlin.jvm.JvmOverloads +import kotlin.math.ceil +import kotlin.math.max +import kotlin.math.min + +open class AliasMethodDiscreteSampler private constructor( + // Deliberate direct storage of input arrays + protected val probability: LongArray, + protected val alias: IntArray +) : Sampler { + + private class SmallTableAliasMethodDiscreteSampler internal constructor( + probability: LongArray, + alias: IntArray + ) : AliasMethodDiscreteSampler(probability, alias) { + // Assume the table size is a power of 2 and create the mask + private val mask: Int = alias.size - 1 + + override fun sample(generator: RandomGenerator): Chain = generator.chain { + val bits = generator.nextInt() + // Isolate lower bits + val j = bits and mask + + // Optimisation for zero-padded input tables + if (j >= probability.size) + // No probability must use the alias + return@chain alias[j] + + // Create a uniform random deviate as a long. + // This replicates functionality from the o.a.c.rng.core.utils.NumberFactory.makeLong + val longBits = generator.nextInt().toLong() shl 32 or (bits.toLong() and hex_ffffffff) + // Choose between the two. Use a 53-bit long for the probability. + if (longBits ushr 11 < probability[j]) j else alias[j] + } + + private companion object { + private const val hex_ffffffff = 4294967295L + } + } + + override fun sample(generator: RandomGenerator): Chain = generator.chain { + // This implements the algorithm as per Vose (1991): + // v = uniform() in [0, 1) + // j = uniform(n) in [0, n) + // if v < prob[j] then + // return j + // else + // return alias[j] + val j = generator.nextInt(alias.size) + + // Optimisation for zero-padded input tables + // No probability must use the alias + if (j >= probability.size) return@chain alias[j] + + // Note: We could check the probability before computing a deviate. + // p(j) == 0 => alias[j] + // p(j) == 1 => j + // However it is assumed these edge cases are rare: + // + // The probability table will be 1 for approximately 1/n samples, i.e. only the + // last unpaired probability. This is only worth checking for when the table size (n) + // is small. But in that case the user should zero-pad the table for performance. + // + // The probability table will be 0 when an input probability was zero. We + // will assume this is also rare if modelling a discrete distribution where + // all samples are possible. The edge case for zero-padded tables is handled above. + + // Choose between the two. Use a 53-bit long for the probability. + if (generator.nextLong() ushr 11 < probability[j]) j else alias[j] + } + + override fun toString(): String = "Alias method" + + companion object { + private const val DEFAULT_ALPHA = 0 + private const val ZERO = 0.0 + private const val ONE_AS_NUMERATOR = 1L shl 53 + private const val CONVERT_TO_NUMERATOR: Double = ONE_AS_NUMERATOR.toDouble() + private const val MAX_SMALL_POWER_2_SIZE = 1 shl 11 + + @JvmOverloads + fun of( + probabilities: DoubleArray, + alpha: Int = DEFAULT_ALPHA + ): Sampler { + // The Alias method balances N categories with counts around the mean into N sections, + // each allocated 'mean' observations. + // + // Consider 4 categories with counts 6,3,2,1. The histogram can be balanced into a + // 2D array as 4 sections with a height of the mean: + // + // 6 + // 6 + // 6 + // 63 => 6366 -- + // 632 6326 |-- mean + // 6321 6321 -- + // + // section abcd + // + // Each section is divided as: + // a: 6=1/1 + // b: 3=1/1 + // c: 2=2/3; 6=1/3 (6 is the alias) + // d: 1=1/3; 6=2/3 (6 is the alias) + // + // The sample is obtained by randomly selecting a section, then choosing which category + // from the pair based on a uniform random deviate. + val sumProb = InternalUtils.validateProbabilities(probabilities) + // Allow zero-padding + val n = computeSize(probabilities.size, alpha) + // Partition into small and large by splitting on the average. + val mean = sumProb / n + // The cardinality of smallSize + largeSize = n. + // So fill the same array from either end. + val indices = IntArray(n) + var large = n + var small = 0 + + probabilities.indices.forEach { i -> + if (probabilities[i] >= mean) indices[--large] = i else indices[small++] = i + } + + small = fillRemainingIndices(probabilities.size, indices, small) + // This may be smaller than the input length if the probabilities were already padded. + val nonZeroIndex = findLastNonZeroIndex(probabilities) + // The probabilities are modified so use a copy. + // Note: probabilities are required only up to last nonZeroIndex + val remainingProbabilities = probabilities.copyOf(nonZeroIndex + 1) + // Allocate the final tables. + // Probability table may be truncated (when zero padded). + // The alias table is full length. + val probability = LongArray(remainingProbabilities.size) + val alias = IntArray(n) + + // This loop uses each large in turn to fill the alias table for small probabilities that + // do not reach the requirement to fill an entire section alone (i.e. p < mean). + // Since the sum of the small should be less than the sum of the large it should use up + // all the small first. However floating point round-off can result in + // misclassification of items as small or large. The Vose algorithm handles this using + // a while loop conditioned on the size of both sets and a subsequent loop to use + // unpaired items. + while (large != n && small != 0) { + // Index of the small and the large probabilities. + val j = indices[--small] + val k = indices[large++] + + // Optimisation for zero-padded input: + // p(j) = 0 above the last nonZeroIndex + if (j > nonZeroIndex) + // The entire amount for the section is taken from the alias. + remainingProbabilities[k] -= mean + else { + val pj = remainingProbabilities[j] + // Item j is a small probability that is below the mean. + // Compute the weight of the section for item j: pj / mean. + // This is scaled by 2^53 and the ceiling function used to round-up + // the probability to a numerator of a fraction in the range [1,2^53]. + // Ceiling ensures non-zero values. + probability[j] = ceil(CONVERT_TO_NUMERATOR * (pj / mean)).toLong() + // The remaining amount for the section is taken from the alias. + // Effectively: probabilities[k] -= (mean - pj) + remainingProbabilities[k] += pj - mean + } + + // If not j then the alias is k + alias[j] = k + + // Add the remaining probability from large to the appropriate list. + if (remainingProbabilities[k] >= mean) indices[--large] = k else indices[small++] = k + } + + // Final loop conditions to consume unpaired items. + // Note: The large set should never be non-empty but this can occur due to round-off + // error so consume from both. + fillTable(probability, alias, indices, 0, small) + fillTable(probability, alias, indices, large, n) + + // Change the algorithm for small power of 2 sized tables + return if (isSmallPowerOf2(n)) + SmallTableAliasMethodDiscreteSampler(probability, alias) + else + AliasMethodDiscreteSampler(probability, alias) + } + + private fun fillRemainingIndices(length: Int, indices: IntArray, small: Int): Int { + var updatedSmall = small + (length until indices.size).forEach { i -> indices[updatedSmall++] = i } + return updatedSmall + } + + private fun findLastNonZeroIndex(probabilities: DoubleArray): Int { + // No bounds check is performed when decrementing as the array contains at least one + // value above zero. + var nonZeroIndex = probabilities.size - 1 + while (probabilities[nonZeroIndex] == ZERO) nonZeroIndex-- + return nonZeroIndex + } + + private fun computeSize(length: Int, alpha: Int): Int { + // If No padding + if (alpha < 0) return length + // Use the number of leading zeros function to find the next power of 2, + // i.e. ceil(log2(x)) + var pow2 = 32 - numberOfLeadingZeros(length - 1) + // Increase by the alpha. Clip this to limit to a positive integer (2^30) + pow2 = min(30, pow2 + alpha) + // Use max to handle a length above the highest possible power of 2 + return max(length, 1 shl pow2) + } + + private fun fillTable( + probability: LongArray, + alias: IntArray, + indices: IntArray, + start: Int, + end: Int + ) = (start until end).forEach { i -> + val index = indices[i] + probability[index] = ONE_AS_NUMERATOR + alias[index] = index + } + + private fun isSmallPowerOf2(n: Int): Boolean = n <= MAX_SMALL_POWER_2_SIZE && n and n - 1 == 0 + + private fun numberOfLeadingZeros(i: Int): Int { + var mutI = i + if (mutI <= 0) return if (mutI == 0) 32 else 0 + var n = 31 + + if (mutI >= 1 shl 16) { + n -= 16 + mutI = mutI ushr 16 + } + + if (mutI >= 1 shl 8) { + n -= 8 + mutI = mutI ushr 8 + } + + if (mutI >= 1 shl 4) { + n -= 4 + mutI = mutI ushr 4 + } + + if (mutI >= 1 shl 2) { + n -= 2 + mutI = mutI ushr 2 + } + + return n - (mutI ushr 1) + } + } +} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/InternalUtils.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/InternalUtils.kt index c83f35d32..611c4064d 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/InternalUtils.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/InternalUtils.kt @@ -18,6 +18,22 @@ internal object InternalUtils { fun factorial(n: Int): Long = FACTORIALS[n] + fun validateProbabilities(probabilities: DoubleArray?): Double { + require(!(probabilities == null || probabilities.isEmpty())) { "Probabilities must not be empty." } + var sumProb = 0.0 + + probabilities.forEach { prob -> + validateProbability(prob) + sumProb += prob + } + + require(!(sumProb.isInfinite() || sumProb <= 0)) { "Invalid sum of probabilities: $sumProb" } + return sumProb + } + + private fun validateProbability(probability: Double): Unit = + require(!(probability < 0 || probability.isInfinite() || probability.isNaN())) { "Invalid probability: $probability" } + class FactorialLog private constructor( numValues: Int, cache: DoubleArray? @@ -30,9 +46,11 @@ internal object InternalUtils { if (cache != null && cache.size > BEGIN_LOG_FACTORIALS) { // Copy available values. endCopy = min(cache.size, numValues) - cache.copyInto(logFactorials, + cache.copyInto( + logFactorials, BEGIN_LOG_FACTORIALS, - BEGIN_LOG_FACTORIALS, endCopy) + BEGIN_LOG_FACTORIALS, endCopy + ) } // All values to be computed else endCopy = BEGIN_LOG_FACTORIALS @@ -50,15 +68,11 @@ internal object InternalUtils { if (n < logFactorials.size) return logFactorials[n] - return if (n < FACTORIALS.size) ln( - FACTORIALS[n].toDouble()) else InternalGamma.logGamma( - n + 1.0 - ) + return if (n < FACTORIALS.size) ln(FACTORIALS[n].toDouble()) else InternalGamma.logGamma(n + 1.0) } companion object { - fun create(): FactorialLog = - FactorialLog(0, null) + fun create(): FactorialLog = FactorialLog(0, null) } } } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt index f14aaa8ab..47e84327a 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt @@ -2,9 +2,9 @@ package scientifik.kmath.prob.samplers import scientifik.kmath.chains.Chain import scientifik.kmath.chains.ConstantChain -import scientifik.kmath.chains.SimpleChain import scientifik.kmath.prob.RandomGenerator import scientifik.kmath.prob.Sampler +import scientifik.kmath.prob.chain import scientifik.kmath.prob.next import kotlin.math.* @@ -35,7 +35,7 @@ class LargeMeanPoissonSampler private constructor(val mean: Double) : Sampler = SimpleChain { + override fun sample(generator: RandomGenerator): Chain = generator.chain { // This will never be null. It may be a no-op delegate that returns zero. val y2 = smallMeanPoissonSampler.next(generator) var x: Double diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt index 966d7db9a..dd1a419c9 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt @@ -1,7 +1,6 @@ package scientifik.kmath.prob.samplers import scientifik.kmath.chains.Chain -import scientifik.kmath.chains.SimpleChain import scientifik.kmath.prob.RandomGenerator import scientifik.kmath.prob.Sampler import scientifik.kmath.prob.chain diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt index de593a811..a7956deee 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt @@ -1,7 +1,6 @@ package scientifik.kmath.prob.samplers import scientifik.kmath.chains.Chain -import scientifik.kmath.chains.SimpleChain import scientifik.kmath.prob.RandomGenerator import scientifik.kmath.prob.Sampler import scientifik.kmath.prob.chain From a03c82f758d58aac4f001cae2dcc4cc83ef716c4 Mon Sep 17 00:00:00 2001 From: Iaroslav Date: Fri, 12 Jun 2020 02:49:06 +0700 Subject: [PATCH 12/31] Simplify BlockingIntChain and BlockingRealChain; add blocking extension function for RandomChain; copy general documentation to samplers created with Apache Commons RNG --- .../kmath/chains/BlockingIntChain.kt | 8 ++---- .../kmath/chains/BlockingRealChain.kt | 8 ++---- .../scientifik/kmath/prob/RandomChain.kt | 16 +++++++++++ .../AhrensDieterExponentialSampler.kt | 3 ++- .../AhrensDieterMarsagliaTsangGammaSampler.kt | 10 +++++++ .../samplers/AliasMethodDiscreteSampler.kt | 27 +++++++++++++++++++ .../BoxMullerNormalizedGaussianSampler.kt | 4 ++- .../kmath/prob/samplers/GaussianSampler.kt | 3 ++- .../samplers/KempSmallMeanPoissonSampler.kt | 9 ++++++- .../prob/samplers/LargeMeanPoissonSampler.kt | 10 +++++-- .../MarsagliaNormalizedGaussianSampler.kt | 7 +++-- .../samplers/NormalizedGaussianSampler.kt | 4 +++ .../kmath/prob/samplers/PoissonSampler.kt | 11 ++++++-- .../prob/samplers/SmallMeanPoissonSampler.kt | 9 ++++++- .../ZigguratNormalizedGaussianSampler.kt | 5 +++- 15 files changed, 110 insertions(+), 24 deletions(-) diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt index 6ec84d5c7..ec1633fb0 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt @@ -4,9 +4,5 @@ package scientifik.kmath.chains * Performance optimized chain for integer values */ abstract class BlockingIntChain : Chain { - abstract fun nextInt(): Int - - override suspend fun next(): Int = nextInt() - - fun nextBlock(size: Int): IntArray = IntArray(size) { nextInt() } -} \ No newline at end of file + suspend fun nextBlock(size: Int): IntArray = IntArray(size) { next() } +} diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt index 6b69d2734..f8930815c 100644 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt @@ -4,9 +4,5 @@ package scientifik.kmath.chains * Performance optimized chain for real values */ abstract class BlockingRealChain : Chain { - abstract fun nextDouble(): Double - - override suspend fun next(): Double = nextDouble() - - fun nextBlock(size: Int): DoubleArray = DoubleArray(size) { nextDouble() } -} \ No newline at end of file + suspend fun nextBlock(size: Int): DoubleArray = DoubleArray(size) { next() } +} 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 49163c701..68602e1ea 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt @@ -1,5 +1,7 @@ package scientifik.kmath.prob +import scientifik.kmath.chains.BlockingIntChain +import scientifik.kmath.chains.BlockingRealChain import scientifik.kmath.chains.Chain /** @@ -12,3 +14,17 @@ class RandomChain(val generator: RandomGenerator, private val gen: suspen } fun RandomGenerator.chain(gen: suspend RandomGenerator.() -> R): RandomChain = RandomChain(this, gen) + +fun RandomChain.blocking(): BlockingRealChain = let { + object : BlockingRealChain() { + override suspend fun next(): Double = it.next() + override fun fork(): Chain = it.fork() + } +} + +fun RandomChain.blocking(): BlockingIntChain = let { + object : BlockingIntChain() { + override suspend fun next(): Int = it.next() + override fun fork(): Chain = it.fork() + } +} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterExponentialSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterExponentialSampler.kt index a83bf6e12..fa49f194e 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterExponentialSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterExponentialSampler.kt @@ -8,8 +8,9 @@ import kotlin.math.ln import kotlin.math.pow /** - * Based on commons-rng implementation. + * Sampling from an [exponential distribution](http://mathworld.wolfram.com/ExponentialDistribution.html). * + * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.html */ class AhrensDieterExponentialSampler private constructor(val mean: Double) : Sampler { diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt index 57bc778ba..f1e622a27 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt @@ -7,6 +7,16 @@ import scientifik.kmath.prob.chain import scientifik.kmath.prob.next import kotlin.math.* +/** + * Sampling from the [gamma distribution](http://mathworld.wolfram.com/GammaDistribution.html). + * - For 0 < alpha < 1: + * Ahrens, J. H. and Dieter, U., Computer methods for sampling from gamma, beta, Poisson and binomial distributions, Computing, 12, 223-246, 1974. + * - For alpha >= 1: + * Marsaglia and Tsang, A Simple Method for Generating Gamma Variables. ACM Transactions on Mathematical Software, Volume 26 Issue 3, September, 2000. + * + * Based on Commons RNG implementation. + * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.html + */ class AhrensDieterMarsagliaTsangGammaSampler private constructor( alpha: Double, theta: Double diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AliasMethodDiscreteSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AliasMethodDiscreteSampler.kt index 6b514a40b..5af6986ea 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AliasMethodDiscreteSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/AliasMethodDiscreteSampler.kt @@ -9,6 +9,33 @@ import kotlin.math.ceil import kotlin.math.max import kotlin.math.min +/** + * Distribution sampler that uses the Alias method. It can be used to sample from n values each with an associated + * probability. This implementation is based on the detailed explanation of the alias method by Keith Schartz and + * implements Vose's algorithm. + * + * Vose, M.D., A linear algorithm for generating random numbers with a given distribution, IEEE Transactions on + * Software Engineering, 17, 972-975, 1991. he algorithm will sample values in O(1) time after a pre-processing step + * of O(n) time. + * + * The alias tables are constructed using fraction probabilities with an assumed denominator of 253. In the generic + * case sampling uses UniformRandomProvider.nextInt(int) and the upper 53-bits from UniformRandomProvider.nextLong(). + * + * Zero padding the input probabilities can be used to make more sampling more efficient. Any zero entry will always be + * aliased removing the requirement to compute a long. Increased sampling speed comes at the cost of increased storage + * space. The algorithm requires approximately 12 bytes of storage per input probability, that is n * 12 for size n. + * Zero-padding only requires 4 bytes of storage per padded value as the probability is known to be zero. + * + * An optimisation is performed for small table sizes that are a power of 2. In this case the sampling uses 1 or 2 + * calls from UniformRandomProvider.nextInt() to generate up to 64-bits for creation of an 11-bit index and 53-bits + * for the long. This optimisation requires a generator with a high cycle length for the lower order bits. + * + * Larger table sizes that are a power of 2 will benefit from fast algorithms for UniformRandomProvider.nextInt(int) + * that exploit the power of 2. + * + * Based on Commons RNG implementation. + * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSampler.html + */ open class AliasMethodDiscreteSampler private constructor( // Deliberate direct storage of input arrays protected val probability: LongArray, diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt index ef5b36289..a2d10e9f1 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt @@ -7,8 +7,10 @@ import scientifik.kmath.prob.chain import kotlin.math.* /** - * Based on commons-rng implementation. + * [Box-Muller algorithm](https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform) for sampling from a Gaussian + * distribution. * + * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.html */ class BoxMullerNormalizedGaussianSampler private constructor() : NormalizedGaussianSampler, Sampler { diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/GaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/GaussianSampler.kt index 755c73df3..2516f0480 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/GaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/GaussianSampler.kt @@ -6,8 +6,9 @@ import scientifik.kmath.prob.RandomGenerator import scientifik.kmath.prob.Sampler /** - * Based on commons-rng implementation. + * Sampling from a Gaussian distribution with given mean and standard deviation. * + * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/GaussianSampler.html */ class GaussianSampler private constructor( diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt index 45afaa179..bb1dfa0c2 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt @@ -7,8 +7,15 @@ import scientifik.kmath.prob.chain import kotlin.math.exp /** - * Based on commons-rng implementation. + * Sampler for the Poisson distribution. + * - Kemp, A, W, (1981) Efficient Generation of Logarithmically Distributed Pseudo-Random Variables. Journal of the Royal Statistical Society. Vol. 30, No. 3, pp. 249-253. + * This sampler is suitable for mean < 40. For large means, LargeMeanPoissonSampler should be used instead. * + * Note: The algorithm uses a recurrence relation to compute the Poisson probability and a rolling summation for the cumulative probability. When the mean is large the initial probability (Math.exp(-mean)) is zero and an exception is raised by the constructor. + * + * Sampling uses 1 call to UniformRandomProvider.nextDouble(). This method provides an alternative to the SmallMeanPoissonSampler for slow generators of double. + * + * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.html */ class KempSmallMeanPoissonSampler private constructor( diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt index 47e84327a..eafe28b39 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/LargeMeanPoissonSampler.kt @@ -9,8 +9,14 @@ import scientifik.kmath.prob.next import kotlin.math.* /** - * Based on commons-rng implementation. + * Sampler for the Poisson distribution. + * - For large means, we use the rejection algorithm described in + * Devroye, Luc. (1981).The Computer Generation of Poisson Random Variables + * Computing vol. 26 pp. 197-207. * + * This sampler is suitable for mean >= 40. + * + * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.html */ class LargeMeanPoissonSampler private constructor(val mean: Double) : Sampler { @@ -112,7 +118,7 @@ class LargeMeanPoissonSampler private constructor(val mean: Double) : Sampler { + private val NO_SMALL_MEAN_POISSON_SAMPLER: Sampler = object : Sampler { override fun sample(generator: RandomGenerator): Chain = ConstantChain(0) } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt index 80d93055d..eec791906 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt @@ -8,11 +8,14 @@ import kotlin.math.ln import kotlin.math.sqrt /** - * Based on commons-rng implementation. + * [Marsaglia polar method](https://en.wikipedia.org/wiki/Marsaglia_polar_method) for sampling from a Gaussian + * distribution with mean 0 and standard deviation 1. This is a variation of the algorithm implemented in + * [BoxMullerNormalizedGaussianSampler]. * + * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.html */ -class MarsagliaNormalizedGaussianSampler private constructor(): NormalizedGaussianSampler, Sampler { +class MarsagliaNormalizedGaussianSampler private constructor() : NormalizedGaussianSampler, Sampler { private var nextGaussian = Double.NaN override fun sample(generator: RandomGenerator): Chain = generator.chain { diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/NormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/NormalizedGaussianSampler.kt index 0e5d6db59..0ead77b5a 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/NormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/NormalizedGaussianSampler.kt @@ -2,4 +2,8 @@ package scientifik.kmath.prob.samplers import scientifik.kmath.prob.Sampler +/** + * Marker interface for a sampler that generates values from an N(0,1) + * [Gaussian distribution](https://en.wikipedia.org/wiki/Normal_distribution). + */ interface NormalizedGaussianSampler : Sampler diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/PoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/PoissonSampler.kt index 3a85e6992..4648940e1 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/PoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/PoissonSampler.kt @@ -5,9 +5,16 @@ import scientifik.kmath.prob.RandomGenerator import scientifik.kmath.prob.Sampler /** - * Based on commons-rng implementation. + * Sampler for the Poisson distribution. + * - For small means, a Poisson process is simulated using uniform deviates, as described in + * Knuth (1969). Seminumerical Algorithms. The Art of Computer Programming, Volume 2. Chapter 3.4.1.F.3 + * Important integer-valued distributions: The Poisson distribution. Addison Wesley. + * The Poisson process (and hence, the returned value) is bounded by 1000 * mean. + * - For large means, we use the rejection algorithm described in + * Devroye, Luc. (1981). The Computer Generation of Poisson Random Variables Computing vol. 26 pp. 197-207. * - * https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/PoissonSampler.html + * Based on Commons RNG implementation. + * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/PoissonSampler.html */ class PoissonSampler private constructor( mean: Double diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt index dd1a419c9..f5b0eef68 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/SmallMeanPoissonSampler.kt @@ -8,7 +8,14 @@ import kotlin.math.ceil import kotlin.math.exp /** - * Based on commons-rng implementation. + * Sampler for the Poisson distribution. + * - For small means, a Poisson process is simulated using uniform deviates, as described in + * Knuth (1969). Seminumerical Algorithms. The Art of Computer Programming, Volume 2. Chapter 3.4.1.F.3 Important + * integer-valued distributions: The Poisson distribution. Addison Wesley. + * - The Poisson process (and hence, the returned value) is bounded by 1000 * mean. + * This sampler is suitable for mean < 40. For large means, [LargeMeanPoissonSampler] should be used instead. + * + * Based on Commons RNG implementation. * * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSampler.html */ diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt index a7956deee..421555d64 100644 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt @@ -7,8 +7,11 @@ import scientifik.kmath.prob.chain import kotlin.math.* /** - * Based on commons-rng implementation. + * [Marsaglia and Tsang "Ziggurat"](https://en.wikipedia.org/wiki/Ziggurat_algorithm) method for sampling from a + * Gaussian distribution with mean 0 and standard deviation 1. The algorithm is explained in this paper and this + * implementation has been adapted from the C code provided therein. * + * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.html */ class ZigguratNormalizedGaussianSampler private constructor() : From 53ebec2e01bd7d0bfe1eda758bec01e92af2ebb7 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Sun, 27 Sep 2020 15:16:12 +0700 Subject: [PATCH 13/31] Perform 1.4 and explicit API migrations, refactor blocking chain, make tests work --- .../kscience/kmath/chains/BlockingIntChain.kt | 9 ++-- .../kmath/chains/BlockingRealChain.kt | 9 ++-- .../kmath/chains/BlockingIntChain.kt | 8 ---- .../kmath/chains/BlockingRealChain.kt | 8 ---- .../kscience/kmath/prob/Distribution.kt | 3 +- .../kmath/prob/FactorizedDistribution.kt | 2 +- .../kotlin/kscience/kmath/prob/RandomChain.kt | 7 ++- .../AhrensDieterExponentialSampler.kt | 18 +++---- .../AhrensDieterMarsagliaTsangGammaSampler.kt | 31 ++++++------ .../samplers/AliasMethodDiscreteSampler.kt | 20 ++++---- .../BoxMullerNormalizedGaussianSampler.kt | 20 ++++---- .../kmath/prob/samplers/GaussianSampler.kt | 22 ++++----- .../kmath/prob/samplers/InternalGamma.kt | 2 +- .../kmath/prob/samplers/InternalUtils.kt | 2 +- .../samplers/KempSmallMeanPoissonSampler.kt | 20 ++++---- .../prob/samplers/LargeMeanPoissonSampler.kt | 22 ++++----- .../MarsagliaNormalizedGaussianSampler.kt | 20 ++++---- .../samplers/NormalizedGaussianSampler.kt | 6 +-- .../kmath/prob/samplers/PoissonSampler.kt | 20 ++++---- .../prob/samplers/SmallMeanPoissonSampler.kt | 20 ++++---- .../ZigguratNormalizedGaussianSampler.kt | 21 ++++----- .../kmath/prob/FactorizedDistribution.kt | 46 ------------------ .../scientifik/kmath/prob/RandomChain.kt | 30 ------------ .../scientifik/kmath/prob/RandomGenerator.kt | 47 ------------------- .../kmath/prob/RandomSourceGenerator.kt | 21 +++++---- .../kmath/prob/CommonsDistributionsTest.kt | 11 ++--- 26 files changed, 149 insertions(+), 296 deletions(-) delete mode 100644 kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt delete mode 100644 kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt delete mode 100644 kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt diff --git a/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingIntChain.kt b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingIntChain.kt index 6088267a2..766311fc3 100644 --- a/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingIntChain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingIntChain.kt @@ -3,10 +3,7 @@ package kscience.kmath.chains /** * Performance optimized chain for integer values */ -public abstract class BlockingIntChain : Chain { - public abstract fun nextInt(): Int - - override suspend fun next(): Int = nextInt() - - public fun nextBlock(size: Int): IntArray = IntArray(size) { nextInt() } +public interface BlockingIntChain : Chain { + public override suspend fun next(): Int + public suspend fun nextBlock(size: Int): IntArray = IntArray(size) { next() } } diff --git a/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingRealChain.kt b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingRealChain.kt index 718b3a18b..7c463b109 100644 --- a/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingRealChain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingRealChain.kt @@ -3,10 +3,7 @@ package kscience.kmath.chains /** * Performance optimized chain for real values */ -public abstract class BlockingRealChain : Chain { - public abstract fun nextDouble(): Double - - override suspend fun next(): Double = nextDouble() - - public fun nextBlock(size: Int): DoubleArray = DoubleArray(size) { nextDouble() } +public interface BlockingRealChain : Chain { + public override suspend fun next(): Double + public suspend fun nextBlock(size: Int): DoubleArray = DoubleArray(size) { next() } } diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt deleted file mode 100644 index ec1633fb0..000000000 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingIntChain.kt +++ /dev/null @@ -1,8 +0,0 @@ -package scientifik.kmath.chains - -/** - * Performance optimized chain for integer values - */ -abstract class BlockingIntChain : Chain { - suspend fun nextBlock(size: Int): IntArray = IntArray(size) { next() } -} diff --git a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt b/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt deleted file mode 100644 index f8930815c..000000000 --- a/kmath-coroutines/src/commonMain/kotlin/scientifik/kmath/chains/BlockingRealChain.kt +++ /dev/null @@ -1,8 +0,0 @@ -package scientifik.kmath.chains - -/** - * Performance optimized chain for real values - */ -abstract class BlockingRealChain : Chain { - suspend fun nextBlock(size: Int): DoubleArray = DoubleArray(size) { next() } -} diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt index 8ca28aedb..b3f1524ea 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt @@ -1,5 +1,6 @@ package kscience.kmath.prob +import kotlinx.coroutines.flow.first import kscience.kmath.chains.Chain import kscience.kmath.chains.collect import kscience.kmath.structures.Buffer @@ -70,7 +71,7 @@ public fun Sampler.sampleBuffer( } } -suspend fun Sampler.next(generator: RandomGenerator) = sample(generator).first() +public suspend fun Sampler.next(generator: RandomGenerator): T = sample(generator).first() /** * Generate a bunch of samples from real distributions diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/FactorizedDistribution.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/FactorizedDistribution.kt index 4d713fc4e..128b284be 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/FactorizedDistribution.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/FactorizedDistribution.kt @@ -14,7 +14,7 @@ public interface NamedDistribution : Distribution> public class FactorizedDistribution(public val distributions: Collection>) : NamedDistribution { override fun probability(arg: Map): Double = - distributions.fold(1.0) { acc, distr -> acc * distr.probability(arg) } + distributions.fold(1.0) { acc, dist -> acc * dist.probability(arg) } override fun sample(generator: RandomGenerator): Chain> { val chains = distributions.map { it.sample(generator) } diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomChain.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomChain.kt index b4a80f6c5..70fa8b97b 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomChain.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomChain.kt @@ -1,17 +1,22 @@ package kscience.kmath.prob +import kscience.kmath.chains.BlockingIntChain +import kscience.kmath.chains.BlockingRealChain import kscience.kmath.chains.Chain /** * A possibly stateful chain producing random values. + * + * @property generator the underlying [RandomGenerator] instance. */ public class RandomChain( public val generator: RandomGenerator, private val gen: suspend RandomGenerator.() -> R ) : Chain { override suspend fun next(): R = generator.gen() - override fun fork(): Chain = RandomChain(generator.fork(), gen) } public fun RandomGenerator.chain(gen: suspend RandomGenerator.() -> R): RandomChain = RandomChain(this, gen) +public fun Chain.blocking(): BlockingRealChain = object : Chain by this, BlockingRealChain {} +public fun Chain.blocking(): BlockingIntChain = object : Chain by this, BlockingIntChain {} diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AhrensDieterExponentialSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AhrensDieterExponentialSampler.kt index fa49f194e..dc388a3ea 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AhrensDieterExponentialSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AhrensDieterExponentialSampler.kt @@ -1,9 +1,9 @@ -package scientifik.kmath.prob.samplers +package kscience.kmath.prob.samplers -import scientifik.kmath.chains.Chain -import scientifik.kmath.prob.RandomGenerator -import scientifik.kmath.prob.Sampler -import scientifik.kmath.prob.chain +import kscience.kmath.chains.Chain +import kscience.kmath.prob.RandomGenerator +import kscience.kmath.prob.Sampler +import kscience.kmath.prob.chain import kotlin.math.ln import kotlin.math.pow @@ -13,8 +13,8 @@ import kotlin.math.pow * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.html */ -class AhrensDieterExponentialSampler private constructor(val mean: Double) : Sampler { - override fun sample(generator: RandomGenerator): Chain = generator.chain { +public class AhrensDieterExponentialSampler private constructor(public val mean: Double) : Sampler { + public override fun sample(generator: RandomGenerator): Chain = generator.chain { // Step 1: var a = 0.0 var u = nextDouble() @@ -47,7 +47,7 @@ class AhrensDieterExponentialSampler private constructor(val mean: Double) : Sam override fun toString(): String = "Ahrens-Dieter Exponential deviate" - companion object { + public companion object { private val EXPONENTIAL_SA_QI by lazy { DoubleArray(16) } init { @@ -64,7 +64,7 @@ class AhrensDieterExponentialSampler private constructor(val mean: Double) : Sam } } - fun of(mean: Double): AhrensDieterExponentialSampler { + public fun of(mean: Double): AhrensDieterExponentialSampler { require(mean > 0) { "mean is not strictly positive: $mean" } return AhrensDieterExponentialSampler(mean) } diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt index f1e622a27..e18a44cb9 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt @@ -1,10 +1,10 @@ -package scientifik.kmath.prob.samplers +package kscience.kmath.prob.samplers -import scientifik.kmath.chains.Chain -import scientifik.kmath.prob.RandomGenerator -import scientifik.kmath.prob.Sampler -import scientifik.kmath.prob.chain -import scientifik.kmath.prob.next +import kscience.kmath.chains.Chain +import kscience.kmath.prob.RandomGenerator +import kscience.kmath.prob.Sampler +import kscience.kmath.prob.chain +import kscience.kmath.prob.next import kotlin.math.* /** @@ -17,15 +17,12 @@ import kotlin.math.* * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.html */ -class AhrensDieterMarsagliaTsangGammaSampler private constructor( +public class AhrensDieterMarsagliaTsangGammaSampler private constructor( alpha: Double, theta: Double ) : Sampler { private val delegate: BaseGammaSampler = - if (alpha < 1) AhrensDieterGammaSampler(alpha, theta) else MarsagliaTsangGammaSampler( - alpha, - theta - ) + if (alpha < 1) AhrensDieterGammaSampler(alpha, theta) else MarsagliaTsangGammaSampler(alpha, theta) private abstract class BaseGammaSampler internal constructor( protected val alpha: Double, @@ -39,7 +36,7 @@ class AhrensDieterMarsagliaTsangGammaSampler private constructor( override fun toString(): String = "Ahrens-Dieter-Marsaglia-Tsang Gamma deviate" } - private class AhrensDieterGammaSampler internal constructor(alpha: Double, theta: Double) : + private class AhrensDieterGammaSampler(alpha: Double, theta: Double) : BaseGammaSampler(alpha, theta) { private val oneOverAlpha: Double = 1.0 / alpha private val bGSOptim: Double = 1.0 + alpha / E @@ -75,7 +72,7 @@ class AhrensDieterMarsagliaTsangGammaSampler private constructor( } } - private class MarsagliaTsangGammaSampler internal constructor(alpha: Double, theta: Double) : + private class MarsagliaTsangGammaSampler(alpha: Double, theta: Double) : BaseGammaSampler(alpha, theta) { private val dOptim: Double private val cOptim: Double @@ -110,11 +107,11 @@ class AhrensDieterMarsagliaTsangGammaSampler private constructor( } } - override fun sample(generator: RandomGenerator): Chain = delegate.sample(generator) - override fun toString(): String = delegate.toString() + public override fun sample(generator: RandomGenerator): Chain = delegate.sample(generator) + public override fun toString(): String = delegate.toString() - companion object { - fun of( + public companion object { + public fun of( alpha: Double, theta: Double ): Sampler = AhrensDieterMarsagliaTsangGammaSampler(alpha, theta) diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AliasMethodDiscreteSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AliasMethodDiscreteSampler.kt index 5af6986ea..c5eed2990 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AliasMethodDiscreteSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AliasMethodDiscreteSampler.kt @@ -1,10 +1,9 @@ -package scientifik.kmath.prob.samplers +package kscience.kmath.prob.samplers -import scientifik.kmath.chains.Chain -import scientifik.kmath.prob.RandomGenerator -import scientifik.kmath.prob.Sampler -import scientifik.kmath.prob.chain -import kotlin.jvm.JvmOverloads +import kscience.kmath.chains.Chain +import kscience.kmath.prob.RandomGenerator +import kscience.kmath.prob.Sampler +import kscience.kmath.prob.chain import kotlin.math.ceil import kotlin.math.max import kotlin.math.min @@ -36,7 +35,7 @@ import kotlin.math.min * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSampler.html */ -open class AliasMethodDiscreteSampler private constructor( +public open class AliasMethodDiscreteSampler private constructor( // Deliberate direct storage of input arrays protected val probability: LongArray, protected val alias: IntArray @@ -102,17 +101,16 @@ open class AliasMethodDiscreteSampler private constructor( if (generator.nextLong() ushr 11 < probability[j]) j else alias[j] } - override fun toString(): String = "Alias method" + public override fun toString(): String = "Alias method" - companion object { + public companion object { private const val DEFAULT_ALPHA = 0 private const val ZERO = 0.0 private const val ONE_AS_NUMERATOR = 1L shl 53 private const val CONVERT_TO_NUMERATOR: Double = ONE_AS_NUMERATOR.toDouble() private const val MAX_SMALL_POWER_2_SIZE = 1 shl 11 - @JvmOverloads - fun of( + public fun of( probabilities: DoubleArray, alpha: Int = DEFAULT_ALPHA ): Sampler { diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt index a2d10e9f1..50a7b00c2 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/BoxMullerNormalizedGaussianSampler.kt @@ -1,9 +1,9 @@ -package scientifik.kmath.prob.samplers +package kscience.kmath.prob.samplers -import scientifik.kmath.chains.Chain -import scientifik.kmath.prob.RandomGenerator -import scientifik.kmath.prob.Sampler -import scientifik.kmath.prob.chain +import kscience.kmath.chains.Chain +import kscience.kmath.prob.RandomGenerator +import kscience.kmath.prob.Sampler +import kscience.kmath.prob.chain import kotlin.math.* /** @@ -13,10 +13,10 @@ import kotlin.math.* * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.html */ -class BoxMullerNormalizedGaussianSampler private constructor() : NormalizedGaussianSampler, Sampler { +public class BoxMullerNormalizedGaussianSampler private constructor() : NormalizedGaussianSampler, Sampler { private var nextGaussian: Double = Double.NaN - override fun sample(generator: RandomGenerator): Chain = generator.chain { + public override fun sample(generator: RandomGenerator): Chain = generator.chain { val random: Double if (nextGaussian.isNaN()) { @@ -40,9 +40,9 @@ class BoxMullerNormalizedGaussianSampler private constructor() : NormalizedGauss random } - override fun toString(): String = "Box-Muller normalized Gaussian deviate" + public override fun toString(): String = "Box-Muller normalized Gaussian deviate" - companion object { - fun of(): BoxMullerNormalizedGaussianSampler = BoxMullerNormalizedGaussianSampler() + public companion object { + public fun of(): BoxMullerNormalizedGaussianSampler = BoxMullerNormalizedGaussianSampler() } } diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/GaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/GaussianSampler.kt index 2516f0480..1a0ccac90 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/GaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/GaussianSampler.kt @@ -1,9 +1,9 @@ -package scientifik.kmath.prob.samplers +package kscience.kmath.prob.samplers -import scientifik.kmath.chains.Chain -import scientifik.kmath.chains.map -import scientifik.kmath.prob.RandomGenerator -import scientifik.kmath.prob.Sampler +import kscience.kmath.chains.Chain +import kscience.kmath.chains.map +import kscience.kmath.prob.RandomGenerator +import kscience.kmath.prob.Sampler /** * Sampling from a Gaussian distribution with given mean and standard deviation. @@ -11,24 +11,24 @@ import scientifik.kmath.prob.Sampler * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/GaussianSampler.html */ -class GaussianSampler private constructor( +public class GaussianSampler private constructor( private val mean: Double, private val standardDeviation: Double, private val normalized: NormalizedGaussianSampler ) : Sampler { - override fun sample(generator: RandomGenerator): Chain = normalized + public override fun sample(generator: RandomGenerator): Chain = normalized .sample(generator) .map { standardDeviation * it + mean } override fun toString(): String = "Gaussian deviate [$normalized]" - companion object { - fun of( + public companion object { + public fun of( mean: Double, standardDeviation: Double, - normalized: NormalizedGaussianSampler + normalized: NormalizedGaussianSampler = ZigguratNormalizedGaussianSampler.of() ): GaussianSampler { - require(standardDeviation > 0) { "standard deviation is not strictly positive: $standardDeviation" } + require(standardDeviation > 0.0) { "standard deviation is not strictly positive: $standardDeviation" } return GaussianSampler( mean, diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalGamma.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalGamma.kt index 50c5f4ce0..16a5c96e0 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalGamma.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalGamma.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.prob.samplers +package kscience.kmath.prob.samplers import kotlin.math.PI import kotlin.math.ln diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalUtils.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalUtils.kt index 611c4064d..08a321b75 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalUtils.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalUtils.kt @@ -1,4 +1,4 @@ -package scientifik.kmath.prob.samplers +package kscience.kmath.prob.samplers import kotlin.math.ln import kotlin.math.min diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt index bb1dfa0c2..624fc9a7e 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/KempSmallMeanPoissonSampler.kt @@ -1,9 +1,9 @@ -package scientifik.kmath.prob.samplers +package kscience.kmath.prob.samplers -import scientifik.kmath.chains.Chain -import scientifik.kmath.prob.RandomGenerator -import scientifik.kmath.prob.Sampler -import scientifik.kmath.prob.chain +import kscience.kmath.chains.Chain +import kscience.kmath.prob.RandomGenerator +import kscience.kmath.prob.Sampler +import kscience.kmath.prob.chain import kotlin.math.exp /** @@ -18,11 +18,11 @@ import kotlin.math.exp * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.html */ -class KempSmallMeanPoissonSampler private constructor( +public class KempSmallMeanPoissonSampler private constructor( private val p0: Double, private val mean: Double ) : Sampler { - override fun sample(generator: RandomGenerator): Chain = generator.chain { + public override fun sample(generator: RandomGenerator): Chain = generator.chain { // Note on the algorithm: // - X is the unknown sample deviate (the output of the algorithm) // - x is the current value from the distribution @@ -48,10 +48,10 @@ class KempSmallMeanPoissonSampler private constructor( x } - override fun toString(): String = "Kemp Small Mean Poisson deviate" + public override fun toString(): String = "Kemp Small Mean Poisson deviate" - companion object { - fun of(mean: Double): KempSmallMeanPoissonSampler { + public companion object { + public fun of(mean: Double): KempSmallMeanPoissonSampler { require(mean > 0) { "Mean is not strictly positive: $mean" } val p0 = exp(-mean) // Probability must be positive. As mean increases then p(0) decreases. diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/LargeMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/LargeMeanPoissonSampler.kt index eafe28b39..dba2550cb 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/LargeMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/LargeMeanPoissonSampler.kt @@ -1,11 +1,11 @@ -package scientifik.kmath.prob.samplers +package kscience.kmath.prob.samplers -import scientifik.kmath.chains.Chain -import scientifik.kmath.chains.ConstantChain -import scientifik.kmath.prob.RandomGenerator -import scientifik.kmath.prob.Sampler -import scientifik.kmath.prob.chain -import scientifik.kmath.prob.next +import kscience.kmath.chains.Chain +import kscience.kmath.chains.ConstantChain +import kscience.kmath.prob.RandomGenerator +import kscience.kmath.prob.Sampler +import kscience.kmath.prob.chain +import kscience.kmath.prob.next import kotlin.math.* /** @@ -19,7 +19,7 @@ import kotlin.math.* * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.html */ -class LargeMeanPoissonSampler private constructor(val mean: Double) : Sampler { +public class LargeMeanPoissonSampler private constructor(public val mean: Double) : Sampler { private val exponential: Sampler = AhrensDieterExponentialSampler.of(1.0) private val gaussian: Sampler = ZigguratNormalizedGaussianSampler.of() private val factorialLog: InternalUtils.FactorialLog = NO_CACHE_FACTORIAL_LOG @@ -41,7 +41,7 @@ class LargeMeanPoissonSampler private constructor(val mean: Double) : Sampler = generator.chain { + public override fun sample(generator: RandomGenerator): Chain = generator.chain { // This will never be null. It may be a no-op delegate that returns zero. val y2 = smallMeanPoissonSampler.next(generator) var x: Double @@ -114,7 +114,7 @@ class LargeMeanPoissonSampler private constructor(val mean: Double) : Sampler = ConstantChain(0) } - fun of(mean: Double): LargeMeanPoissonSampler { + public fun of(mean: Double): LargeMeanPoissonSampler { require(mean >= 1) { "mean is not >= 1: $mean" } // The algorithm is not valid if Math.floor(mean) is not an integer. require(mean <= MAX_MEAN) { "mean $mean > $MAX_MEAN" } diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt index eec791906..69c04c20b 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/MarsagliaNormalizedGaussianSampler.kt @@ -1,9 +1,9 @@ -package scientifik.kmath.prob.samplers +package kscience.kmath.prob.samplers -import scientifik.kmath.chains.Chain -import scientifik.kmath.prob.RandomGenerator -import scientifik.kmath.prob.Sampler -import scientifik.kmath.prob.chain +import kscience.kmath.chains.Chain +import kscience.kmath.prob.RandomGenerator +import kscience.kmath.prob.Sampler +import kscience.kmath.prob.chain import kotlin.math.ln import kotlin.math.sqrt @@ -15,10 +15,10 @@ import kotlin.math.sqrt * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.html */ -class MarsagliaNormalizedGaussianSampler private constructor() : NormalizedGaussianSampler, Sampler { +public class MarsagliaNormalizedGaussianSampler private constructor() : NormalizedGaussianSampler, Sampler { private var nextGaussian = Double.NaN - override fun sample(generator: RandomGenerator): Chain = generator.chain { + public override fun sample(generator: RandomGenerator): Chain = generator.chain { if (nextGaussian.isNaN()) { val alpha: Double var x: Double @@ -53,9 +53,9 @@ class MarsagliaNormalizedGaussianSampler private constructor() : NormalizedGauss } } - override fun toString(): String = "Box-Muller (with rejection) normalized Gaussian deviate" + public override fun toString(): String = "Box-Muller (with rejection) normalized Gaussian deviate" - companion object { - fun of(): MarsagliaNormalizedGaussianSampler = MarsagliaNormalizedGaussianSampler() + public companion object { + public fun of(): MarsagliaNormalizedGaussianSampler = MarsagliaNormalizedGaussianSampler() } } diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/NormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/NormalizedGaussianSampler.kt index 0ead77b5a..af2ab876d 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/NormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/NormalizedGaussianSampler.kt @@ -1,9 +1,9 @@ -package scientifik.kmath.prob.samplers +package kscience.kmath.prob.samplers -import scientifik.kmath.prob.Sampler +import kscience.kmath.prob.Sampler /** * Marker interface for a sampler that generates values from an N(0,1) * [Gaussian distribution](https://en.wikipedia.org/wiki/Normal_distribution). */ -interface NormalizedGaussianSampler : Sampler +public interface NormalizedGaussianSampler : Sampler diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/PoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/PoissonSampler.kt index 4648940e1..02d8d5632 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/PoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/PoissonSampler.kt @@ -1,8 +1,8 @@ -package scientifik.kmath.prob.samplers +package kscience.kmath.prob.samplers -import scientifik.kmath.chains.Chain -import scientifik.kmath.prob.RandomGenerator -import scientifik.kmath.prob.Sampler +import kscience.kmath.chains.Chain +import kscience.kmath.prob.RandomGenerator +import kscience.kmath.prob.Sampler /** * Sampler for the Poisson distribution. @@ -16,17 +16,15 @@ import scientifik.kmath.prob.Sampler * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/PoissonSampler.html */ -class PoissonSampler private constructor( - mean: Double -) : Sampler { +public class PoissonSampler private constructor(mean: Double) : Sampler { private val poissonSamplerDelegate: Sampler = of(mean) - override fun sample(generator: RandomGenerator): Chain = poissonSamplerDelegate.sample(generator) - override fun toString(): String = poissonSamplerDelegate.toString() + public override fun sample(generator: RandomGenerator): Chain = poissonSamplerDelegate.sample(generator) + public override fun toString(): String = poissonSamplerDelegate.toString() - companion object { + public companion object { private const val PIVOT = 40.0 - fun of(mean: Double) =// Each sampler should check the input arguments. + public fun of(mean: Double): Sampler =// Each sampler should check the input arguments. if (mean < PIVOT) SmallMeanPoissonSampler.of(mean) else LargeMeanPoissonSampler.of(mean) } } diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/SmallMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/SmallMeanPoissonSampler.kt index f5b0eef68..ff4233288 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/SmallMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/SmallMeanPoissonSampler.kt @@ -1,9 +1,9 @@ -package scientifik.kmath.prob.samplers +package kscience.kmath.prob.samplers -import scientifik.kmath.chains.Chain -import scientifik.kmath.prob.RandomGenerator -import scientifik.kmath.prob.Sampler -import scientifik.kmath.prob.chain +import kscience.kmath.chains.Chain +import kscience.kmath.prob.RandomGenerator +import kscience.kmath.prob.Sampler +import kscience.kmath.prob.chain import kotlin.math.ceil import kotlin.math.exp @@ -19,7 +19,7 @@ import kotlin.math.exp * * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSampler.html */ -class SmallMeanPoissonSampler private constructor(mean: Double) : Sampler { +public class SmallMeanPoissonSampler private constructor(mean: Double) : Sampler { private val p0: Double = exp(-mean) private val limit: Int = (if (p0 > 0) @@ -27,7 +27,7 @@ class SmallMeanPoissonSampler private constructor(mean: Double) : Sampler { else throw IllegalArgumentException("No p(x=0) probability for mean: $mean")).toInt() - override fun sample(generator: RandomGenerator): Chain = generator.chain { + public override fun sample(generator: RandomGenerator): Chain = generator.chain { var n = 0 var r = 1.0 @@ -39,10 +39,10 @@ class SmallMeanPoissonSampler private constructor(mean: Double) : Sampler { n } - override fun toString(): String = "Small Mean Poisson deviate" + public override fun toString(): String = "Small Mean Poisson deviate" - companion object { - fun of(mean: Double): SmallMeanPoissonSampler { + public companion object { + public fun of(mean: Double): SmallMeanPoissonSampler { require(mean > 0) { "mean is not strictly positive: $mean" } return SmallMeanPoissonSampler(mean) } diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt index 421555d64..c9103ba86 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt @@ -1,9 +1,9 @@ -package scientifik.kmath.prob.samplers +package kscience.kmath.prob.samplers -import scientifik.kmath.chains.Chain -import scientifik.kmath.prob.RandomGenerator -import scientifik.kmath.prob.Sampler -import scientifik.kmath.prob.chain +import kscience.kmath.chains.Chain +import kscience.kmath.prob.RandomGenerator +import kscience.kmath.prob.Sampler +import kscience.kmath.prob.chain import kotlin.math.* /** @@ -14,7 +14,7 @@ import kotlin.math.* * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.html */ -class ZigguratNormalizedGaussianSampler private constructor() : +public class ZigguratNormalizedGaussianSampler private constructor() : NormalizedGaussianSampler, Sampler { private fun sampleOne(generator: RandomGenerator): Double { @@ -23,9 +23,8 @@ class ZigguratNormalizedGaussianSampler private constructor() : return if (abs(j) < K[i]) j * W[i] else fix(generator, j, i) } - override fun sample(generator: RandomGenerator): Chain = generator.chain { sampleOne(this) } - - override fun toString(): String = "Ziggurat normalized Gaussian deviate" + public override fun sample(generator: RandomGenerator): Chain = generator.chain { sampleOne(this) } + public override fun toString(): String = "Ziggurat normalized Gaussian deviate" private fun fix( generator: RandomGenerator, @@ -59,7 +58,7 @@ class ZigguratNormalizedGaussianSampler private constructor() : } } - companion object { + public companion object { private const val R: Double = 3.442619855899 private const val ONE_OVER_R: Double = 1 / R private const val V: Double = 9.91256303526217e-3 @@ -94,7 +93,7 @@ class ZigguratNormalizedGaussianSampler private constructor() : } } - fun of(): ZigguratNormalizedGaussianSampler = ZigguratNormalizedGaussianSampler() + public fun of(): ZigguratNormalizedGaussianSampler = ZigguratNormalizedGaussianSampler() private fun gauss(x: Double): Double = exp(-0.5 * x * x) } } diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt deleted file mode 100644 index ae3f918ff..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/FactorizedDistribution.kt +++ /dev/null @@ -1,46 +0,0 @@ -package scientifik.kmath.prob - -import scientifik.kmath.chains.Chain -import scientifik.kmath.chains.SimpleChain - -/** - * A multivariate distribution which takes a map of parameters - */ -interface NamedDistribution : Distribution> - -/** - * A multivariate distribution that has independent distributions for separate axis - */ -class FactorizedDistribution(val distributions: Collection>) : NamedDistribution { - override fun probability(arg: Map): Double { - return distributions.fold(1.0) { acc, distr -> acc * distr.probability(arg) } - } - - override fun sample(generator: RandomGenerator): Chain> { - val chains = distributions.map { it.sample(generator) } - return SimpleChain> { - chains.fold(emptyMap()) { acc, chain -> acc + chain.next() } - } - } -} - -class NamedDistributionWrapper(val name: String, val distribution: Distribution) : NamedDistribution { - override fun probability(arg: Map): Double = distribution.probability( - arg[name] ?: error("Argument with name $name not found in input parameters") - ) - - override fun sample(generator: RandomGenerator): Chain> { - val chain = distribution.sample(generator) - return SimpleChain { - mapOf(name to chain.next()) - } - } -} - -class DistributionBuilder{ - private val distributions = ArrayList>() - - infix fun String.to(distribution: Distribution){ - distributions.add(NamedDistributionWrapper(this,distribution)) - } -} \ 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 deleted file mode 100644 index 68602e1ea..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomChain.kt +++ /dev/null @@ -1,30 +0,0 @@ -package scientifik.kmath.prob - -import scientifik.kmath.chains.BlockingIntChain -import scientifik.kmath.chains.BlockingRealChain -import scientifik.kmath.chains.Chain - -/** - * A possibly stateful chain producing random values. - */ -class RandomChain(val generator: RandomGenerator, private val gen: suspend RandomGenerator.() -> R) : Chain { - override suspend fun next(): R = generator.gen() - - override fun fork(): Chain = RandomChain(generator.fork(), gen) -} - -fun RandomGenerator.chain(gen: suspend RandomGenerator.() -> R): RandomChain = RandomChain(this, gen) - -fun RandomChain.blocking(): BlockingRealChain = let { - object : BlockingRealChain() { - override suspend fun next(): Double = it.next() - override fun fork(): Chain = it.fork() - } -} - -fun RandomChain.blocking(): BlockingIntChain = let { - object : BlockingIntChain() { - override suspend fun next(): Int = it.next() - override fun fork(): Chain = it.fork() - } -} diff --git a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt b/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt deleted file mode 100644 index 6bdabed9d..000000000 --- a/kmath-prob/src/commonMain/kotlin/scientifik/kmath/prob/RandomGenerator.kt +++ /dev/null @@ -1,47 +0,0 @@ -package scientifik.kmath.prob - -import kotlin.random.Random - -/** - * A basic generator - */ -interface RandomGenerator { - fun nextBoolean(): Boolean - fun nextDouble(): Double - fun nextInt(): Int - fun nextInt(until: Int): Int - fun nextLong(): Long - fun nextLong(until: Long): Long - fun fillBytes(array: ByteArray, fromIndex: Int = 0, toIndex: Int = array.size) - fun nextBytes(size: Int): ByteArray = ByteArray(size).also { fillBytes(it) } - - /** - * 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 { - val default by lazy { DefaultGenerator() } - fun default(seed: Long) = DefaultGenerator(Random(seed)) - } -} - -inline class DefaultGenerator(private val random: Random = Random) : RandomGenerator { - override fun nextBoolean(): Boolean = random.nextBoolean() - override fun nextDouble(): Double = random.nextDouble() - override fun nextInt(): Int = random.nextInt() - override fun nextInt(until: Int): Int = random.nextInt(until) - override fun nextLong(): Long = random.nextLong() - override fun nextLong(until: Long): Long = random.nextLong(until) - - override fun fillBytes(array: ByteArray, fromIndex: Int, toIndex: Int) { - random.nextBytes(array, fromIndex, toIndex) - } - - override fun nextBytes(size: Int): ByteArray = random.nextBytes(size) - override fun fork(): RandomGenerator = RandomGenerator.default(random.nextLong()) -} diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt index 797c932ad..67007358a 100644 --- a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt +++ b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt @@ -1,21 +1,22 @@ package scientifik.kmath.prob +import kscience.kmath.prob.RandomGenerator import org.apache.commons.rng.simple.RandomSource -class RandomSourceGenerator(private val source: RandomSource, seed: Long?) : +public class RandomSourceGenerator(private val source: RandomSource, seed: Long?) : RandomGenerator { private val random = seed?.let { RandomSource.create(source, seed) } ?: RandomSource.create(source) - override fun nextBoolean(): Boolean = random.nextBoolean() - override fun nextDouble(): Double = random.nextDouble() - override fun nextInt(): Int = random.nextInt() - override fun nextInt(until: Int): Int = random.nextInt(until) - override fun nextLong(): Long = random.nextLong() - override fun nextLong(until: Long): Long = random.nextLong(until) + public override fun nextBoolean(): Boolean = random.nextBoolean() + public override fun nextDouble(): Double = random.nextDouble() + public override fun nextInt(): Int = random.nextInt() + public override fun nextInt(until: Int): Int = random.nextInt(until) + public override fun nextLong(): Long = random.nextLong() + public override fun nextLong(until: Long): Long = random.nextLong(until) - override fun fillBytes(array: ByteArray, fromIndex: Int, toIndex: Int) { + public override fun fillBytes(array: ByteArray, fromIndex: Int, toIndex: Int) { require(toIndex > fromIndex) random.nextBytes(array, fromIndex, toIndex - fromIndex) } @@ -23,8 +24,8 @@ class RandomSourceGenerator(private val source: RandomSource, seed: Long?) : override fun fork(): RandomGenerator = RandomSourceGenerator(source, nextLong()) } -fun RandomGenerator.Companion.fromSource(source: RandomSource, seed: Long? = null): RandomSourceGenerator = +public fun RandomGenerator.Companion.fromSource(source: RandomSource, seed: Long? = null): RandomSourceGenerator = RandomSourceGenerator(source, seed) -fun RandomGenerator.Companion.mersenneTwister(seed: Long? = null): RandomSourceGenerator = +public fun RandomGenerator.Companion.mersenneTwister(seed: Long? = null): RandomSourceGenerator = fromSource(RandomSource.MT, seed) diff --git a/kmath-prob/src/jvmTest/kotlin/kscience/kmath/prob/CommonsDistributionsTest.kt b/kmath-prob/src/jvmTest/kotlin/kscience/kmath/prob/CommonsDistributionsTest.kt index 12a00684b..02fac366e 100644 --- a/kmath-prob/src/jvmTest/kotlin/kscience/kmath/prob/CommonsDistributionsTest.kt +++ b/kmath-prob/src/jvmTest/kotlin/kscience/kmath/prob/CommonsDistributionsTest.kt @@ -3,25 +3,24 @@ package kscience.kmath.prob import kotlinx.coroutines.flow.take import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking +import kscience.kmath.prob.samplers.GaussianSampler import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test internal class CommonsDistributionsTest { @Test fun testNormalDistributionSuspend() { - val distribution = Distribution.normal(7.0, 2.0) + val distribution = GaussianSampler.of(7.0, 2.0) val generator = RandomGenerator.default(1) - val sample = runBlocking { - distribution.sample(generator).take(1000).toList() - } + val sample = runBlocking { distribution.sample(generator).take(1000).toList() } Assertions.assertEquals(7.0, sample.average(), 0.1) } @Test fun testNormalDistributionBlocking() { - val distribution = Distribution.normal(7.0, 2.0) + val distribution = GaussianSampler.of(7.0, 2.0) val generator = RandomGenerator.default(1) - val sample = distribution.sample(generator).nextBlock(1000) + val sample = runBlocking { distribution.sample(generator).blocking().nextBlock(1000) } Assertions.assertEquals(7.0, sample.average(), 0.1) } } From b83293a057bba9b65f0e72920b68af846307f436 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Sun, 27 Sep 2020 15:52:18 +0700 Subject: [PATCH 14/31] Update example --- .../commons/prob/DistributionBenchmark.kt | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt b/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt index 8f68a9d3f..e4a5bc534 100644 --- a/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt +++ b/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt @@ -3,19 +3,20 @@ package kscience.kmath.commons.prob import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking -import org.apache.commons.rng.simple.RandomSource -import kscience.kmath.prob.RandomChain import kscience.kmath.prob.RandomGenerator +import kscience.kmath.prob.blocking import kscience.kmath.prob.fromSource +import kscience.kmath.prob.samplers.GaussianSampler +import org.apache.commons.rng.simple.RandomSource import java.time.Duration import java.time.Instant -import org.apache.commons.rng.sampling.distribution.ZigguratNormalizedGaussianSampler as ApacheZigguratNormalizedGaussianSampler -import kscience.kmath.prob.samplers.ZigguratNormalizedGaussianSampler as KMathZigguratNormalizedGaussianSampler +import org.apache.commons.rng.sampling.distribution.GaussianSampler as CMGaussianSampler +import org.apache.commons.rng.sampling.distribution.ZigguratNormalizedGaussianSampler as CMZigguratNormalizedGaussianSampler private suspend fun runKMathChained(): Duration { val generator = RandomGenerator.fromSource(RandomSource.MT, 123L) - val normal = KMathZigguratNormalizedGaussianSampler.of() - val chain = normal.sample(generator) as RandomChain + val normal = GaussianSampler.of(7.0, 2.0) + val chain = normal.sample(generator).blocking() val startTime = Instant.now() var sum = 0.0 @@ -28,17 +29,23 @@ private suspend fun runKMathChained(): Duration { println("Chain sampler completed $counter elements in $duration: $meanValue") } } + return Duration.between(startTime, Instant.now()) } private fun runApacheDirect(): Duration { val rng = RandomSource.create(RandomSource.MT, 123L) - val sampler = ApacheZigguratNormalizedGaussianSampler.of(rng) + + val sampler = CMGaussianSampler.of( + CMZigguratNormalizedGaussianSampler.of(rng), + 7.0, + 2.0 + ) + val startTime = Instant.now() - var sum = 0.0 - repeat(10000001) { counter -> + repeat(10000001) { counter -> sum += sampler.sample() if (counter % 100000 == 0) { @@ -47,17 +54,16 @@ private fun runApacheDirect(): Duration { println("Direct sampler completed $counter elements in $duration: $meanValue") } } + return Duration.between(startTime, Instant.now()) } /** * Comparing chain sampling performance with direct sampling performance */ -fun main() { - runBlocking(Dispatchers.Default) { - val chainJob = async { runKMathChained() } - val directJob = async { runApacheDirect() } - println("KMath Chained: ${chainJob.await()}") - println("Apache Direct: ${directJob.await()}") - } +fun main(): Unit = runBlocking(Dispatchers.Default) { + val chainJob = async { runKMathChained() } + val directJob = async { runApacheDirect() } + println("KMath Chained: ${chainJob.await()}") + println("Apache Direct: ${directJob.await()}") } From 0c6fff3878d71c23f1254cb52e5a3139ac0f4697 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Thu, 15 Oct 2020 23:52:50 +0700 Subject: [PATCH 15/31] Code refactoring, implement NormalDistribution --- .../commons/expressions/DiffExpression.kt | 6 +- .../kscience/kmath/prob/Distribution.kt | 12 +-- .../prob/distributions/NormalDistribution.kt | 30 ++++++++ .../AhrensDieterExponentialSampler.kt | 1 + .../samplers/AliasMethodDiscreteSampler.kt | 1 + .../kmath/prob/samplers/GaussianSampler.kt | 7 +- .../kmath/prob/samplers/InternalGamma.kt | 41 ---------- .../kmath/prob/samplers/InternalUtils.kt | 76 ------------------- .../prob/samplers/LargeMeanPoissonSampler.kt | 8 +- .../kmath/prob/RandomSourceGenerator.kt | 5 +- 10 files changed, 45 insertions(+), 142 deletions(-) create mode 100644 kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/distributions/NormalDistribution.kt diff --git a/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DiffExpression.kt b/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DiffExpression.kt index c39f0d04c..601675167 100644 --- a/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DiffExpression.kt +++ b/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DiffExpression.kt @@ -33,10 +33,7 @@ public class DerivativeStructureField( variables[name] ?: default ?: error("A variable with name $name does not exist") public fun Number.const(): DerivativeStructure = DerivativeStructure(order, parameters.size, toDouble()) - - public fun DerivativeStructure.deriv(parName: String, order: Int = 1): Double { - return deriv(mapOf(parName to order)) - } + public fun DerivativeStructure.deriv(parName: String, order: Int = 1): Double = deriv(mapOf(parName to order)) public fun DerivativeStructure.deriv(orders: Map): Double { return getPartialDerivative(*parameters.keys.map { orders[it] ?: 0 }.toIntArray()) @@ -75,7 +72,6 @@ public class DerivativeStructureField( public fun power(arg: DerivativeStructure, pow: DerivativeStructure): DerivativeStructure = arg.pow(pow) public override fun exp(arg: DerivativeStructure): DerivativeStructure = arg.exp() public override fun ln(arg: DerivativeStructure): DerivativeStructure = arg.log() - public override operator fun DerivativeStructure.plus(b: Number): DerivativeStructure = add(b.toDouble()) public override operator fun DerivativeStructure.minus(b: Number): DerivativeStructure = subtract(b.toDouble()) public override operator fun Number.plus(b: DerivativeStructure): DerivativeStructure = b + this diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt index 965635e09..bbb1de1e3 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt @@ -6,7 +6,7 @@ import kscience.kmath.chains.collect import kscience.kmath.structures.Buffer import kscience.kmath.structures.BufferFactory -public interface Sampler { +public fun interface Sampler { public fun sample(generator: RandomGenerator): Chain } @@ -20,11 +20,7 @@ public interface Distribution : Sampler { */ public fun probability(arg: T): Double - /** - * Create a chain of samples from this distribution. - * The chain is not guaranteed to be stateless, but different sample chains should be independent. - */ - override fun sample(generator: RandomGenerator): Chain + public override fun sample(generator: RandomGenerator): Chain /** * An empty companion. Distribution factories should be written as its extensions @@ -63,9 +59,7 @@ public fun Sampler.sampleBuffer( //clear list from previous run tmp.clear() //Fill list - repeat(size) { - tmp.add(chain.next()) - } + repeat(size) { tmp.add(chain.next()) } //return new buffer with elements from tmp bufferFactory(size) { tmp[it] } } diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/distributions/NormalDistribution.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/distributions/NormalDistribution.kt new file mode 100644 index 000000000..095ac5ea9 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/distributions/NormalDistribution.kt @@ -0,0 +1,30 @@ +package kscience.kmath.prob.distributions + +import kscience.kmath.chains.Chain +import kscience.kmath.prob.RandomGenerator +import kscience.kmath.prob.UnivariateDistribution +import kscience.kmath.prob.internal.InternalErf +import kscience.kmath.prob.samplers.GaussianSampler +import kotlin.math.* + +public inline class NormalDistribution(public val sampler: GaussianSampler) : UnivariateDistribution { + public override fun probability(arg: Double): Double { + val x1 = (arg - sampler.mean) / sampler.standardDeviation + return exp(-0.5 * x1 * x1 - (ln(sampler.standardDeviation) + 0.5 * ln(2 * PI))) + } + + public override fun sample(generator: RandomGenerator): Chain = sampler.sample(generator) + + public override fun cumulative(arg: Double): Double { + val dev = arg - sampler.mean + + return when { + abs(dev) > 40 * sampler.standardDeviation -> if (dev < 0) 0.0 else 1.0 + else -> 0.5 * InternalErf.erfc(-dev / (sampler.standardDeviation * SQRT2)) + } + } + + private companion object { + private val SQRT2 = sqrt(2.0) + } +} diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AhrensDieterExponentialSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AhrensDieterExponentialSampler.kt index dc388a3ea..d4b443e9c 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AhrensDieterExponentialSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AhrensDieterExponentialSampler.kt @@ -4,6 +4,7 @@ import kscience.kmath.chains.Chain import kscience.kmath.prob.RandomGenerator import kscience.kmath.prob.Sampler import kscience.kmath.prob.chain +import kscience.kmath.prob.internal.InternalUtils import kotlin.math.ln import kotlin.math.pow diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AliasMethodDiscreteSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AliasMethodDiscreteSampler.kt index c5eed2990..f04b5bcb0 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AliasMethodDiscreteSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AliasMethodDiscreteSampler.kt @@ -4,6 +4,7 @@ import kscience.kmath.chains.Chain import kscience.kmath.prob.RandomGenerator import kscience.kmath.prob.Sampler import kscience.kmath.prob.chain +import kscience.kmath.prob.internal.InternalUtils import kotlin.math.ceil import kotlin.math.max import kotlin.math.min diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/GaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/GaussianSampler.kt index 1a0ccac90..1a5e4cfdd 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/GaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/GaussianSampler.kt @@ -10,10 +10,13 @@ import kscience.kmath.prob.Sampler * * Based on Commons RNG implementation. * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/GaussianSampler.html + * + * @property mean the mean of the distribution. + * @property standardDeviation the variance of the distribution. */ public class GaussianSampler private constructor( - private val mean: Double, - private val standardDeviation: Double, + public val mean: Double, + public val standardDeviation: Double, private val normalized: NormalizedGaussianSampler ) : Sampler { public override fun sample(generator: RandomGenerator): Chain = normalized diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalGamma.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalGamma.kt index 16a5c96e0..d970d1447 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalGamma.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalGamma.kt @@ -1,43 +1,2 @@ package kscience.kmath.prob.samplers -import kotlin.math.PI -import kotlin.math.ln - -internal object InternalGamma { - private const val LANCZOS_G = 607.0 / 128.0 - - private val LANCZOS_COEFFICIENTS = doubleArrayOf( - 0.99999999999999709182, - 57.156235665862923517, - -59.597960355475491248, - 14.136097974741747174, - -0.49191381609762019978, - .33994649984811888699e-4, - .46523628927048575665e-4, - -.98374475304879564677e-4, - .15808870322491248884e-3, - -.21026444172410488319e-3, - .21743961811521264320e-3, - -.16431810653676389022e-3, - .84418223983852743293e-4, - -.26190838401581408670e-4, - .36899182659531622704e-5 - ) - - private val HALF_LOG_2_PI: Double = 0.5 * ln(2.0 * PI) - - fun logGamma(x: Double): Double { - // Stripped-down version of the same method defined in "Commons Math": - // Unused "if" branches (for when x < 8) have been removed here since - // this method is only used (by class "InternalUtils") in order to - // compute log(n!) for x > 20. - val sum = lanczos(x) - val tmp = x + LANCZOS_G + 0.5 - return (x + 0.5) * ln(tmp) - tmp + HALF_LOG_2_PI + ln(sum / x) - } - - private fun lanczos(x: Double): Double { - val sum = (LANCZOS_COEFFICIENTS.size - 1 downTo 1).sumByDouble { LANCZOS_COEFFICIENTS[it] / (x + it) } - return sum + LANCZOS_COEFFICIENTS[0] - } -} diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalUtils.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalUtils.kt index 08a321b75..d970d1447 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalUtils.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalUtils.kt @@ -1,78 +1,2 @@ package kscience.kmath.prob.samplers -import kotlin.math.ln -import kotlin.math.min - -internal object InternalUtils { - private val FACTORIALS = longArrayOf( - 1L, 1L, 2L, - 6L, 24L, 120L, - 720L, 5040L, 40320L, - 362880L, 3628800L, 39916800L, - 479001600L, 6227020800L, 87178291200L, - 1307674368000L, 20922789888000L, 355687428096000L, - 6402373705728000L, 121645100408832000L, 2432902008176640000L - ) - - private const val BEGIN_LOG_FACTORIALS = 2 - - fun factorial(n: Int): Long = FACTORIALS[n] - - fun validateProbabilities(probabilities: DoubleArray?): Double { - require(!(probabilities == null || probabilities.isEmpty())) { "Probabilities must not be empty." } - var sumProb = 0.0 - - probabilities.forEach { prob -> - validateProbability(prob) - sumProb += prob - } - - require(!(sumProb.isInfinite() || sumProb <= 0)) { "Invalid sum of probabilities: $sumProb" } - return sumProb - } - - private fun validateProbability(probability: Double): Unit = - require(!(probability < 0 || probability.isInfinite() || probability.isNaN())) { "Invalid probability: $probability" } - - class FactorialLog private constructor( - numValues: Int, - cache: DoubleArray? - ) { - private val logFactorials: DoubleArray = DoubleArray(numValues) - - init { - val endCopy: Int - - if (cache != null && cache.size > BEGIN_LOG_FACTORIALS) { - // Copy available values. - endCopy = min(cache.size, numValues) - cache.copyInto( - logFactorials, - BEGIN_LOG_FACTORIALS, - BEGIN_LOG_FACTORIALS, endCopy - ) - } - // All values to be computed - else endCopy = BEGIN_LOG_FACTORIALS - - // Compute remaining values. - (endCopy until numValues).forEach { i -> - if (i < FACTORIALS.size) - logFactorials[i] = ln(FACTORIALS[i].toDouble()) - else - logFactorials[i] = logFactorials[i - 1] + ln(i.toDouble()) - } - } - - fun value(n: Int): Double { - if (n < logFactorials.size) - return logFactorials[n] - - return if (n < FACTORIALS.size) ln(FACTORIALS[n].toDouble()) else InternalGamma.logGamma(n + 1.0) - } - - companion object { - fun create(): FactorialLog = FactorialLog(0, null) - } - } -} diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/LargeMeanPoissonSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/LargeMeanPoissonSampler.kt index dba2550cb..8a54fabaa 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/LargeMeanPoissonSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/LargeMeanPoissonSampler.kt @@ -5,6 +5,7 @@ import kscience.kmath.chains.ConstantChain import kscience.kmath.prob.RandomGenerator import kscience.kmath.prob.Sampler import kscience.kmath.prob.chain +import kscience.kmath.prob.internal.InternalUtils import kscience.kmath.prob.next import kotlin.math.* @@ -111,16 +112,13 @@ public class LargeMeanPoissonSampler private constructor(public val mean: Double } private fun getFactorialLog(n: Int): Double = factorialLog.value(n) - - override fun toString(): String = "Large Mean Poisson deviate" + public override fun toString(): String = "Large Mean Poisson deviate" public companion object { private const val MAX_MEAN: Double = 0.5 * Int.MAX_VALUE private val NO_CACHE_FACTORIAL_LOG: InternalUtils.FactorialLog = InternalUtils.FactorialLog.create() - private val NO_SMALL_MEAN_POISSON_SAMPLER: Sampler = object : Sampler { - override fun sample(generator: RandomGenerator): Chain = ConstantChain(0) - } + private val NO_SMALL_MEAN_POISSON_SAMPLER: Sampler = Sampler { ConstantChain(0) } public fun of(mean: Double): LargeMeanPoissonSampler { require(mean >= 1) { "mean is not >= 1: $mean" } diff --git a/kmath-prob/src/jvmMain/kotlin/kscience/kmath/prob/RandomSourceGenerator.kt b/kmath-prob/src/jvmMain/kotlin/kscience/kmath/prob/RandomSourceGenerator.kt index 18be6f019..9cdde6b4b 100644 --- a/kmath-prob/src/jvmMain/kotlin/kscience/kmath/prob/RandomSourceGenerator.kt +++ b/kmath-prob/src/jvmMain/kotlin/kscience/kmath/prob/RandomSourceGenerator.kt @@ -26,10 +26,7 @@ public class RandomSourceGenerator(public val source: RandomSource, seed: Long?) public inline class RandomGeneratorProvider(public val generator: RandomGenerator) : UniformRandomProvider { public override fun nextBoolean(): Boolean = generator.nextBoolean() public override fun nextFloat(): Float = generator.nextDouble().toFloat() - - public override fun nextBytes(bytes: ByteArray) { - generator.fillBytes(bytes) - } + public override fun nextBytes(bytes: ByteArray): Unit = generator.fillBytes(bytes) public override fun nextBytes(bytes: ByteArray, start: Int, len: Int) { generator.fillBytes(bytes, start, start + len) From d4aa4587a9a5f1b417db4d7b479151f8d5663ac0 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Thu, 15 Oct 2020 23:53:19 +0700 Subject: [PATCH 16/31] Add missing files --- .../kmath/prob/internal/InternalErf.kt | 11 + .../kmath/prob/internal/InternalGamma.kt | 245 ++++++++++++++++++ .../kmath/prob/internal/InternalUtils.kt | 78 ++++++ .../kmath/prob/RandomSourceGenerator.kt | 31 --- 4 files changed, 334 insertions(+), 31 deletions(-) create mode 100644 kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalErf.kt create mode 100644 kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalGamma.kt create mode 100644 kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalUtils.kt delete mode 100644 kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalErf.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalErf.kt new file mode 100644 index 000000000..178423e68 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalErf.kt @@ -0,0 +1,11 @@ +package kscience.kmath.prob.internal + +import kotlin.math.abs + +internal object InternalErf { + fun erfc(x: Double): Double { + if (abs(x) > 40) return if (x > 0) 0.0 else 2.0 + val ret = InternalGamma.regularizedGammaQ(0.5, x * x, 10000) + return if (x < 0) 2 - ret else ret + } +} \ No newline at end of file diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalGamma.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalGamma.kt new file mode 100644 index 000000000..7480d2396 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalGamma.kt @@ -0,0 +1,245 @@ +package kscience.kmath.prob.internal + +import kotlin.math.* + +private abstract class ContinuedFraction protected constructor() { + protected abstract fun getA(n: Int, x: Double): Double + protected abstract fun getB(n: Int, x: Double): Double + + fun evaluate(x: Double, maxIterations: Int): Double { + val small = 1e-50 + var hPrev = getA(0, x) + if (hPrev == 0.0 || abs(0.0 - hPrev) <= small) hPrev = small + var n = 1 + var dPrev = 0.0 + var cPrev = hPrev + var hN = hPrev + + while (n < maxIterations) { + val a = getA(n, x) + val b = getB(n, x) + var dN = a + b * dPrev + if (dN == 0.0 || abs(0.0 - dN) <= small) dN = small + var cN = a + b / cPrev + if (cN == 0.0 || abs(0.0 - cN) <= small) cN = small + dN = 1 / dN + val deltaN = cN * dN + hN = hPrev * deltaN + check(!hN.isInfinite()) { "hN is infinite" } + check(!hN.isNaN()) { "hN is NaN" } + if (abs(deltaN - 1.0) < 10e-9) break + dPrev = dN + cPrev = cN + hPrev = hN + n++ + } + + check(n < maxIterations) { "n is more than maxIterations" } + return hN + } +} + +internal object InternalGamma { + const val LANCZOS_G = 607.0 / 128.0 + + private val LANCZOS = doubleArrayOf( + 0.99999999999999709182, + 57.156235665862923517, + -59.597960355475491248, + 14.136097974741747174, + -0.49191381609762019978, + .33994649984811888699e-4, + .46523628927048575665e-4, + -.98374475304879564677e-4, + .15808870322491248884e-3, + -.21026444172410488319e-3, + .21743961811521264320e-3, + -.16431810653676389022e-3, + .84418223983852743293e-4, + -.26190838401581408670e-4, + .36899182659531622704e-5 + ) + + private val HALF_LOG_2_PI = 0.5 * ln(2.0 * PI) + private const val INV_GAMMA1P_M1_A0 = .611609510448141581788E-08 + private const val INV_GAMMA1P_M1_A1 = .624730830116465516210E-08 + private const val INV_GAMMA1P_M1_B1 = .203610414066806987300E+00 + private const val INV_GAMMA1P_M1_B2 = .266205348428949217746E-01 + private const val INV_GAMMA1P_M1_B3 = .493944979382446875238E-03 + private const val INV_GAMMA1P_M1_B4 = -.851419432440314906588E-05 + private const val INV_GAMMA1P_M1_B5 = -.643045481779353022248E-05 + private const val INV_GAMMA1P_M1_B6 = .992641840672773722196E-06 + private const val INV_GAMMA1P_M1_B7 = -.607761895722825260739E-07 + private const val INV_GAMMA1P_M1_B8 = .195755836614639731882E-09 + private const val INV_GAMMA1P_M1_P0 = .6116095104481415817861E-08 + private const val INV_GAMMA1P_M1_P1 = .6871674113067198736152E-08 + private const val INV_GAMMA1P_M1_P2 = .6820161668496170657918E-09 + private const val INV_GAMMA1P_M1_P3 = .4686843322948848031080E-10 + private const val INV_GAMMA1P_M1_P4 = .1572833027710446286995E-11 + private const val INV_GAMMA1P_M1_P5 = -.1249441572276366213222E-12 + private const val INV_GAMMA1P_M1_P6 = .4343529937408594255178E-14 + private const val INV_GAMMA1P_M1_Q1 = .3056961078365221025009E+00 + private const val INV_GAMMA1P_M1_Q2 = .5464213086042296536016E-01 + private const val INV_GAMMA1P_M1_Q3 = .4956830093825887312020E-02 + private const val INV_GAMMA1P_M1_Q4 = .2692369466186361192876E-03 + private const val INV_GAMMA1P_M1_C = -.422784335098467139393487909917598E+00 + private const val INV_GAMMA1P_M1_C0 = .577215664901532860606512090082402E+00 + private const val INV_GAMMA1P_M1_C1 = -.655878071520253881077019515145390E+00 + private const val INV_GAMMA1P_M1_C2 = -.420026350340952355290039348754298E-01 + private const val INV_GAMMA1P_M1_C3 = .166538611382291489501700795102105E+00 + private const val INV_GAMMA1P_M1_C4 = -.421977345555443367482083012891874E-01 + private const val INV_GAMMA1P_M1_C5 = -.962197152787697356211492167234820E-02 + private const val INV_GAMMA1P_M1_C6 = .721894324666309954239501034044657E-02 + private const val INV_GAMMA1P_M1_C7 = -.116516759185906511211397108401839E-02 + private const val INV_GAMMA1P_M1_C8 = -.215241674114950972815729963053648E-03 + private const val INV_GAMMA1P_M1_C9 = .128050282388116186153198626328164E-03 + private const val INV_GAMMA1P_M1_C10 = -.201348547807882386556893914210218E-04 + private const val INV_GAMMA1P_M1_C11 = -.125049348214267065734535947383309E-05 + private const val INV_GAMMA1P_M1_C12 = .113302723198169588237412962033074E-05 + private const val INV_GAMMA1P_M1_C13 = -.205633841697760710345015413002057E-06 + + fun logGamma(x: Double): Double { + val ret: Double + + when { + x.isNaN() || x <= 0.0 -> ret = Double.NaN + x < 0.5 -> return logGamma1p(x) - ln(x) + x <= 2.5 -> return logGamma1p(x - 0.5 - 0.5) + + x <= 8.0 -> { + val n = floor(x - 1.5).toInt() + var prod = 1.0 + (1..n).forEach { i -> prod *= x - i } + return logGamma1p(x - (n + 1)) + ln(prod) + } + + else -> { + val tmp = x + LANCZOS_G + .5 + ret = (x + .5) * ln(tmp) - tmp + HALF_LOG_2_PI + ln(lanczos(x) / x) + } + } + + return ret + } + + private fun regularizedGammaP( + a: Double, + x: Double, + maxIterations: Int = Int.MAX_VALUE + ): Double = when { + a.isNaN() || x.isNaN() || a <= 0.0 || x < 0.0 -> Double.NaN + x == 0.0 -> 0.0 + x >= a + 1 -> 1.0 - regularizedGammaQ(a, x, maxIterations) + + else -> { + // calculate series + var n = 0.0 // current element index + var an = 1.0 / a // n-th element in the series + var sum = an // partial sum + + while (abs(an / sum) > 10e-15 && n < maxIterations && sum < Double.POSITIVE_INFINITY) { + // compute next element in the series + n += 1.0 + an *= x / (a + n) + + // update partial sum + sum += an + } + + when { + n >= maxIterations -> throw error("Maximal iterations is exceeded $maxIterations") + sum.isInfinite() -> 1.0 + else -> exp(-x + a * ln(x) - logGamma(a)) * sum + } + } + } + + fun regularizedGammaQ( + a: Double, + x: Double, + maxIterations: Int = Int.MAX_VALUE + ): Double = when { + a.isNaN() || x.isNaN() || a <= 0.0 || x < 0.0 -> Double.NaN + x == 0.0 -> 1.0 + x < a + 1.0 -> 1.0 - regularizedGammaP(a, x, maxIterations) + + else -> 1.0 / object : ContinuedFraction() { + override fun getA(n: Int, x: Double): Double = 2.0 * n + 1.0 - a + x + override fun getB(n: Int, x: Double): Double = n * (a - n) + }.evaluate(x, maxIterations) * exp(-x + a * ln(x) - logGamma(a)) + } + + private fun lanczos(x: Double): Double = + (LANCZOS.size - 1 downTo 1).sumByDouble { LANCZOS[it] / (x + it) } + LANCZOS[0] + + private fun invGamma1pm1(x: Double): Double { + require(x >= -0.5) + require(x <= 1.5) + val ret: Double + val t = if (x <= 0.5) x else x - 0.5 - 0.5 + + if (t < 0.0) { + val a = INV_GAMMA1P_M1_A0 + t * INV_GAMMA1P_M1_A1 + var b = INV_GAMMA1P_M1_B8 + b = INV_GAMMA1P_M1_B7 + t * b + b = INV_GAMMA1P_M1_B6 + t * b + b = INV_GAMMA1P_M1_B5 + t * b + b = INV_GAMMA1P_M1_B4 + t * b + b = INV_GAMMA1P_M1_B3 + t * b + b = INV_GAMMA1P_M1_B2 + t * b + b = INV_GAMMA1P_M1_B1 + t * b + b = 1.0 + t * b + var c = INV_GAMMA1P_M1_C13 + t * (a / b) + c = INV_GAMMA1P_M1_C12 + t * c + c = INV_GAMMA1P_M1_C11 + t * c + c = INV_GAMMA1P_M1_C10 + t * c + c = INV_GAMMA1P_M1_C9 + t * c + c = INV_GAMMA1P_M1_C8 + t * c + c = INV_GAMMA1P_M1_C7 + t * c + c = INV_GAMMA1P_M1_C6 + t * c + c = INV_GAMMA1P_M1_C5 + t * c + c = INV_GAMMA1P_M1_C4 + t * c + c = INV_GAMMA1P_M1_C3 + t * c + c = INV_GAMMA1P_M1_C2 + t * c + c = INV_GAMMA1P_M1_C1 + t * c + c = INV_GAMMA1P_M1_C + t * c + ret = (if (x > 0.5) t * c / x else x * (c + 0.5 + 0.5)) + } else { + var p = INV_GAMMA1P_M1_P6 + p = INV_GAMMA1P_M1_P5 + t * p + p = INV_GAMMA1P_M1_P4 + t * p + p = INV_GAMMA1P_M1_P3 + t * p + p = INV_GAMMA1P_M1_P2 + t * p + p = INV_GAMMA1P_M1_P1 + t * p + p = INV_GAMMA1P_M1_P0 + t * p + var q = INV_GAMMA1P_M1_Q4 + q = INV_GAMMA1P_M1_Q3 + t * q + q = INV_GAMMA1P_M1_Q2 + t * q + q = INV_GAMMA1P_M1_Q1 + t * q + q = 1.0 + t * q + var c = INV_GAMMA1P_M1_C13 + p / q * t + c = INV_GAMMA1P_M1_C12 + t * c + c = INV_GAMMA1P_M1_C11 + t * c + c = INV_GAMMA1P_M1_C10 + t * c + c = INV_GAMMA1P_M1_C9 + t * c + c = INV_GAMMA1P_M1_C8 + t * c + c = INV_GAMMA1P_M1_C7 + t * c + c = INV_GAMMA1P_M1_C6 + t * c + c = INV_GAMMA1P_M1_C5 + t * c + c = INV_GAMMA1P_M1_C4 + t * c + c = INV_GAMMA1P_M1_C3 + t * c + c = INV_GAMMA1P_M1_C2 + t * c + c = INV_GAMMA1P_M1_C1 + t * c + c = INV_GAMMA1P_M1_C0 + t * c + ret = (if (x > 0.5) t / x * (c - 0.5 - 0.5) else x * c) + } + + return ret + } + + private fun logGamma1p(x: Double): Double { + require(x >= -0.5) + require(x <= 1.5) + return -ln1p(invGamma1pm1(x)) + } +} diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalUtils.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalUtils.kt new file mode 100644 index 000000000..655e284c0 --- /dev/null +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalUtils.kt @@ -0,0 +1,78 @@ +package kscience.kmath.prob.internal + +import kotlin.math.ln +import kotlin.math.min + +internal object InternalUtils { + private val FACTORIALS = longArrayOf( + 1L, 1L, 2L, + 6L, 24L, 120L, + 720L, 5040L, 40320L, + 362880L, 3628800L, 39916800L, + 479001600L, 6227020800L, 87178291200L, + 1307674368000L, 20922789888000L, 355687428096000L, + 6402373705728000L, 121645100408832000L, 2432902008176640000L + ) + + private const val BEGIN_LOG_FACTORIALS = 2 + + fun factorial(n: Int): Long = FACTORIALS[n] + + fun validateProbabilities(probabilities: DoubleArray?): Double { + require(!(probabilities == null || probabilities.isEmpty())) { "Probabilities must not be empty." } + var sumProb = 0.0 + + probabilities.forEach { prob -> + validateProbability(prob) + sumProb += prob + } + + require(!(sumProb.isInfinite() || sumProb <= 0)) { "Invalid sum of probabilities: $sumProb" } + return sumProb + } + + private fun validateProbability(probability: Double): Unit = + require(!(probability < 0 || probability.isInfinite() || probability.isNaN())) { "Invalid probability: $probability" } + + class FactorialLog private constructor( + numValues: Int, + cache: DoubleArray? + ) { + private val logFactorials: DoubleArray = DoubleArray(numValues) + + init { + val endCopy: Int + + if (cache != null && cache.size > BEGIN_LOG_FACTORIALS) { + // Copy available values. + endCopy = min(cache.size, numValues) + cache.copyInto( + logFactorials, + BEGIN_LOG_FACTORIALS, + BEGIN_LOG_FACTORIALS, endCopy + ) + } + // All values to be computed + else endCopy = BEGIN_LOG_FACTORIALS + + // Compute remaining values. + (endCopy until numValues).forEach { i -> + if (i < FACTORIALS.size) + logFactorials[i] = ln(FACTORIALS[i].toDouble()) + else + logFactorials[i] = logFactorials[i - 1] + ln(i.toDouble()) + } + } + + fun value(n: Int): Double { + if (n < logFactorials.size) + return logFactorials[n] + + return if (n < FACTORIALS.size) ln(FACTORIALS[n].toDouble()) else InternalGamma.logGamma(n + 1.0) + } + + companion object { + fun create(): FactorialLog = FactorialLog(0, null) + } + } +} \ No newline at end of file diff --git a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt b/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt deleted file mode 100644 index 67007358a..000000000 --- a/kmath-prob/src/jvmMain/kotlin/scientifik/kmath/prob/RandomSourceGenerator.kt +++ /dev/null @@ -1,31 +0,0 @@ -package scientifik.kmath.prob - -import kscience.kmath.prob.RandomGenerator -import org.apache.commons.rng.simple.RandomSource - -public class RandomSourceGenerator(private val source: RandomSource, seed: Long?) : - RandomGenerator { - private val random = seed?.let { - RandomSource.create(source, seed) - } ?: RandomSource.create(source) - - public override fun nextBoolean(): Boolean = random.nextBoolean() - public override fun nextDouble(): Double = random.nextDouble() - public override fun nextInt(): Int = random.nextInt() - public override fun nextInt(until: Int): Int = random.nextInt(until) - public override fun nextLong(): Long = random.nextLong() - public override fun nextLong(until: Long): Long = random.nextLong(until) - - public override fun fillBytes(array: ByteArray, fromIndex: Int, toIndex: Int) { - require(toIndex > fromIndex) - random.nextBytes(array, fromIndex, toIndex - fromIndex) - } - - override fun fork(): RandomGenerator = RandomSourceGenerator(source, nextLong()) -} - -public fun RandomGenerator.Companion.fromSource(source: RandomSource, seed: Long? = null): RandomSourceGenerator = - RandomSourceGenerator(source, seed) - -public fun RandomGenerator.Companion.mersenneTwister(seed: Long? = null): RandomSourceGenerator = - fromSource(RandomSource.MT, seed) From 612f6f00828e3595773d964902daa2c96e48fa4b Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Fri, 16 Oct 2020 16:49:47 +0700 Subject: [PATCH 17/31] Refactor, remove unused files, remove BasicSampler --- .../structures/StructureReadBenchmark.kt | 2 +- kmath-core/build.gradle.kts | 12 ++- .../kscience/kmath/operations/BigInt.kt | 23 ++--- .../kscience/kmath/structures/Buffers.kt | 2 +- .../kscience/kmath/structures/Structure2D.kt | 3 +- .../kmath/structures/NumberNDFieldTest.kt | 3 +- .../kotlin/kscience/kmath/chains/Chain.kt | 20 ++--- .../kscience/kmath/streaming/BufferFlow.kt | 2 +- .../kscience/kmath/streaming/RingBuffer.kt | 8 +- .../kscience/kmath/functions/Piecewise.kt | 6 +- .../kscience/kmath/prob/Distribution.kt | 23 ++++- .../kmath/prob/FactorizedDistribution.kt | 2 +- .../kscience/kmath/prob/RandomGenerator.kt | 2 + .../kscience/kmath/prob/SamplerAlgebra.kt | 17 ++-- .../kmath/prob/internal/InternalGamma.kt | 31 +++---- .../kmath/prob/internal/InternalUtils.kt | 24 ++---- .../samplers/AliasMethodDiscreteSampler.kt | 4 +- .../kmath/prob/samplers/InternalGamma.kt | 2 - .../kmath/prob/samplers/InternalUtils.kt | 2 - .../ZigguratNormalizedGaussianSampler.kt | 43 ++++------ .../kmath/prob/RandomSourceGenerator.kt | 83 ++++++++++++++++++- .../kotlin/kscience/kmath/prob/SamplerTest.kt | 7 +- 22 files changed, 194 insertions(+), 127 deletions(-) delete mode 100644 kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalGamma.kt delete mode 100644 kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalUtils.kt diff --git a/examples/src/main/kotlin/kscience/kmath/structures/StructureReadBenchmark.kt b/examples/src/main/kotlin/kscience/kmath/structures/StructureReadBenchmark.kt index 51fd4f956..c8c5d7b56 100644 --- a/examples/src/main/kotlin/kscience/kmath/structures/StructureReadBenchmark.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/StructureReadBenchmark.kt @@ -31,4 +31,4 @@ fun main() { strides.indices().forEach { res = array[strides.offset(it)] } } println("Array reading finished in $time3 millis") -} \ No newline at end of file +} diff --git a/kmath-core/build.gradle.kts b/kmath-core/build.gradle.kts index b56151abe..79cfce1f7 100644 --- a/kmath-core/build.gradle.kts +++ b/kmath-core/build.gradle.kts @@ -1,3 +1,5 @@ +import ru.mipt.npm.gradle.Maturity + plugins { id("ru.mipt.npm.mpp") id("ru.mipt.npm.native") @@ -11,36 +13,42 @@ kotlin.sourceSets.commonMain { readme { description = "Core classes, algebra definitions, basic linear algebra" - maturity = ru.mipt.npm.gradle.Maturity.DEVELOPMENT + maturity = Maturity.DEVELOPMENT propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md")) + feature( id = "algebras", description = "Algebraic structures: contexts and elements", ref = "src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt" ) + feature( id = "nd", description = "Many-dimensional structures", ref = "src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt" ) + feature( id = "buffers", description = "One-dimensional structure", ref = "src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt" ) + feature( id = "expressions", description = "Functional Expressions", ref = "src/commonMain/kotlin/kscience/kmath/expressions" ) + feature( id = "domains", description = "Domains", ref = "src/commonMain/kotlin/kscience/kmath/domains" ) + feature( id = "autodif", description = "Automatic differentiation", ref = "src/commonMain/kotlin/kscience/kmath/misc/AutoDiff.kt" ) -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt index 20f289596..7af207edc 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/BigInt.kt @@ -237,18 +237,18 @@ public class BigInt internal constructor( ) private fun compareMagnitudes(mag1: Magnitude, mag2: Magnitude): Int { - when { - mag1.size > mag2.size -> return 1 - mag1.size < mag2.size -> return -1 + return when { + mag1.size > mag2.size -> 1 + mag1.size < mag2.size -> -1 + else -> { - for (i in mag1.size - 1 downTo 0) { - if (mag1[i] > mag2[i]) { - return 1 - } else if (mag1[i] < mag2[i]) { - return -1 - } + for (i in mag1.size - 1 downTo 0) return when { + mag1[i] > mag2[i] -> 1 + mag1[i] < mag2[i] -> -1 + else -> continue } - return 0 + + 0 } } } @@ -298,10 +298,11 @@ public class BigInt internal constructor( var carry = 0uL for (i in mag.indices) { - val cur: ULong = carry + mag[i].toULong() * x.toULong() + val cur = carry + mag[i].toULong() * x.toULong() result[i] = (cur and BASE).toUInt() carry = cur shr BASE_SIZE } + result[resultLength - 1] = (carry and BASE).toUInt() return stripLeadingZeros(result) diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt index 5174eb314..1806e3559 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt @@ -71,7 +71,7 @@ public interface Buffer { @Suppress("UNCHECKED_CAST") public inline fun auto(type: KClass, size: Int, initializer: (Int) -> T): Buffer = when (type) { - Double::class -> RealBuffer(size) { initializer(it) as Double } as Buffer + Double::class -> real(size) { initializer(it) as Double } as Buffer Short::class -> ShortBuffer(size) { initializer(it) as Short } as Buffer Int::class -> IntBuffer(size) { initializer(it) as Int } as Buffer Long::class -> LongBuffer(size) { initializer(it) as Long } as Buffer diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Structure2D.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Structure2D.kt index 25fdf3f3d..99c1de9bf 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Structure2D.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Structure2D.kt @@ -22,7 +22,7 @@ public interface Structure2D : NDStructure { override fun elements(): Sequence> = sequence { for (i in (0 until rowNum)) - for (j in (0 until colNum)) yield(intArrayOf(i, j) to get(i, j)) + for (j in (0 until colNum)) yield(intArrayOf(i, j) to this@Structure2D[i, j]) } public companion object @@ -35,7 +35,6 @@ private inline class Structure2DWrapper(val structure: NDStructure) : Stru override val shape: IntArray get() = structure.shape override operator fun get(i: Int, j: Int): T = structure[i, j] - override fun elements(): Sequence> = structure.elements() } diff --git a/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt index f5e008ef3..4320821e6 100644 --- a/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/kscience/kmath/structures/NumberNDFieldTest.kt @@ -29,12 +29,11 @@ class NumberNDFieldTest { val array = real2D(3, 3) { i, j -> (i * 10 + j).toDouble() } - for (i in 0..2) { + for (i in 0..2) for (j in 0..2) { val expected = (i * 10 + j).toDouble() assertEquals(expected, array[i, j], "Error at index [$i, $j]") } - } } @Test diff --git a/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/Chain.kt b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/Chain.kt index 8c15e52c7..9b9f3e509 100644 --- a/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/Chain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/Chain.kt @@ -63,12 +63,10 @@ public class MarkovChain(private val seed: suspend () -> R, private public fun value(): R? = value - public override suspend fun next(): R { - mutex.withLock { - val newValue = gen(value ?: seed()) - value = newValue - return newValue - } + public override suspend fun next(): R = mutex.withLock { + val newValue = gen(value ?: seed()) + value = newValue + newValue } public override fun fork(): Chain = MarkovChain(seed = { value ?: seed() }, gen = gen) @@ -90,12 +88,10 @@ public class StatefulChain( public fun value(): R? = value - public override suspend fun next(): R { - mutex.withLock { - val newValue = state.gen(value ?: state.seed()) - value = newValue - return newValue - } + public override suspend fun next(): R = mutex.withLock { + val newValue = state.gen(value ?: state.seed()) + value = newValue + newValue } public override fun fork(): Chain = StatefulChain(forkState(state), seed, forkState, gen) diff --git a/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/streaming/BufferFlow.kt b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/streaming/BufferFlow.kt index 328a7807c..7be86b6e7 100644 --- a/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/streaming/BufferFlow.kt +++ b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/streaming/BufferFlow.kt @@ -28,7 +28,7 @@ public fun Flow.chunked(bufferSize: Int, bufferFactory: BufferFactory) var counter = 0 this@chunked.collect { element -> - list.add(element) + list += element counter++ if (counter == bufferSize) { diff --git a/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/streaming/RingBuffer.kt b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/streaming/RingBuffer.kt index 385bbaae2..a59979238 100644 --- a/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/streaming/RingBuffer.kt +++ b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/streaming/RingBuffer.kt @@ -48,11 +48,9 @@ public class RingBuffer( /** * A safe snapshot operation */ - public suspend fun snapshot(): Buffer { - mutex.withLock { - val copy = buffer.copy() - return VirtualBuffer(size) { i -> copy[startIndex.forward(i)] as T } - } + public suspend fun snapshot(): Buffer = mutex.withLock { + val copy = buffer.copy() + VirtualBuffer(size) { i -> copy[startIndex.forward(i)] as T } } public suspend fun push(element: T) { diff --git a/kmath-functions/src/commonMain/kotlin/kscience/kmath/functions/Piecewise.kt b/kmath-functions/src/commonMain/kotlin/kscience/kmath/functions/Piecewise.kt index a8c020c05..6dab9820d 100644 --- a/kmath-functions/src/commonMain/kotlin/kscience/kmath/functions/Piecewise.kt +++ b/kmath-functions/src/commonMain/kotlin/kscience/kmath/functions/Piecewise.kt @@ -23,8 +23,8 @@ public class OrderedPiecewisePolynomial>(delimiter: T) : */ public fun putRight(right: T, piece: Polynomial) { require(right > delimiters.last()) { "New delimiter should be to the right of old one" } - delimiters.add(right) - pieces.add(piece) + delimiters += right + pieces += piece } public fun putLeft(left: T, piece: Polynomial) { @@ -52,4 +52,4 @@ public class OrderedPiecewisePolynomial>(delimiter: T) : public fun , C : Ring> PiecewisePolynomial.value(ring: C, arg: T): T? = findPiece(arg)?.value(ring, arg) -public fun , C : Ring> PiecewisePolynomial.asFunction(ring: C): (T) -> T? = { value(ring, it) } \ No newline at end of file +public fun , C : Ring> PiecewisePolynomial.asFunction(ring: C): (T) -> T? = { value(ring, it) } diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt index bbb1de1e3..6c64fb6f5 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt @@ -5,8 +5,18 @@ import kscience.kmath.chains.Chain import kscience.kmath.chains.collect import kscience.kmath.structures.Buffer import kscience.kmath.structures.BufferFactory +import kscience.kmath.structures.IntBuffer +/** + * Sampler that generates chains of values of type [T]. + */ public fun interface Sampler { + /** + * Generates a chain of samples. + * + * @param generator the randomness provider. + * @return the new chain. + */ public fun sample(generator: RandomGenerator): Chain } @@ -59,16 +69,25 @@ public fun Sampler.sampleBuffer( //clear list from previous run tmp.clear() //Fill list - repeat(size) { tmp.add(chain.next()) } + repeat(size) { tmp += chain.next() } //return new buffer with elements from tmp bufferFactory(size) { tmp[it] } } } +/** + * Samples one value from this [Sampler]. + */ public suspend fun Sampler.next(generator: RandomGenerator): T = sample(generator).first() /** - * Generate a bunch of samples from real distributions + * Generates [size] real samples and chunks them into some buffers. */ public fun Sampler.sampleBuffer(generator: RandomGenerator, size: Int): Chain> = sampleBuffer(generator, size, Buffer.Companion::real) + +/** + * Generates [size] integer samples and chunks them into some buffers. + */ +public fun Sampler.sampleBuffer(generator: RandomGenerator, size: Int): Chain> = + sampleBuffer(generator, size, ::IntBuffer) diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/FactorizedDistribution.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/FactorizedDistribution.kt index 128b284be..cc1f0efab 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/FactorizedDistribution.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/FactorizedDistribution.kt @@ -38,6 +38,6 @@ public class DistributionBuilder { private val distributions = ArrayList>() public infix fun String.to(distribution: Distribution) { - distributions.add(NamedDistributionWrapper(this, distribution)) + distributions += NamedDistributionWrapper(this, distribution) } } diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomGenerator.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomGenerator.kt index 2dd4ce51e..ec660fe46 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomGenerator.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomGenerator.kt @@ -82,6 +82,8 @@ public interface RandomGenerator { /** * Implements [RandomGenerator] by delegating all operations to [Random]. + * + * @property random the underlying [Random] object. */ public inline class DefaultGenerator(public val random: Random = Random) : RandomGenerator { public override fun nextBoolean(): Boolean = random.nextBoolean() diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/SamplerAlgebra.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/SamplerAlgebra.kt index e363ba30b..7660f2ea0 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/SamplerAlgebra.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/SamplerAlgebra.kt @@ -7,25 +7,28 @@ import kscience.kmath.chains.zip import kscience.kmath.operations.Space import kscience.kmath.operations.invoke -public class BasicSampler(public val chainBuilder: (RandomGenerator) -> Chain) : Sampler { - public override fun sample(generator: RandomGenerator): Chain = chainBuilder(generator) -} - +/** + * Implements [Sampler] by sampling only certain [value]. + * + * @property value the value to sample. + */ public class ConstantSampler(public val value: T) : Sampler { public override fun sample(generator: RandomGenerator): Chain = ConstantChain(value) } /** - * A space for samplers. Allows to perform simple operations on distributions + * A space of samplers. Allows to perform simple operations on distributions. + * + * @property space the space to provide addition and scalar multiplication for [T]. */ public class SamplerSpace(public val space: Space) : Space> { public override val zero: Sampler = ConstantSampler(space.zero) - public override fun add(a: Sampler, b: Sampler): Sampler = BasicSampler { generator -> + public override fun add(a: Sampler, b: Sampler): Sampler = Sampler { generator -> a.sample(generator).zip(b.sample(generator)) { aValue, bValue -> space { aValue + bValue } } } - public override fun multiply(a: Sampler, k: Number): Sampler = BasicSampler { generator -> + public override fun multiply(a: Sampler, k: Number): Sampler = Sampler { generator -> a.sample(generator).map { space { it * k.toDouble() } } } } diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalGamma.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalGamma.kt index 7480d2396..8a73659db 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalGamma.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalGamma.kt @@ -98,28 +98,21 @@ internal object InternalGamma { private const val INV_GAMMA1P_M1_C12 = .113302723198169588237412962033074E-05 private const val INV_GAMMA1P_M1_C13 = -.205633841697760710345015413002057E-06 - fun logGamma(x: Double): Double { - val ret: Double + fun logGamma(x: Double): Double = when { + x.isNaN() || x <= 0.0 -> Double.NaN + x < 0.5 -> logGamma1p(x) - ln(x) + x <= 2.5 -> logGamma1p(x - 0.5 - 0.5) - when { - x.isNaN() || x <= 0.0 -> ret = Double.NaN - x < 0.5 -> return logGamma1p(x) - ln(x) - x <= 2.5 -> return logGamma1p(x - 0.5 - 0.5) - - x <= 8.0 -> { - val n = floor(x - 1.5).toInt() - var prod = 1.0 - (1..n).forEach { i -> prod *= x - i } - return logGamma1p(x - (n + 1)) + ln(prod) - } - - else -> { - val tmp = x + LANCZOS_G + .5 - ret = (x + .5) * ln(tmp) - tmp + HALF_LOG_2_PI + ln(lanczos(x) / x) - } + x <= 8.0 -> { + val n = floor(x - 1.5).toInt() + val prod = (1..n).fold(1.0, { prod, i -> prod * (x - i) }) + logGamma1p(x - (n + 1)) + ln(prod) } - return ret + else -> { + val tmp = x + LANCZOS_G + .5 + (x + .5) * ln(tmp) - tmp + HALF_LOG_2_PI + ln(lanczos(x) / x) + } } private fun regularizedGammaP( diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalUtils.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalUtils.kt index 655e284c0..837c9796c 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalUtils.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/internal/InternalUtils.kt @@ -20,24 +20,17 @@ internal object InternalUtils { fun validateProbabilities(probabilities: DoubleArray?): Double { require(!(probabilities == null || probabilities.isEmpty())) { "Probabilities must not be empty." } - var sumProb = 0.0 - probabilities.forEach { prob -> - validateProbability(prob) - sumProb += prob + val sumProb = probabilities.sumByDouble { prob -> + require(!(prob < 0 || prob.isInfinite() || prob.isNaN())) { "Invalid probability: $prob" } + prob } require(!(sumProb.isInfinite() || sumProb <= 0)) { "Invalid sum of probabilities: $sumProb" } return sumProb } - private fun validateProbability(probability: Double): Unit = - require(!(probability < 0 || probability.isInfinite() || probability.isNaN())) { "Invalid probability: $probability" } - - class FactorialLog private constructor( - numValues: Int, - cache: DoubleArray? - ) { + class FactorialLog private constructor(numValues: Int, cache: DoubleArray?) { private val logFactorials: DoubleArray = DoubleArray(numValues) init { @@ -46,14 +39,15 @@ internal object InternalUtils { if (cache != null && cache.size > BEGIN_LOG_FACTORIALS) { // Copy available values. endCopy = min(cache.size, numValues) + cache.copyInto( logFactorials, BEGIN_LOG_FACTORIALS, BEGIN_LOG_FACTORIALS, endCopy ) - } + } else // All values to be computed - else endCopy = BEGIN_LOG_FACTORIALS + endCopy = BEGIN_LOG_FACTORIALS // Compute remaining values. (endCopy until numValues).forEach { i -> @@ -65,9 +59,7 @@ internal object InternalUtils { } fun value(n: Int): Double { - if (n < logFactorials.size) - return logFactorials[n] - + if (n < logFactorials.size) return logFactorials[n] return if (n < FACTORIALS.size) ln(FACTORIALS[n].toDouble()) else InternalGamma.logGamma(n + 1.0) } diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AliasMethodDiscreteSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AliasMethodDiscreteSampler.kt index f04b5bcb0..db78a41a8 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AliasMethodDiscreteSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/AliasMethodDiscreteSampler.kt @@ -42,7 +42,7 @@ public open class AliasMethodDiscreteSampler private constructor( protected val alias: IntArray ) : Sampler { - private class SmallTableAliasMethodDiscreteSampler internal constructor( + private class SmallTableAliasMethodDiscreteSampler( probability: LongArray, alias: IntArray ) : AliasMethodDiscreteSampler(probability, alias) { @@ -71,7 +71,7 @@ public open class AliasMethodDiscreteSampler private constructor( } } - override fun sample(generator: RandomGenerator): Chain = generator.chain { + public override fun sample(generator: RandomGenerator): Chain = generator.chain { // This implements the algorithm as per Vose (1991): // v = uniform() in [0, 1) // j = uniform(n) in [0, n) diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalGamma.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalGamma.kt deleted file mode 100644 index d970d1447..000000000 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalGamma.kt +++ /dev/null @@ -1,2 +0,0 @@ -package kscience.kmath.prob.samplers - diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalUtils.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalUtils.kt deleted file mode 100644 index d970d1447..000000000 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/InternalUtils.kt +++ /dev/null @@ -1,2 +0,0 @@ -package kscience.kmath.prob.samplers - diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt index c9103ba86..60407e604 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/samplers/ZigguratNormalizedGaussianSampler.kt @@ -26,35 +26,24 @@ public class ZigguratNormalizedGaussianSampler private constructor() : public override fun sample(generator: RandomGenerator): Chain = generator.chain { sampleOne(this) } public override fun toString(): String = "Ziggurat normalized Gaussian deviate" - private fun fix( - generator: RandomGenerator, - hz: Long, - iz: Int - ): Double { - var x: Double - var y: Double - x = hz * W[iz] + private fun fix(generator: RandomGenerator, hz: Long, iz: Int): Double { + var x = hz * W[iz] - return if (iz == 0) { - // Base strip. - // This branch is called about 5.7624515E-4 times per sample. - do { - y = -ln(generator.nextDouble()) - x = -ln(generator.nextDouble()) * ONE_OVER_R - } while (y + y < x * x) + return when { + iz == 0 -> { + var y: Double - val out = R + x - if (hz > 0) out else -out - } else { - // Wedge of other strips. - // This branch is called about 0.027323 times per sample. - // else - // Try again. - // This branch is called about 0.012362 times per sample. - if (F[iz] + generator.nextDouble() * (F[iz - 1] - F[iz]) < gauss( - x - ) - ) x else sampleOne(generator) + do { + y = -ln(generator.nextDouble()) + x = -ln(generator.nextDouble()) * ONE_OVER_R + } while (y + y < x * x) + + val out = R + x + if (hz > 0) out else -out + } + + F[iz] + generator.nextDouble() * (F[iz - 1] - F[iz]) < gauss(x) -> x + else -> sampleOne(generator) } } diff --git a/kmath-prob/src/jvmMain/kotlin/kscience/kmath/prob/RandomSourceGenerator.kt b/kmath-prob/src/jvmMain/kotlin/kscience/kmath/prob/RandomSourceGenerator.kt index 9cdde6b4b..e6a7ac1f1 100644 --- a/kmath-prob/src/jvmMain/kotlin/kscience/kmath/prob/RandomSourceGenerator.kt +++ b/kmath-prob/src/jvmMain/kotlin/kscience/kmath/prob/RandomSourceGenerator.kt @@ -3,10 +3,14 @@ package kscience.kmath.prob import org.apache.commons.rng.UniformRandomProvider import org.apache.commons.rng.simple.RandomSource -public class RandomSourceGenerator(public val source: RandomSource, seed: Long?) : RandomGenerator { - internal val random: UniformRandomProvider = seed?.let { - RandomSource.create(source, seed) - } ?: RandomSource.create(source) +/** + * Implements [RandomGenerator] by delegating all operations to [RandomSource]. + * + * @property source the underlying [RandomSource] object. + */ +public class RandomSourceGenerator internal constructor(public val source: RandomSource, seed: Long?) : RandomGenerator { + internal val random: UniformRandomProvider = seed?.let { RandomSource.create(source, seed) } + ?: RandomSource.create(source) public override fun nextBoolean(): Boolean = random.nextBoolean() public override fun nextDouble(): Double = random.nextDouble() @@ -23,19 +27,84 @@ public class RandomSourceGenerator(public val source: RandomSource, seed: Long?) public override fun fork(): RandomGenerator = RandomSourceGenerator(source, nextLong()) } +/** + * Implements [UniformRandomProvider] by delegating all operations to [RandomGenerator]. + * + * @property generator the underlying [RandomGenerator] object. + */ public inline class RandomGeneratorProvider(public val generator: RandomGenerator) : UniformRandomProvider { + /** + * Generates a [Boolean] value. + * + * @return the next random value. + */ public override fun nextBoolean(): Boolean = generator.nextBoolean() + + /** + * Generates a [Float] value between 0 and 1. + * + * @return the next random value between 0 and 1. + */ public override fun nextFloat(): Float = generator.nextDouble().toFloat() + + /** + * Generates [Byte] values and places them into a user-supplied array. + * + * The number of random bytes produced is equal to the length of the the byte array. + * + * @param bytes byte array in which to put the random bytes. + */ public override fun nextBytes(bytes: ByteArray): Unit = generator.fillBytes(bytes) + /** + * Generates [Byte] values and places them into a user-supplied array. + * + * The array is filled with bytes extracted from random integers. This implies that the number of random bytes + * generated may be larger than the length of the byte array. + * + * @param bytes the array in which to put the generated bytes. + * @param start the index at which to start inserting the generated bytes. + * @param len the number of bytes to insert. + */ public override fun nextBytes(bytes: ByteArray, start: Int, len: Int) { generator.fillBytes(bytes, start, start + len) } + /** + * Generates an [Int] value. + * + * @return the next random value. + */ public override fun nextInt(): Int = generator.nextInt() + + /** + * Generates an [Int] value between 0 (inclusive) and the specified value (exclusive). + * + * @param n the bound on the random number to be returned. Must be positive. + * @return a random integer between 0 (inclusive) and [n] (exclusive). + */ public override fun nextInt(n: Int): Int = generator.nextInt(n) + + /** + * Generates a [Double] value between 0 and 1. + * + * @return the next random value between 0 and 1. + */ public override fun nextDouble(): Double = generator.nextDouble() + + /** + * Generates a [Long] value. + * + * @return the next random value. + */ public override fun nextLong(): Long = generator.nextLong() + + /** + * Generates a [Long] value between 0 (inclusive) and the specified value (exclusive). + * + * @param n Bound on the random number to be returned. Must be positive. + * @return a random long value between 0 (inclusive) and [n] (exclusive). + */ public override fun nextLong(n: Long): Long = generator.nextLong(n) } @@ -48,8 +117,14 @@ public fun RandomGenerator.asUniformRandomProvider(): UniformRandomProvider = if else RandomGeneratorProvider(this) +/** + * Returns [RandomSourceGenerator] with given [RandomSource] and [seed]. + */ public fun RandomGenerator.Companion.fromSource(source: RandomSource, seed: Long? = null): RandomSourceGenerator = RandomSourceGenerator(source, seed) +/** + * Returns [RandomSourceGenerator] with [RandomSource.MT] algorithm and given [seed]. + */ public fun RandomGenerator.Companion.mersenneTwister(seed: Long? = null): RandomSourceGenerator = fromSource(RandomSource.MT, seed) diff --git a/kmath-prob/src/jvmTest/kotlin/kscience/kmath/prob/SamplerTest.kt b/kmath-prob/src/jvmTest/kotlin/kscience/kmath/prob/SamplerTest.kt index 75db5c402..0ca5a8aeb 100644 --- a/kmath-prob/src/jvmTest/kotlin/kscience/kmath/prob/SamplerTest.kt +++ b/kmath-prob/src/jvmTest/kotlin/kscience/kmath/prob/SamplerTest.kt @@ -7,11 +7,8 @@ class SamplerTest { @Test fun bufferSamplerTest() { - val sampler: Sampler = - BasicSampler { it.chain { nextDouble() } } + val sampler = Sampler { it.chain { nextDouble() } } val data = sampler.sampleBuffer(RandomGenerator.default, 100) - runBlocking { - println(data.next()) - } + runBlocking { println(data.next()) } } } \ No newline at end of file From 59b120e0861432b77fa9096fc8d662c2680e593f Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Fri, 23 Oct 2020 17:01:16 +0700 Subject: [PATCH 18/31] Fix platform declarations --- .../src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt index 6c64fb6f5..1b2681703 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/Distribution.kt @@ -6,6 +6,7 @@ import kscience.kmath.chains.collect import kscience.kmath.structures.Buffer import kscience.kmath.structures.BufferFactory import kscience.kmath.structures.IntBuffer +import kotlin.jvm.JvmName /** * Sampler that generates chains of values of type [T]. @@ -83,11 +84,13 @@ public suspend fun Sampler.next(generator: RandomGenerator): T = sa /** * Generates [size] real samples and chunks them into some buffers. */ +@JvmName("sampleRealBuffer") public fun Sampler.sampleBuffer(generator: RandomGenerator, size: Int): Chain> = sampleBuffer(generator, size, Buffer.Companion::real) /** * Generates [size] integer samples and chunks them into some buffers. */ +@JvmName("sampleIntBuffer") public fun Sampler.sampleBuffer(generator: RandomGenerator, size: Int): Chain> = sampleBuffer(generator, size, ::IntBuffer) From 55909aee0d1c929c239a4f837409e2c722c1790f Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Wed, 28 Oct 2020 18:36:00 +0700 Subject: [PATCH 19/31] Add additional constructor --- .../kotlin/kscience/kmath/structures/ViktorBenchmark.kt | 2 +- .../kmath/prob/distributions/NormalDistribution.kt | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/structures/ViktorBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/structures/ViktorBenchmark.kt index 464925ca0..df35b0b78 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/structures/ViktorBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/structures/ViktorBenchmark.kt @@ -36,7 +36,7 @@ internal class ViktorBenchmark { @Benchmark fun rawViktor() { - val one = F64Array.full(init = 1.0, shape = *intArrayOf(dim, dim)) + val one = F64Array.full(init = 1.0, shape = intArrayOf(dim, dim)) var res = one repeat(n) { res = res + one } } diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/distributions/NormalDistribution.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/distributions/NormalDistribution.kt index 095ac5ea9..57c39018d 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/distributions/NormalDistribution.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/distributions/NormalDistribution.kt @@ -5,9 +5,17 @@ import kscience.kmath.prob.RandomGenerator import kscience.kmath.prob.UnivariateDistribution import kscience.kmath.prob.internal.InternalErf import kscience.kmath.prob.samplers.GaussianSampler +import kscience.kmath.prob.samplers.NormalizedGaussianSampler +import kscience.kmath.prob.samplers.ZigguratNormalizedGaussianSampler import kotlin.math.* public inline class NormalDistribution(public val sampler: GaussianSampler) : UnivariateDistribution { + public constructor( + mean: Double, + standardDeviation: Double, + normalized: NormalizedGaussianSampler = ZigguratNormalizedGaussianSampler.of(), + ) : this(GaussianSampler.of(mean, standardDeviation, normalized)) + public override fun probability(arg: Double): Double { val x1 = (arg - sampler.mean) / sampler.standardDeviation return exp(-0.5 * x1 * x1 - (ln(sampler.standardDeviation) + 0.5 * ln(2 * PI))) From f18cd9ad40e2e3b9ef0365c743d059267eab0b3a Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Sun, 29 Nov 2020 16:25:08 +0700 Subject: [PATCH 20/31] Fix package names --- .../kmath/commons/prob/DistributionBenchmark.kt | 8 ++++---- .../kmath/commons/prob/DistributionDemo.kt | 4 ++-- .../kmath/commons/optimization/OptimizeTest.kt | 15 +++++---------- .../stat/distributions/NormalDistribution.kt | 14 +++++++------- .../kscience/kmath/stat/internal/InternalErf.kt | 2 +- .../kscience/kmath/stat/internal/InternalGamma.kt | 2 +- .../kscience/kmath/stat/internal/InternalUtils.kt | 2 +- .../samplers/AhrensDieterExponentialSampler.kt | 10 +++++----- .../AhrensDieterMarsagliaTsangGammaSampler.kt | 10 +++++----- .../stat/samplers/AliasMethodDiscreteSampler.kt | 10 +++++----- .../BoxMullerNormalizedGaussianSampler.kt | 8 ++++---- .../kmath/stat/samplers/GaussianSampler.kt | 6 +++--- .../stat/samplers/KempSmallMeanPoissonSampler.kt | 8 ++++---- .../stat/samplers/LargeMeanPoissonSampler.kt | 12 ++++++------ .../MarsagliaNormalizedGaussianSampler.kt | 8 ++++---- .../stat/samplers/NormalizedGaussianSampler.kt | 4 ++-- .../kmath/stat/samplers/PoissonSampler.kt | 6 +++--- .../stat/samplers/SmallMeanPoissonSampler.kt | 8 ++++---- .../samplers/ZigguratNormalizedGaussianSampler.kt | 8 ++++---- .../kmath/stat/CommonsDistributionsTest.kt | 2 +- 20 files changed, 71 insertions(+), 76 deletions(-) diff --git a/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt b/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt index e4a5bc534..5c7442c1b 100644 --- a/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt +++ b/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt @@ -3,10 +3,10 @@ package kscience.kmath.commons.prob import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking -import kscience.kmath.prob.RandomGenerator -import kscience.kmath.prob.blocking -import kscience.kmath.prob.fromSource -import kscience.kmath.prob.samplers.GaussianSampler +import kscience.kmath.stat.RandomGenerator +import kscience.kmath.stat.blocking +import kscience.kmath.stat.fromSource +import kscience.kmath.stat.samplers.GaussianSampler import org.apache.commons.rng.simple.RandomSource import java.time.Duration import java.time.Instant diff --git a/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionDemo.kt b/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionDemo.kt index 7f06f4a1f..c96826ef7 100644 --- a/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionDemo.kt +++ b/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionDemo.kt @@ -3,8 +3,8 @@ package kscience.kmath.commons.prob import kotlinx.coroutines.runBlocking import kscience.kmath.chains.Chain import kscience.kmath.chains.collectWithState -import kscience.kmath.prob.RandomGenerator -import kscience.kmath.prob.samplers.ZigguratNormalizedGaussianSampler +import kscience.kmath.stat.RandomGenerator +import kscience.kmath.stat.samplers.ZigguratNormalizedGaussianSampler private data class AveragingChainState(var num: Int = 0, var value: Double = 0.0) diff --git a/kmath-commons/src/test/kotlin/kscience/kmath/commons/optimization/OptimizeTest.kt b/kmath-commons/src/test/kotlin/kscience/kmath/commons/optimization/OptimizeTest.kt index 3290c8f32..184d209d5 100644 --- a/kmath-commons/src/test/kotlin/kscience/kmath/commons/optimization/OptimizeTest.kt +++ b/kmath-commons/src/test/kotlin/kscience/kmath/commons/optimization/OptimizeTest.kt @@ -1,11 +1,11 @@ package kscience.kmath.commons.optimization +import kotlinx.coroutines.runBlocking import kscience.kmath.commons.expressions.DerivativeStructureExpression import kscience.kmath.expressions.symbol -import kscience.kmath.stat.Distribution import kscience.kmath.stat.Fitting import kscience.kmath.stat.RandomGenerator -import kscience.kmath.stat.normal +import kscience.kmath.stat.distributions.NormalDistribution import org.junit.jupiter.api.Test import kotlin.math.pow @@ -39,20 +39,15 @@ internal class OptimizeTest { } @Test - fun testCmFit() { + fun testCmFit() = runBlocking { val a by symbol val b by symbol val c by symbol - val sigma = 1.0 - val generator = Distribution.normal(0.0, sigma) + val generator = NormalDistribution(0.0, sigma) val chain = generator.sample(RandomGenerator.default(112667)) val x = (1..100).map(Int::toDouble) - - val y = x.map { - it.pow(2) + it + 1 + chain.nextDouble() - } - + val y = x.map { it.pow(2) + it + 1.0 + chain.next() } val yErr = List(x.size) { sigma } val chi2 = Fitting.chiSquared(x, y, yErr) { x1 -> diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/distributions/NormalDistribution.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/distributions/NormalDistribution.kt index 57c39018d..4332d92fe 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/distributions/NormalDistribution.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/distributions/NormalDistribution.kt @@ -1,12 +1,12 @@ -package kscience.kmath.prob.distributions +package kscience.kmath.stat.distributions import kscience.kmath.chains.Chain -import kscience.kmath.prob.RandomGenerator -import kscience.kmath.prob.UnivariateDistribution -import kscience.kmath.prob.internal.InternalErf -import kscience.kmath.prob.samplers.GaussianSampler -import kscience.kmath.prob.samplers.NormalizedGaussianSampler -import kscience.kmath.prob.samplers.ZigguratNormalizedGaussianSampler +import kscience.kmath.stat.RandomGenerator +import kscience.kmath.stat.UnivariateDistribution +import kscience.kmath.stat.internal.InternalErf +import kscience.kmath.stat.samplers.GaussianSampler +import kscience.kmath.stat.samplers.NormalizedGaussianSampler +import kscience.kmath.stat.samplers.ZigguratNormalizedGaussianSampler import kotlin.math.* public inline class NormalDistribution(public val sampler: GaussianSampler) : UnivariateDistribution { diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalErf.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalErf.kt index 178423e68..bcc4d1481 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalErf.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalErf.kt @@ -1,4 +1,4 @@ -package kscience.kmath.prob.internal +package kscience.kmath.stat.internal import kotlin.math.abs diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalGamma.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalGamma.kt index 8a73659db..dce84712a 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalGamma.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalGamma.kt @@ -1,4 +1,4 @@ -package kscience.kmath.prob.internal +package kscience.kmath.stat.internal import kotlin.math.* diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalUtils.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalUtils.kt index 837c9796c..9997eeb57 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalUtils.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalUtils.kt @@ -1,4 +1,4 @@ -package kscience.kmath.prob.internal +package kscience.kmath.stat.internal import kotlin.math.ln import kotlin.math.min diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterExponentialSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterExponentialSampler.kt index d4b443e9c..cea6de20d 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterExponentialSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterExponentialSampler.kt @@ -1,10 +1,10 @@ -package kscience.kmath.prob.samplers +package kscience.kmath.stat.samplers import kscience.kmath.chains.Chain -import kscience.kmath.prob.RandomGenerator -import kscience.kmath.prob.Sampler -import kscience.kmath.prob.chain -import kscience.kmath.prob.internal.InternalUtils +import kscience.kmath.stat.RandomGenerator +import kscience.kmath.stat.Sampler +import kscience.kmath.stat.chain +import kscience.kmath.stat.internal.InternalUtils import kotlin.math.ln import kotlin.math.pow diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt index e18a44cb9..f36e34f34 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt @@ -1,10 +1,10 @@ -package kscience.kmath.prob.samplers +package kscience.kmath.stat.samplers import kscience.kmath.chains.Chain -import kscience.kmath.prob.RandomGenerator -import kscience.kmath.prob.Sampler -import kscience.kmath.prob.chain -import kscience.kmath.prob.next +import kscience.kmath.stat.RandomGenerator +import kscience.kmath.stat.Sampler +import kscience.kmath.stat.chain +import kscience.kmath.stat.next import kotlin.math.* /** diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AliasMethodDiscreteSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AliasMethodDiscreteSampler.kt index db78a41a8..a76fcd543 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AliasMethodDiscreteSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AliasMethodDiscreteSampler.kt @@ -1,10 +1,10 @@ -package kscience.kmath.prob.samplers +package kscience.kmath.stat.samplers import kscience.kmath.chains.Chain -import kscience.kmath.prob.RandomGenerator -import kscience.kmath.prob.Sampler -import kscience.kmath.prob.chain -import kscience.kmath.prob.internal.InternalUtils +import kscience.kmath.stat.RandomGenerator +import kscience.kmath.stat.Sampler +import kscience.kmath.stat.chain +import kscience.kmath.stat.internal.InternalUtils import kotlin.math.ceil import kotlin.math.max import kotlin.math.min diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/BoxMullerNormalizedGaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/BoxMullerNormalizedGaussianSampler.kt index 50a7b00c2..dc3a9b3e6 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/BoxMullerNormalizedGaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/BoxMullerNormalizedGaussianSampler.kt @@ -1,9 +1,9 @@ -package kscience.kmath.prob.samplers +package kscience.kmath.stat.samplers import kscience.kmath.chains.Chain -import kscience.kmath.prob.RandomGenerator -import kscience.kmath.prob.Sampler -import kscience.kmath.prob.chain +import kscience.kmath.stat.RandomGenerator +import kscience.kmath.stat.Sampler +import kscience.kmath.stat.chain import kotlin.math.* /** diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/GaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/GaussianSampler.kt index 1a5e4cfdd..5e54d2f18 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/GaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/GaussianSampler.kt @@ -1,9 +1,9 @@ -package kscience.kmath.prob.samplers +package kscience.kmath.stat.samplers import kscience.kmath.chains.Chain import kscience.kmath.chains.map -import kscience.kmath.prob.RandomGenerator -import kscience.kmath.prob.Sampler +import kscience.kmath.stat.RandomGenerator +import kscience.kmath.stat.Sampler /** * Sampling from a Gaussian distribution with given mean and standard deviation. diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/KempSmallMeanPoissonSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/KempSmallMeanPoissonSampler.kt index 624fc9a7e..4b04f3e6f 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/KempSmallMeanPoissonSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/KempSmallMeanPoissonSampler.kt @@ -1,9 +1,9 @@ -package kscience.kmath.prob.samplers +package kscience.kmath.stat.samplers import kscience.kmath.chains.Chain -import kscience.kmath.prob.RandomGenerator -import kscience.kmath.prob.Sampler -import kscience.kmath.prob.chain +import kscience.kmath.stat.RandomGenerator +import kscience.kmath.stat.Sampler +import kscience.kmath.stat.chain import kotlin.math.exp /** diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/LargeMeanPoissonSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/LargeMeanPoissonSampler.kt index 8a54fabaa..a841d47fc 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/LargeMeanPoissonSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/LargeMeanPoissonSampler.kt @@ -1,12 +1,12 @@ -package kscience.kmath.prob.samplers +package kscience.kmath.stat.samplers import kscience.kmath.chains.Chain import kscience.kmath.chains.ConstantChain -import kscience.kmath.prob.RandomGenerator -import kscience.kmath.prob.Sampler -import kscience.kmath.prob.chain -import kscience.kmath.prob.internal.InternalUtils -import kscience.kmath.prob.next +import kscience.kmath.stat.RandomGenerator +import kscience.kmath.stat.Sampler +import kscience.kmath.stat.chain +import kscience.kmath.stat.internal.InternalUtils +import kscience.kmath.stat.next import kotlin.math.* /** diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/MarsagliaNormalizedGaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/MarsagliaNormalizedGaussianSampler.kt index 69c04c20b..1696a7701 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/MarsagliaNormalizedGaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/MarsagliaNormalizedGaussianSampler.kt @@ -1,9 +1,9 @@ -package kscience.kmath.prob.samplers +package kscience.kmath.stat.samplers import kscience.kmath.chains.Chain -import kscience.kmath.prob.RandomGenerator -import kscience.kmath.prob.Sampler -import kscience.kmath.prob.chain +import kscience.kmath.stat.RandomGenerator +import kscience.kmath.stat.Sampler +import kscience.kmath.stat.chain import kotlin.math.ln import kotlin.math.sqrt diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/NormalizedGaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/NormalizedGaussianSampler.kt index af2ab876d..8995d3030 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/NormalizedGaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/NormalizedGaussianSampler.kt @@ -1,6 +1,6 @@ -package kscience.kmath.prob.samplers +package kscience.kmath.stat.samplers -import kscience.kmath.prob.Sampler +import kscience.kmath.stat.Sampler /** * Marker interface for a sampler that generates values from an N(0,1) diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/PoissonSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/PoissonSampler.kt index 02d8d5632..415e9c826 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/PoissonSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/PoissonSampler.kt @@ -1,8 +1,8 @@ -package kscience.kmath.prob.samplers +package kscience.kmath.stat.samplers import kscience.kmath.chains.Chain -import kscience.kmath.prob.RandomGenerator -import kscience.kmath.prob.Sampler +import kscience.kmath.stat.RandomGenerator +import kscience.kmath.stat.Sampler /** * Sampler for the Poisson distribution. diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/SmallMeanPoissonSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/SmallMeanPoissonSampler.kt index ff4233288..61d7e3484 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/SmallMeanPoissonSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/SmallMeanPoissonSampler.kt @@ -1,9 +1,9 @@ -package kscience.kmath.prob.samplers +package kscience.kmath.stat.samplers import kscience.kmath.chains.Chain -import kscience.kmath.prob.RandomGenerator -import kscience.kmath.prob.Sampler -import kscience.kmath.prob.chain +import kscience.kmath.stat.RandomGenerator +import kscience.kmath.stat.Sampler +import kscience.kmath.stat.chain import kotlin.math.ceil import kotlin.math.exp diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/ZigguratNormalizedGaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/ZigguratNormalizedGaussianSampler.kt index 60407e604..c0c9c28e0 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/ZigguratNormalizedGaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/ZigguratNormalizedGaussianSampler.kt @@ -1,9 +1,9 @@ -package kscience.kmath.prob.samplers +package kscience.kmath.stat.samplers import kscience.kmath.chains.Chain -import kscience.kmath.prob.RandomGenerator -import kscience.kmath.prob.Sampler -import kscience.kmath.prob.chain +import kscience.kmath.stat.RandomGenerator +import kscience.kmath.stat.Sampler +import kscience.kmath.stat.chain import kotlin.math.* /** diff --git a/kmath-stat/src/jvmTest/kotlin/kscience/kmath/stat/CommonsDistributionsTest.kt b/kmath-stat/src/jvmTest/kotlin/kscience/kmath/stat/CommonsDistributionsTest.kt index 63cb33f7b..8090f9b2b 100644 --- a/kmath-stat/src/jvmTest/kotlin/kscience/kmath/stat/CommonsDistributionsTest.kt +++ b/kmath-stat/src/jvmTest/kotlin/kscience/kmath/stat/CommonsDistributionsTest.kt @@ -3,7 +3,7 @@ package kscience.kmath.stat import kotlinx.coroutines.flow.take import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking -import kscience.kmath.prob.samplers.GaussianSampler +import kscience.kmath.stat.samplers.GaussianSampler import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test From 5a3fccb455667f98996b2ec7aeb4116a4465abe2 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Sun, 29 Nov 2020 22:02:06 +0700 Subject: [PATCH 21/31] Add reference to Commons Math implementation of InternalErf, fix markdown issues, rename prob package in examples to stat --- .../kmath/{commons/prob => stat}/DistributionBenchmark.kt | 5 +---- .../kmath/{commons/prob => stat}/DistributionDemo.kt | 2 +- .../kscience/kmath/stat/distributions/NormalDistribution.kt | 3 +++ .../kotlin/kscience/kmath/stat/internal/InternalErf.kt | 4 ++++ .../kmath/stat/samplers/AhrensDieterExponentialSampler.kt | 2 +- .../stat/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt | 3 ++- .../kmath/stat/samplers/AliasMethodDiscreteSampler.kt | 2 +- .../stat/samplers/BoxMullerNormalizedGaussianSampler.kt | 2 +- .../kotlin/kscience/kmath/stat/samplers/GaussianSampler.kt | 2 +- .../kmath/stat/samplers/KempSmallMeanPoissonSampler.kt | 2 +- .../kscience/kmath/stat/samplers/LargeMeanPoissonSampler.kt | 2 +- .../stat/samplers/MarsagliaNormalizedGaussianSampler.kt | 2 +- .../kotlin/kscience/kmath/stat/samplers/PoissonSampler.kt | 2 +- .../kscience/kmath/stat/samplers/SmallMeanPoissonSampler.kt | 2 +- .../kmath/stat/samplers/ZigguratNormalizedGaussianSampler.kt | 2 +- 15 files changed, 21 insertions(+), 16 deletions(-) rename examples/src/main/kotlin/kscience/kmath/{commons/prob => stat}/DistributionBenchmark.kt (93%) rename examples/src/main/kotlin/kscience/kmath/{commons/prob => stat}/DistributionDemo.kt (96%) diff --git a/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt b/examples/src/main/kotlin/kscience/kmath/stat/DistributionBenchmark.kt similarity index 93% rename from examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt rename to examples/src/main/kotlin/kscience/kmath/stat/DistributionBenchmark.kt index 5c7442c1b..4478903d9 100644 --- a/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt +++ b/examples/src/main/kotlin/kscience/kmath/stat/DistributionBenchmark.kt @@ -1,11 +1,8 @@ -package kscience.kmath.commons.prob +package kscience.kmath.stat import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking -import kscience.kmath.stat.RandomGenerator -import kscience.kmath.stat.blocking -import kscience.kmath.stat.fromSource import kscience.kmath.stat.samplers.GaussianSampler import org.apache.commons.rng.simple.RandomSource import java.time.Duration diff --git a/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionDemo.kt b/examples/src/main/kotlin/kscience/kmath/stat/DistributionDemo.kt similarity index 96% rename from examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionDemo.kt rename to examples/src/main/kotlin/kscience/kmath/stat/DistributionDemo.kt index c96826ef7..6bc72215e 100644 --- a/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionDemo.kt +++ b/examples/src/main/kotlin/kscience/kmath/stat/DistributionDemo.kt @@ -1,4 +1,4 @@ -package kscience.kmath.commons.prob +package kscience.kmath.stat import kotlinx.coroutines.runBlocking import kscience.kmath.chains.Chain diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/distributions/NormalDistribution.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/distributions/NormalDistribution.kt index 4332d92fe..9059a0038 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/distributions/NormalDistribution.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/distributions/NormalDistribution.kt @@ -9,6 +9,9 @@ import kscience.kmath.stat.samplers.NormalizedGaussianSampler import kscience.kmath.stat.samplers.ZigguratNormalizedGaussianSampler import kotlin.math.* +/** + * Implements [UnivariateDistribution] for the normal (gaussian) distribution. + */ public inline class NormalDistribution(public val sampler: GaussianSampler) : UnivariateDistribution { public constructor( mean: Double, diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalErf.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalErf.kt index bcc4d1481..04eb3ef0e 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalErf.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalErf.kt @@ -2,6 +2,10 @@ package kscience.kmath.stat.internal import kotlin.math.abs +/** + * Based on Commons Math implementation. + * See [https://commons.apache.org/proper/commons-math/javadocs/api-3.3/org/apache/commons/math3/special/Erf.html]. + */ internal object InternalErf { fun erfc(x: Double): Double { if (abs(x) > 40) return if (x > 0) 0.0 else 2.0 diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterExponentialSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterExponentialSampler.kt index cea6de20d..853e952bb 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterExponentialSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterExponentialSampler.kt @@ -12,7 +12,7 @@ import kotlin.math.pow * Sampling from an [exponential distribution](http://mathworld.wolfram.com/ExponentialDistribution.html). * * Based on Commons RNG implementation. - * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.html + * See [https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/AhrensDieterExponentialSampler.html]. */ public class AhrensDieterExponentialSampler private constructor(public val mean: Double) : Sampler { public override fun sample(generator: RandomGenerator): Chain = generator.chain { diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt index f36e34f34..98cb83332 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt @@ -15,7 +15,8 @@ import kotlin.math.* * Marsaglia and Tsang, A Simple Method for Generating Gamma Variables. ACM Transactions on Mathematical Software, Volume 26 Issue 3, September, 2000. * * Based on Commons RNG implementation. - * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.html + * + * See [https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/AhrensDieterMarsagliaTsangGammaSampler.html]. */ public class AhrensDieterMarsagliaTsangGammaSampler private constructor( alpha: Double, diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AliasMethodDiscreteSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AliasMethodDiscreteSampler.kt index a76fcd543..a4dc537b5 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AliasMethodDiscreteSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AliasMethodDiscreteSampler.kt @@ -34,7 +34,7 @@ import kotlin.math.min * that exploit the power of 2. * * Based on Commons RNG implementation. - * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSampler.html + * See [https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSampler.html]. */ public open class AliasMethodDiscreteSampler private constructor( // Deliberate direct storage of input arrays diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/BoxMullerNormalizedGaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/BoxMullerNormalizedGaussianSampler.kt index dc3a9b3e6..20412f64b 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/BoxMullerNormalizedGaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/BoxMullerNormalizedGaussianSampler.kt @@ -11,7 +11,7 @@ import kotlin.math.* * distribution. * * Based on Commons RNG implementation. - * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.html + * See [https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/BoxMullerNormalizedGaussianSampler.html]. */ public class BoxMullerNormalizedGaussianSampler private constructor() : NormalizedGaussianSampler, Sampler { private var nextGaussian: Double = Double.NaN diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/GaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/GaussianSampler.kt index 5e54d2f18..92dd27d02 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/GaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/GaussianSampler.kt @@ -9,7 +9,7 @@ import kscience.kmath.stat.Sampler * Sampling from a Gaussian distribution with given mean and standard deviation. * * Based on Commons RNG implementation. - * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/GaussianSampler.html + * See [https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/GaussianSampler.html]. * * @property mean the mean of the distribution. * @property standardDeviation the variance of the distribution. diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/KempSmallMeanPoissonSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/KempSmallMeanPoissonSampler.kt index 4b04f3e6f..78da230ca 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/KempSmallMeanPoissonSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/KempSmallMeanPoissonSampler.kt @@ -16,7 +16,7 @@ import kotlin.math.exp * Sampling uses 1 call to UniformRandomProvider.nextDouble(). This method provides an alternative to the SmallMeanPoissonSampler for slow generators of double. * * Based on Commons RNG implementation. - * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.html + * See [https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.html]. */ public class KempSmallMeanPoissonSampler private constructor( private val p0: Double, diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/LargeMeanPoissonSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/LargeMeanPoissonSampler.kt index a841d47fc..c880d7a20 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/LargeMeanPoissonSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/LargeMeanPoissonSampler.kt @@ -18,7 +18,7 @@ import kotlin.math.* * This sampler is suitable for mean >= 40. * * Based on Commons RNG implementation. - * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.html + * See [https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.html]. */ public class LargeMeanPoissonSampler private constructor(public val mean: Double) : Sampler { private val exponential: Sampler = AhrensDieterExponentialSampler.of(1.0) diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/MarsagliaNormalizedGaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/MarsagliaNormalizedGaussianSampler.kt index 1696a7701..8077f9f75 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/MarsagliaNormalizedGaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/MarsagliaNormalizedGaussianSampler.kt @@ -13,7 +13,7 @@ import kotlin.math.sqrt * [BoxMullerNormalizedGaussianSampler]. * * Based on Commons RNG implementation. - * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.html + * See [https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/MarsagliaNormalizedGaussianSampler.html] */ public class MarsagliaNormalizedGaussianSampler private constructor() : NormalizedGaussianSampler, Sampler { private var nextGaussian = Double.NaN diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/PoissonSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/PoissonSampler.kt index 415e9c826..e9a6244a6 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/PoissonSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/PoissonSampler.kt @@ -14,7 +14,7 @@ import kscience.kmath.stat.Sampler * Devroye, Luc. (1981). The Computer Generation of Poisson Random Variables Computing vol. 26 pp. 197-207. * * Based on Commons RNG implementation. - * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/PoissonSampler.html + * See [https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/PoissonSampler.html]. */ public class PoissonSampler private constructor(mean: Double) : Sampler { private val poissonSamplerDelegate: Sampler = of(mean) diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/SmallMeanPoissonSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/SmallMeanPoissonSampler.kt index 61d7e3484..e9ce8a6a6 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/SmallMeanPoissonSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/SmallMeanPoissonSampler.kt @@ -17,7 +17,7 @@ import kotlin.math.exp * * Based on Commons RNG implementation. * - * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSampler.html + * See [https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/SmallMeanPoissonSampler.html]. */ public class SmallMeanPoissonSampler private constructor(mean: Double) : Sampler { private val p0: Double = exp(-mean) diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/ZigguratNormalizedGaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/ZigguratNormalizedGaussianSampler.kt index c0c9c28e0..e9e8d91da 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/ZigguratNormalizedGaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/ZigguratNormalizedGaussianSampler.kt @@ -12,7 +12,7 @@ import kotlin.math.* * implementation has been adapted from the C code provided therein. * * Based on Commons RNG implementation. - * See https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.html + * See [https://commons.apache.org/proper/commons-rng/commons-rng-sampling/apidocs/org/apache/commons/rng/sampling/distribution/ZigguratNormalizedGaussianSampler.html]. */ public class ZigguratNormalizedGaussianSampler private constructor() : NormalizedGaussianSampler, Sampler { From e43aad33fe26d14758940f70d98457e59252cb15 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Sat, 12 Dec 2020 17:13:14 +0700 Subject: [PATCH 22/31] Add missing import --- .../src/main/kotlin/kscience/kmath/stat/DistributionDemo.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/src/main/kotlin/kscience/kmath/stat/DistributionDemo.kt b/examples/src/main/kotlin/kscience/kmath/stat/DistributionDemo.kt index 8562c15df..dd04c12e5 100644 --- a/examples/src/main/kotlin/kscience/kmath/stat/DistributionDemo.kt +++ b/examples/src/main/kotlin/kscience/kmath/stat/DistributionDemo.kt @@ -3,6 +3,7 @@ package kscience.kmath.stat import kotlinx.coroutines.runBlocking import kscience.kmath.chains.Chain import kscience.kmath.chains.collectWithState +import kscience.kmath.stat.distributions.NormalDistribution /** * The state of distribution averager. @@ -21,7 +22,7 @@ private fun Chain.mean(): Chain = collectWithState(AveragingChai fun main() { - val normal = NormalDistribution() + val normal = NormalDistribution(0.0, 2.0) val chain = normal.sample(RandomGenerator.default).mean() runBlocking { From 2f116604398fed07a1ccdd50ea2ab20cc297365f Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Sat, 12 Dec 2020 21:03:28 +0700 Subject: [PATCH 23/31] Replace Distribution.normal with NormalDistribution --- .../kscience/kmath/commons/fit/fitWithAutoDiff.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/src/main/kotlin/kscience/kmath/commons/fit/fitWithAutoDiff.kt b/examples/src/main/kotlin/kscience/kmath/commons/fit/fitWithAutoDiff.kt index c0cd9dc5c..22d642d92 100644 --- a/examples/src/main/kotlin/kscience/kmath/commons/fit/fitWithAutoDiff.kt +++ b/examples/src/main/kotlin/kscience/kmath/commons/fit/fitWithAutoDiff.kt @@ -9,6 +9,7 @@ import kscience.kmath.real.RealVector import kscience.kmath.real.map import kscience.kmath.real.step import kscience.kmath.stat.* +import kscience.kmath.stat.distributions.NormalDistribution import kscience.kmath.structures.asIterable import kscience.kmath.structures.toList import kscience.plotly.* @@ -33,10 +34,9 @@ operator fun TraceValues.invoke(vector: RealVector) { /** * Least squares fie with auto-differentiation. Uses `kmath-commons` and `kmath-for-real` modules. */ -fun main() { - +suspend fun main() { //A generator for a normally distributed values - val generator = Distribution.normal() + val generator = NormalDistribution(2.0, 7.0) //A chain/flow of random values with the given seed val chain = generator.sample(RandomGenerator.default(112667)) @@ -49,7 +49,7 @@ fun main() { //Perform an operation on each x value (much more effective, than numpy) val y = x.map { val value = it.pow(2) + it + 1 - value + chain.nextDouble() * sqrt(value) + value + chain.next() * sqrt(value) } // this will also work, but less effective: // val y = x.pow(2)+ x + 1 + chain.nextDouble() @@ -99,4 +99,4 @@ fun main() { } page.makeFile() -} \ No newline at end of file +} From 9c77f8f02f6a5ce98bec2045872c761e37c46bd3 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Sun, 24 Jan 2021 01:59:42 +0700 Subject: [PATCH 24/31] Remove incorrect lines --- .../main/kotlin/kscience/kmath/stat/DistributionBenchmark.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/src/main/kotlin/kscience/kmath/stat/DistributionBenchmark.kt b/examples/src/main/kotlin/kscience/kmath/stat/DistributionBenchmark.kt index 92b6f046d..4478903d9 100644 --- a/examples/src/main/kotlin/kscience/kmath/stat/DistributionBenchmark.kt +++ b/examples/src/main/kotlin/kscience/kmath/stat/DistributionBenchmark.kt @@ -63,6 +63,4 @@ fun main(): Unit = runBlocking(Dispatchers.Default) { val directJob = async { runApacheDirect() } println("KMath Chained: ${chainJob.await()}") println("Apache Direct: ${directJob.await()}") - val normal = GaussianSampler.of(7.0, 2.0) - val chain = normal.sample(generator).blocking() } From 88d0c19a741f32145877c65d1cf7f70ff20b9ade Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 19 Mar 2021 11:07:27 +0300 Subject: [PATCH 25/31] Refactor structure features. Basic curve fitting --- build.gradle.kts | 6 +- .../kmath/commons/fit/fitWithAutoDiff.kt | 8 +- .../kscience/kmath/commons/linear/CMMatrix.kt | 3 +- ...timizationProblem.kt => CMOptimization.kt} | 42 +++--- .../kmath/commons/optimization/cmFit.kt | 22 ++-- .../commons/optimization/OptimizeTest.kt | 4 +- kmath-core/api/kmath-core.api | 9 +- .../kscience/kmath/linear/LinearSpace.kt | 4 +- .../kscience/kmath/linear/MatrixFeatures.kt | 4 +- .../kscience/kmath/linear/MatrixWrapper.kt | 3 +- .../space/kscience/kmath/nd/AlgebraND.kt | 5 +- .../space/kscience/kmath/nd/Structure1D.kt | 2 + .../space/kscience/kmath/nd/Structure2D.kt | 2 +- .../space/kscience/kmath/nd/StructureND.kt | 6 +- .../kmath/chains/BlockingDoubleChain.kt | 2 +- .../kscience/kmath/ejml/EjmlLinearSpace.kt | 3 +- .../kotlin/space/kscience/kmath/real/grids.kt | 74 ++++++----- kmath-stat/build.gradle.kts | 8 -- .../kscience/kmath/optimization/DataFit.kt | 17 +++ .../optimization/FunctionOptimization.kt | 122 ++++++++++++++++++ .../kmath/optimization/Optimization.kt | 44 +++++++ .../space/kscience/kmath/stat/Fitting.kt | 63 --------- .../kmath/stat/OptimizationProblem.kt | 88 ------------- .../space/kscience/kmath/stat/RandomChain.kt | 2 +- .../kscience/kmath/stat/distributions.kt | 25 ++-- 25 files changed, 311 insertions(+), 257 deletions(-) rename kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/{CMOptimizationProblem.kt => CMOptimization.kt} (75%) create mode 100644 kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/DataFit.kt create mode 100644 kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/FunctionOptimization.kt create mode 100644 kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/Optimization.kt delete mode 100644 kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Fitting.kt delete mode 100644 kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/OptimizationProblem.kt diff --git a/build.gradle.kts b/build.gradle.kts index 5f1a8b88a..9570d7744 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,3 @@ -import ru.mipt.npm.gradle.KSciencePublishingPlugin - plugins { id("ru.mipt.npm.gradle.project") } @@ -20,11 +18,11 @@ allprojects { } group = "space.kscience" - version = "0.3.0-dev-3" + version = "0.3.0-dev-4" } subprojects { - if (name.startsWith("kmath")) apply() + if (name.startsWith("kmath")) apply(plugin = "maven-publish") } readme { diff --git a/examples/src/main/kotlin/space/kscience/kmath/commons/fit/fitWithAutoDiff.kt b/examples/src/main/kotlin/space/kscience/kmath/commons/fit/fitWithAutoDiff.kt index ef0c29a2d..6141a1058 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/commons/fit/fitWithAutoDiff.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/commons/fit/fitWithAutoDiff.kt @@ -8,10 +8,14 @@ import kscience.plotly.models.TraceValues import space.kscience.kmath.commons.optimization.chiSquared import space.kscience.kmath.commons.optimization.minimize import space.kscience.kmath.expressions.symbol +import space.kscience.kmath.optimization.FunctionOptimization +import space.kscience.kmath.optimization.OptimizationResult import space.kscience.kmath.real.DoubleVector import space.kscience.kmath.real.map import space.kscience.kmath.real.step -import space.kscience.kmath.stat.* +import space.kscience.kmath.stat.Distribution +import space.kscience.kmath.stat.RandomGenerator +import space.kscience.kmath.stat.normal import space.kscience.kmath.structures.asIterable import space.kscience.kmath.structures.toList import kotlin.math.pow @@ -58,7 +62,7 @@ fun main() { val yErr = y.map { sqrt(it) }//RealVector.same(x.size, sigma) // compute differentiable chi^2 sum for given model ax^2 + bx + c - val chi2 = Fitting.chiSquared(x, y, yErr) { x1 -> + val chi2 = FunctionOptimization.chiSquared(x, y, yErr) { x1 -> //bind variables to autodiff context val a = bind(a) val b = bind(b) diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt index 89e9649e9..80929e6b9 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt @@ -3,6 +3,7 @@ package space.kscience.kmath.commons.linear import org.apache.commons.math3.linear.* import space.kscience.kmath.linear.* import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.nd.StructureFeature import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.structures.DoubleBuffer import kotlin.reflect.KClass @@ -89,7 +90,7 @@ public object CMLinearSpace : LinearSpace { v * this @UnstableKMathAPI - override fun getFeature(structure: Matrix, type: KClass): F? { + override fun getFeature(structure: Matrix, type: KClass): F? { //Return the feature if it is intrinsic to the structure structure.getFeature(type)?.let { return it } diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimizationProblem.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimization.kt similarity index 75% rename from kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimizationProblem.kt rename to kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimization.kt index 13a10475f..6200b61a9 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimizationProblem.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimization.kt @@ -10,21 +10,25 @@ import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.AbstractSimplex import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.NelderMeadSimplex import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.SimplexOptimizer import space.kscience.kmath.expressions.* -import space.kscience.kmath.stat.OptimizationFeature -import space.kscience.kmath.stat.OptimizationProblem -import space.kscience.kmath.stat.OptimizationProblemFactory -import space.kscience.kmath.stat.OptimizationResult +import space.kscience.kmath.optimization.FunctionOptimization +import space.kscience.kmath.optimization.OptimizationFeature +import space.kscience.kmath.optimization.OptimizationProblemFactory +import space.kscience.kmath.optimization.OptimizationResult import kotlin.reflect.KClass public operator fun PointValuePair.component1(): DoubleArray = point public operator fun PointValuePair.component2(): Double = value -public class CMOptimizationProblem(override val symbols: List) : - OptimizationProblem, SymbolIndexer, OptimizationFeature { +public class CMOptimization( + override val symbols: List, +) : FunctionOptimization, SymbolIndexer, OptimizationFeature { private val optimizationData: HashMap, OptimizationData> = HashMap() - private var optimizatorBuilder: (() -> MultivariateOptimizer)? = null - public var convergenceChecker: ConvergenceChecker = SimpleValueChecker(DEFAULT_RELATIVE_TOLERANCE, - DEFAULT_ABSOLUTE_TOLERANCE, DEFAULT_MAX_ITER) + private var optimizerBuilder: (() -> MultivariateOptimizer)? = null + public var convergenceChecker: ConvergenceChecker = SimpleValueChecker( + DEFAULT_RELATIVE_TOLERANCE, + DEFAULT_ABSOLUTE_TOLERANCE, + DEFAULT_MAX_ITER + ) public fun addOptimizationData(data: OptimizationData) { optimizationData[data::class] = data @@ -57,8 +61,8 @@ public class CMOptimizationProblem(override val symbols: List) : } } addOptimizationData(gradientFunction) - if (optimizatorBuilder == null) { - optimizatorBuilder = { + if (optimizerBuilder == null) { + optimizerBuilder = { NonLinearConjugateGradientOptimizer( NonLinearConjugateGradientOptimizer.Formula.FLETCHER_REEVES, convergenceChecker @@ -70,8 +74,8 @@ public class CMOptimizationProblem(override val symbols: List) : public fun simplex(simplex: AbstractSimplex) { addOptimizationData(simplex) //Set optimization builder to simplex if it is not present - if (optimizatorBuilder == null) { - optimizatorBuilder = { SimplexOptimizer(convergenceChecker) } + if (optimizerBuilder == null) { + optimizerBuilder = { SimplexOptimizer(convergenceChecker) } } } @@ -84,7 +88,7 @@ public class CMOptimizationProblem(override val symbols: List) : } public fun optimizer(block: () -> MultivariateOptimizer) { - optimizatorBuilder = block + optimizerBuilder = block } override fun update(result: OptimizationResult) { @@ -92,19 +96,19 @@ public class CMOptimizationProblem(override val symbols: List) : } override fun optimize(): OptimizationResult { - val optimizer = optimizatorBuilder?.invoke() ?: error("Optimizer not defined") + val optimizer = optimizerBuilder?.invoke() ?: error("Optimizer not defined") val (point, value) = optimizer.optimize(*optimizationData.values.toTypedArray()) return OptimizationResult(point.toMap(), value, setOf(this)) } - public companion object : OptimizationProblemFactory { + public companion object : OptimizationProblemFactory { public const val DEFAULT_RELATIVE_TOLERANCE: Double = 1e-4 public const val DEFAULT_ABSOLUTE_TOLERANCE: Double = 1e-4 public const val DEFAULT_MAX_ITER: Int = 1000 - override fun build(symbols: List): CMOptimizationProblem = CMOptimizationProblem(symbols) + override fun build(symbols: List): CMOptimization = CMOptimization(symbols) } } -public fun CMOptimizationProblem.initialGuess(vararg pairs: Pair): Unit = initialGuess(pairs.toMap()) -public fun CMOptimizationProblem.simplexSteps(vararg pairs: Pair): Unit = simplexSteps(pairs.toMap()) +public fun CMOptimization.initialGuess(vararg pairs: Pair): Unit = initialGuess(pairs.toMap()) +public fun CMOptimization.simplexSteps(vararg pairs: Pair): Unit = simplexSteps(pairs.toMap()) diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/cmFit.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/cmFit.kt index 5ecd5b756..384414e6d 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/cmFit.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/cmFit.kt @@ -6,16 +6,16 @@ import space.kscience.kmath.commons.expressions.DerivativeStructureField import space.kscience.kmath.expressions.DifferentiableExpression import space.kscience.kmath.expressions.Expression import space.kscience.kmath.expressions.Symbol -import space.kscience.kmath.stat.Fitting -import space.kscience.kmath.stat.OptimizationResult -import space.kscience.kmath.stat.optimizeWith +import space.kscience.kmath.optimization.FunctionOptimization +import space.kscience.kmath.optimization.OptimizationResult +import space.kscience.kmath.optimization.optimizeWith import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.asBuffer /** * Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic differentiation */ -public fun Fitting.chiSquared( +public fun FunctionOptimization.Companion.chiSquared( x: Buffer, y: Buffer, yErr: Buffer, @@ -25,7 +25,7 @@ public fun Fitting.chiSquared( /** * Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic differentiation */ -public fun Fitting.chiSquared( +public fun FunctionOptimization.Companion.chiSquared( x: Iterable, y: Iterable, yErr: Iterable, @@ -43,23 +43,23 @@ public fun Fitting.chiSquared( */ public fun Expression.optimize( vararg symbols: Symbol, - configuration: CMOptimizationProblem.() -> Unit, -): OptimizationResult = optimizeWith(CMOptimizationProblem, symbols = symbols, configuration) + configuration: CMOptimization.() -> Unit, +): OptimizationResult = optimizeWith(CMOptimization, symbols = symbols, configuration) /** * Optimize differentiable expression */ public fun DifferentiableExpression>.optimize( vararg symbols: Symbol, - configuration: CMOptimizationProblem.() -> Unit, -): OptimizationResult = optimizeWith(CMOptimizationProblem, symbols = symbols, configuration) + configuration: CMOptimization.() -> Unit, +): OptimizationResult = optimizeWith(CMOptimization, symbols = symbols, configuration) public fun DifferentiableExpression>.minimize( vararg startPoint: Pair, - configuration: CMOptimizationProblem.() -> Unit = {}, + configuration: CMOptimization.() -> Unit = {}, ): OptimizationResult { require(startPoint.isNotEmpty()) { "Must provide a list of symbols for optimization" } - val problem = CMOptimizationProblem(startPoint.map { it.first }).apply(configuration) + val problem = CMOptimization(startPoint.map { it.first }).apply(configuration) problem.diffExpression(this) problem.initialGuess(startPoint.toMap()) problem.goal(GoalType.MINIMIZE) diff --git a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt index d29934a4d..cbbe1457e 100644 --- a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt +++ b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt @@ -3,8 +3,8 @@ package space.kscience.kmath.commons.optimization import org.junit.jupiter.api.Test import space.kscience.kmath.commons.expressions.DerivativeStructureExpression import space.kscience.kmath.expressions.symbol +import space.kscience.kmath.optimization.FunctionOptimization import space.kscience.kmath.stat.Distribution -import space.kscience.kmath.stat.Fitting import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.normal import kotlin.math.pow @@ -55,7 +55,7 @@ internal class OptimizeTest { val yErr = List(x.size) { sigma } - val chi2 = Fitting.chiSquared(x, y, yErr) { x1 -> + val chi2 = FunctionOptimization.chiSquared(x, y, yErr) { x1 -> val cWithDefault = bindSymbolOrNull(c) ?: one bind(a) * x1.pow(2) + bind(b) * x1 + cWithDefault } diff --git a/kmath-core/api/kmath-core.api b/kmath-core/api/kmath-core.api index 04325379e..8c62198cd 100644 --- a/kmath-core/api/kmath-core.api +++ b/kmath-core/api/kmath-core.api @@ -575,7 +575,7 @@ public final class space/kscience/kmath/linear/MatrixBuilderKt { public static final fun row (Lspace/kscience/kmath/linear/LinearSpace;[Ljava/lang/Object;)Lspace/kscience/kmath/nd/Structure2D; } -public abstract interface class space/kscience/kmath/linear/MatrixFeature { +public abstract interface class space/kscience/kmath/linear/MatrixFeature : space/kscience/kmath/nd/StructureFeature { } public final class space/kscience/kmath/linear/MatrixFeaturesKt { @@ -1060,11 +1060,15 @@ public final class space/kscience/kmath/nd/Strides$DefaultImpls { } public abstract interface class space/kscience/kmath/nd/Structure1D : space/kscience/kmath/nd/StructureND, space/kscience/kmath/structures/Buffer { + public static final field Companion Lspace/kscience/kmath/nd/Structure1D$Companion; public abstract fun get ([I)Ljava/lang/Object; public abstract fun getDimension ()I public abstract fun iterator ()Ljava/util/Iterator; } +public final class space/kscience/kmath/nd/Structure1D$Companion { +} + public final class space/kscience/kmath/nd/Structure1D$DefaultImpls { public static fun get (Lspace/kscience/kmath/nd/Structure1D;[I)Ljava/lang/Object; public static fun getDimension (Lspace/kscience/kmath/nd/Structure1D;)I @@ -1104,6 +1108,9 @@ public final class space/kscience/kmath/nd/Structure2DKt { public static final fun as2D (Lspace/kscience/kmath/nd/StructureND;)Lspace/kscience/kmath/nd/Structure2D; } +public abstract interface class space/kscience/kmath/nd/StructureFeature { +} + public abstract interface class space/kscience/kmath/nd/StructureND { public static final field Companion Lspace/kscience/kmath/nd/StructureND$Companion; public abstract fun elements ()Lkotlin/sequences/Sequence; diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt index 6a587270b..dfc4c7c9b 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt @@ -164,7 +164,7 @@ public interface LinearSpace> { * @return a feature object or `null` if it isn't present. */ @UnstableKMathAPI - public fun getFeature(structure: Matrix, type: KClass): F? = structure.getFeature(type) + public fun getFeature(structure: Matrix, type: KClass): F? = structure.getFeature(type) public companion object { @@ -194,7 +194,7 @@ public interface LinearSpace> { * @return a feature object or `null` if it isn't present. */ @UnstableKMathAPI -public inline fun LinearSpace.getFeature(structure: Matrix): F? = +public inline fun LinearSpace.getFeature(structure: Matrix): F? = getFeature(structure, F::class) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixFeatures.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixFeatures.kt index 6b97e89ef..30e3daa7a 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixFeatures.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixFeatures.kt @@ -1,10 +1,12 @@ package space.kscience.kmath.linear +import space.kscience.kmath.nd.StructureFeature + /** * A marker interface representing some properties of matrices or additional transformations of them. Features are used * to optimize matrix operations performance in some cases or retrieve the APIs. */ -public interface MatrixFeature +public interface MatrixFeature: StructureFeature /** * Matrices with this feature are considered to have only diagonal non-null elements. diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixWrapper.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixWrapper.kt index 868f74cc6..def3b87f7 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixWrapper.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixWrapper.kt @@ -1,6 +1,7 @@ package space.kscience.kmath.linear import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.nd.StructureFeature import space.kscience.kmath.nd.getFeature import space.kscience.kmath.operations.Ring import kotlin.reflect.KClass @@ -20,7 +21,7 @@ public class MatrixWrapper internal constructor( */ @UnstableKMathAPI @Suppress("UNCHECKED_CAST") - override fun getFeature(type: KClass): T? = features.singleOrNull { type.isInstance(it) } as? T + override fun getFeature(type: KClass): F? = features.singleOrNull { type.isInstance(it) } as? F ?: origin.getFeature(type) override fun toString(): String { diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt index b23ce947d..2821a6648 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/AlgebraND.kt @@ -67,7 +67,8 @@ public interface AlgebraND> { * @return a feature object or `null` if it isn't present. */ @UnstableKMathAPI - public fun getFeature(structure: StructureND, type: KClass): F? = structure.getFeature(type) + public fun getFeature(structure: StructureND, type: KClass): F? = + structure.getFeature(type) public companion object } @@ -81,7 +82,7 @@ public interface AlgebraND> { * @return a feature object or `null` if it isn't present. */ @UnstableKMathAPI -public inline fun AlgebraND.getFeature(structure: StructureND): F? = +public inline fun AlgebraND.getFeature(structure: StructureND): F? = getFeature(structure, F::class) /** diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure1D.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure1D.kt index 8cf5d18cb..5483ed28f 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure1D.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure1D.kt @@ -15,6 +15,8 @@ public interface Structure1D : StructureND, Buffer { } public override operator fun iterator(): Iterator = (0 until size).asSequence().map(::get).iterator() + + public companion object } /** diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure2D.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure2D.kt index 3834cd97c..5dfdd028a 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure2D.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Structure2D.kt @@ -69,7 +69,7 @@ private inline class Structure2DWrapper(val structure: StructureND) : Stru override operator fun get(i: Int, j: Int): T = structure[i, j] @UnstableKMathAPI - override fun getFeature(type: KClass): F? = structure.getFeature(type) + override fun getFeature(type: KClass): F? = structure.getFeature(type) override fun elements(): Sequence> = structure.elements() } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt index 78eac1809..a1aa5e554 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt @@ -7,6 +7,8 @@ import kotlin.jvm.JvmName import kotlin.native.concurrent.ThreadLocal import kotlin.reflect.KClass +public interface StructureFeature + /** * Represents n-dimensional structure, i.e. multidimensional container of items of the same type and size. The number * of dimensions and items in an array is defined by its shape, which is a sequence of non-negative integers that @@ -48,7 +50,7 @@ public interface StructureND { * If the feature is not present, null is returned. */ @UnstableKMathAPI - public fun getFeature(type: KClass): F? = null + public fun getFeature(type: KClass): F? = null public companion object { /** @@ -144,7 +146,7 @@ public interface StructureND { public operator fun StructureND.get(vararg index: Int): T = get(index) @UnstableKMathAPI -public inline fun StructureND<*>.getFeature(): T? = getFeature(T::class) +public inline fun StructureND<*>.getFeature(): T? = getFeature(T::class) /** * Represents mutable [StructureND]. diff --git a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingDoubleChain.kt b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingDoubleChain.kt index bc4508994..ba6adf35b 100644 --- a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingDoubleChain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingDoubleChain.kt @@ -8,5 +8,5 @@ public abstract class BlockingDoubleChain : Chain { override suspend fun next(): Double = nextDouble() - public fun nextBlock(size: Int): DoubleArray = DoubleArray(size) { nextDouble() } + public open fun nextBlock(size: Int): DoubleArray = DoubleArray(size) { nextDouble() } } diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt index a82fe933e..f84a57c9d 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt @@ -4,6 +4,7 @@ import org.ejml.dense.row.factory.DecompositionFactory_DDRM import org.ejml.simple.SimpleMatrix import space.kscience.kmath.linear.* import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.nd.StructureFeature import space.kscience.kmath.nd.getFeature import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.structures.DoubleBuffer @@ -89,7 +90,7 @@ public object EjmlLinearSpace : LinearSpace { v.toEjml().origin.scale(this).wrapVector() @UnstableKMathAPI - override fun getFeature(structure: Matrix, type: KClass): F? { + override fun getFeature(structure: Matrix, type: KClass): F? { //Return the feature if it is intrinsic to the structure structure.getFeature(type)?.let { return it } diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/grids.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/grids.kt index 35297a3ac..446a3d9cb 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/grids.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/grids.kt @@ -1,7 +1,43 @@ package space.kscience.kmath.real -import space.kscience.kmath.structures.asBuffer -import kotlin.math.abs +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.DoubleBuffer +import kotlin.math.floor + +public val ClosedFloatingPointRange.length: Double get() = endInclusive - start + +/** + * Create a Buffer-based grid with equally distributed [numberOfPoints] points. The range could be increasing or decreasing. + * If range has a zero size, then the buffer consisting of [numberOfPoints] equal values is returned. + */ +@UnstableKMathAPI +public fun Buffer.Companion.fromRange(range: ClosedFloatingPointRange, numberOfPoints: Int): DoubleBuffer { + require(numberOfPoints >= 2) { "Number of points in grid must be more than 1" } + val normalizedRange = when { + range.endInclusive > range.start -> range + range.endInclusive < range.start -> range.endInclusive..range.start + else -> return DoubleBuffer(numberOfPoints) { range.start } + } + val step = normalizedRange.length / (numberOfPoints - 1) + return DoubleBuffer(numberOfPoints) { normalizedRange.start + step * it / (numberOfPoints - 1) } +} + +/** + * Create a Buffer-based grid with equally distributed points with a fixed [step]. The range could be increasing or decreasing. + * If the step is larger than the range size, single point is returned. + */ +@UnstableKMathAPI +public fun Buffer.Companion.fromRange(range: ClosedFloatingPointRange, step: Double): DoubleBuffer { + require(step > 0) { "The grid step must be positive" } + val normalizedRange = when { + range.endInclusive > range.start -> range + range.endInclusive < range.start -> range.endInclusive..range.start + else -> return DoubleBuffer(range.start) + } + val numberOfPoints = floor(normalizedRange.length / step).toInt() + return DoubleBuffer(numberOfPoints) { normalizedRange.start + step * it / (numberOfPoints - 1) } +} /** * Convert double range to sequence. @@ -11,35 +47,5 @@ import kotlin.math.abs * * If step is negative, the same goes from upper boundary downwards */ -public fun ClosedFloatingPointRange.toSequenceWithStep(step: Double): Sequence = when { - step == 0.0 -> error("Zero step in double progression") - - step > 0 -> sequence { - var current = start - - while (current <= endInclusive) { - yield(current) - current += step - } - } - - else -> sequence { - var current = endInclusive - - while (current >= start) { - yield(current) - current += step - } - } -} - -public infix fun ClosedFloatingPointRange.step(step: Double): DoubleVector = - toSequenceWithStep(step).toList().asBuffer() - -/** - * Convert double range to sequence with the fixed number of points - */ -public fun ClosedFloatingPointRange.toSequenceWithPoints(numPoints: Int): Sequence { - require(numPoints > 1) { "The number of points should be more than 2" } - return toSequenceWithStep(abs(endInclusive - start) / (numPoints - 1)) -} +@UnstableKMathAPI +public infix fun ClosedFloatingPointRange.step(step: Double): DoubleBuffer = Buffer.fromRange(this, step) \ No newline at end of file diff --git a/kmath-stat/build.gradle.kts b/kmath-stat/build.gradle.kts index 5b29a9e64..bc3890b1e 100644 --- a/kmath-stat/build.gradle.kts +++ b/kmath-stat/build.gradle.kts @@ -3,14 +3,6 @@ plugins { } kotlin.sourceSets { - all { - languageSettings.apply { - useExperimentalAnnotation("kotlinx.coroutines.FlowPreview") - useExperimentalAnnotation("kotlinx.coroutines.ExperimentalCoroutinesApi") - useExperimentalAnnotation("kotlinx.coroutines.ObsoleteCoroutinesApi") - } - } - commonMain { dependencies { api(project(":kmath-coroutines")) diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/DataFit.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/DataFit.kt new file mode 100644 index 000000000..e9a5dea86 --- /dev/null +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/DataFit.kt @@ -0,0 +1,17 @@ +package space.kscience.kmath.optimization + +import space.kscience.kmath.expressions.DifferentiableExpression +import space.kscience.kmath.expressions.StringSymbol +import space.kscience.kmath.expressions.Symbol +import space.kscience.kmath.structures.Buffer + +public interface DataFit : Optimization { + + public fun modelAndData( + x: Buffer, + y: Buffer, + yErr: Buffer, + model: DifferentiableExpression, + xSymbol: Symbol = StringSymbol("x"), + ) +} \ No newline at end of file diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/FunctionOptimization.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/FunctionOptimization.kt new file mode 100644 index 000000000..bf37f5c64 --- /dev/null +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/FunctionOptimization.kt @@ -0,0 +1,122 @@ +package space.kscience.kmath.optimization + +import space.kscience.kmath.expressions.* +import space.kscience.kmath.operations.ExtendedField +import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.indices +import kotlin.math.pow + +/** + * A likelihood function optimization problem + */ +public interface FunctionOptimization: Optimization, DataFit { + /** + * Define the initial guess for the optimization problem + */ + public fun initialGuess(map: Map) + + /** + * Set an objective function expression + */ + public fun expression(expression: Expression) + + /** + * Set a differentiable expression as objective function as function and gradient provider + */ + public fun diffExpression(expression: DifferentiableExpression>) + + override fun modelAndData( + x: Buffer, + y: Buffer, + yErr: Buffer, + model: DifferentiableExpression, + xSymbol: Symbol, + ) { + require(x.size == y.size) { "X and y buffers should be of the same size" } + require(y.size == yErr.size) { "Y and yErr buffer should of the same size" } + + } + + public companion object{ + /** + * Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic differentiation + */ + public fun chiSquared( + autoDiff: AutoDiffProcessor>, + x: Buffer, + y: Buffer, + yErr: Buffer, + model: A.(I) -> I, + ): DifferentiableExpression> where A : ExtendedField, A : ExpressionAlgebra { + require(x.size == y.size) { "X and y buffers should be of the same size" } + require(y.size == yErr.size) { "Y and yErr buffer should of the same size" } + + return autoDiff.process { + var sum = zero + + x.indices.forEach { + val xValue = const(x[it]) + val yValue = const(y[it]) + val yErrValue = const(yErr[it]) + val modelValue = model(xValue) + sum += ((yValue - modelValue) / yErrValue).pow(2) + } + + sum + } + } + + /** + * Generate a chi squared expression from given x-y-sigma model represented by an expression. Does not provide derivatives + */ + public fun chiSquared( + x: Buffer, + y: Buffer, + yErr: Buffer, + model: Expression, + xSymbol: Symbol = StringSymbol("x"), + ): Expression { + require(x.size == y.size) { "X and y buffers should be of the same size" } + require(y.size == yErr.size) { "Y and yErr buffer should of the same size" } + + return Expression { arguments -> + x.indices.sumByDouble { + val xValue = x[it] + val yValue = y[it] + val yErrValue = yErr[it] + val modifiedArgs = arguments + (xSymbol to xValue) + val modelValue = model(modifiedArgs) + ((yValue - modelValue) / yErrValue).pow(2) + } + } + } + } +} + +/** + * Optimize expression without derivatives using specific [OptimizationProblemFactory] + */ +public fun > Expression.optimizeWith( + factory: OptimizationProblemFactory, + vararg symbols: Symbol, + configuration: F.() -> Unit, +): OptimizationResult { + require(symbols.isNotEmpty()) { "Must provide a list of symbols for optimization" } + val problem = factory(symbols.toList(), configuration) + problem.expression(this) + return problem.optimize() +} + +/** + * Optimize differentiable expression using specific [OptimizationProblemFactory] + */ +public fun > DifferentiableExpression>.optimizeWith( + factory: OptimizationProblemFactory, + vararg symbols: Symbol, + configuration: F.() -> Unit, +): OptimizationResult { + require(symbols.isNotEmpty()) { "Must provide a list of symbols for optimization" } + val problem = factory(symbols.toList(), configuration) + problem.diffExpression(this) + return problem.optimize() +} diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/Optimization.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/Optimization.kt new file mode 100644 index 000000000..370274b41 --- /dev/null +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/Optimization.kt @@ -0,0 +1,44 @@ +package space.kscience.kmath.optimization + +import space.kscience.kmath.expressions.Symbol + +public interface OptimizationFeature + +public class OptimizationResult( + public val point: Map, + public val value: T, + public val features: Set = emptySet(), +) { + override fun toString(): String { + return "OptimizationResult(point=$point, value=$value)" + } +} + +public operator fun OptimizationResult.plus( + feature: OptimizationFeature, +): OptimizationResult = OptimizationResult(point, value, features + feature) + +/** + * An optimization problem builder over [T] variables + */ +public interface Optimization { + + /** + * Update the problem from previous optimization run + */ + public fun update(result: OptimizationResult) + + /** + * Make an optimization run + */ + public fun optimize(): OptimizationResult +} + +public fun interface OptimizationProblemFactory> { + public fun build(symbols: List): P +} + +public operator fun > OptimizationProblemFactory.invoke( + symbols: List, + block: P.() -> Unit, +): P = build(symbols).apply(block) diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Fitting.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Fitting.kt deleted file mode 100644 index b006c8ba2..000000000 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Fitting.kt +++ /dev/null @@ -1,63 +0,0 @@ -package space.kscience.kmath.stat - -import space.kscience.kmath.expressions.* -import space.kscience.kmath.operations.ExtendedField -import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.indices -import kotlin.math.pow - -public object Fitting { - - /** - * Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic differentiation - */ - public fun chiSquared( - autoDiff: AutoDiffProcessor>, - x: Buffer, - y: Buffer, - yErr: Buffer, - model: A.(I) -> I, - ): DifferentiableExpression> where A : ExtendedField, A : ExpressionAlgebra { - require(x.size == y.size) { "X and y buffers should be of the same size" } - require(y.size == yErr.size) { "Y and yErr buffer should of the same size" } - - return autoDiff.process { - var sum = zero - - x.indices.forEach { - val xValue = const(x[it]) - val yValue = const(y[it]) - val yErrValue = const(yErr[it]) - val modelValue = model(xValue) - sum += ((yValue - modelValue) / yErrValue).pow(2) - } - - sum - } - } - - /** - * Generate a chi squared expression from given x-y-sigma model represented by an expression. Does not provide derivatives - */ - public fun chiSquared( - x: Buffer, - y: Buffer, - yErr: Buffer, - model: Expression, - xSymbol: Symbol = StringSymbol("x"), - ): Expression { - require(x.size == y.size) { "X and y buffers should be of the same size" } - require(y.size == yErr.size) { "Y and yErr buffer should of the same size" } - - return Expression { arguments -> - x.indices.sumByDouble { - val xValue = x[it] - val yValue = y[it] - val yErrValue = yErr[it] - val modifiedArgs = arguments + (xSymbol to xValue) - val modelValue = model(modifiedArgs) - ((yValue - modelValue) / yErrValue).pow(2) - } - } - } -} diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/OptimizationProblem.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/OptimizationProblem.kt deleted file mode 100644 index ffa24fa98..000000000 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/OptimizationProblem.kt +++ /dev/null @@ -1,88 +0,0 @@ -package space.kscience.kmath.stat - -import space.kscience.kmath.expressions.DifferentiableExpression -import space.kscience.kmath.expressions.Expression -import space.kscience.kmath.expressions.Symbol - -public interface OptimizationFeature - -public class OptimizationResult( - public val point: Map, - public val value: T, - public val features: Set = emptySet(), -) { - override fun toString(): String { - return "OptimizationResult(point=$point, value=$value)" - } -} - -public operator fun OptimizationResult.plus( - feature: OptimizationFeature, -): OptimizationResult = OptimizationResult(point, value, features + feature) - -/** - * A configuration builder for optimization problem - */ -public interface OptimizationProblem { - /** - * Define the initial guess for the optimization problem - */ - public fun initialGuess(map: Map) - - /** - * Set an objective function expression - */ - public fun expression(expression: Expression) - - /** - * Set a differentiable expression as objective function as function and gradient provider - */ - public fun diffExpression(expression: DifferentiableExpression>) - - /** - * Update the problem from previous optimization run - */ - public fun update(result: OptimizationResult) - - /** - * Make an optimization run - */ - public fun optimize(): OptimizationResult -} - -public fun interface OptimizationProblemFactory> { - public fun build(symbols: List): P -} - -public operator fun > OptimizationProblemFactory.invoke( - symbols: List, - block: P.() -> Unit, -): P = build(symbols).apply(block) - -/** - * Optimize expression without derivatives using specific [OptimizationProblemFactory] - */ -public fun > Expression.optimizeWith( - factory: OptimizationProblemFactory, - vararg symbols: Symbol, - configuration: F.() -> Unit, -): OptimizationResult { - require(symbols.isNotEmpty()) { "Must provide a list of symbols for optimization" } - val problem = factory(symbols.toList(), configuration) - problem.expression(this) - return problem.optimize() -} - -/** - * Optimize differentiable expression using specific [OptimizationProblemFactory] - */ -public fun > DifferentiableExpression>.optimizeWith( - factory: OptimizationProblemFactory, - vararg symbols: Symbol, - configuration: F.() -> Unit, -): OptimizationResult { - require(symbols.isNotEmpty()) { "Must provide a list of symbols for optimization" } - val problem = factory(symbols.toList(), configuration) - problem.diffExpression(this) - return problem.optimize() -} diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/RandomChain.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/RandomChain.kt index 6e1f36c8a..978094ffd 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/RandomChain.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/RandomChain.kt @@ -14,4 +14,4 @@ public class RandomChain( override fun fork(): Chain = RandomChain(generator.fork(), gen) } -public fun RandomGenerator.chain(gen: suspend RandomGenerator.() -> R): RandomChain = RandomChain(this, gen) +public fun RandomGenerator.chain(gen: suspend RandomGenerator.() -> R): RandomChain = RandomChain(this, gen) \ No newline at end of file diff --git a/kmath-stat/src/jvmMain/kotlin/space/kscience/kmath/stat/distributions.kt b/kmath-stat/src/jvmMain/kotlin/space/kscience/kmath/stat/distributions.kt index 8b5551a16..c3d711789 100644 --- a/kmath-stat/src/jvmMain/kotlin/space/kscience/kmath/stat/distributions.kt +++ b/kmath-stat/src/jvmMain/kotlin/space/kscience/kmath/stat/distributions.kt @@ -80,19 +80,20 @@ public fun Distribution.Companion.normal( override fun probability(arg: Double): Double = exp(-(arg - mean).pow(2) / 2 / sigma2) / norm } -public fun Distribution.Companion.poisson(lambda: Double): DiscreteSamplerDistribution = - object : DiscreteSamplerDistribution() { - private val computedProb: MutableMap = hashMapOf(0 to exp(-lambda)) +public fun Distribution.Companion.poisson( + lambda: Double, +): DiscreteSamplerDistribution = object : DiscreteSamplerDistribution() { + private val computedProb: HashMap = hashMapOf(0 to exp(-lambda)) - override fun buildSampler(generator: RandomGenerator): DiscreteSampler = - PoissonSampler.of(generator.asUniformRandomProvider(), lambda) + override fun buildSampler(generator: RandomGenerator): DiscreteSampler = + PoissonSampler.of(generator.asUniformRandomProvider(), lambda) - override fun probability(arg: Int): Double { - require(arg >= 0) { "The argument must be >= 0" } + override fun probability(arg: Int): Double { + require(arg >= 0) { "The argument must be >= 0" } - return if (arg > 40) - exp(-(arg - lambda).pow(2) / 2 / lambda) / sqrt(2 * PI * lambda) - else - computedProb.getOrPut(arg) { probability(arg - 1) * lambda / arg } - } + return if (arg > 40) + exp(-(arg - lambda).pow(2) / 2 / lambda) / sqrt(2 * PI * lambda) + else + computedProb.getOrPut(arg) { probability(arg - 1) * lambda / arg } } +} From aeceb4a3371576565581c2f0b7a181a320c8894b Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 19 Mar 2021 14:53:14 +0300 Subject: [PATCH 26/31] Move dataset to core --- CHANGELOG.md | 1 + .../ExpressionsInterpretersBenchmark.kt | 2 +- .../kscience/kmath/ast/kotlingradSupport.kt | 2 +- .../kmath/commons/fit/fitWithAutoDiff.kt | 2 +- .../space/kscience/kmath/ast/MstExpression.kt | 2 + .../kmath/estree/internal/ESTreeBuilder.kt | 2 +- .../kmath/asm/internal/mapIntrinsics.kt | 4 +- .../DerivativeStructureExpression.kt | 2 + .../commons/optimization/CMOptimization.kt | 8 +- .../kmath/commons/optimization/cmFit.kt | 2 +- .../DerivativeStructureExpressionTest.kt | 6 +- .../commons/optimization/OptimizeTest.kt | 2 +- .../complex/ExpressionFieldForComplexTest.kt | 2 +- kmath-core/api/kmath-core.api | 155 ++++++++---------- .../expressions/DifferentiableExpression.kt | 3 + .../kscience/kmath/expressions/Expression.kt | 26 +-- .../FunctionalExpressionAlgebra.kt | 1 + .../kmath/expressions/SimpleAutoDiff.kt | 1 + .../kmath/expressions/SymbolIndexer.kt | 6 + .../space/kscience/kmath/misc/ColumnarData.kt | 15 ++ .../space/kscience/kmath/misc/Symbol.kt | 34 ++++ .../space/kscience/kmath/misc}/XYPointSet.kt | 39 +++-- .../kscience/kmath/operations/Algebra.kt | 2 +- .../kmath/structures/bufferOperation.kt | 9 + .../kmath/expressions/ExpressionFieldTest.kt | 1 + .../kmath/expressions/SimpleAutoDiffTest.kt | 2 + .../space/kscience/kmath/real/RealVector.kt | 14 +- .../kotlin/space/kscience/kmath/real/grids.kt | 12 +- .../kotlin/kaceince/kmath/real/GridTest.kt | 5 + .../kmath/interpolation/Interpolator.kt | 15 +- .../kmath/interpolation/LinearInterpolator.kt | 11 +- .../kmath/interpolation/SplineInterpolator.kt | 5 +- .../kotlingrad/DifferentiableMstExpression.kt | 2 +- .../kscience/kmath/optimization/DataFit.kt | 4 +- .../optimization/FunctionOptimization.kt | 7 +- .../kmath/optimization/Optimization.kt | 2 +- 36 files changed, 246 insertions(+), 162 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/ColumnarData.kt create mode 100644 kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/Symbol.kt rename {kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation => kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc}/XYPointSet.kt (57%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b4dddf81..4ade9cd9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Buffer factories for primitives moved to MutableBuffer.Companion - NDStructure and NDAlgebra to StructureND and AlgebraND respectively - Real -> Double +- DataSets are moved from functions to core ### Deprecated diff --git a/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt b/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt index 0899241f9..2438e3979 100644 --- a/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt +++ b/examples/src/benchmarks/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt @@ -9,7 +9,7 @@ import space.kscience.kmath.ast.mstInField import space.kscience.kmath.expressions.Expression import space.kscience.kmath.expressions.expressionInField import space.kscience.kmath.expressions.invoke -import space.kscience.kmath.expressions.symbol +import space.kscience.kmath.misc.symbol import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.bindSymbol import kotlin.random.Random diff --git a/examples/src/main/kotlin/space/kscience/kmath/ast/kotlingradSupport.kt b/examples/src/main/kotlin/space/kscience/kmath/ast/kotlingradSupport.kt index 23c9d5b41..138b3e708 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/ast/kotlingradSupport.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/ast/kotlingradSupport.kt @@ -3,8 +3,8 @@ package space.kscience.kmath.ast import space.kscience.kmath.asm.compile import space.kscience.kmath.expressions.derivative import space.kscience.kmath.expressions.invoke -import space.kscience.kmath.expressions.symbol import space.kscience.kmath.kotlingrad.differentiable +import space.kscience.kmath.misc.symbol import space.kscience.kmath.operations.DoubleField /** diff --git a/examples/src/main/kotlin/space/kscience/kmath/commons/fit/fitWithAutoDiff.kt b/examples/src/main/kotlin/space/kscience/kmath/commons/fit/fitWithAutoDiff.kt index 6141a1058..ca4d9c181 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/commons/fit/fitWithAutoDiff.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/commons/fit/fitWithAutoDiff.kt @@ -7,7 +7,7 @@ import kscience.plotly.models.ScatterMode import kscience.plotly.models.TraceValues import space.kscience.kmath.commons.optimization.chiSquared import space.kscience.kmath.commons.optimization.minimize -import space.kscience.kmath.expressions.symbol +import space.kscience.kmath.misc.symbol import space.kscience.kmath.optimization.FunctionOptimization import space.kscience.kmath.optimization.OptimizationResult import space.kscience.kmath.real.DoubleVector diff --git a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/MstExpression.kt b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/MstExpression.kt index 63dfb38f7..5c43df068 100644 --- a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/MstExpression.kt +++ b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/MstExpression.kt @@ -1,6 +1,8 @@ package space.kscience.kmath.ast import space.kscience.kmath.expressions.* +import space.kscience.kmath.misc.StringSymbol +import space.kscience.kmath.misc.Symbol import space.kscience.kmath.operations.* import kotlin.contracts.InvocationKind import kotlin.contracts.contract diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/ESTreeBuilder.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/ESTreeBuilder.kt index b4de9968d..1e966d986 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/ESTreeBuilder.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/estree/internal/ESTreeBuilder.kt @@ -3,7 +3,7 @@ package space.kscience.kmath.estree.internal import space.kscience.kmath.estree.internal.astring.generate import space.kscience.kmath.estree.internal.estree.* import space.kscience.kmath.expressions.Expression -import space.kscience.kmath.expressions.Symbol +import space.kscience.kmath.misc.Symbol internal class ESTreeBuilder(val bodyCallback: ESTreeBuilder.() -> BaseExpression) { private class GeneratedExpression(val executable: dynamic, val constants: Array) : Expression { diff --git a/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/mapIntrinsics.kt b/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/mapIntrinsics.kt index f54bc070c..0a0c21d8a 100644 --- a/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/mapIntrinsics.kt +++ b/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/mapIntrinsics.kt @@ -2,8 +2,8 @@ package space.kscience.kmath.asm.internal -import space.kscience.kmath.expressions.StringSymbol -import space.kscience.kmath.expressions.Symbol +import space.kscience.kmath.misc.StringSymbol +import space.kscience.kmath.misc.Symbol /** * Gets value with given [key] or throws [NoSuchElementException] whenever it is not present. diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt index b74167c3f..58e9687e5 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt @@ -2,6 +2,8 @@ package space.kscience.kmath.commons.expressions import org.apache.commons.math3.analysis.differentiation.DerivativeStructure import space.kscience.kmath.expressions.* +import space.kscience.kmath.misc.StringSymbol +import space.kscience.kmath.misc.Symbol import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.operations.ExtendedField import space.kscience.kmath.operations.NumbersAddOperations diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimization.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimization.kt index 6200b61a9..93d0f0bba 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimization.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimization.kt @@ -9,7 +9,12 @@ import org.apache.commons.math3.optim.nonlinear.scalar.gradient.NonLinearConjuga import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.AbstractSimplex import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.NelderMeadSimplex import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.SimplexOptimizer -import space.kscience.kmath.expressions.* +import space.kscience.kmath.expressions.DifferentiableExpression +import space.kscience.kmath.expressions.Expression +import space.kscience.kmath.expressions.SymbolIndexer +import space.kscience.kmath.expressions.derivative +import space.kscience.kmath.misc.Symbol +import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.optimization.FunctionOptimization import space.kscience.kmath.optimization.OptimizationFeature import space.kscience.kmath.optimization.OptimizationProblemFactory @@ -19,6 +24,7 @@ import kotlin.reflect.KClass public operator fun PointValuePair.component1(): DoubleArray = point public operator fun PointValuePair.component2(): Double = value +@OptIn(UnstableKMathAPI::class) public class CMOptimization( override val symbols: List, ) : FunctionOptimization, SymbolIndexer, OptimizationFeature { diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/cmFit.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/cmFit.kt index 384414e6d..8e9ce7a80 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/cmFit.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/cmFit.kt @@ -5,7 +5,7 @@ import org.apache.commons.math3.optim.nonlinear.scalar.GoalType import space.kscience.kmath.commons.expressions.DerivativeStructureField import space.kscience.kmath.expressions.DifferentiableExpression import space.kscience.kmath.expressions.Expression -import space.kscience.kmath.expressions.Symbol +import space.kscience.kmath.misc.Symbol import space.kscience.kmath.optimization.FunctionOptimization import space.kscience.kmath.optimization.OptimizationResult import space.kscience.kmath.optimization.optimizeWith diff --git a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpressionTest.kt b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpressionTest.kt index 8d9bab652..b19eb5950 100644 --- a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpressionTest.kt +++ b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpressionTest.kt @@ -1,6 +1,10 @@ package space.kscience.kmath.commons.expressions -import space.kscience.kmath.expressions.* +import space.kscience.kmath.expressions.binding +import space.kscience.kmath.expressions.derivative +import space.kscience.kmath.expressions.invoke +import space.kscience.kmath.misc.Symbol +import space.kscience.kmath.misc.symbol import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.test.Test diff --git a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt index cbbe1457e..5f5d3e1e4 100644 --- a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt +++ b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt @@ -2,7 +2,7 @@ package space.kscience.kmath.commons.optimization import org.junit.jupiter.api.Test import space.kscience.kmath.commons.expressions.DerivativeStructureExpression -import space.kscience.kmath.expressions.symbol +import space.kscience.kmath.misc.symbol import space.kscience.kmath.optimization.FunctionOptimization import space.kscience.kmath.stat.Distribution import space.kscience.kmath.stat.RandomGenerator diff --git a/kmath-complex/src/commonTest/kotlin/space/kscience/kmath/complex/ExpressionFieldForComplexTest.kt b/kmath-complex/src/commonTest/kotlin/space/kscience/kmath/complex/ExpressionFieldForComplexTest.kt index 81a131318..3837b0d40 100644 --- a/kmath-complex/src/commonTest/kotlin/space/kscience/kmath/complex/ExpressionFieldForComplexTest.kt +++ b/kmath-complex/src/commonTest/kotlin/space/kscience/kmath/complex/ExpressionFieldForComplexTest.kt @@ -3,7 +3,7 @@ package space.kscience.kmath.complex import space.kscience.kmath.expressions.FunctionalExpressionField import space.kscience.kmath.expressions.bindSymbol import space.kscience.kmath.expressions.invoke -import space.kscience.kmath.expressions.symbol +import space.kscience.kmath.misc.symbol import kotlin.test.Test import kotlin.test.assertEquals diff --git a/kmath-core/api/kmath-core.api b/kmath-core/api/kmath-core.api index 8c62198cd..570e50a86 100644 --- a/kmath-core/api/kmath-core.api +++ b/kmath-core/api/kmath-core.api @@ -14,7 +14,7 @@ public class space/kscience/kmath/expressions/AutoDiffValue { public final class space/kscience/kmath/expressions/DerivationResult { public fun (Ljava/lang/Object;Ljava/util/Map;Lspace/kscience/kmath/operations/Field;)V - public final fun derivative (Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; + public final fun derivative (Lspace/kscience/kmath/misc/Symbol;)Ljava/lang/Object; public final fun div ()Ljava/lang/Object; public final fun getContext ()Lspace/kscience/kmath/operations/Field; public final fun getValue ()Ljava/lang/Object; @@ -27,7 +27,7 @@ public abstract interface class space/kscience/kmath/expressions/DifferentiableE public final class space/kscience/kmath/expressions/DifferentiableExpressionKt { public static final fun derivative (Lspace/kscience/kmath/expressions/DifferentiableExpression;Ljava/lang/String;)Lspace/kscience/kmath/expressions/Expression; public static final fun derivative (Lspace/kscience/kmath/expressions/DifferentiableExpression;Ljava/util/List;)Lspace/kscience/kmath/expressions/Expression; - public static final fun derivative (Lspace/kscience/kmath/expressions/DifferentiableExpression;[Lspace/kscience/kmath/expressions/Symbol;)Lspace/kscience/kmath/expressions/Expression; + public static final fun derivative (Lspace/kscience/kmath/expressions/DifferentiableExpression;[Lspace/kscience/kmath/misc/Symbol;)Lspace/kscience/kmath/expressions/Expression; } public abstract interface class space/kscience/kmath/expressions/Expression { @@ -36,7 +36,7 @@ public abstract interface class space/kscience/kmath/expressions/Expression { public abstract interface class space/kscience/kmath/expressions/ExpressionAlgebra : space/kscience/kmath/operations/Algebra { public abstract fun bindSymbol (Ljava/lang/String;)Ljava/lang/Object; - public abstract fun bindSymbolOrNull (Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; + public abstract fun bindSymbolOrNull (Lspace/kscience/kmath/misc/Symbol;)Ljava/lang/Object; public abstract fun const (Ljava/lang/Object;)Ljava/lang/Object; } @@ -56,18 +56,17 @@ public final class space/kscience/kmath/expressions/ExpressionBuildersKt { } public final class space/kscience/kmath/expressions/ExpressionKt { - public static final fun bindSymbol (Lspace/kscience/kmath/expressions/ExpressionAlgebra;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; + public static final fun bindSymbol (Lspace/kscience/kmath/expressions/ExpressionAlgebra;Lspace/kscience/kmath/misc/Symbol;)Ljava/lang/Object; public static final fun binding (Lspace/kscience/kmath/expressions/ExpressionAlgebra;)Lkotlin/properties/ReadOnlyProperty; public static final fun callByString (Lspace/kscience/kmath/expressions/Expression;[Lkotlin/Pair;)Ljava/lang/Object; public static final fun callBySymbol (Lspace/kscience/kmath/expressions/Expression;[Lkotlin/Pair;)Ljava/lang/Object; - public static final fun getSymbol ()Lkotlin/properties/ReadOnlyProperty; public static final fun invoke (Lspace/kscience/kmath/expressions/Expression;)Ljava/lang/Object; } public abstract class space/kscience/kmath/expressions/FirstDerivativeExpression : space/kscience/kmath/expressions/DifferentiableExpression { public fun ()V public final fun derivativeOrNull (Ljava/util/List;)Lspace/kscience/kmath/expressions/Expression; - public abstract fun derivativeOrNull (Lspace/kscience/kmath/expressions/Symbol;)Lspace/kscience/kmath/expressions/Expression; + public abstract fun derivativeOrNull (Lspace/kscience/kmath/misc/Symbol;)Lspace/kscience/kmath/expressions/Expression; } public abstract class space/kscience/kmath/expressions/FunctionalExpressionAlgebra : space/kscience/kmath/expressions/ExpressionAlgebra { @@ -77,8 +76,8 @@ public abstract class space/kscience/kmath/expressions/FunctionalExpressionAlgeb public fun binaryOperationFunction (Ljava/lang/String;)Lkotlin/jvm/functions/Function2; public synthetic fun bindSymbol (Ljava/lang/String;)Ljava/lang/Object; public fun bindSymbol (Ljava/lang/String;)Lspace/kscience/kmath/expressions/Expression; - public synthetic fun bindSymbolOrNull (Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public fun bindSymbolOrNull (Lspace/kscience/kmath/expressions/Symbol;)Lspace/kscience/kmath/expressions/Expression; + public synthetic fun bindSymbolOrNull (Lspace/kscience/kmath/misc/Symbol;)Ljava/lang/Object; + public fun bindSymbolOrNull (Lspace/kscience/kmath/misc/Symbol;)Lspace/kscience/kmath/expressions/Expression; public synthetic fun const (Ljava/lang/Object;)Ljava/lang/Object; public fun const (Ljava/lang/Object;)Lspace/kscience/kmath/expressions/Expression; public final fun getAlgebra ()Lspace/kscience/kmath/operations/Algebra; @@ -203,7 +202,7 @@ public class space/kscience/kmath/expressions/FunctionalExpressionRing : space/k public final class space/kscience/kmath/expressions/SimpleAutoDiffExpression : space/kscience/kmath/expressions/FirstDerivativeExpression { public fun (Lspace/kscience/kmath/operations/Field;Lkotlin/jvm/functions/Function1;)V - public fun derivativeOrNull (Lspace/kscience/kmath/expressions/Symbol;)Lspace/kscience/kmath/expressions/Expression; + public fun derivativeOrNull (Lspace/kscience/kmath/misc/Symbol;)Lspace/kscience/kmath/expressions/Expression; public final fun getField ()Lspace/kscience/kmath/operations/Field; public final fun getFunction ()Lkotlin/jvm/functions/Function1; public fun invoke (Ljava/util/Map;)Ljava/lang/Object; @@ -264,8 +263,8 @@ public class space/kscience/kmath/expressions/SimpleAutoDiffField : space/kscien public fun binaryOperationFunction (Ljava/lang/String;)Lkotlin/jvm/functions/Function2; public synthetic fun bindSymbol (Ljava/lang/String;)Ljava/lang/Object; public fun bindSymbol (Ljava/lang/String;)Lspace/kscience/kmath/expressions/AutoDiffValue; - public synthetic fun bindSymbolOrNull (Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public fun bindSymbolOrNull (Lspace/kscience/kmath/expressions/Symbol;)Lspace/kscience/kmath/expressions/AutoDiffValue; + public synthetic fun bindSymbolOrNull (Lspace/kscience/kmath/misc/Symbol;)Ljava/lang/Object; + public fun bindSymbolOrNull (Lspace/kscience/kmath/misc/Symbol;)Lspace/kscience/kmath/expressions/AutoDiffValue; public synthetic fun const (Ljava/lang/Object;)Ljava/lang/Object; public fun const (Ljava/lang/Object;)Lspace/kscience/kmath/expressions/AutoDiffValue; public final fun const (Lkotlin/jvm/functions/Function1;)Lspace/kscience/kmath/expressions/AutoDiffValue; @@ -332,7 +331,7 @@ public final class space/kscience/kmath/expressions/SimpleAutoDiffKt { public static final fun cos (Lspace/kscience/kmath/expressions/SimpleAutoDiffField;Lspace/kscience/kmath/expressions/AutoDiffValue;)Lspace/kscience/kmath/expressions/AutoDiffValue; public static final fun cosh (Lspace/kscience/kmath/expressions/SimpleAutoDiffField;Lspace/kscience/kmath/expressions/AutoDiffValue;)Lspace/kscience/kmath/expressions/AutoDiffValue; public static final fun exp (Lspace/kscience/kmath/expressions/SimpleAutoDiffField;Lspace/kscience/kmath/expressions/AutoDiffValue;)Lspace/kscience/kmath/expressions/AutoDiffValue; - public static final fun grad (Lspace/kscience/kmath/expressions/DerivationResult;[Lspace/kscience/kmath/expressions/Symbol;)Lspace/kscience/kmath/structures/Buffer; + public static final fun grad (Lspace/kscience/kmath/expressions/DerivationResult;[Lspace/kscience/kmath/misc/Symbol;)Lspace/kscience/kmath/structures/Buffer; public static final fun ln (Lspace/kscience/kmath/expressions/SimpleAutoDiffField;Lspace/kscience/kmath/expressions/AutoDiffValue;)Lspace/kscience/kmath/expressions/AutoDiffValue; public static final fun pow (Lspace/kscience/kmath/expressions/SimpleAutoDiffField;Lspace/kscience/kmath/expressions/AutoDiffValue;D)Lspace/kscience/kmath/expressions/AutoDiffValue; public static final fun pow (Lspace/kscience/kmath/expressions/SimpleAutoDiffField;Lspace/kscience/kmath/expressions/AutoDiffValue;I)Lspace/kscience/kmath/expressions/AutoDiffValue; @@ -348,79 +347,13 @@ public final class space/kscience/kmath/expressions/SimpleAutoDiffKt { public static final fun tanh (Lspace/kscience/kmath/expressions/SimpleAutoDiffField;Lspace/kscience/kmath/expressions/AutoDiffValue;)Lspace/kscience/kmath/expressions/AutoDiffValue; } -public final class space/kscience/kmath/expressions/SimpleSymbolIndexer : space/kscience/kmath/expressions/SymbolIndexer { - public static final synthetic fun box-impl (Ljava/util/List;)Lspace/kscience/kmath/expressions/SimpleSymbolIndexer; - public static fun constructor-impl (Ljava/util/List;)Ljava/util/List; - public fun equals (Ljava/lang/Object;)Z - public static fun equals-impl (Ljava/util/List;Ljava/lang/Object;)Z - public static final fun equals-impl0 (Ljava/util/List;Ljava/util/List;)Z - public fun get (Ljava/util/List;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public fun get (Lspace/kscience/kmath/nd/Structure2D;Lspace/kscience/kmath/expressions/Symbol;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public fun get (Lspace/kscience/kmath/structures/Buffer;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public fun get ([DLspace/kscience/kmath/expressions/Symbol;)D - public fun get ([Ljava/lang/Object;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public static fun get-impl (Ljava/util/List;Ljava/util/List;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public static fun get-impl (Ljava/util/List;Lspace/kscience/kmath/nd/Structure2D;Lspace/kscience/kmath/expressions/Symbol;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public static fun get-impl (Ljava/util/List;Lspace/kscience/kmath/structures/Buffer;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public static fun get-impl (Ljava/util/List;[DLspace/kscience/kmath/expressions/Symbol;)D - public static fun get-impl (Ljava/util/List;[Ljava/lang/Object;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public fun getSymbols ()Ljava/util/List; - public fun hashCode ()I - public static fun hashCode-impl (Ljava/util/List;)I - public fun indexOf (Lspace/kscience/kmath/expressions/Symbol;)I - public static fun indexOf-impl (Ljava/util/List;Lspace/kscience/kmath/expressions/Symbol;)I - public fun toDoubleArray (Ljava/util/Map;)[D - public static fun toDoubleArray-impl (Ljava/util/List;Ljava/util/Map;)[D - public fun toList (Ljava/util/Map;)Ljava/util/List; - public static fun toList-impl (Ljava/util/List;Ljava/util/Map;)Ljava/util/List; - public fun toMap ([D)Ljava/util/Map; - public static fun toMap-impl (Ljava/util/List;[D)Ljava/util/Map; - public fun toPoint (Ljava/util/Map;Lkotlin/jvm/functions/Function2;)Lspace/kscience/kmath/structures/Buffer; - public static fun toPoint-impl (Ljava/util/List;Ljava/util/Map;Lkotlin/jvm/functions/Function2;)Lspace/kscience/kmath/structures/Buffer; - public fun toString ()Ljava/lang/String; - public static fun toString-impl (Ljava/util/List;)Ljava/lang/String; - public final synthetic fun unbox-impl ()Ljava/util/List; -} - -public final class space/kscience/kmath/expressions/StringSymbol : space/kscience/kmath/expressions/Symbol { - public static final synthetic fun box-impl (Ljava/lang/String;)Lspace/kscience/kmath/expressions/StringSymbol; - public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String; - public fun equals (Ljava/lang/Object;)Z - public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z - public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z - public fun getIdentity ()Ljava/lang/String; - public fun hashCode ()I - public static fun hashCode-impl (Ljava/lang/String;)I - public fun toString ()Ljava/lang/String; - public static fun toString-impl (Ljava/lang/String;)Ljava/lang/String; - public final synthetic fun unbox-impl ()Ljava/lang/String; -} - -public abstract interface class space/kscience/kmath/expressions/Symbol { - public abstract fun getIdentity ()Ljava/lang/String; -} - -public abstract interface class space/kscience/kmath/expressions/SymbolIndexer { - public abstract fun get (Ljava/util/List;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public abstract fun get (Lspace/kscience/kmath/nd/Structure2D;Lspace/kscience/kmath/expressions/Symbol;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public abstract fun get (Lspace/kscience/kmath/structures/Buffer;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public abstract fun get ([DLspace/kscience/kmath/expressions/Symbol;)D - public abstract fun get ([Ljava/lang/Object;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public abstract fun getSymbols ()Ljava/util/List; - public abstract fun indexOf (Lspace/kscience/kmath/expressions/Symbol;)I - public abstract fun toDoubleArray (Ljava/util/Map;)[D - public abstract fun toList (Ljava/util/Map;)Ljava/util/List; - public abstract fun toMap ([D)Ljava/util/Map; - public abstract fun toPoint (Ljava/util/Map;Lkotlin/jvm/functions/Function2;)Lspace/kscience/kmath/structures/Buffer; -} - public final class space/kscience/kmath/expressions/SymbolIndexer$DefaultImpls { - public static fun get (Lspace/kscience/kmath/expressions/SymbolIndexer;Ljava/util/List;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public static fun get (Lspace/kscience/kmath/expressions/SymbolIndexer;Lspace/kscience/kmath/nd/Structure2D;Lspace/kscience/kmath/expressions/Symbol;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public static fun get (Lspace/kscience/kmath/expressions/SymbolIndexer;Lspace/kscience/kmath/structures/Buffer;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public static fun get (Lspace/kscience/kmath/expressions/SymbolIndexer;[DLspace/kscience/kmath/expressions/Symbol;)D - public static fun get (Lspace/kscience/kmath/expressions/SymbolIndexer;[Ljava/lang/Object;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; - public static fun indexOf (Lspace/kscience/kmath/expressions/SymbolIndexer;Lspace/kscience/kmath/expressions/Symbol;)I + public static fun get (Lspace/kscience/kmath/expressions/SymbolIndexer;Ljava/util/List;Lspace/kscience/kmath/misc/Symbol;)Ljava/lang/Object; + public static fun get (Lspace/kscience/kmath/expressions/SymbolIndexer;Lspace/kscience/kmath/nd/Structure2D;Lspace/kscience/kmath/misc/Symbol;Lspace/kscience/kmath/misc/Symbol;)Ljava/lang/Object; + public static fun get (Lspace/kscience/kmath/expressions/SymbolIndexer;Lspace/kscience/kmath/structures/Buffer;Lspace/kscience/kmath/misc/Symbol;)Ljava/lang/Object; + public static fun get (Lspace/kscience/kmath/expressions/SymbolIndexer;[DLspace/kscience/kmath/misc/Symbol;)D + public static fun get (Lspace/kscience/kmath/expressions/SymbolIndexer;[Ljava/lang/Object;Lspace/kscience/kmath/misc/Symbol;)Ljava/lang/Object; + public static fun indexOf (Lspace/kscience/kmath/expressions/SymbolIndexer;Lspace/kscience/kmath/misc/Symbol;)I public static fun toDoubleArray (Lspace/kscience/kmath/expressions/SymbolIndexer;Ljava/util/Map;)[D public static fun toList (Lspace/kscience/kmath/expressions/SymbolIndexer;Ljava/util/Map;)Ljava/util/List; public static fun toMap (Lspace/kscience/kmath/expressions/SymbolIndexer;[D)Ljava/util/Map; @@ -428,8 +361,6 @@ public final class space/kscience/kmath/expressions/SymbolIndexer$DefaultImpls { } public final class space/kscience/kmath/expressions/SymbolIndexerKt { - public static final fun withSymbols (Ljava/util/Collection;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public static final fun withSymbols ([Lspace/kscience/kmath/expressions/Symbol;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; } public final class space/kscience/kmath/linear/BufferedLinearSpace : space/kscience/kmath/linear/LinearSpace { @@ -672,9 +603,58 @@ public final class space/kscience/kmath/misc/CumulativeKt { public static final fun cumulativeSumOfLong (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; } +public final class space/kscience/kmath/misc/NDStructureColumn : space/kscience/kmath/structures/Buffer { + public fun (Lspace/kscience/kmath/nd/Structure2D;I)V + public fun get (I)Ljava/lang/Object; + public final fun getColumn ()I + public fun getSize ()I + public final fun getStructure ()Lspace/kscience/kmath/nd/Structure2D; + public fun iterator ()Ljava/util/Iterator; +} + +public final class space/kscience/kmath/misc/StringSymbol : space/kscience/kmath/misc/Symbol { + public static final synthetic fun box-impl (Ljava/lang/String;)Lspace/kscience/kmath/misc/StringSymbol; + public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String; + public fun equals (Ljava/lang/Object;)Z + public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z + public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z + public fun getIdentity ()Ljava/lang/String; + public fun hashCode ()I + public static fun hashCode-impl (Ljava/lang/String;)I + public fun toString ()Ljava/lang/String; + public static fun toString-impl (Ljava/lang/String;)Ljava/lang/String; + public final synthetic fun unbox-impl ()Ljava/lang/String; +} + +public abstract interface class space/kscience/kmath/misc/Symbol { + public static final field Companion Lspace/kscience/kmath/misc/Symbol$Companion; + public abstract fun getIdentity ()Ljava/lang/String; +} + +public final class space/kscience/kmath/misc/Symbol$Companion { + public final fun getX-tWtZOCg ()Ljava/lang/String; + public final fun getY-tWtZOCg ()Ljava/lang/String; + public final fun getZ-tWtZOCg ()Ljava/lang/String; +} + +public final class space/kscience/kmath/misc/SymbolKt { + public static final fun getSymbol ()Lkotlin/properties/ReadOnlyProperty; +} + public abstract interface annotation class space/kscience/kmath/misc/UnstableKMathAPI : java/lang/annotation/Annotation { } +public final class space/kscience/kmath/misc/XYPointSet$DefaultImpls { + public static fun get (Lspace/kscience/kmath/misc/XYPointSet;Lspace/kscience/kmath/misc/Symbol;)Lspace/kscience/kmath/structures/Buffer; +} + +public final class space/kscience/kmath/misc/XYPointSetKt { +} + +public final class space/kscience/kmath/misc/XYZPointSet$DefaultImpls { + public static fun get (Lspace/kscience/kmath/misc/XYZPointSet;Lspace/kscience/kmath/misc/Symbol;)Lspace/kscience/kmath/structures/Buffer; +} + public abstract interface class space/kscience/kmath/nd/AlgebraND { public static final field Companion Lspace/kscience/kmath/nd/AlgebraND$Companion; public abstract fun combine (Lspace/kscience/kmath/nd/StructureND;Lspace/kscience/kmath/nd/StructureND;Lkotlin/jvm/functions/Function3;)Lspace/kscience/kmath/nd/StructureND; @@ -1180,7 +1160,7 @@ public final class space/kscience/kmath/operations/AlgebraExtensionsKt { } public final class space/kscience/kmath/operations/AlgebraKt { - public static final fun bindSymbol (Lspace/kscience/kmath/operations/Algebra;Lspace/kscience/kmath/expressions/Symbol;)Ljava/lang/Object; + public static final fun bindSymbol (Lspace/kscience/kmath/operations/Algebra;Lspace/kscience/kmath/misc/Symbol;)Ljava/lang/Object; public static final fun invoke (Lspace/kscience/kmath/operations/Algebra;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; } @@ -2119,6 +2099,7 @@ public final class space/kscience/kmath/structures/BufferKt { public final class space/kscience/kmath/structures/BufferOperationKt { public static final fun asIterable (Lspace/kscience/kmath/structures/Buffer;)Ljava/lang/Iterable; public static final fun asSequence (Lspace/kscience/kmath/structures/Buffer;)Lkotlin/sequences/Sequence; + public static final fun fold (Lspace/kscience/kmath/structures/Buffer;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public static final fun toList (Lspace/kscience/kmath/structures/Buffer;)Ljava/util/List; } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/DifferentiableExpression.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/DifferentiableExpression.kt index 5cbc4dbf4..1f0ceaec3 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/DifferentiableExpression.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/DifferentiableExpression.kt @@ -1,5 +1,8 @@ package space.kscience.kmath.expressions +import space.kscience.kmath.misc.StringSymbol +import space.kscience.kmath.misc.Symbol + /** * Represents expression which structure can be differentiated. * diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/Expression.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/Expression.kt index 5ba24aa62..7918f199e 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/Expression.kt @@ -1,26 +1,11 @@ package space.kscience.kmath.expressions +import space.kscience.kmath.misc.StringSymbol +import space.kscience.kmath.misc.Symbol import space.kscience.kmath.operations.Algebra import kotlin.jvm.JvmName import kotlin.properties.ReadOnlyProperty -/** - * A marker interface for a symbol. A symbol mus have an identity - */ -public interface Symbol { - /** - * Identity object for the symbol. Two symbols with the same identity are considered to be the same symbol. - */ - public val identity: String -} - -/** - * A [Symbol] with a [String] identity - */ -public inline class StringSymbol(override val identity: String) : Symbol { - override fun toString(): String = identity -} - /** * An elementary function that could be invoked on a map of arguments. * @@ -92,13 +77,6 @@ public interface ExpressionAlgebra : Algebra { public fun ExpressionAlgebra.bindSymbol(symbol: Symbol): E = bindSymbolOrNull(symbol) ?: error("Symbol $symbol could not be bound to $this") -/** - * A delegate to create a symbol with a string identity in this scope - */ -public val symbol: ReadOnlyProperty = ReadOnlyProperty { _, property -> - StringSymbol(property.name) -} - /** * Bind a symbol by name inside the [ExpressionAlgebra] */ diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt index cca75754f..ebd9e7f22 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt @@ -1,5 +1,6 @@ package space.kscience.kmath.expressions +import space.kscience.kmath.misc.Symbol import space.kscience.kmath.operations.* /** diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt index 4b0d402ed..d9be4a92e 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt @@ -1,6 +1,7 @@ package space.kscience.kmath.expressions import space.kscience.kmath.linear.Point +import space.kscience.kmath.misc.Symbol import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.operations.* import space.kscience.kmath.structures.asBuffer diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SymbolIndexer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SymbolIndexer.kt index 580acaafb..4db4b5828 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SymbolIndexer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SymbolIndexer.kt @@ -1,6 +1,8 @@ package space.kscience.kmath.expressions import space.kscience.kmath.linear.Point +import space.kscience.kmath.misc.Symbol +import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.structures.BufferFactory @@ -8,6 +10,7 @@ import space.kscience.kmath.structures.BufferFactory * An environment to easy transform indexed variables to symbols and back. * TODO requires multi-receivers to be beautiful */ +@UnstableKMathAPI public interface SymbolIndexer { public val symbols: List public fun indexOf(symbol: Symbol): Int = symbols.indexOf(symbol) @@ -49,13 +52,16 @@ public interface SymbolIndexer { public fun Map.toDoubleArray(): DoubleArray = DoubleArray(symbols.size) { getValue(symbols[it]) } } +@UnstableKMathAPI public inline class SimpleSymbolIndexer(override val symbols: List) : SymbolIndexer /** * Execute the block with symbol indexer based on given symbol order */ +@UnstableKMathAPI public inline fun withSymbols(vararg symbols: Symbol, block: SymbolIndexer.() -> R): R = with(SimpleSymbolIndexer(symbols.toList()), block) +@UnstableKMathAPI public inline fun withSymbols(symbols: Collection, block: SymbolIndexer.() -> R): R = with(SimpleSymbolIndexer(symbols.toList()), block) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/ColumnarData.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/ColumnarData.kt new file mode 100644 index 000000000..ed5a7e8f0 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/ColumnarData.kt @@ -0,0 +1,15 @@ +package space.kscience.kmath.misc + +import space.kscience.kmath.structures.Buffer + +/** + * A column-based data set with all columns of the same size (not necessary fixed in time). + * The column could be retrieved by a [get] operation. + */ +@UnstableKMathAPI +public interface ColumnarData { + public val size: Int + + public operator fun get(symbol: Symbol): Buffer +} + diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/Symbol.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/Symbol.kt new file mode 100644 index 000000000..2c9774b6a --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/Symbol.kt @@ -0,0 +1,34 @@ +package space.kscience.kmath.misc + +import kotlin.properties.ReadOnlyProperty + +/** + * A marker interface for a symbol. A symbol mus have an identity + */ +public interface Symbol { + /** + * Identity object for the symbol. Two symbols with the same identity are considered to be the same symbol. + */ + public val identity: String + + public companion object{ + public val x: StringSymbol = StringSymbol("x") + public val y: StringSymbol = StringSymbol("y") + public val z: StringSymbol = StringSymbol("z") + } +} + +/** + * A [Symbol] with a [String] identity + */ +public inline class StringSymbol(override val identity: String) : Symbol { + override fun toString(): String = identity +} + + +/** + * A delegate to create a symbol with a string identity in this scope + */ +public val symbol: ReadOnlyProperty = ReadOnlyProperty { _, property -> + StringSymbol(property.name) +} diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/XYPointSet.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/XYPointSet.kt similarity index 57% rename from kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/XYPointSet.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/XYPointSet.kt index 1ff7b6351..bc9052348 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/XYPointSet.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/XYPointSet.kt @@ -1,22 +1,32 @@ -package space.kscience.kmath.interpolation +package space.kscience.kmath.misc import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.structures.Buffer -public interface XYPointSet { - public val size: Int +@UnstableKMathAPI +public interface XYPointSet : ColumnarData { public val x: Buffer public val y: Buffer + + override fun get(symbol: Symbol): Buffer = when (symbol) { + Symbol.x -> x + Symbol.y -> y + else -> error("A column for symbol $symbol not found") + } } -public interface XYZPointSet : XYPointSet { +@UnstableKMathAPI +public interface XYZPointSet : XYPointSet { public val z: Buffer + + override fun get(symbol: Symbol): Buffer = when (symbol) { + Symbol.x -> x + Symbol.y -> y + Symbol.z -> z + else -> error("A column for symbol $symbol not found") + } } -internal fun > insureSorted(points: XYPointSet) { - for (i in 0 until points.size - 1) - require(points.x[i + 1] > points.x[i]) { "Input data is not sorted at index $i" } -} public class NDStructureColumn(public val structure: Structure2D, public val column: Int) : Buffer { public override val size: Int @@ -30,22 +40,23 @@ public class NDStructureColumn(public val structure: Structure2D, public v public override operator fun iterator(): Iterator = sequence { repeat(size) { yield(get(it)) } }.iterator() } -public class BufferXYPointSet( +@UnstableKMathAPI +public class BufferXYPointSet( public override val x: Buffer, public override val y: Buffer, -) : XYPointSet { - public override val size: Int - get() = x.size +) : XYPointSet { + public override val size: Int get() = x.size init { require(x.size == y.size) { "Sizes of x and y buffers should be the same" } } } -public fun Structure2D.asXYPointSet(): XYPointSet { +@UnstableKMathAPI +public fun Structure2D.asXYPointSet(): XYPointSet { require(shape[1] == 2) { "Structure second dimension should be of size 2" } - return object : XYPointSet { + return object : XYPointSet { override val size: Int get() = this@asXYPointSet.shape[0] override val x: Buffer get() = NDStructureColumn(this@asXYPointSet, 0) override val y: Buffer get() = NDStructureColumn(this@asXYPointSet, 1) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt index 9f57bc4c1..04234205f 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt @@ -1,6 +1,6 @@ package space.kscience.kmath.operations -import space.kscience.kmath.expressions.Symbol +import space.kscience.kmath.misc.Symbol /** * Stub for DSL the [Algebra] is. diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferOperation.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferOperation.kt index 4355ba17f..5f8bbe21f 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferOperation.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferOperation.kt @@ -70,6 +70,15 @@ public inline fun Buffer.mapIndexed( crossinline block: (index: Int, value: T) -> R, ): Buffer = bufferFactory(size) { block(it, get(it)) } +/** + * Fold given buffer according to [operation] + */ +public inline fun Buffer.fold(initial: R, operation: (acc: R, T) -> R): R { + var accumulator = initial + for (index in this.indices) accumulator = operation(accumulator, get(index)) + return accumulator +} + /** * Zip two buffers using given [transform]. */ diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/ExpressionFieldTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/ExpressionFieldTest.kt index b1be0c392..61b1d03f0 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/ExpressionFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/ExpressionFieldTest.kt @@ -1,5 +1,6 @@ package space.kscience.kmath.expressions +import space.kscience.kmath.misc.symbol import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.invoke import kotlin.test.Test diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/SimpleAutoDiffTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/SimpleAutoDiffTest.kt index ca7aca905..666db13d8 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/SimpleAutoDiffTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/SimpleAutoDiffTest.kt @@ -1,5 +1,7 @@ package space.kscience.kmath.expressions +import space.kscience.kmath.misc.Symbol +import space.kscience.kmath.misc.symbol import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.asBuffer diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealVector.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealVector.kt index 433e3c756..6b059ef56 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealVector.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealVector.kt @@ -6,17 +6,13 @@ import space.kscience.kmath.operations.Norm import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.MutableBuffer.Companion.double import space.kscience.kmath.structures.asBuffer -import space.kscience.kmath.structures.asIterable +import space.kscience.kmath.structures.fold import space.kscience.kmath.structures.indices import kotlin.math.pow import kotlin.math.sqrt public typealias DoubleVector = Point -public object VectorL2Norm : Norm, Double> { - override fun norm(arg: Point): Double = sqrt(arg.asIterable().sumByDouble(Number::toDouble)) -} - @Suppress("FunctionName") public fun DoubleVector(vararg doubles: Double): DoubleVector = doubles.asBuffer() @@ -102,4 +98,10 @@ public fun DoubleVector.sum(): Double { res += get(i) } return res -} \ No newline at end of file +} + +public object VectorL2Norm : Norm { + override fun norm(arg: DoubleVector): Double = sqrt(arg.fold(0.0) { acc: Double, d: Double -> acc + d.pow(2) }) +} + +public val DoubleVector.norm: Double get() = VectorL2Norm.norm(this) \ No newline at end of file diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/grids.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/grids.kt index 446a3d9cb..7f02c7db3 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/grids.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/grids.kt @@ -11,7 +11,6 @@ public val ClosedFloatingPointRange.length: Double get() = endInclusive * Create a Buffer-based grid with equally distributed [numberOfPoints] points. The range could be increasing or decreasing. * If range has a zero size, then the buffer consisting of [numberOfPoints] equal values is returned. */ -@UnstableKMathAPI public fun Buffer.Companion.fromRange(range: ClosedFloatingPointRange, numberOfPoints: Int): DoubleBuffer { require(numberOfPoints >= 2) { "Number of points in grid must be more than 1" } val normalizedRange = when { @@ -20,23 +19,22 @@ public fun Buffer.Companion.fromRange(range: ClosedFloatingPointRange, n else -> return DoubleBuffer(numberOfPoints) { range.start } } val step = normalizedRange.length / (numberOfPoints - 1) - return DoubleBuffer(numberOfPoints) { normalizedRange.start + step * it / (numberOfPoints - 1) } + return DoubleBuffer(numberOfPoints) { normalizedRange.start + step * it } } /** * Create a Buffer-based grid with equally distributed points with a fixed [step]. The range could be increasing or decreasing. * If the step is larger than the range size, single point is returned. */ -@UnstableKMathAPI -public fun Buffer.Companion.fromRange(range: ClosedFloatingPointRange, step: Double): DoubleBuffer { +public fun Buffer.Companion.withFixedStep(range: ClosedFloatingPointRange, step: Double): DoubleBuffer { require(step > 0) { "The grid step must be positive" } val normalizedRange = when { range.endInclusive > range.start -> range range.endInclusive < range.start -> range.endInclusive..range.start else -> return DoubleBuffer(range.start) } - val numberOfPoints = floor(normalizedRange.length / step).toInt() - return DoubleBuffer(numberOfPoints) { normalizedRange.start + step * it / (numberOfPoints - 1) } + val numberOfPoints = floor(normalizedRange.length / step).toInt() + 1 + return DoubleBuffer(numberOfPoints) { normalizedRange.start + step * it } } /** @@ -48,4 +46,4 @@ public fun Buffer.Companion.fromRange(range: ClosedFloatingPointRange, s * If step is negative, the same goes from upper boundary downwards */ @UnstableKMathAPI -public infix fun ClosedFloatingPointRange.step(step: Double): DoubleBuffer = Buffer.fromRange(this, step) \ No newline at end of file +public infix fun ClosedFloatingPointRange.step(step: Double): DoubleBuffer = Buffer.withFixedStep(this, step) \ No newline at end of file diff --git a/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/GridTest.kt b/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/GridTest.kt index 5a644c8f9..91ee517ab 100644 --- a/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/GridTest.kt +++ b/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/GridTest.kt @@ -1,13 +1,18 @@ package kaceince.kmath.real +import space.kscience.kmath.real.DoubleVector +import space.kscience.kmath.real.minus +import space.kscience.kmath.real.norm import space.kscience.kmath.real.step import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertTrue class GridTest { @Test fun testStepGrid() { val grid = 0.0..1.0 step 0.2 assertEquals(6, grid.size) + assertTrue { (grid - DoubleVector(0.0, 0.2, 0.4, 0.6, 0.8, 1.0)).norm < 1e-4 } } } \ No newline at end of file diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/Interpolator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/Interpolator.kt index fa978a9bc..864431d7a 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/Interpolator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/Interpolator.kt @@ -1,27 +1,32 @@ +@file:OptIn(UnstableKMathAPI::class) package space.kscience.kmath.interpolation import space.kscience.kmath.functions.PiecewisePolynomial import space.kscience.kmath.functions.value +import space.kscience.kmath.misc.BufferXYPointSet +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.misc.XYPointSet import space.kscience.kmath.operations.Ring import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.asBuffer -public fun interface Interpolator { - public fun interpolate(points: XYPointSet): (X) -> Y +public fun interface Interpolator { + public fun interpolate(points: XYPointSet): (X) -> Y } -public interface PolynomialInterpolator> : Interpolator { +public interface PolynomialInterpolator> : Interpolator { public val algebra: Ring public fun getDefaultValue(): T = error("Out of bounds") - public fun interpolatePolynomials(points: XYPointSet): PiecewisePolynomial + public fun interpolatePolynomials(points: XYPointSet): PiecewisePolynomial - override fun interpolate(points: XYPointSet): (T) -> T = { x -> + override fun interpolate(points: XYPointSet): (T) -> T = { x -> interpolatePolynomials(points).value(algebra, x) ?: getDefaultValue() } } + public fun > PolynomialInterpolator.interpolatePolynomials( x: Buffer, y: Buffer, diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/LinearInterpolator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/LinearInterpolator.kt index c939384e3..89a242ece 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/LinearInterpolator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/LinearInterpolator.kt @@ -3,14 +3,23 @@ package space.kscience.kmath.interpolation import space.kscience.kmath.functions.OrderedPiecewisePolynomial import space.kscience.kmath.functions.PiecewisePolynomial import space.kscience.kmath.functions.Polynomial +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.misc.XYPointSet import space.kscience.kmath.operations.Field import space.kscience.kmath.operations.invoke +@OptIn(UnstableKMathAPI::class) +internal fun > insureSorted(points: XYPointSet<*, T, *>) { + for (i in 0 until points.size - 1) + require(points.x[i + 1] > points.x[i]) { "Input data is not sorted at index $i" } +} + /** * Reference JVM implementation: https://github.com/apache/commons-math/blob/master/src/main/java/org/apache/commons/math4/analysis/interpolation/LinearInterpolator.java */ public class LinearInterpolator>(public override val algebra: Field) : PolynomialInterpolator { - public override fun interpolatePolynomials(points: XYPointSet): PiecewisePolynomial = algebra { + @OptIn(UnstableKMathAPI::class) + public override fun interpolatePolynomials(points: XYPointSet): PiecewisePolynomial = algebra { require(points.size > 0) { "Point array should not be empty" } insureSorted(points) diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt index ddbe743f0..8b6db8cf9 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt @@ -3,6 +3,8 @@ package space.kscience.kmath.interpolation import space.kscience.kmath.functions.OrderedPiecewisePolynomial import space.kscience.kmath.functions.PiecewisePolynomial import space.kscience.kmath.functions.Polynomial +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.misc.XYPointSet import space.kscience.kmath.operations.Field import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.MutableBufferFactory @@ -17,7 +19,8 @@ public class SplineInterpolator>( ) : PolynomialInterpolator { //TODO possibly optimize zeroed buffers - public override fun interpolatePolynomials(points: XYPointSet): PiecewisePolynomial = algebra { + @OptIn(UnstableKMathAPI::class) + public override fun interpolatePolynomials(points: XYPointSet): PiecewisePolynomial = algebra { require(points.size >= 3) { "Can't use spline interpolator with less than 3 points" } insureSorted(points) // Number of intervals. The number of data points is n + 1. diff --git a/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/DifferentiableMstExpression.kt b/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/DifferentiableMstExpression.kt index 39a7248b4..fe27b7e4d 100644 --- a/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/DifferentiableMstExpression.kt +++ b/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/DifferentiableMstExpression.kt @@ -5,7 +5,7 @@ import space.kscience.kmath.ast.MST import space.kscience.kmath.ast.MstAlgebra import space.kscience.kmath.ast.MstExpression import space.kscience.kmath.expressions.DifferentiableExpression -import space.kscience.kmath.expressions.Symbol +import space.kscience.kmath.misc.Symbol import space.kscience.kmath.operations.NumericAlgebra /** diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/DataFit.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/DataFit.kt index e9a5dea86..70dd8417c 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/DataFit.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/DataFit.kt @@ -1,8 +1,8 @@ package space.kscience.kmath.optimization import space.kscience.kmath.expressions.DifferentiableExpression -import space.kscience.kmath.expressions.StringSymbol -import space.kscience.kmath.expressions.Symbol +import space.kscience.kmath.misc.StringSymbol +import space.kscience.kmath.misc.Symbol import space.kscience.kmath.structures.Buffer public interface DataFit : Optimization { diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/FunctionOptimization.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/FunctionOptimization.kt index bf37f5c64..02aa9e9bb 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/FunctionOptimization.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/FunctionOptimization.kt @@ -1,6 +1,11 @@ package space.kscience.kmath.optimization -import space.kscience.kmath.expressions.* +import space.kscience.kmath.expressions.AutoDiffProcessor +import space.kscience.kmath.expressions.DifferentiableExpression +import space.kscience.kmath.expressions.Expression +import space.kscience.kmath.expressions.ExpressionAlgebra +import space.kscience.kmath.misc.StringSymbol +import space.kscience.kmath.misc.Symbol import space.kscience.kmath.operations.ExtendedField import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.indices diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/Optimization.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/Optimization.kt index 370274b41..0b13e07ba 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/Optimization.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/Optimization.kt @@ -1,6 +1,6 @@ package space.kscience.kmath.optimization -import space.kscience.kmath.expressions.Symbol +import space.kscience.kmath.misc.Symbol public interface OptimizationFeature From 67aa173927c3f50dcea398d201236c54ad5fe4e0 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Mon, 22 Mar 2021 16:11:54 +0700 Subject: [PATCH 27/31] Some documentation updates --- .../kscience/kmath/domains/DoubleDomain.kt | 1 - .../kscience/kmath/linear/LinearSolver.kt | 14 ++++++-- .../kscience/kmath/linear/LinearSpace.kt | 2 ++ .../kscience/kmath/operations/Algebra.kt | 19 +++++----- .../space/kscience/kmath/operations/BigInt.kt | 7 ++-- .../kscience/kmath/functions/Piecewise.kt | 35 +++++++++++++++---- .../kscience/kmath/functions/Polynomial.kt | 3 ++ 7 files changed, 61 insertions(+), 20 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/DoubleDomain.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/DoubleDomain.kt index 057a4a344..154763159 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/DoubleDomain.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/DoubleDomain.kt @@ -24,7 +24,6 @@ import space.kscience.kmath.misc.UnstableKMathAPI */ @UnstableKMathAPI public interface DoubleDomain : Domain { - /** * Global lower edge * @param num axis number diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSolver.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSolver.kt index af136c552..3e2dbee3f 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSolver.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSolver.kt @@ -3,7 +3,10 @@ package space.kscience.kmath.linear import space.kscience.kmath.nd.as1D /** - * A group of methods to resolve equation A dot X = B, where A and B are matrices or vectors + * A group of methods to solve for *X* in equation *X = A -1 · B*, where *A* and *B* are matrices or + * vectors. + * + * @param T the type of items. */ public interface LinearSolver { /** @@ -23,7 +26,7 @@ public interface LinearSolver { } /** - * Convert matrix to vector if it is possible + * Convert matrix to vector if it is possible. */ public fun Matrix.asVector(): Point = if (this.colNum == 1) @@ -31,4 +34,11 @@ public fun Matrix.asVector(): Point = else error("Can't convert matrix with more than one column to vector") +/** + * Creates an n × 1 [VirtualMatrix], where n is the size of the given buffer. + * + * @param T the type of elements contained in the buffer. + * @receiver a buffer. + * @return the new matrix. + */ public fun Point.asMatrix(): VirtualMatrix = VirtualMatrix(size, 1) { i, _ -> get(i) } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt index 6a587270b..921f4c79c 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt @@ -17,6 +17,8 @@ public typealias Matrix = Structure2D /** * Alias or using [Buffer] as a point/vector in a many-dimensional space. + * + * @param T the type of elements contained in the buffer. */ public typealias Point = Buffer diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt index 9f57bc4c1..960b7581c 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt @@ -100,8 +100,8 @@ public fun Algebra.bindSymbol(symbol: Symbol): T = bindSymbol(symbo public inline operator fun , R> A.invoke(block: A.() -> R): R = run(block) /** - * Represents linear space without neutral element, i.e. algebraic structure with associative, binary operation [add] - * and scalar multiplication [multiply]. + * Represents group without neutral element (also known as inverse semigroup), i.e. algebraic structure with + * associative, binary operation [add]. * * @param T the type of element of this semispace. */ @@ -177,7 +177,7 @@ public interface GroupOperations : Algebra { } /** - * Represents linear space with neutral element, i.e. algebraic structure with associative, binary operation [add]. + * Represents group, i.e. algebraic structure with associative, binary operation [add]. * * @param T the type of element of this semispace. */ @@ -189,8 +189,8 @@ public interface Group : GroupOperations { } /** - * Represents rng, i.e. algebraic structure with associative, binary, commutative operation [add] and associative, - * operation [multiply] distributive over [add]. + * Represents ring without multiplicative and additive identities, i.e. algebraic structure with + * associative, binary, commutative operation [add] and associative, operation [multiply] distributive over [add]. * * @param T the type of element of this semiring. */ @@ -238,7 +238,7 @@ public interface Ring : Group, RingOperations { } /** - * Represents field without identity elements, i.e. algebraic structure with associative, binary, commutative operations + * Represents field without without multiplicative and additive identities, i.e. algebraic structure with associative, binary, commutative operations * [add] and [multiply]; binary operation [divide] as multiplication of left operand by reciprocal of right one. * * @param T the type of element of this semifield. @@ -276,10 +276,11 @@ public interface FieldOperations : RingOperations { } /** - * Represents field, i.e. algebraic structure with three operations: associative "addition" and "multiplication", - * and "division" and their neutral elements. + * Represents field, i.e. algebraic structure with three operations: associative, commutative addition and + * multiplication, and division. **This interface differs from the eponymous mathematical definition: fields in KMath + * also support associative multiplication by scalar.** * - * @param T the type of element of this semifield. + * @param T the type of element of this field. */ public interface Field : Ring, FieldOperations, ScaleOperations, NumericAlgebra { override fun number(value: Number): T = scale(one, value.toDouble()) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt index 55bb68850..b5e27575b 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BigInt.kt @@ -12,8 +12,8 @@ import kotlin.math.max import kotlin.math.min import kotlin.math.sign -public typealias Magnitude = UIntArray -public typealias TBase = ULong +private typealias Magnitude = UIntArray +private typealias TBase = ULong /** * Kotlin Multiplatform implementation of Big Integer numbers (KBigInteger). @@ -358,6 +358,9 @@ private fun stripLeadingZeros(mag: Magnitude): Magnitude { return mag.sliceArray(IntRange(0, resSize)) } +/** + * Returns the absolute value of the given value [x]. + */ public fun abs(x: BigInt): BigInt = x.abs() /** diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Piecewise.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Piecewise.kt index d2470a4b4..3510973be 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Piecewise.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Piecewise.kt @@ -2,14 +2,28 @@ package space.kscience.kmath.functions import space.kscience.kmath.operations.Ring +/** + * Represents piecewise-defined function. + * + * @param T the piece key type. + * @param R the sub-function type. + */ public fun interface Piecewise { + /** + * Returns the appropriate sub-function for given piece key. + */ public fun findPiece(arg: T): R? } +/** + * Represents piecewise-defined function where all the sub-functions are polynomials. + */ public fun interface PiecewisePolynomial : Piecewise> /** - * Ordered list of pieces in piecewise function + * Basic [Piecewise] implementation where all the pieces are ordered by the [Comparable] type instances. + * + * @param T the comparable piece key type. */ public class OrderedPiecewisePolynomial>(delimiter: T) : PiecewisePolynomial { @@ -17,8 +31,10 @@ public class OrderedPiecewisePolynomial>(delimiter: T) : private val pieces: MutableList> = arrayListOf() /** - * Dynamically add a piece to the "right" side (beyond maximum argument value of previous piece) - * @param right new rightmost position. If is less then current rightmost position, a error is thrown. + * Dynamically adds a piece to the right side (beyond maximum argument value of previous piece) + * + * @param right new rightmost position. If is less then current rightmost position, an error is thrown. + * @param piece the sub-function. */ public fun putRight(right: T, piece: Polynomial) { require(right > delimiters.last()) { "New delimiter should be to the right of old one" } @@ -26,13 +42,19 @@ public class OrderedPiecewisePolynomial>(delimiter: T) : pieces.add(piece) } + /** + * Dynamically adds a piece to the left side (beyond maximum argument value of previous piece) + * + * @param left the new leftmost position. If is less then current rightmost position, an error is thrown. + * @param piece the sub-function. + */ public fun putLeft(left: T, piece: Polynomial) { require(left < delimiters.first()) { "New delimiter should be to the left of old one" } delimiters.add(0, left) pieces.add(0, piece) } - override fun findPiece(arg: T): Polynomial? { + public override fun findPiece(arg: T): Polynomial? { if (arg < delimiters.first() || arg >= delimiters.last()) return null else { @@ -46,9 +68,10 @@ public class OrderedPiecewisePolynomial>(delimiter: T) : } /** - * Return a value of polynomial function with given [ring] an given [arg] or null if argument is outside of piecewise definition. + * Return a value of polynomial function with given [ring] an given [arg] or null if argument is outside of piecewise + * definition. */ public fun , C : Ring> PiecewisePolynomial.value(ring: C, arg: T): T? = findPiece(arg)?.value(ring, arg) -public fun , C : Ring> PiecewisePolynomial.asFunction(ring: C): (T) -> T? = { value(ring, it) } \ No newline at end of file +public fun , C : Ring> PiecewisePolynomial.asFunction(ring: C): (T) -> T? = { value(ring, it) } diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt index 049909fe1..9f4e21991 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt @@ -15,6 +15,9 @@ import kotlin.math.pow */ public inline class Polynomial(public val coefficients: List) +/** + * Returns a [Polynomial] instance with given [coefficients]. + */ @Suppress("FunctionName") public fun Polynomial(vararg coefficients: T): Polynomial = Polynomial(coefficients.toList()) From 1b01654667877eff08b5ffe0fb83fc6458cae238 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Mon, 22 Mar 2021 18:32:08 +0700 Subject: [PATCH 28/31] Improve Dokka configuration --- README.md | 11 +++-- build.gradle.kts | 24 ++++++++- docs/templates/ARTIFACT-TEMPLATE.md | 62 +++++++++++------------ docs/templates/README-TEMPLATE.md | 5 +- gradle.properties | 2 +- kmath-ast/README.md | 66 +++++++++++-------------- kmath-ast/docs/README-TEMPLATE.md | 4 +- kmath-complex/README.md | 66 +++++++++++-------------- kmath-complex/docs/README-TEMPLATE.md | 4 +- kmath-core/README.md | 66 +++++++++++-------------- kmath-core/docs/README-TEMPLATE.md | 4 +- kmath-ejml/README.md | 66 +++++++++++-------------- kmath-ejml/docs/README-TEMPLATE.md | 4 +- kmath-for-real/README.md | 66 ++++++++++++------------- kmath-for-real/docs/README-TEMPLATE.md | 4 +- kmath-functions/README.md | 66 +++++++++++-------------- kmath-functions/docs/README-TEMPLATE.md | 4 +- kmath-nd4j/README.md | 66 +++++++++++-------------- kmath-nd4j/docs/README-TEMPLATE.md | 4 +- 19 files changed, 288 insertions(+), 306 deletions(-) diff --git a/README.md b/README.md index cc9439d27..7080c757e 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@ [![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) [![DOI](https://zenodo.org/badge/129486382.svg)](https://zenodo.org/badge/latestdoi/129486382) - ![Gradle build](https://github.com/mipt-npm/kmath/workflows/Gradle%20build/badge.svg) - -[![Maven Central](https://img.shields.io/maven-central/v/space.kscience/kmath-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22space.kscience%22%20AND%20a:%22kmath-core%22) +[![Maven Central](https://img.shields.io/maven-central/v/space.kscience/kmath-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22space.kscience%22) +[![Space](https://img.shields.io/maven-metadata/v?label=Space&metadataUrl=https%3A%2F%2Fmaven.pkg.jetbrains.space%2Fmipt-npm%2Fp%2Fsci%2Fmaven%2Fkscience%2Fkmath%2Fkmath-core%2Fmaven-metadata.xml)](https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven/space/kscience/) # KMath @@ -147,6 +146,12 @@ performance calculations to code generation. > > > **Maturity**: PROTOTYPE +> +> **Features:** +> - [ejml-vector](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt) : The Point implementation using SimpleMatrix. +> - [ejml-matrix](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt) : The Matrix implementation using SimpleMatrix. +> - [ejml-linear-space](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt) : The LinearSpace implementation using SimpleMatrix. +
* ### [kmath-for-real](kmath-for-real) diff --git a/build.gradle.kts b/build.gradle.kts index 5f1a8b88a..67cb2a2fc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,6 @@ +import org.jetbrains.dokka.gradle.DokkaTask import ru.mipt.npm.gradle.KSciencePublishingPlugin +import java.net.URL plugins { id("ru.mipt.npm.gradle.project") @@ -9,7 +11,6 @@ allprojects { jcenter() maven("https://clojars.org/repo") maven("https://dl.bintray.com/egor-bogomolov/astminer/") - maven("https://dl.bintray.com/hotkeytlt/maven") maven("https://dl.bintray.com/kotlin/kotlin-eap") maven("https://dl.bintray.com/kotlin/kotlinx") maven("https://dl.bintray.com/mipt-npm/dev") @@ -25,6 +26,27 @@ allprojects { subprojects { if (name.startsWith("kmath")) apply() + + afterEvaluate { + tasks.withType { + dokkaSourceSets.all { + val readmeFile = File(this@subprojects.projectDir, "./README.md") + if (readmeFile.exists()) + includes.setFrom(includes + readmeFile.absolutePath) + + arrayOf( + "http://ejml.org/javadoc/", + "https://commons.apache.org/proper/commons-math/javadocs/api-3.6.1/", + "https://deeplearning4j.org/api/latest/" + ).map { URL("${it}package-list") to URL(it) }.forEach { (a, b) -> + externalDocumentationLink { + packageListUrl.set(a) + url.set(b) + } + } + } + } + } } readme { diff --git a/docs/templates/ARTIFACT-TEMPLATE.md b/docs/templates/ARTIFACT-TEMPLATE.md index cb741bc6f..01d9c51da 100644 --- a/docs/templates/ARTIFACT-TEMPLATE.md +++ b/docs/templates/ARTIFACT-TEMPLATE.md @@ -1,34 +1,28 @@ -> #### Artifact: -> -> This module artifact: `${group}:${name}:${version}`. -> -> Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/${name}/images/download.svg) ](https://bintray.com/mipt-npm/kscience/${name}/_latestVersion) -> -> Bintray development version: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/${name}/images/download.svg) ](https://bintray.com/mipt-npm/dev/${name}/_latestVersion) -> -> **Gradle:** -> -> ```gradle -> repositories { -> maven { url 'https://repo.kotlin.link' } -> maven { url 'https://dl.bintray.com/hotkeytlt/maven' } -> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap -> } -> -> dependencies { -> implementation '${group}:${name}:${version}' -> } -> ``` -> **Gradle Kotlin DSL:** -> -> ```kotlin -> repositories { -> maven("https://repo.kotlin.link") -> maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap -> maven("https://dl.bintray.com/hotkeytlt/maven") // required for a -> } -> -> dependencies { -> implementation("${group}:${name}:${version}") -> } -> ``` \ No newline at end of file +## Artifact: + +The Maven coordinates of this project are `${group}:${name}:${version}`. + +**Gradle:** +```gradle +repositories { + maven { url 'https://repo.kotlin.link' } + maven { url 'https://dl.bintray.com/hotkeytlt/maven' } + maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap +} + +dependencies { + implementation '${group}:${name}:${version}' +} +``` +**Gradle Kotlin DSL:** +```kotlin +repositories { + maven("https://repo.kotlin.link") + maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap + maven("https://dl.bintray.com/hotkeytlt/maven") // required for a +} + +dependencies { + implementation("${group}:${name}:${version}") +} +``` \ No newline at end of file diff --git a/docs/templates/README-TEMPLATE.md b/docs/templates/README-TEMPLATE.md index 4366c8fcd..3502cdccd 100644 --- a/docs/templates/README-TEMPLATE.md +++ b/docs/templates/README-TEMPLATE.md @@ -1,9 +1,8 @@ [![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) [![DOI](https://zenodo.org/badge/129486382.svg)](https://zenodo.org/badge/latestdoi/129486382) - ![Gradle build](https://github.com/mipt-npm/kmath/workflows/Gradle%20build/badge.svg) - -[![Maven Central](https://img.shields.io/maven-central/v/space.kscience/kmath-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22space.kscience%22%20AND%20a:%22kmath-core%22) +[![Maven Central](https://img.shields.io/maven-central/v/space.kscience/kmath-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22space.kscience%22) +[![Space](https://img.shields.io/maven-metadata/v?label=Space&metadataUrl=https%3A%2F%2Fmaven.pkg.jetbrains.space%2Fmipt-npm%2Fp%2Fsci%2Fmaven%2Fkscience%2Fkmath%2Fkmath-core%2Fmaven-metadata.xml)](https://maven.pkg.jetbrains.space/mipt-npm/p/sci/maven/space/kscience/) # KMath diff --git a/gradle.properties b/gradle.properties index 7ff50a435..50123b16c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,5 +4,5 @@ kotlin.mpp.stability.nowarn=true kotlin.native.enableDependencyPropagation=false kotlin.parallel.tasks.in.project=true org.gradle.configureondemand=true -org.gradle.jvmargs=-XX:MaxMetaspaceSize=2G +org.gradle.jvmargs=-XX:MaxMetaspaceSize=9G org.gradle.parallel=true diff --git a/kmath-ast/README.md b/kmath-ast/README.md index ee14604d2..ff954b914 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -1,6 +1,6 @@ -# Abstract Syntax Tree Expression Representation and Operations (`kmath-ast`) +# Module kmath-ast -This subproject implements the following features: +Abstract syntax tree expression representation and related optimizations. - [expression-language](src/jvmMain/kotlin/space/kscience/kmath/ast/parser.kt) : Expression language and its parser - [mst](src/commonMain/kotlin/space/kscience/kmath/ast/MST.kt) : MST (Mathematical Syntax Tree) as expression language's syntax intermediate representation @@ -10,40 +10,34 @@ This subproject implements the following features: - [mst-js-codegen](src/jsMain/kotlin/space/kscience/kmath/estree/estree.kt) : Dynamic MST to JS compiler -> #### Artifact: -> -> This module artifact: `space.kscience:kmath-ast:0.3.0-dev-3`. -> -> Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-ast/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-ast/_latestVersion) -> -> Bintray development version: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-ast/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-ast/_latestVersion) -> -> **Gradle:** -> -> ```gradle -> repositories { -> maven { url 'https://repo.kotlin.link' } -> maven { url 'https://dl.bintray.com/hotkeytlt/maven' } -> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap -> } -> -> dependencies { -> implementation 'space.kscience:kmath-ast:0.3.0-dev-3' -> } -> ``` -> **Gradle Kotlin DSL:** -> -> ```kotlin -> repositories { -> maven("https://repo.kotlin.link") -> maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap -> maven("https://dl.bintray.com/hotkeytlt/maven") // required for a -> } -> -> dependencies { -> implementation("space.kscience:kmath-ast:0.3.0-dev-3") -> } -> ``` +## Artifact: + +The Maven coordinates of this project are `space.kscience:kmath-ast:0.3.0-dev-3`. + +**Gradle:** +```gradle +repositories { + maven { url 'https://repo.kotlin.link' } + maven { url 'https://dl.bintray.com/hotkeytlt/maven' } + maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap +} + +dependencies { + implementation 'space.kscience:kmath-ast:0.3.0-dev-3' +} +``` +**Gradle Kotlin DSL:** +```kotlin +repositories { + maven("https://repo.kotlin.link") + maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap + maven("https://dl.bintray.com/hotkeytlt/maven") // required for a +} + +dependencies { + implementation("space.kscience:kmath-ast:0.3.0-dev-3") +} +``` ## Dynamic expression code generation diff --git a/kmath-ast/docs/README-TEMPLATE.md b/kmath-ast/docs/README-TEMPLATE.md index 80e48008b..db071adb4 100644 --- a/kmath-ast/docs/README-TEMPLATE.md +++ b/kmath-ast/docs/README-TEMPLATE.md @@ -1,6 +1,6 @@ -# Abstract Syntax Tree Expression Representation and Operations (`kmath-ast`) +# Module kmath-ast -This subproject implements the following features: +Abstract syntax tree expression representation and related optimizations. ${features} diff --git a/kmath-complex/README.md b/kmath-complex/README.md index 9e9cd5b6f..d7b2937fd 100644 --- a/kmath-complex/README.md +++ b/kmath-complex/README.md @@ -1,42 +1,36 @@ -# The Core Module (`kmath-core`) +# Module kmath-complex -Complex and hypercomplex number systems in KMath: +Complex and hypercomplex number systems in KMath. - [complex](src/commonMain/kotlin/space/kscience/kmath/complex/Complex.kt) : Complex Numbers - [quaternion](src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt) : Quaternions -> #### Artifact: -> -> This module artifact: `space.kscience:kmath-complex:0.3.0-dev-3`. -> -> Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-complex/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-complex/_latestVersion) -> -> Bintray development version: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-complex/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-complex/_latestVersion) -> -> **Gradle:** -> -> ```gradle -> repositories { -> maven { url 'https://repo.kotlin.link' } -> maven { url 'https://dl.bintray.com/hotkeytlt/maven' } -> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap -> } -> -> dependencies { -> implementation 'space.kscience:kmath-complex:0.3.0-dev-3' -> } -> ``` -> **Gradle Kotlin DSL:** -> -> ```kotlin -> repositories { -> maven("https://repo.kotlin.link") -> maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap -> maven("https://dl.bintray.com/hotkeytlt/maven") // required for a -> } -> -> dependencies { -> implementation("space.kscience:kmath-complex:0.3.0-dev-3") -> } -> ``` +## Artifact: + +The Maven coordinates of this project are `space.kscience:kmath-complex:0.3.0-dev-3`. + +**Gradle:** +```gradle +repositories { + maven { url 'https://repo.kotlin.link' } + maven { url 'https://dl.bintray.com/hotkeytlt/maven' } + maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap +} + +dependencies { + implementation 'space.kscience:kmath-complex:0.3.0-dev-3' +} +``` +**Gradle Kotlin DSL:** +```kotlin +repositories { + maven("https://repo.kotlin.link") + maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap + maven("https://dl.bintray.com/hotkeytlt/maven") // required for a +} + +dependencies { + implementation("space.kscience:kmath-complex:0.3.0-dev-3") +} +``` diff --git a/kmath-complex/docs/README-TEMPLATE.md b/kmath-complex/docs/README-TEMPLATE.md index 462fd617e..106d4aff1 100644 --- a/kmath-complex/docs/README-TEMPLATE.md +++ b/kmath-complex/docs/README-TEMPLATE.md @@ -1,6 +1,6 @@ -# The Core Module (`kmath-core`) +# Module kmath-complex -Complex and hypercomplex number systems in KMath: +Complex and hypercomplex number systems in KMath. ${features} diff --git a/kmath-core/README.md b/kmath-core/README.md index 4e4b5273d..096c7d833 100644 --- a/kmath-core/README.md +++ b/kmath-core/README.md @@ -1,6 +1,6 @@ -# The Core Module (`kmath-core`) +# Module kmath-core -The core features of KMath: +The core interfaces of KMath. - [algebras](src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt) : Algebraic structures like rings, spaces and fields. - [nd](src/commonMain/kotlin/space/kscience/kmath/structures/StructureND.kt) : Many-dimensional structures and operations on them. @@ -13,37 +13,31 @@ performance calculations to code generation. - [autodif](src/commonMain/kotlin/space/kscience/kmath/expressions/SimpleAutoDiff.kt) : Automatic differentiation -> #### Artifact: -> -> This module artifact: `space.kscience:kmath-core:0.3.0-dev-3`. -> -> Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-core/_latestVersion) -> -> Bintray development version: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-core/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-core/_latestVersion) -> -> **Gradle:** -> -> ```gradle -> repositories { -> maven { url 'https://repo.kotlin.link' } -> maven { url 'https://dl.bintray.com/hotkeytlt/maven' } -> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap -> } -> -> dependencies { -> implementation 'space.kscience:kmath-core:0.3.0-dev-3' -> } -> ``` -> **Gradle Kotlin DSL:** -> -> ```kotlin -> repositories { -> maven("https://repo.kotlin.link") -> maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap -> maven("https://dl.bintray.com/hotkeytlt/maven") // required for a -> } -> -> dependencies { -> implementation("space.kscience:kmath-core:0.3.0-dev-3") -> } -> ``` +## Artifact: + +The Maven coordinates of this project are `space.kscience:kmath-core:0.3.0-dev-3`. + +**Gradle:** +```gradle +repositories { + maven { url 'https://repo.kotlin.link' } + maven { url 'https://dl.bintray.com/hotkeytlt/maven' } + maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap +} + +dependencies { + implementation 'space.kscience:kmath-core:0.3.0-dev-3' +} +``` +**Gradle Kotlin DSL:** +```kotlin +repositories { + maven("https://repo.kotlin.link") + maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap + maven("https://dl.bintray.com/hotkeytlt/maven") // required for a +} + +dependencies { + implementation("space.kscience:kmath-core:0.3.0-dev-3") +} +``` diff --git a/kmath-core/docs/README-TEMPLATE.md b/kmath-core/docs/README-TEMPLATE.md index 83d1ebdce..41cfe1ccb 100644 --- a/kmath-core/docs/README-TEMPLATE.md +++ b/kmath-core/docs/README-TEMPLATE.md @@ -1,6 +1,6 @@ -# The Core Module (`kmath-core`) +# Module kmath-core -The core features of KMath: +The core interfaces of KMath. ${features} diff --git a/kmath-ejml/README.md b/kmath-ejml/README.md index 1081b2b7f..2551703a4 100644 --- a/kmath-ejml/README.md +++ b/kmath-ejml/README.md @@ -1,43 +1,37 @@ -# ejml-simple support (`kmath-ejml`) +# Module kmath-ejml -This subproject implements the following features: +EJML based linear algebra implementation. - [ejml-vector](src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt) : The Point implementation using SimpleMatrix. - [ejml-matrix](src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt) : The Matrix implementation using SimpleMatrix. - [ejml-linear-space](src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt) : The LinearSpace implementation using SimpleMatrix. -> #### Artifact: -> -> This module artifact: `space.kscience:kmath-ejml:0.3.0-dev-3`. -> -> Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-ejml/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-ejml/_latestVersion) -> -> Bintray development version: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-ejml/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-ejml/_latestVersion) -> -> **Gradle:** -> -> ```gradle -> repositories { -> maven { url 'https://repo.kotlin.link' } -> maven { url 'https://dl.bintray.com/hotkeytlt/maven' } -> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap -> } -> -> dependencies { -> implementation 'space.kscience:kmath-ejml:0.3.0-dev-3' -> } -> ``` -> **Gradle Kotlin DSL:** -> -> ```kotlin -> repositories { -> maven("https://repo.kotlin.link") -> maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap -> maven("https://dl.bintray.com/hotkeytlt/maven") // required for a -> } -> -> dependencies { -> implementation("space.kscience:kmath-ejml:0.3.0-dev-3") -> } -> ``` +## Artifact: + +The Maven coordinates of this project are `space.kscience:kmath-ejml:0.3.0-dev-3`. + +**Gradle:** +```gradle +repositories { + maven { url 'https://repo.kotlin.link' } + maven { url 'https://dl.bintray.com/hotkeytlt/maven' } + maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap +} + +dependencies { + implementation 'space.kscience:kmath-ejml:0.3.0-dev-3' +} +``` +**Gradle Kotlin DSL:** +```kotlin +repositories { + maven("https://repo.kotlin.link") + maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap + maven("https://dl.bintray.com/hotkeytlt/maven") // required for a +} + +dependencies { + implementation("space.kscience:kmath-ejml:0.3.0-dev-3") +} +``` diff --git a/kmath-ejml/docs/README-TEMPLATE.md b/kmath-ejml/docs/README-TEMPLATE.md index c53f4a81c..27fcedd65 100644 --- a/kmath-ejml/docs/README-TEMPLATE.md +++ b/kmath-ejml/docs/README-TEMPLATE.md @@ -1,6 +1,6 @@ -# ejml-simple support (`kmath-ejml`) +# Module kmath-ejml -This subproject implements the following features: +EJML based linear algebra implementation. ${features} diff --git a/kmath-for-real/README.md b/kmath-for-real/README.md index 139cd3cef..ad3d33062 100644 --- a/kmath-for-real/README.md +++ b/kmath-for-real/README.md @@ -1,41 +1,37 @@ -# Real number specialization module (`kmath-for-real`) +# Module kmath-for-real + +Specialization of KMath APIs for Double numbers. - [DoubleVector](src/commonMain/kotlin/space/kscience/kmath/real/DoubleVector.kt) : Numpy-like operations for Buffers/Points - [DoubleMatrix](src/commonMain/kotlin/space/kscience/kmath/real/DoubleMatrix.kt) : Numpy-like operations for 2d real structures - [grids](src/commonMain/kotlin/space/kscience/kmath/structures/grids.kt) : Uniform grid generators -> #### Artifact: -> -> This module artifact: `space.kscience:kmath-for-real:0.3.0-dev-3`. -> -> Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-for-real/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-for-real/_latestVersion) -> -> Bintray development version: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-for-real/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-for-real/_latestVersion) -> -> **Gradle:** -> -> ```gradle -> repositories { -> maven { url 'https://repo.kotlin.link' } -> maven { url 'https://dl.bintray.com/hotkeytlt/maven' } -> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap -> } -> -> dependencies { -> implementation 'space.kscience:kmath-for-real:0.3.0-dev-3' -> } -> ``` -> **Gradle Kotlin DSL:** -> -> ```kotlin -> repositories { -> maven("https://repo.kotlin.link") -> maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap -> maven("https://dl.bintray.com/hotkeytlt/maven") // required for a -> } -> -> dependencies { -> implementation("space.kscience:kmath-for-real:0.3.0-dev-3") -> } -> ``` +## Artifact: + +The Maven coordinates of this project are `space.kscience:kmath-for-real:0.3.0-dev-3`. + +**Gradle:** +```gradle +repositories { + maven { url 'https://repo.kotlin.link' } + maven { url 'https://dl.bintray.com/hotkeytlt/maven' } + maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap +} + +dependencies { + implementation 'space.kscience:kmath-for-real:0.3.0-dev-3' +} +``` +**Gradle Kotlin DSL:** +```kotlin +repositories { + maven("https://repo.kotlin.link") + maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap + maven("https://dl.bintray.com/hotkeytlt/maven") // required for a +} + +dependencies { + implementation("space.kscience:kmath-for-real:0.3.0-dev-3") +} +``` diff --git a/kmath-for-real/docs/README-TEMPLATE.md b/kmath-for-real/docs/README-TEMPLATE.md index 670844bd0..c2ef25aa7 100644 --- a/kmath-for-real/docs/README-TEMPLATE.md +++ b/kmath-for-real/docs/README-TEMPLATE.md @@ -1,4 +1,6 @@ -# Real number specialization module (`kmath-for-real`) +# Module kmath-for-real + +Specialization of KMath APIs for Double numbers. ${features} diff --git a/kmath-functions/README.md b/kmath-functions/README.md index d13c4c107..531e97a44 100644 --- a/kmath-functions/README.md +++ b/kmath-functions/README.md @@ -1,6 +1,6 @@ -# Functions (`kmath-functions`) +# Module kmath-functions -Functions and interpolations: +Functions and interpolations. - [piecewise](Piecewise functions.) : src/commonMain/kotlin/space/kscience/kmath/functions/Piecewise.kt - [polynomials](Polynomial functions.) : src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt @@ -8,37 +8,31 @@ Functions and interpolations: - [spline interpolation](Cubic spline XY interpolator.) : src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt -> #### Artifact: -> -> This module artifact: `space.kscience:kmath-functions:0.3.0-dev-3`. -> -> Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-functions/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-functions/_latestVersion) -> -> Bintray development version: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-functions/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-functions/_latestVersion) -> -> **Gradle:** -> -> ```gradle -> repositories { -> maven { url 'https://repo.kotlin.link' } -> maven { url 'https://dl.bintray.com/hotkeytlt/maven' } -> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap -> } -> -> dependencies { -> implementation 'space.kscience:kmath-functions:0.3.0-dev-3' -> } -> ``` -> **Gradle Kotlin DSL:** -> -> ```kotlin -> repositories { -> maven("https://repo.kotlin.link") -> maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap -> maven("https://dl.bintray.com/hotkeytlt/maven") // required for a -> } -> -> dependencies { -> implementation("space.kscience:kmath-functions:0.3.0-dev-3") -> } -> ``` +## Artifact: + +The Maven coordinates of this project are `space.kscience:kmath-functions:0.3.0-dev-3`. + +**Gradle:** +```gradle +repositories { + maven { url 'https://repo.kotlin.link' } + maven { url 'https://dl.bintray.com/hotkeytlt/maven' } + maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap +} + +dependencies { + implementation 'space.kscience:kmath-functions:0.3.0-dev-3' +} +``` +**Gradle Kotlin DSL:** +```kotlin +repositories { + maven("https://repo.kotlin.link") + maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap + maven("https://dl.bintray.com/hotkeytlt/maven") // required for a +} + +dependencies { + implementation("space.kscience:kmath-functions:0.3.0-dev-3") +} +``` diff --git a/kmath-functions/docs/README-TEMPLATE.md b/kmath-functions/docs/README-TEMPLATE.md index 8a34a7cc4..2e163eee5 100644 --- a/kmath-functions/docs/README-TEMPLATE.md +++ b/kmath-functions/docs/README-TEMPLATE.md @@ -1,6 +1,6 @@ -# Functions (`kmath-functions`) +# Module kmath-functions -Functions and interpolations: +Functions and interpolations. ${features} diff --git a/kmath-nd4j/README.md b/kmath-nd4j/README.md index 2771722eb..938d05c33 100644 --- a/kmath-nd4j/README.md +++ b/kmath-nd4j/README.md @@ -1,46 +1,40 @@ -# ND4J NDStructure implementation (`kmath-nd4j`) +# Module kmath-nd4j -This subproject implements the following features: +ND4J based implementations of KMath abstractions. - [nd4jarraystructure](#) : NDStructure wrapper for INDArray - [nd4jarrayrings](#) : Rings over Nd4jArrayStructure of Int and Long - [nd4jarrayfields](#) : Fields over Nd4jArrayStructure of Float and Double -> #### Artifact: -> -> This module artifact: `space.kscience:kmath-nd4j:0.3.0-dev-3`. -> -> Bintray release version: [ ![Download](https://api.bintray.com/packages/mipt-npm/kscience/kmath-nd4j/images/download.svg) ](https://bintray.com/mipt-npm/kscience/kmath-nd4j/_latestVersion) -> -> Bintray development version: [ ![Download](https://api.bintray.com/packages/mipt-npm/dev/kmath-nd4j/images/download.svg) ](https://bintray.com/mipt-npm/dev/kmath-nd4j/_latestVersion) -> -> **Gradle:** -> -> ```gradle -> repositories { -> maven { url 'https://repo.kotlin.link' } -> maven { url 'https://dl.bintray.com/hotkeytlt/maven' } -> maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap -> } -> -> dependencies { -> implementation 'space.kscience:kmath-nd4j:0.3.0-dev-3' -> } -> ``` -> **Gradle Kotlin DSL:** -> -> ```kotlin -> repositories { -> maven("https://repo.kotlin.link") -> maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap -> maven("https://dl.bintray.com/hotkeytlt/maven") // required for a -> } -> -> dependencies { -> implementation("space.kscience:kmath-nd4j:0.3.0-dev-3") -> } -> ``` +## Artifact: + +The Maven coordinates of this project are `space.kscience:kmath-nd4j:0.3.0-dev-3`. + +**Gradle:** +```gradle +repositories { + maven { url 'https://repo.kotlin.link' } + maven { url 'https://dl.bintray.com/hotkeytlt/maven' } + maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap +} + +dependencies { + implementation 'space.kscience:kmath-nd4j:0.3.0-dev-3' +} +``` +**Gradle Kotlin DSL:** +```kotlin +repositories { + maven("https://repo.kotlin.link") + maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap + maven("https://dl.bintray.com/hotkeytlt/maven") // required for a +} + +dependencies { + implementation("space.kscience:kmath-nd4j:0.3.0-dev-3") +} +``` ## Examples diff --git a/kmath-nd4j/docs/README-TEMPLATE.md b/kmath-nd4j/docs/README-TEMPLATE.md index 9783e74be..5f325cab5 100644 --- a/kmath-nd4j/docs/README-TEMPLATE.md +++ b/kmath-nd4j/docs/README-TEMPLATE.md @@ -1,6 +1,6 @@ -# ND4J NDStructure implementation (`kmath-nd4j`) +# Module kmath-nd4j -This subproject implements the following features: +ND4J based implementations of KMath abstractions. ${features} From 4d160b81b9620c6ab47e244f727db62355948479 Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Tue, 23 Mar 2021 19:48:12 +0700 Subject: [PATCH 29/31] Improve KDoc comments for kmath-functions --- .../kscience/kmath/functions/Polynomial.kt | 24 +++++++++++++---- .../kscience/kmath/integration/Integrator.kt | 6 ++--- .../kmath/integration/UnivariateIntegrand.kt | 2 +- .../kmath/interpolation/SplineInterpolator.kt | 7 +++-- .../kmath/interpolation/XYPointSet.kt | 27 +++++++++++++++++++ 5 files changed, 55 insertions(+), 11 deletions(-) diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt index 9f4e21991..550785812 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt @@ -10,8 +10,9 @@ import kotlin.math.max import kotlin.math.pow /** - * Polynomial coefficients without fixation on specific context they are applied to - * @param coefficients constant is the leftmost coefficient + * Polynomial coefficients model without fixation on specific context they are applied to. + * + * @param coefficients constant is the leftmost coefficient. */ public inline class Polynomial(public val coefficients: List) @@ -21,8 +22,14 @@ public inline class Polynomial(public val coefficients: List) @Suppress("FunctionName") public fun Polynomial(vararg coefficients: T): Polynomial = Polynomial(coefficients.toList()) +/** + * Evaluates the value of the given double polynomial for given double argument. + */ public fun Polynomial.value(): Double = coefficients.reduceIndexed { index, acc, d -> acc + d.pow(index) } +/** + * Evaluates the value of the given polynomial for given argument. + */ public fun > Polynomial.value(ring: C, arg: T): T = ring { if (coefficients.isEmpty()) return@ring zero var res = coefficients.first() @@ -38,19 +45,23 @@ public fun > Polynomial.value(ring: C, arg: T): T = ring } /** - * Represent the polynomial as a regular context-less function + * Represent the polynomial as a regular context-less function. */ public fun > Polynomial.asFunction(ring: C): (T) -> T = { value(ring, it) } /** - * An algebra for polynomials + * Space of polynomials. + * + * @param T the type of operated polynomials. + * @param C the intersection of [Ring] of [T] and [ScaleOperations] of [T]. + * @param ring the [C] instance. */ public class PolynomialSpace( private val ring: C, ) : Group>, ScaleOperations> where C : Ring, C : ScaleOperations { public override val zero: Polynomial = Polynomial(emptyList()) - override fun Polynomial.unaryMinus(): Polynomial = with(ring) { + override fun Polynomial.unaryMinus(): Polynomial = ring { Polynomial(coefficients.map { -it }) } @@ -67,6 +78,9 @@ public class PolynomialSpace( public override fun scale(a: Polynomial, value: Double): Polynomial = ring { Polynomial(List(a.coefficients.size) { index -> a.coefficients[index] * value }) } + /** + * Evaluates the polynomial for the given value [arg]. + */ public operator fun Polynomial.invoke(arg: T): T = value(ring, arg) } diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/Integrator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/Integrator.kt index ebc53ad2e..e421fb680 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/Integrator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/Integrator.kt @@ -1,11 +1,11 @@ package space.kscience.kmath.integration /** - * A general interface for all integrators + * A general interface for all integrators. */ public interface Integrator { /** - * Run one integration pass and return a new [Integrand] with a new set of features + * Runs one integration pass and return a new [Integrand] with a new set of features. */ public fun integrate(integrand: I): I -} \ No newline at end of file +} diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/UnivariateIntegrand.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/UnivariateIntegrand.kt index 9389318e8..ca4bbf6b8 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/UnivariateIntegrand.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/UnivariateIntegrand.kt @@ -61,4 +61,4 @@ public fun UnivariateIntegrator.integrate( return integrate( UnivariateIntegrand(function, *features.toTypedArray()) ).value ?: error("Unexpected: no value after integration.") -} \ No newline at end of file +} diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt index ddbe743f0..ef75a1ef8 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt @@ -8,8 +8,11 @@ import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.MutableBufferFactory /** - * Generic spline interpolator. Not recommended for performance critical places, use platform-specific and type specific ones. - * Based on https://github.com/apache/commons-math/blob/eb57d6d457002a0bb5336d789a3381a24599affe/src/main/java/org/apache/commons/math4/analysis/interpolation/SplineInterpolator.java + * Generic spline interpolator. Not recommended for performance critical places, use platform-specific and type + * specific ones. + * + * Based on + * https://github.com/apache/commons-math/blob/eb57d6d457002a0bb5336d789a3381a24599affe/src/main/java/org/apache/commons/math4/analysis/interpolation/SplineInterpolator.java */ public class SplineInterpolator>( public override val algebra: Field, diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/XYPointSet.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/XYPointSet.kt index 1ff7b6351..2b1312c0f 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/XYPointSet.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/XYPointSet.kt @@ -3,13 +3,40 @@ package space.kscience.kmath.interpolation import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.structures.Buffer +/** + * Pair of associated buffers for X and Y axes values. + * + * @param X the type of X values. + * @param Y the type of Y values. + */ public interface XYPointSet { + /** + * The size of all the involved buffers. + */ public val size: Int + + /** + * The buffer of X values. + */ public val x: Buffer + + /** + * The buffer of Y values. + */ public val y: Buffer } +/** + * Triple of associated buffers for X, Y, and Z axes values. + * + * @param X the type of X values. + * @param Y the type of Y values. + * @param Z the type of Z values. + */ public interface XYZPointSet : XYPointSet { + /** + * The buffer of Z values. + */ public val z: Buffer } From cd05ca6e952ad4475301c4e0b47c0f6c100e57d9 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 24 Mar 2021 16:36:06 +0300 Subject: [PATCH 30/31] Initial Optimization API --- build.gradle.kts | 5 +- .../commons/optimization/CMOptimization.kt | 20 ++-- .../kmath/commons/optimization/cmFit.kt | 17 ++-- kmath-core/api/kmath-core.api | 35 +++---- .../space/kscience/kmath/data/ColumnarData.kt | 34 +++++++ .../kscience/kmath/data/XYColumnarData.kt | 55 +++++++++++ .../kscience/kmath/data/XYZColumnarData.kt | 21 ++++ .../space/kscience/kmath/misc/ColumnarData.kt | 15 --- .../space/kscience/kmath/misc/XYPointSet.kt | 98 ------------------- .../kmath/interpolation/Interpolator.kt | 16 +-- .../kmath/interpolation/LinearInterpolator.kt | 6 +- .../kmath/interpolation/SplineInterpolator.kt | 4 +- .../kotlingrad/DifferentiableMstExpression.kt | 6 +- .../kscience/kmath/optimization/DataFit.kt | 17 ---- .../optimization/FunctionOptimization.kt | 80 ++++----------- .../NoDerivFunctionOptimization.kt | 69 +++++++++++++ .../kscience/kmath/optimization/XYFit.kt | 40 ++++++++ settings.gradle.kts | 5 +- 18 files changed, 296 insertions(+), 247 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/ColumnarData.kt create mode 100644 kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/XYColumnarData.kt create mode 100644 kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/XYZColumnarData.kt delete mode 100644 kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/ColumnarData.kt delete mode 100644 kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/XYPointSet.kt delete mode 100644 kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/DataFit.kt create mode 100644 kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/NoDerivFunctionOptimization.kt create mode 100644 kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/XYFit.kt diff --git a/build.gradle.kts b/build.gradle.kts index d4453ad5c..cc863a957 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,10 +11,7 @@ allprojects { jcenter() maven("https://clojars.org/repo") maven("https://dl.bintray.com/egor-bogomolov/astminer/") - maven("https://dl.bintray.com/kotlin/kotlin-eap") - maven("https://dl.bintray.com/kotlin/kotlinx") - maven("https://dl.bintray.com/mipt-npm/dev") - maven("https://dl.bintray.com/mipt-npm/kscience") + maven("https://dl.bintray.com/hotkeytlt/maven") maven("https://jitpack.io") maven("http://logicrunch.research.it.uu.se/maven/") mavenCentral() diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimization.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimization.kt index 93d0f0bba..444c505c9 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimization.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimization.kt @@ -15,10 +15,7 @@ import space.kscience.kmath.expressions.SymbolIndexer import space.kscience.kmath.expressions.derivative import space.kscience.kmath.misc.Symbol import space.kscience.kmath.misc.UnstableKMathAPI -import space.kscience.kmath.optimization.FunctionOptimization -import space.kscience.kmath.optimization.OptimizationFeature -import space.kscience.kmath.optimization.OptimizationProblemFactory -import space.kscience.kmath.optimization.OptimizationResult +import space.kscience.kmath.optimization.* import kotlin.reflect.KClass public operator fun PointValuePair.component1(): DoubleArray = point @@ -27,7 +24,8 @@ public operator fun PointValuePair.component2(): Double = value @OptIn(UnstableKMathAPI::class) public class CMOptimization( override val symbols: List, -) : FunctionOptimization, SymbolIndexer, OptimizationFeature { +) : FunctionOptimization, NoDerivFunctionOptimization, SymbolIndexer, OptimizationFeature { + private val optimizationData: HashMap, OptimizationData> = HashMap() private var optimizerBuilder: (() -> MultivariateOptimizer)? = null public var convergenceChecker: ConvergenceChecker = SimpleValueChecker( @@ -36,6 +34,12 @@ public class CMOptimization( DEFAULT_MAX_ITER ) + override var maximize: Boolean + get() = optimizationData[GoalType::class] == GoalType.MAXIMIZE + set(value) { + optimizationData[GoalType::class] = if (value) GoalType.MAXIMIZE else GoalType.MINIMIZE + } + public fun addOptimizationData(data: OptimizationData) { optimizationData[data::class] = data } @@ -50,7 +54,7 @@ public class CMOptimization( addOptimizationData(InitialGuess(map.toDoubleArray())) } - public override fun expression(expression: Expression): Unit { + public override fun function(expression: Expression): Unit { val objectiveFunction = ObjectiveFunction { val args = it.toMap() expression(args) @@ -58,8 +62,8 @@ public class CMOptimization( addOptimizationData(objectiveFunction) } - public override fun diffExpression(expression: DifferentiableExpression>) { - expression(expression) + public override fun diffFunction(expression: DifferentiableExpression>) { + function(expression) val gradientFunction = ObjectiveFunctionGradient { val args = it.toMap() DoubleArray(symbols.size) { index -> diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/cmFit.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/cmFit.kt index 8e9ce7a80..f84dae693 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/cmFit.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/cmFit.kt @@ -1,13 +1,13 @@ package space.kscience.kmath.commons.optimization import org.apache.commons.math3.analysis.differentiation.DerivativeStructure -import org.apache.commons.math3.optim.nonlinear.scalar.GoalType import space.kscience.kmath.commons.expressions.DerivativeStructureField import space.kscience.kmath.expressions.DifferentiableExpression import space.kscience.kmath.expressions.Expression import space.kscience.kmath.misc.Symbol import space.kscience.kmath.optimization.FunctionOptimization import space.kscience.kmath.optimization.OptimizationResult +import space.kscience.kmath.optimization.noDerivOptimizeWith import space.kscience.kmath.optimization.optimizeWith import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.asBuffer @@ -44,7 +44,7 @@ public fun FunctionOptimization.Companion.chiSquared( public fun Expression.optimize( vararg symbols: Symbol, configuration: CMOptimization.() -> Unit, -): OptimizationResult = optimizeWith(CMOptimization, symbols = symbols, configuration) +): OptimizationResult = noDerivOptimizeWith(CMOptimization, symbols = symbols, configuration) /** * Optimize differentiable expression @@ -58,10 +58,11 @@ public fun DifferentiableExpression>.minimize( vararg startPoint: Pair, configuration: CMOptimization.() -> Unit = {}, ): OptimizationResult { - require(startPoint.isNotEmpty()) { "Must provide a list of symbols for optimization" } - val problem = CMOptimization(startPoint.map { it.first }).apply(configuration) - problem.diffExpression(this) - problem.initialGuess(startPoint.toMap()) - problem.goal(GoalType.MINIMIZE) - return problem.optimize() + val symbols = startPoint.map { it.first }.toTypedArray() + return optimize(*symbols){ + maximize = false + initialGuess(startPoint.toMap()) + diffFunction(this@minimize) + configuration() + } } \ No newline at end of file diff --git a/kmath-core/api/kmath-core.api b/kmath-core/api/kmath-core.api index 570e50a86..e6f4697aa 100644 --- a/kmath-core/api/kmath-core.api +++ b/kmath-core/api/kmath-core.api @@ -1,3 +1,18 @@ +public final class space/kscience/kmath/data/ColumnarDataKt { +} + +public final class space/kscience/kmath/data/XYColumnarData$DefaultImpls { + public static fun get (Lspace/kscience/kmath/data/XYColumnarData;Lspace/kscience/kmath/misc/Symbol;)Lspace/kscience/kmath/structures/Buffer; +} + +public final class space/kscience/kmath/data/XYColumnarDataKt { + public static synthetic fun asXYData$default (Lspace/kscience/kmath/nd/Structure2D;IIILjava/lang/Object;)Lspace/kscience/kmath/data/XYColumnarData; +} + +public final class space/kscience/kmath/data/XYZColumnarData$DefaultImpls { + public static fun get (Lspace/kscience/kmath/data/XYZColumnarData;Lspace/kscience/kmath/misc/Symbol;)Lspace/kscience/kmath/structures/Buffer; +} + public abstract interface class space/kscience/kmath/domains/Domain { public abstract fun contains (Lspace/kscience/kmath/structures/Buffer;)Z public abstract fun getDimension ()I @@ -603,15 +618,6 @@ public final class space/kscience/kmath/misc/CumulativeKt { public static final fun cumulativeSumOfLong (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; } -public final class space/kscience/kmath/misc/NDStructureColumn : space/kscience/kmath/structures/Buffer { - public fun (Lspace/kscience/kmath/nd/Structure2D;I)V - public fun get (I)Ljava/lang/Object; - public final fun getColumn ()I - public fun getSize ()I - public final fun getStructure ()Lspace/kscience/kmath/nd/Structure2D; - public fun iterator ()Ljava/util/Iterator; -} - public final class space/kscience/kmath/misc/StringSymbol : space/kscience/kmath/misc/Symbol { public static final synthetic fun box-impl (Ljava/lang/String;)Lspace/kscience/kmath/misc/StringSymbol; public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String; @@ -644,17 +650,6 @@ public final class space/kscience/kmath/misc/SymbolKt { public abstract interface annotation class space/kscience/kmath/misc/UnstableKMathAPI : java/lang/annotation/Annotation { } -public final class space/kscience/kmath/misc/XYPointSet$DefaultImpls { - public static fun get (Lspace/kscience/kmath/misc/XYPointSet;Lspace/kscience/kmath/misc/Symbol;)Lspace/kscience/kmath/structures/Buffer; -} - -public final class space/kscience/kmath/misc/XYPointSetKt { -} - -public final class space/kscience/kmath/misc/XYZPointSet$DefaultImpls { - public static fun get (Lspace/kscience/kmath/misc/XYZPointSet;Lspace/kscience/kmath/misc/Symbol;)Lspace/kscience/kmath/structures/Buffer; -} - public abstract interface class space/kscience/kmath/nd/AlgebraND { public static final field Companion Lspace/kscience/kmath/nd/AlgebraND$Companion; public abstract fun combine (Lspace/kscience/kmath/nd/StructureND;Lspace/kscience/kmath/nd/StructureND;Lkotlin/jvm/functions/Function3;)Lspace/kscience/kmath/nd/StructureND; diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/ColumnarData.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/ColumnarData.kt new file mode 100644 index 000000000..761255158 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/ColumnarData.kt @@ -0,0 +1,34 @@ +package space.kscience.kmath.data + +import space.kscience.kmath.misc.Symbol +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.nd.Structure2D +import space.kscience.kmath.structures.Buffer + +/** + * A column-based data set with all columns of the same size (not necessary fixed in time). + * The column could be retrieved by a [get] operation. + */ +@UnstableKMathAPI +public interface ColumnarData { + public val size: Int + + public operator fun get(symbol: Symbol): Buffer +} + +/** + * A zero-copy method to represent a [Structure2D] as a two-column x-y data. + * There could more than two columns in the structure. + */ +@UnstableKMathAPI +public fun Structure2D.asColumnarData(mapping: Map): ColumnarData { + require(shape[1] >= mapping.maxOf { it.value }) { "Column index out of bounds" } + return object : ColumnarData { + override val size: Int get() = shape[0] + override fun get(symbol: Symbol): Buffer { + val index = mapping[symbol] ?: error("No column mapping for symbol $symbol") + return columns[index] + } + } +} + diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/XYColumnarData.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/XYColumnarData.kt new file mode 100644 index 000000000..15239bca1 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/XYColumnarData.kt @@ -0,0 +1,55 @@ +package space.kscience.kmath.data + +import space.kscience.kmath.misc.Symbol +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.nd.Structure2D +import space.kscience.kmath.structures.Buffer +import kotlin.math.max + +/** + * The buffer of X values. + */ +@UnstableKMathAPI +public interface XYColumnarData : ColumnarData { + /** + * The buffer of X values + */ + public val x: Buffer + + /** + * The buffer of Y values. + */ + public val y: Buffer + + override fun get(symbol: Symbol): Buffer = when (symbol) { + Symbol.x -> x + Symbol.y -> y + else -> error("A column for symbol $symbol not found") + } +} + +@Suppress("FunctionName") +@UnstableKMathAPI +public fun XYColumnarData(x: Buffer, y: Buffer): XYColumnarData { + require(x.size == y.size) { "Buffer size mismatch. x buffer size is ${x.size}, y buffer size is ${y.size}" } + return object : XYColumnarData { + override val size: Int = x.size + override val x: Buffer = x + override val y: Buffer = y + } +} + + +/** + * A zero-copy method to represent a [Structure2D] as a two-column x-y data. + * There could more than two columns in the structure. + */ +@UnstableKMathAPI +public fun Structure2D.asXYData(xIndex: Int = 0, yIndex: Int = 1): XYColumnarData { + require(shape[1] >= max(xIndex, yIndex)) { "Column index out of bounds" } + return object : XYColumnarData { + override val size: Int get() = this@asXYData.shape[0] + override val x: Buffer get() = columns[xIndex] + override val y: Buffer get() = columns[yIndex] + } +} diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/XYZColumnarData.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/XYZColumnarData.kt new file mode 100644 index 000000000..f74c6e2d6 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/XYZColumnarData.kt @@ -0,0 +1,21 @@ +package space.kscience.kmath.data + +import space.kscience.kmath.misc.Symbol +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.structures.Buffer + +/** + * A [XYColumnarData] with guaranteed [x], [y] and [z] columns designated by corresponding symbols. + * Inherits [XYColumnarData]. + */ +@UnstableKMathAPI +public interface XYZColumnarData : XYColumnarData { + public val z: Buffer + + override fun get(symbol: Symbol): Buffer = when (symbol) { + Symbol.x -> x + Symbol.y -> y + Symbol.z -> z + else -> error("A column for symbol $symbol not found") + } +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/ColumnarData.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/ColumnarData.kt deleted file mode 100644 index ed5a7e8f0..000000000 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/ColumnarData.kt +++ /dev/null @@ -1,15 +0,0 @@ -package space.kscience.kmath.misc - -import space.kscience.kmath.structures.Buffer - -/** - * A column-based data set with all columns of the same size (not necessary fixed in time). - * The column could be retrieved by a [get] operation. - */ -@UnstableKMathAPI -public interface ColumnarData { - public val size: Int - - public operator fun get(symbol: Symbol): Buffer -} - diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/XYPointSet.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/XYPointSet.kt deleted file mode 100644 index 51582974f..000000000 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/XYPointSet.kt +++ /dev/null @@ -1,98 +0,0 @@ -package space.kscience.kmath.misc - -import space.kscience.kmath.nd.Structure2D -import space.kscience.kmath.structures.Buffer - -/** - * Pair of associated buffers for X and Y axes values. - * - * @param X the type of X values. - * @param Y the type of Y values. - */ -public interface XYPointSet { - /** - * The size of all the involved buffers. - */ - public val size: Int - - /** - * The buffer of X values. - */ -@UnstableKMathAPI -public interface XYPointSet : ColumnarData { - public val x: Buffer - - /** - * The buffer of Y values. - */ - public val y: Buffer - - override fun get(symbol: Symbol): Buffer = when (symbol) { - Symbol.x -> x - Symbol.y -> y - else -> error("A column for symbol $symbol not found") - } -} - -/** - * Triple of associated buffers for X, Y, and Z axes values. - * - * @param X the type of X values. - * @param Y the type of Y values. - * @param Z the type of Z values. - */ -public interface XYZPointSet : XYPointSet { - /** - * The buffer of Z values. - */ -@UnstableKMathAPI -public interface XYZPointSet : XYPointSet { - public val z: Buffer - - override fun get(symbol: Symbol): Buffer = when (symbol) { - Symbol.x -> x - Symbol.y -> y - Symbol.z -> z - else -> error("A column for symbol $symbol not found") - } -} - -internal fun > insureSorted(points: XYPointSet) { - for (i in 0 until points.size - 1) - require(points.x[i + 1] > points.x[i]) { "Input data is not sorted at index $i" } -} - -public class NDStructureColumn(public val structure: Structure2D, public val column: Int) : Buffer { - public override val size: Int - get() = structure.rowNum - - init { - require(column < structure.colNum) { "Column index is outside of structure column range" } - } - - public override operator fun get(index: Int): T = structure[index, column] - public override operator fun iterator(): Iterator = sequence { repeat(size) { yield(get(it)) } }.iterator() -} - -@UnstableKMathAPI -public class BufferXYPointSet( - public override val x: Buffer, - public override val y: Buffer, -) : XYPointSet { - public override val size: Int get() = x.size - - init { - require(x.size == y.size) { "Sizes of x and y buffers should be the same" } - } -} - -@UnstableKMathAPI -public fun Structure2D.asXYPointSet(): XYPointSet { - require(shape[1] == 2) { "Structure second dimension should be of size 2" } - - return object : XYPointSet { - override val size: Int get() = this@asXYPointSet.shape[0] - override val x: Buffer get() = NDStructureColumn(this@asXYPointSet, 0) - override val y: Buffer get() = NDStructureColumn(this@asXYPointSet, 1) - } -} diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/Interpolator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/Interpolator.kt index 864431d7a..9fad30abb 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/Interpolator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/Interpolator.kt @@ -1,17 +1,17 @@ @file:OptIn(UnstableKMathAPI::class) + package space.kscience.kmath.interpolation +import space.kscience.kmath.data.XYColumnarData import space.kscience.kmath.functions.PiecewisePolynomial import space.kscience.kmath.functions.value -import space.kscience.kmath.misc.BufferXYPointSet import space.kscience.kmath.misc.UnstableKMathAPI -import space.kscience.kmath.misc.XYPointSet import space.kscience.kmath.operations.Ring import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.asBuffer public fun interface Interpolator { - public fun interpolate(points: XYPointSet): (X) -> Y + public fun interpolate(points: XYColumnarData): (X) -> Y } public interface PolynomialInterpolator> : Interpolator { @@ -19,9 +19,9 @@ public interface PolynomialInterpolator> : Interpolator): PiecewisePolynomial + public fun interpolatePolynomials(points: XYColumnarData): PiecewisePolynomial - override fun interpolate(points: XYPointSet): (T) -> T = { x -> + override fun interpolate(points: XYColumnarData): (T) -> T = { x -> interpolatePolynomials(points).value(algebra, x) ?: getDefaultValue() } } @@ -31,20 +31,20 @@ public fun > PolynomialInterpolator.interpolatePolynomials( x: Buffer, y: Buffer, ): PiecewisePolynomial { - val pointSet = BufferXYPointSet(x, y) + val pointSet = XYColumnarData(x, y) return interpolatePolynomials(pointSet) } public fun > PolynomialInterpolator.interpolatePolynomials( data: Map, ): PiecewisePolynomial { - val pointSet = BufferXYPointSet(data.keys.toList().asBuffer(), data.values.toList().asBuffer()) + val pointSet = XYColumnarData(data.keys.toList().asBuffer(), data.values.toList().asBuffer()) return interpolatePolynomials(pointSet) } public fun > PolynomialInterpolator.interpolatePolynomials( data: List>, ): PiecewisePolynomial { - val pointSet = BufferXYPointSet(data.map { it.first }.asBuffer(), data.map { it.second }.asBuffer()) + val pointSet = XYColumnarData(data.map { it.first }.asBuffer(), data.map { it.second }.asBuffer()) return interpolatePolynomials(pointSet) } diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/LinearInterpolator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/LinearInterpolator.kt index 89a242ece..37d378ad0 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/LinearInterpolator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/LinearInterpolator.kt @@ -1,15 +1,15 @@ package space.kscience.kmath.interpolation +import space.kscience.kmath.data.XYColumnarData import space.kscience.kmath.functions.OrderedPiecewisePolynomial import space.kscience.kmath.functions.PiecewisePolynomial import space.kscience.kmath.functions.Polynomial import space.kscience.kmath.misc.UnstableKMathAPI -import space.kscience.kmath.misc.XYPointSet import space.kscience.kmath.operations.Field import space.kscience.kmath.operations.invoke @OptIn(UnstableKMathAPI::class) -internal fun > insureSorted(points: XYPointSet<*, T, *>) { +internal fun > insureSorted(points: XYColumnarData<*, T, *>) { for (i in 0 until points.size - 1) require(points.x[i + 1] > points.x[i]) { "Input data is not sorted at index $i" } } @@ -19,7 +19,7 @@ internal fun > insureSorted(points: XYPointSet<*, T, *>) { */ public class LinearInterpolator>(public override val algebra: Field) : PolynomialInterpolator { @OptIn(UnstableKMathAPI::class) - public override fun interpolatePolynomials(points: XYPointSet): PiecewisePolynomial = algebra { + public override fun interpolatePolynomials(points: XYColumnarData): PiecewisePolynomial = algebra { require(points.size > 0) { "Point array should not be empty" } insureSorted(points) diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt index 0756e2901..3a3dfab59 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt @@ -1,10 +1,10 @@ package space.kscience.kmath.interpolation +import space.kscience.kmath.data.XYColumnarData import space.kscience.kmath.functions.OrderedPiecewisePolynomial import space.kscience.kmath.functions.PiecewisePolynomial import space.kscience.kmath.functions.Polynomial import space.kscience.kmath.misc.UnstableKMathAPI -import space.kscience.kmath.misc.XYPointSet import space.kscience.kmath.operations.Field import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.MutableBufferFactory @@ -23,7 +23,7 @@ public class SplineInterpolator>( //TODO possibly optimize zeroed buffers @OptIn(UnstableKMathAPI::class) - public override fun interpolatePolynomials(points: XYPointSet): PiecewisePolynomial = algebra { + public override fun interpolatePolynomials(points: XYColumnarData): PiecewisePolynomial = algebra { require(points.size >= 3) { "Can't use spline interpolator with less than 3 points" } insureSorted(points) // Number of intervals. The number of data points is n + 1. diff --git a/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/DifferentiableMstExpression.kt b/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/DifferentiableMstExpression.kt index fe27b7e4d..1275b0c90 100644 --- a/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/DifferentiableMstExpression.kt +++ b/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/DifferentiableMstExpression.kt @@ -18,8 +18,10 @@ import space.kscience.kmath.operations.NumericAlgebra * @param A the [NumericAlgebra] of [T]. * @property expr the underlying [MstExpression]. */ -public inline class DifferentiableMstExpression(public val expr: MstExpression) : - DifferentiableExpression> where A : NumericAlgebra, T : Number { +public inline class DifferentiableMstExpression( + public val expr: MstExpression, +) : DifferentiableExpression> where A : NumericAlgebra { + public constructor(algebra: A, mst: MST) : this(MstExpression(algebra, mst)) /** diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/DataFit.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/DataFit.kt deleted file mode 100644 index 70dd8417c..000000000 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/DataFit.kt +++ /dev/null @@ -1,17 +0,0 @@ -package space.kscience.kmath.optimization - -import space.kscience.kmath.expressions.DifferentiableExpression -import space.kscience.kmath.misc.StringSymbol -import space.kscience.kmath.misc.Symbol -import space.kscience.kmath.structures.Buffer - -public interface DataFit : Optimization { - - public fun modelAndData( - x: Buffer, - y: Buffer, - yErr: Buffer, - model: DifferentiableExpression, - xSymbol: Symbol = StringSymbol("x"), - ) -} \ No newline at end of file diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/FunctionOptimization.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/FunctionOptimization.kt index 02aa9e9bb..528a5744e 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/FunctionOptimization.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/FunctionOptimization.kt @@ -4,45 +4,31 @@ import space.kscience.kmath.expressions.AutoDiffProcessor import space.kscience.kmath.expressions.DifferentiableExpression import space.kscience.kmath.expressions.Expression import space.kscience.kmath.expressions.ExpressionAlgebra -import space.kscience.kmath.misc.StringSymbol import space.kscience.kmath.misc.Symbol import space.kscience.kmath.operations.ExtendedField import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.indices -import kotlin.math.pow /** - * A likelihood function optimization problem + * A likelihood function optimization problem with provided derivatives */ -public interface FunctionOptimization: Optimization, DataFit { +public interface FunctionOptimization : Optimization { + /** + * The optimization direction. If true search for function maximum, if false, search for the minimum + */ + public var maximize: Boolean + /** * Define the initial guess for the optimization problem */ public fun initialGuess(map: Map) - /** - * Set an objective function expression - */ - public fun expression(expression: Expression) - /** * Set a differentiable expression as objective function as function and gradient provider */ - public fun diffExpression(expression: DifferentiableExpression>) + public fun diffFunction(expression: DifferentiableExpression>) - override fun modelAndData( - x: Buffer, - y: Buffer, - yErr: Buffer, - model: DifferentiableExpression, - xSymbol: Symbol, - ) { - require(x.size == y.size) { "X and y buffers should be of the same size" } - require(y.size == yErr.size) { "Y and yErr buffer should of the same size" } - - } - - public companion object{ + public companion object { /** * Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic differentiation */ @@ -70,46 +56,22 @@ public interface FunctionOptimization: Optimization, DataFit { sum } } - - /** - * Generate a chi squared expression from given x-y-sigma model represented by an expression. Does not provide derivatives - */ - public fun chiSquared( - x: Buffer, - y: Buffer, - yErr: Buffer, - model: Expression, - xSymbol: Symbol = StringSymbol("x"), - ): Expression { - require(x.size == y.size) { "X and y buffers should be of the same size" } - require(y.size == yErr.size) { "Y and yErr buffer should of the same size" } - - return Expression { arguments -> - x.indices.sumByDouble { - val xValue = x[it] - val yValue = y[it] - val yErrValue = yErr[it] - val modifiedArgs = arguments + (xSymbol to xValue) - val modelValue = model(modifiedArgs) - ((yValue - modelValue) / yErrValue).pow(2) - } - } - } } } /** - * Optimize expression without derivatives using specific [OptimizationProblemFactory] + * Define a chi-squared-based objective function */ -public fun > Expression.optimizeWith( - factory: OptimizationProblemFactory, - vararg symbols: Symbol, - configuration: F.() -> Unit, -): OptimizationResult { - require(symbols.isNotEmpty()) { "Must provide a list of symbols for optimization" } - val problem = factory(symbols.toList(), configuration) - problem.expression(this) - return problem.optimize() +public fun FunctionOptimization.chiSquared( + autoDiff: AutoDiffProcessor>, + x: Buffer, + y: Buffer, + yErr: Buffer, + model: A.(I) -> I, +) where A : ExtendedField, A : ExpressionAlgebra { + val chiSquared = FunctionOptimization.chiSquared(autoDiff, x, y, yErr, model) + diffFunction(chiSquared) + maximize = false } /** @@ -122,6 +84,6 @@ public fun > DifferentiableExpression { require(symbols.isNotEmpty()) { "Must provide a list of symbols for optimization" } val problem = factory(symbols.toList(), configuration) - problem.diffExpression(this) + problem.diffFunction(this) return problem.optimize() } diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/NoDerivFunctionOptimization.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/NoDerivFunctionOptimization.kt new file mode 100644 index 000000000..b8785dd8c --- /dev/null +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/NoDerivFunctionOptimization.kt @@ -0,0 +1,69 @@ +package space.kscience.kmath.optimization + +import space.kscience.kmath.expressions.Expression +import space.kscience.kmath.misc.Symbol +import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.indices +import kotlin.math.pow + +/** + * A likelihood function optimization problem + */ +public interface NoDerivFunctionOptimization : Optimization { + /** + * The optimization direction. If true search for function maximum, if false, search for the minimum + */ + public var maximize: Boolean + + /** + * Define the initial guess for the optimization problem + */ + public fun initialGuess(map: Map) + + /** + * Set an objective function expression + */ + public fun function(expression: Expression) + + public companion object { + /** + * Generate a chi squared expression from given x-y-sigma model represented by an expression. Does not provide derivatives + */ + public fun chiSquared( + x: Buffer, + y: Buffer, + yErr: Buffer, + model: Expression, + xSymbol: Symbol = Symbol.x, + ): Expression { + require(x.size == y.size) { "X and y buffers should be of the same size" } + require(y.size == yErr.size) { "Y and yErr buffer should of the same size" } + + return Expression { arguments -> + x.indices.sumByDouble { + val xValue = x[it] + val yValue = y[it] + val yErrValue = yErr[it] + val modifiedArgs = arguments + (xSymbol to xValue) + val modelValue = model(modifiedArgs) + ((yValue - modelValue) / yErrValue).pow(2) + } + } + } + } +} + + +/** + * Optimize expression without derivatives using specific [OptimizationProblemFactory] + */ +public fun > Expression.noDerivOptimizeWith( + factory: OptimizationProblemFactory, + vararg symbols: Symbol, + configuration: F.() -> Unit, +): OptimizationResult { + require(symbols.isNotEmpty()) { "Must provide a list of symbols for optimization" } + val problem = factory(symbols.toList(), configuration) + problem.function(this) + return problem.optimize() +} diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/XYFit.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/XYFit.kt new file mode 100644 index 000000000..c3106c819 --- /dev/null +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/XYFit.kt @@ -0,0 +1,40 @@ +package space.kscience.kmath.optimization + +import space.kscience.kmath.data.ColumnarData +import space.kscience.kmath.expressions.AutoDiffProcessor +import space.kscience.kmath.expressions.DifferentiableExpression +import space.kscience.kmath.expressions.Expression +import space.kscience.kmath.expressions.ExpressionAlgebra +import space.kscience.kmath.misc.Symbol +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.operations.ExtendedField +import space.kscience.kmath.operations.Field + +@UnstableKMathAPI +public interface XYFit : Optimization { + + public val algebra: Field + + /** + * Set X-Y data for this fit optionally including x and y errors + */ + public fun data( + dataSet: ColumnarData, + xSymbol: Symbol, + ySymbol: Symbol, + xErrSymbol: Symbol? = null, + yErrSymbol: Symbol? = null, + ) + + public fun model(model: (T) -> DifferentiableExpression) + + /** + * Set the differentiable model for this fit + */ + public fun model( + autoDiff: AutoDiffProcessor>, + modelFunction: A.(I) -> I, + ): Unit where A : ExtendedField, A : ExpressionAlgebra = model { arg -> + autoDiff.process { modelFunction(const(arg)) } + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index b4d7b3049..4467d5ed6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,12 +4,11 @@ pluginManagement { mavenLocal() gradlePluginPortal() jcenter() - maven("https://dl.bintray.com/kotlin/kotlin-eap") maven("https://dl.bintray.com/kotlin/kotlinx") } - val toolsVersion = "0.9.1" - val kotlinVersion = "1.4.31" + val toolsVersion = "0.9.3" + val kotlinVersion = "1.4.32" plugins { id("kotlinx.benchmark") version "0.2.0-dev-20" From 9574099f9bb7062d5fe364189f7521f760ccad8c Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Wed, 31 Mar 2021 02:24:21 +0700 Subject: [PATCH 31/31] Fix post-merge issues --- examples/build.gradle.kts | 1 + .../kmath/commons/fit/fitWithAutoDiff.kt | 34 ++-- .../kmath/stat/DistributionBenchmark.kt | 4 +- .../kscience/kmath/stat/DistributionDemo.kt | 8 +- .../commons/optimization/OptimizeTest.kt | 29 ++-- .../space/kscience/kmath/structures/Buffer.kt | 157 +++++++++--------- .../kmath/chains/BlockingRealChain.kt | 9 - .../kmath/chains/BlockingDoubleChain.kt | 13 +- .../kscience/dimensions/DMatrixContextTest.kt | 2 +- .../kscience/kmath/stat/SamplerAlgebra.kt | 34 ---- .../space/kscience/kmath/stat/Distribution.kt | 19 ++- .../space/kscience/kmath/stat/RandomChain.kt | 10 +- .../kscience/kmath/stat/SamplerAlgebra.kt | 28 +++- .../stat/distributions/NormalDistribution.kt | 16 +- .../kmath/stat/internal/InternalErf.kt | 2 +- .../kmath/stat/internal/InternalGamma.kt | 2 +- .../kmath/stat/internal/InternalUtils.kt | 2 +- .../AhrensDieterExponentialSampler.kt | 12 +- .../AhrensDieterMarsagliaTsangGammaSampler.kt | 12 +- .../samplers/AliasMethodDiscreteSampler.kt | 12 +- .../BoxMullerNormalizedGaussianSampler.kt | 10 +- .../kmath/stat/samplers/GaussianSampler.kt | 10 +- .../samplers/KempSmallMeanPoissonSampler.kt | 10 +- .../stat/samplers/LargeMeanPoissonSampler.kt | 16 +- .../MarsagliaNormalizedGaussianSampler.kt | 10 +- .../samplers/NormalizedGaussianSampler.kt | 4 +- .../kmath/stat/samplers/PoissonSampler.kt | 8 +- .../stat/samplers/SmallMeanPoissonSampler.kt | 10 +- .../ZigguratNormalizedGaussianSampler.kt | 10 +- .../kmath/stat/CommonsDistributionsTest.kt | 2 +- 30 files changed, 234 insertions(+), 262 deletions(-) delete mode 100644 kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingRealChain.kt delete mode 100644 kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/SamplerAlgebra.kt rename kmath-stat/src/commonMain/kotlin/{ => space}/kscience/kmath/stat/distributions/NormalDistribution.kt (72%) rename kmath-stat/src/commonMain/kotlin/{ => space}/kscience/kmath/stat/internal/InternalErf.kt (90%) rename kmath-stat/src/commonMain/kotlin/{ => space}/kscience/kmath/stat/internal/InternalGamma.kt (99%) rename kmath-stat/src/commonMain/kotlin/{ => space}/kscience/kmath/stat/internal/InternalUtils.kt (98%) rename kmath-stat/src/commonMain/kotlin/{ => space}/kscience/kmath/stat/samplers/AhrensDieterExponentialSampler.kt (88%) rename kmath-stat/src/commonMain/kotlin/{ => space}/kscience/kmath/stat/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt (94%) rename kmath-stat/src/commonMain/kotlin/{ => space}/kscience/kmath/stat/samplers/AliasMethodDiscreteSampler.kt (97%) rename kmath-stat/src/commonMain/kotlin/{ => space}/kscience/kmath/stat/samplers/BoxMullerNormalizedGaussianSampler.kt (88%) rename kmath-stat/src/commonMain/kotlin/{ => space}/kscience/kmath/stat/samplers/GaussianSampler.kt (86%) rename kmath-stat/src/commonMain/kotlin/{ => space}/kscience/kmath/stat/samplers/KempSmallMeanPoissonSampler.kt (92%) rename kmath-stat/src/commonMain/kotlin/{ => space}/kscience/kmath/stat/samplers/LargeMeanPoissonSampler.kt (92%) rename kmath-stat/src/commonMain/kotlin/{ => space}/kscience/kmath/stat/samplers/MarsagliaNormalizedGaussianSampler.kt (91%) rename kmath-stat/src/commonMain/kotlin/{ => space}/kscience/kmath/stat/samplers/NormalizedGaussianSampler.kt (72%) rename kmath-stat/src/commonMain/kotlin/{ => space}/kscience/kmath/stat/samplers/PoissonSampler.kt (88%) rename kmath-stat/src/commonMain/kotlin/{ => space}/kscience/kmath/stat/samplers/SmallMeanPoissonSampler.kt (88%) rename kmath-stat/src/commonMain/kotlin/{ => space}/kscience/kmath/stat/samplers/ZigguratNormalizedGaussianSampler.kt (93%) diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 5dd40b609..a48b4d0d9 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -106,6 +106,7 @@ kotlin.sourceSets.all { with(languageSettings) { useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts") useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes") + useExperimentalAnnotation("space.kscience.kmath.misc.UnstableKMathAPI") } } diff --git a/examples/src/main/kotlin/space/kscience/kmath/commons/fit/fitWithAutoDiff.kt b/examples/src/main/kotlin/space/kscience/kmath/commons/fit/fitWithAutoDiff.kt index 22d642d92..04c55b34c 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/commons/fit/fitWithAutoDiff.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/commons/fit/fitWithAutoDiff.kt @@ -1,20 +1,22 @@ -package kscience.kmath.commons.fit +package space.kscience.kmath.commons.fit import kotlinx.html.br import kotlinx.html.h3 -import kscience.kmath.commons.optimization.chiSquared -import kscience.kmath.commons.optimization.minimize -import kscience.kmath.expressions.symbol -import kscience.kmath.real.RealVector -import kscience.kmath.real.map -import kscience.kmath.real.step -import kscience.kmath.stat.* -import kscience.kmath.stat.distributions.NormalDistribution -import kscience.kmath.structures.asIterable -import kscience.kmath.structures.toList import kscience.plotly.* import kscience.plotly.models.ScatterMode import kscience.plotly.models.TraceValues +import space.kscience.kmath.commons.optimization.chiSquared +import space.kscience.kmath.commons.optimization.minimize +import space.kscience.kmath.misc.symbol +import space.kscience.kmath.optimization.FunctionOptimization +import space.kscience.kmath.optimization.OptimizationResult +import space.kscience.kmath.real.DoubleVector +import space.kscience.kmath.real.map +import space.kscience.kmath.real.step +import space.kscience.kmath.stat.RandomGenerator +import space.kscience.kmath.stat.distributions.NormalDistribution +import space.kscience.kmath.structures.asIterable +import space.kscience.kmath.structures.toList import kotlin.math.pow import kotlin.math.sqrt @@ -27,7 +29,7 @@ private val c by symbol /** * Shortcut to use buffers in plotly */ -operator fun TraceValues.invoke(vector: RealVector) { +operator fun TraceValues.invoke(vector: DoubleVector) { numbers = vector.asIterable() } @@ -58,12 +60,12 @@ suspend fun main() { val yErr = y.map { sqrt(it) }//RealVector.same(x.size, sigma) // compute differentiable chi^2 sum for given model ax^2 + bx + c - val chi2 = Fitting.chiSquared(x, y, yErr) { x1 -> + val chi2 = FunctionOptimization.chiSquared(x, y, yErr) { x1 -> //bind variables to autodiff context val a = bind(a) val b = bind(b) //Include default value for c if it is not provided as a parameter - val c = bindOrNull(c) ?: one + val c = bindSymbolOrNull(c) ?: one a * x1.pow(2) + b * x1 + c } @@ -90,10 +92,10 @@ suspend fun main() { } } br() - h3{ + h3 { +"Fit result: $result" } - h3{ + h3 { +"Chi2/dof = ${result.value / (x.size - 3)}" } } diff --git a/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionBenchmark.kt b/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionBenchmark.kt index 4478903d9..bfd138502 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionBenchmark.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionBenchmark.kt @@ -1,9 +1,9 @@ -package kscience.kmath.stat +package space.kscience.kmath.stat import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking -import kscience.kmath.stat.samplers.GaussianSampler +import space.kscience.kmath.stat.samplers.GaussianSampler import org.apache.commons.rng.simple.RandomSource import java.time.Duration import java.time.Instant diff --git a/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionDemo.kt b/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionDemo.kt index dd04c12e5..aac7d51d4 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionDemo.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/stat/DistributionDemo.kt @@ -1,9 +1,9 @@ -package kscience.kmath.stat +package space.kscience.kmath.stat import kotlinx.coroutines.runBlocking -import kscience.kmath.chains.Chain -import kscience.kmath.chains.collectWithState -import kscience.kmath.stat.distributions.NormalDistribution +import space.kscience.kmath.chains.Chain +import space.kscience.kmath.chains.collectWithState +import space.kscience.kmath.stat.distributions.NormalDistribution /** * The state of distribution averager. diff --git a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt index 184d209d5..36f2639f4 100644 --- a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt +++ b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt @@ -1,13 +1,13 @@ -package kscience.kmath.commons.optimization +package space.kscience.kmath.commons.optimization import kotlinx.coroutines.runBlocking -import kscience.kmath.commons.expressions.DerivativeStructureExpression -import kscience.kmath.expressions.symbol -import kscience.kmath.stat.Fitting -import kscience.kmath.stat.RandomGenerator -import kscience.kmath.stat.distributions.NormalDistribution -import org.junit.jupiter.api.Test +import space.kscience.kmath.commons.expressions.DerivativeStructureExpression +import space.kscience.kmath.misc.symbol +import space.kscience.kmath.optimization.FunctionOptimization +import space.kscience.kmath.stat.RandomGenerator +import space.kscience.kmath.stat.distributions.NormalDistribution import kotlin.math.pow +import kotlin.test.Test internal class OptimizeTest { val x by symbol @@ -34,6 +34,7 @@ internal class OptimizeTest { simplexSteps(x to 2.0, y to 0.5) //this sets simplex optimizer } + println(result.point) println(result.value) } @@ -43,15 +44,20 @@ internal class OptimizeTest { val a by symbol val b by symbol val c by symbol + val sigma = 1.0 val generator = NormalDistribution(0.0, sigma) val chain = generator.sample(RandomGenerator.default(112667)) val x = (1..100).map(Int::toDouble) - val y = x.map { it.pow(2) + it + 1.0 + chain.next() } + + val y = x.map { + it.pow(2) + it + 1 + chain.next() + } + val yErr = List(x.size) { sigma } - val chi2 = Fitting.chiSquared(x, y, yErr) { x1 -> - val cWithDefault = bindOrNull(c) ?: one + val chi2 = FunctionOptimization.chiSquared(x, y, yErr) { x1 -> + val cWithDefault = bindSymbolOrNull(c) ?: one bind(a) * x1.pow(2) + bind(b) * x1 + cWithDefault } @@ -59,5 +65,4 @@ internal class OptimizeTest { println(result) println("Chi2/dof = ${result.value / (x.size - 3)}") } - -} \ No newline at end of file +} diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt index c5a51ca50..168a92c37 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt @@ -1,4 +1,4 @@ -package kscience.kmath.structures +package space.kscience.kmath.structures import kotlin.reflect.KClass @@ -17,11 +17,13 @@ public typealias BufferFactory = (Int, (Int) -> T) -> Buffer public typealias MutableBufferFactory = (Int, (Int) -> T) -> MutableBuffer /** - * A generic immutable random-access structure for both primitives and objects. + * A generic read-only random-access structure for both primitives and objects. + * + * [Buffer] is in general identity-free. [Buffer.contentEquals] should be used for content equality checks. * * @param T the type of elements contained in the buffer. */ -public interface Buffer { +public interface Buffer { /** * The size of this buffer. */ @@ -37,49 +39,45 @@ public interface Buffer { */ public operator fun iterator(): Iterator - /** - * Checks content equality with another buffer. - */ - public fun contentEquals(other: Buffer<*>): Boolean = - asSequence().mapIndexed { index, value -> value == other[index] }.all { it } - public companion object { /** - * Creates a [RealBuffer] with the specified [size], where each element is calculated by calling the specified - * [initializer] function. + * Check the element-by-element match of content of two buffers. */ - public inline fun real(size: Int, initializer: (Int) -> Double): RealBuffer = - RealBuffer(size) { initializer(it) } + public fun contentEquals(first: Buffer, second: Buffer): Boolean{ + if (first.size != second.size) return false + for (i in first.indices) { + if (first[i] != second[i]) return false + } + return true + } /** * Creates a [ListBuffer] of given type [T] with given [size]. Each element is calculated by calling the * specified [initializer] function. */ public inline fun boxing(size: Int, initializer: (Int) -> T): Buffer = - ListBuffer(List(size, initializer)) - - // TODO add resolution based on Annotation or companion resolution + List(size, initializer).asBuffer() /** * Creates a [Buffer] of given [type]. If the type is primitive, specialized buffers are used ([IntBuffer], - * [RealBuffer], etc.), [ListBuffer] is returned otherwise. + * [DoubleBuffer], etc.), [ListBuffer] is returned otherwise. * * The [size] is specified, and each element is calculated by calling the specified [initializer] function. */ @Suppress("UNCHECKED_CAST") public inline fun auto(type: KClass, size: Int, initializer: (Int) -> T): Buffer = when (type) { - Double::class -> real(size) { initializer(it) as Double } as Buffer - Short::class -> ShortBuffer(size) { initializer(it) as Short } as Buffer - Int::class -> IntBuffer(size) { initializer(it) as Int } as Buffer - Long::class -> LongBuffer(size) { initializer(it) as Long } as Buffer - Float::class -> FloatBuffer(size) { initializer(it) as Float } as Buffer + Double::class -> MutableBuffer.double(size) { initializer(it) as Double } as Buffer + Short::class -> MutableBuffer.short(size) { initializer(it) as Short } as Buffer + Int::class -> MutableBuffer.int(size) { initializer(it) as Int } as Buffer + Long::class -> MutableBuffer.long(size) { initializer(it) as Long } as Buffer + Float::class -> MutableBuffer.float(size) { initializer(it) as Float } as Buffer else -> boxing(size, initializer) } /** * Creates a [Buffer] of given type [T]. If the type is primitive, specialized buffers are used ([IntBuffer], - * [RealBuffer], etc.), [ListBuffer] is returned otherwise. + * [DoubleBuffer], etc.), [ListBuffer] is returned otherwise. * * The [size] is specified, and each element is calculated by calling the specified [initializer] function. */ @@ -89,21 +87,6 @@ public interface Buffer { } } -/** - * Creates a sequence that returns all elements from this [Buffer]. - */ -public fun Buffer.asSequence(): Sequence = Sequence(::iterator) - -/** - * Creates an iterable that returns all elements from this [Buffer]. - */ -public fun Buffer.asIterable(): Iterable = Iterable(::iterator) - -/** - * Converts this [Buffer] to a new [List] - */ -public fun Buffer.toList(): List = asSequence().toList() - /** * Returns an [IntRange] of the valid indices for this [Buffer]. */ @@ -126,6 +109,43 @@ public interface MutableBuffer : Buffer { public fun copy(): MutableBuffer public companion object { + /** + * Creates a [DoubleBuffer] with the specified [size], where each element is calculated by calling the specified + * [initializer] function. + */ + public inline fun double(size: Int, initializer: (Int) -> Double): DoubleBuffer = + DoubleBuffer(size, initializer) + + /** + * Creates a [ShortBuffer] with the specified [size], where each element is calculated by calling the specified + * [initializer] function. + */ + public inline fun short(size: Int, initializer: (Int) -> Short): ShortBuffer = + ShortBuffer(size, initializer) + + /** + * Creates a [IntBuffer] with the specified [size], where each element is calculated by calling the specified + * [initializer] function. + */ + public inline fun int(size: Int, initializer: (Int) -> Int): IntBuffer = + IntBuffer(size, initializer) + + /** + * Creates a [LongBuffer] with the specified [size], where each element is calculated by calling the specified + * [initializer] function. + */ + public inline fun long(size: Int, initializer: (Int) -> Long): LongBuffer = + LongBuffer(size, initializer) + + + /** + * Creates a [FloatBuffer] with the specified [size], where each element is calculated by calling the specified + * [initializer] function. + */ + public inline fun float(size: Int, initializer: (Int) -> Float): FloatBuffer = + FloatBuffer(size, initializer) + + /** * Create a boxing mutable buffer of given type */ @@ -134,37 +154,30 @@ public interface MutableBuffer : Buffer { /** * Creates a [MutableBuffer] of given [type]. If the type is primitive, specialized buffers are used - * ([IntBuffer], [RealBuffer], etc.), [ListBuffer] is returned otherwise. + * ([IntBuffer], [DoubleBuffer], etc.), [ListBuffer] is returned otherwise. * * The [size] is specified, and each element is calculated by calling the specified [initializer] function. */ @Suppress("UNCHECKED_CAST") public inline fun auto(type: KClass, size: Int, initializer: (Int) -> T): MutableBuffer = when (type) { - Double::class -> RealBuffer(size) { initializer(it) as Double } as MutableBuffer - Short::class -> ShortBuffer(size) { initializer(it) as Short } as MutableBuffer - Int::class -> IntBuffer(size) { initializer(it) as Int } as MutableBuffer - Float::class -> FloatBuffer(size) { initializer(it) as Float } as MutableBuffer - Long::class -> LongBuffer(size) { initializer(it) as Long } as MutableBuffer + Double::class -> double(size) { initializer(it) as Double } as MutableBuffer + Short::class -> short(size) { initializer(it) as Short } as MutableBuffer + Int::class -> int(size) { initializer(it) as Int } as MutableBuffer + Float::class -> float(size) { initializer(it) as Float } as MutableBuffer + Long::class -> long(size) { initializer(it) as Long } as MutableBuffer else -> boxing(size, initializer) } /** * Creates a [MutableBuffer] of given type [T]. If the type is primitive, specialized buffers are used - * ([IntBuffer], [RealBuffer], etc.), [ListBuffer] is returned otherwise. + * ([IntBuffer], [DoubleBuffer], etc.), [ListBuffer] is returned otherwise. * * The [size] is specified, and each element is calculated by calling the specified [initializer] function. */ @Suppress("UNCHECKED_CAST") public inline fun auto(size: Int, initializer: (Int) -> T): MutableBuffer = auto(T::class, size, initializer) - - /** - * Creates a [RealBuffer] with the specified [size], where each element is calculated by calling the specified - * [initializer] function. - */ - public inline fun real(size: Int, initializer: (Int) -> Double): RealBuffer = - RealBuffer(size) { initializer(it) } } } @@ -187,15 +200,6 @@ public inline class ListBuffer(public val list: List) : Buffer { */ public fun List.asBuffer(): ListBuffer = ListBuffer(this) -/** - * Creates a new [ListBuffer] with the specified [size], where each element is calculated by calling the specified - * [init] function. - * - * The function [init] is called for each array element sequentially starting from the first one. - * It should return the value for an array element given its index. - */ -public inline fun ListBuffer(size: Int, init: (Int) -> T): ListBuffer = List(size, init).asBuffer() - /** * [MutableBuffer] implementation over [MutableList]. * @@ -216,16 +220,20 @@ public inline class MutableListBuffer(public val list: MutableList) : Muta override fun copy(): MutableBuffer = MutableListBuffer(ArrayList(list)) } +/** + * Returns an [ListBuffer] that wraps the original list. + */ +public fun MutableList.asMutableBuffer(): MutableListBuffer = MutableListBuffer(this) + /** * [MutableBuffer] implementation over [Array]. * * @param T the type of elements contained in the buffer. * @property array The underlying array. */ -public class ArrayBuffer(private val array: Array) : MutableBuffer { +public class ArrayBuffer(internal val array: Array) : MutableBuffer { // Can't inline because array is invariant - override val size: Int - get() = array.size + override val size: Int get() = array.size override operator fun get(index: Int): T = array[index] @@ -237,6 +245,7 @@ public class ArrayBuffer(private val array: Array) : MutableBuffer { override fun copy(): MutableBuffer = ArrayBuffer(array.copyOf()) } + /** * Returns an [ArrayBuffer] that wraps the original array. */ @@ -269,27 +278,9 @@ public class VirtualBuffer(override val size: Int, private val generator: (In } override operator fun iterator(): Iterator = (0 until size).asSequence().map(generator).iterator() - - override fun contentEquals(other: Buffer<*>): Boolean { - return if (other is VirtualBuffer) { - this.size == other.size && this.generator == other.generator - } else { - super.contentEquals(other) - } - } } /** * Convert this buffer to read-only buffer. */ -public fun Buffer.asReadOnly(): Buffer = if (this is MutableBuffer) ReadOnlyBuffer(this) else this - -/** - * Typealias for buffer transformations. - */ -public typealias BufferTransform = (Buffer) -> Buffer - -/** - * Typealias for buffer transformations with suspend function. - */ -public typealias SuspendBufferTransform = suspend (Buffer) -> Buffer +public fun Buffer.asReadOnly(): Buffer = if (this is MutableBuffer) ReadOnlyBuffer(this) else this \ No newline at end of file diff --git a/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingRealChain.kt b/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingRealChain.kt deleted file mode 100644 index 7c463b109..000000000 --- a/kmath-coroutines/src/commonMain/kotlin/kscience/kmath/chains/BlockingRealChain.kt +++ /dev/null @@ -1,9 +0,0 @@ -package kscience.kmath.chains - -/** - * Performance optimized chain for real values - */ -public interface BlockingRealChain : Chain { - public override suspend fun next(): Double - public suspend fun nextBlock(size: Int): DoubleArray = DoubleArray(size) { next() } -} diff --git a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingDoubleChain.kt b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingDoubleChain.kt index ba6adf35b..d024147b4 100644 --- a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingDoubleChain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingDoubleChain.kt @@ -1,12 +1,13 @@ package space.kscience.kmath.chains /** - * Performance optimized chain for real values + * Chunked, specialized chain for real values. */ -public abstract class BlockingDoubleChain : Chain { - public abstract fun nextDouble(): Double +public interface BlockingDoubleChain : Chain { + public override suspend fun next(): Double - override suspend fun next(): Double = nextDouble() - - public open fun nextBlock(size: Int): DoubleArray = DoubleArray(size) { nextDouble() } + /** + * Returns an [DoubleArray] chunk of [size] values of [next]. + */ + public suspend fun nextBlock(size: Int): DoubleArray = DoubleArray(size) { next() } } diff --git a/kmath-dimensions/src/commonTest/kotlin/kscience/dimensions/DMatrixContextTest.kt b/kmath-dimensions/src/commonTest/kotlin/kscience/dimensions/DMatrixContextTest.kt index e2a9628ac..58ed82723 100644 --- a/kmath-dimensions/src/commonTest/kotlin/kscience/dimensions/DMatrixContextTest.kt +++ b/kmath-dimensions/src/commonTest/kotlin/kscience/dimensions/DMatrixContextTest.kt @@ -1,4 +1,4 @@ -package kscience.dimensions +package space.kscience.dimensions import space.kscience.kmath.dimensions.D2 import space.kscience.kmath.dimensions.D3 diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/SamplerAlgebra.kt b/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/SamplerAlgebra.kt deleted file mode 100644 index 8413424de..000000000 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/SamplerAlgebra.kt +++ /dev/null @@ -1,34 +0,0 @@ -package kscience.kmath.stat - -import kscience.kmath.chains.Chain -import kscience.kmath.chains.ConstantChain -import kscience.kmath.chains.map -import kscience.kmath.chains.zip -import kscience.kmath.operations.Space -import kscience.kmath.operations.invoke - -/** - * Implements [Sampler] by sampling only certain [value]. - * - * @property value the value to sample. - */ -public class ConstantSampler(public val value: T) : Sampler { - public override fun sample(generator: RandomGenerator): Chain = ConstantChain(value) -} - -/** - * A space of samplers. Allows to perform simple operations on distributions. - * - * @property space the space to provide addition and scalar multiplication for [T]. - */ -public class SamplerSpace(public val space: Space) : Space> { - public override val zero: Sampler = ConstantSampler(space.zero) - - public override fun add(a: Sampler, b: Sampler): Sampler = Sampler { generator -> - a.sample(generator).zip(b.sample(generator)) { aValue, bValue -> space { aValue + bValue } } - } - - public override fun multiply(a: Sampler, k: Number): Sampler = Sampler { generator -> - a.sample(generator).map { space { it * k.toDouble() } } - } -} diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Distribution.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Distribution.kt index f85ba5d68..095182160 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Distribution.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Distribution.kt @@ -1,11 +1,12 @@ -package kscience.kmath.stat +package space.kscience.kmath.stat import kotlinx.coroutines.flow.first -import kscience.kmath.chains.Chain -import kscience.kmath.chains.collect -import kscience.kmath.structures.Buffer -import kscience.kmath.structures.BufferFactory -import kscience.kmath.structures.IntBuffer +import space.kscience.kmath.chains.Chain +import space.kscience.kmath.chains.collect +import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.BufferFactory +import space.kscience.kmath.structures.IntBuffer +import space.kscience.kmath.structures.MutableBuffer import kotlin.jvm.JvmName /** @@ -22,7 +23,7 @@ public fun interface Sampler { } /** - * A distribution of typed objects + * A distribution of typed objects. */ public interface Distribution : Sampler { /** @@ -60,7 +61,7 @@ public fun > UnivariateDistribution.integral(from: T, to: T public fun Sampler.sampleBuffer( generator: RandomGenerator, size: Int, - bufferFactory: BufferFactory = Buffer.Companion::boxing + bufferFactory: BufferFactory = Buffer.Companion::boxing, ): Chain> { require(size > 1) //creating temporary storage once @@ -86,7 +87,7 @@ public suspend fun Sampler.next(generator: RandomGenerator): T = sa */ @JvmName("sampleRealBuffer") public fun Sampler.sampleBuffer(generator: RandomGenerator, size: Int): Chain> = - sampleBuffer(generator, size, Buffer.Companion::real) + sampleBuffer(generator, size, MutableBuffer.Companion::double) /** * Generates [size] integer samples and chunks them into some buffers. diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/RandomChain.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/RandomChain.kt index daad7392a..2f117a035 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/RandomChain.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/RandomChain.kt @@ -1,8 +1,8 @@ -package kscience.kmath.stat +package space.kscience.kmath.stat -import kscience.kmath.chains.BlockingIntChain -import kscience.kmath.chains.BlockingRealChain -import kscience.kmath.chains.Chain +import space.kscience.kmath.chains.BlockingDoubleChain +import space.kscience.kmath.chains.BlockingIntChain +import space.kscience.kmath.chains.Chain /** * A possibly stateful chain producing random values. @@ -18,5 +18,5 @@ public class RandomChain( } public fun RandomGenerator.chain(gen: suspend RandomGenerator.() -> R): RandomChain = RandomChain(this, gen) -public fun Chain.blocking(): BlockingRealChain = object : Chain by this, BlockingRealChain {} +public fun Chain.blocking(): BlockingDoubleChain = object : Chain by this, BlockingDoubleChain {} public fun Chain.blocking(): BlockingIntChain = object : Chain by this, BlockingIntChain {} diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/SamplerAlgebra.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/SamplerAlgebra.kt index c5ec99dae..25ec7eca6 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/SamplerAlgebra.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/SamplerAlgebra.kt @@ -8,16 +8,28 @@ import space.kscience.kmath.operations.Group import space.kscience.kmath.operations.ScaleOperations import space.kscience.kmath.operations.invoke -public class BasicSampler(public val chainBuilder: (RandomGenerator) -> Chain) : Sampler { - public override fun sample(generator: RandomGenerator): Chain = chainBuilder(generator) -} - +/** + * Implements [Sampler] by sampling only certain [value]. + * + * @property value the value to sample. + */ public class ConstantSampler(public val value: T) : Sampler { public override fun sample(generator: RandomGenerator): Chain = ConstantChain(value) } /** - * A space for samplers. Allows to perform simple operations on distributions + * Implements [Sampler] by delegating sampling to value of [chainBuilder]. + * + * @property chainBuilder the provider of [Chain]. + */ +public class BasicSampler(public val chainBuilder: (RandomGenerator) -> Chain) : Sampler { + public override fun sample(generator: RandomGenerator): Chain = chainBuilder(generator) +} + +/** + * A space of samplers. Allows to perform simple operations on distributions. + * + * @property algebra the space to provide addition and scalar multiplication for [T]. */ public class SamplerSpace(public val algebra: S) : Group>, ScaleOperations> where S : Group, S : ScaleOperations { @@ -29,8 +41,10 @@ public class SamplerSpace(public val algebra: S) : Group> } public override fun scale(a: Sampler, value: Double): Sampler = BasicSampler { generator -> - a.sample(generator).map { algebra { it * value } } + a.sample(generator).map { a -> + algebra { a * value } + } } - override fun Sampler.unaryMinus(): Sampler = scale(this, -1.0) + public override fun Sampler.unaryMinus(): Sampler = scale(this, -1.0) } diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/distributions/NormalDistribution.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/distributions/NormalDistribution.kt similarity index 72% rename from kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/distributions/NormalDistribution.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/distributions/NormalDistribution.kt index 9059a0038..6515cbaa7 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/distributions/NormalDistribution.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/distributions/NormalDistribution.kt @@ -1,12 +1,12 @@ -package kscience.kmath.stat.distributions +package space.kscience.kmath.stat.distributions -import kscience.kmath.chains.Chain -import kscience.kmath.stat.RandomGenerator -import kscience.kmath.stat.UnivariateDistribution -import kscience.kmath.stat.internal.InternalErf -import kscience.kmath.stat.samplers.GaussianSampler -import kscience.kmath.stat.samplers.NormalizedGaussianSampler -import kscience.kmath.stat.samplers.ZigguratNormalizedGaussianSampler +import space.kscience.kmath.chains.Chain +import space.kscience.kmath.stat.RandomGenerator +import space.kscience.kmath.stat.UnivariateDistribution +import space.kscience.kmath.stat.internal.InternalErf +import space.kscience.kmath.stat.samplers.GaussianSampler +import space.kscience.kmath.stat.samplers.NormalizedGaussianSampler +import space.kscience.kmath.stat.samplers.ZigguratNormalizedGaussianSampler import kotlin.math.* /** diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalErf.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/internal/InternalErf.kt similarity index 90% rename from kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalErf.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/internal/InternalErf.kt index 04eb3ef0e..4e1623867 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalErf.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/internal/InternalErf.kt @@ -1,4 +1,4 @@ -package kscience.kmath.stat.internal +package space.kscience.kmath.stat.internal import kotlin.math.abs diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalGamma.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/internal/InternalGamma.kt similarity index 99% rename from kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalGamma.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/internal/InternalGamma.kt index dce84712a..4f5adbe97 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalGamma.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/internal/InternalGamma.kt @@ -1,4 +1,4 @@ -package kscience.kmath.stat.internal +package space.kscience.kmath.stat.internal import kotlin.math.* diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalUtils.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/internal/InternalUtils.kt similarity index 98% rename from kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalUtils.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/internal/InternalUtils.kt index 9997eeb57..722eee946 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/internal/InternalUtils.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/internal/InternalUtils.kt @@ -1,4 +1,4 @@ -package kscience.kmath.stat.internal +package space.kscience.kmath.stat.internal import kotlin.math.ln import kotlin.math.min diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterExponentialSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/AhrensDieterExponentialSampler.kt similarity index 88% rename from kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterExponentialSampler.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/AhrensDieterExponentialSampler.kt index 853e952bb..504c6b881 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterExponentialSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/AhrensDieterExponentialSampler.kt @@ -1,10 +1,10 @@ -package kscience.kmath.stat.samplers +package space.kscience.kmath.stat.samplers -import kscience.kmath.chains.Chain -import kscience.kmath.stat.RandomGenerator -import kscience.kmath.stat.Sampler -import kscience.kmath.stat.chain -import kscience.kmath.stat.internal.InternalUtils +import space.kscience.kmath.chains.Chain +import space.kscience.kmath.stat.RandomGenerator +import space.kscience.kmath.stat.Sampler +import space.kscience.kmath.stat.chain +import space.kscience.kmath.stat.internal.InternalUtils import kotlin.math.ln import kotlin.math.pow diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt similarity index 94% rename from kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt index 98cb83332..81182f6cd 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/AhrensDieterMarsagliaTsangGammaSampler.kt @@ -1,10 +1,10 @@ -package kscience.kmath.stat.samplers +package space.kscience.kmath.stat.samplers -import kscience.kmath.chains.Chain -import kscience.kmath.stat.RandomGenerator -import kscience.kmath.stat.Sampler -import kscience.kmath.stat.chain -import kscience.kmath.stat.next +import space.kscience.kmath.chains.Chain +import space.kscience.kmath.stat.RandomGenerator +import space.kscience.kmath.stat.Sampler +import space.kscience.kmath.stat.chain +import space.kscience.kmath.stat.next import kotlin.math.* /** diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AliasMethodDiscreteSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/AliasMethodDiscreteSampler.kt similarity index 97% rename from kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AliasMethodDiscreteSampler.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/AliasMethodDiscreteSampler.kt index a4dc537b5..cae97db65 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/AliasMethodDiscreteSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/AliasMethodDiscreteSampler.kt @@ -1,10 +1,10 @@ -package kscience.kmath.stat.samplers +package space.kscience.kmath.stat.samplers -import kscience.kmath.chains.Chain -import kscience.kmath.stat.RandomGenerator -import kscience.kmath.stat.Sampler -import kscience.kmath.stat.chain -import kscience.kmath.stat.internal.InternalUtils +import space.kscience.kmath.chains.Chain +import space.kscience.kmath.stat.RandomGenerator +import space.kscience.kmath.stat.Sampler +import space.kscience.kmath.stat.chain +import space.kscience.kmath.stat.internal.InternalUtils import kotlin.math.ceil import kotlin.math.max import kotlin.math.min diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/BoxMullerNormalizedGaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/BoxMullerNormalizedGaussianSampler.kt similarity index 88% rename from kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/BoxMullerNormalizedGaussianSampler.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/BoxMullerNormalizedGaussianSampler.kt index 20412f64b..04beb448d 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/BoxMullerNormalizedGaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/BoxMullerNormalizedGaussianSampler.kt @@ -1,9 +1,9 @@ -package kscience.kmath.stat.samplers +package space.kscience.kmath.stat.samplers -import kscience.kmath.chains.Chain -import kscience.kmath.stat.RandomGenerator -import kscience.kmath.stat.Sampler -import kscience.kmath.stat.chain +import space.kscience.kmath.chains.Chain +import space.kscience.kmath.stat.RandomGenerator +import space.kscience.kmath.stat.Sampler +import space.kscience.kmath.stat.chain import kotlin.math.* /** diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/GaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/GaussianSampler.kt similarity index 86% rename from kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/GaussianSampler.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/GaussianSampler.kt index 92dd27d02..eba26cfb5 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/GaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/GaussianSampler.kt @@ -1,9 +1,9 @@ -package kscience.kmath.stat.samplers +package space.kscience.kmath.stat.samplers -import kscience.kmath.chains.Chain -import kscience.kmath.chains.map -import kscience.kmath.stat.RandomGenerator -import kscience.kmath.stat.Sampler +import space.kscience.kmath.chains.Chain +import space.kscience.kmath.chains.map +import space.kscience.kmath.stat.RandomGenerator +import space.kscience.kmath.stat.Sampler /** * Sampling from a Gaussian distribution with given mean and standard deviation. diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/KempSmallMeanPoissonSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/KempSmallMeanPoissonSampler.kt similarity index 92% rename from kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/KempSmallMeanPoissonSampler.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/KempSmallMeanPoissonSampler.kt index 78da230ca..1d7f90023 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/KempSmallMeanPoissonSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/KempSmallMeanPoissonSampler.kt @@ -1,9 +1,9 @@ -package kscience.kmath.stat.samplers +package space.kscience.kmath.stat.samplers -import kscience.kmath.chains.Chain -import kscience.kmath.stat.RandomGenerator -import kscience.kmath.stat.Sampler -import kscience.kmath.stat.chain +import space.kscience.kmath.chains.Chain +import space.kscience.kmath.stat.RandomGenerator +import space.kscience.kmath.stat.Sampler +import space.kscience.kmath.stat.chain import kotlin.math.exp /** diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/LargeMeanPoissonSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/LargeMeanPoissonSampler.kt similarity index 92% rename from kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/LargeMeanPoissonSampler.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/LargeMeanPoissonSampler.kt index c880d7a20..de1e7cc89 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/LargeMeanPoissonSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/LargeMeanPoissonSampler.kt @@ -1,12 +1,12 @@ -package kscience.kmath.stat.samplers +package space.kscience.kmath.stat.samplers -import kscience.kmath.chains.Chain -import kscience.kmath.chains.ConstantChain -import kscience.kmath.stat.RandomGenerator -import kscience.kmath.stat.Sampler -import kscience.kmath.stat.chain -import kscience.kmath.stat.internal.InternalUtils -import kscience.kmath.stat.next +import space.kscience.kmath.chains.Chain +import space.kscience.kmath.chains.ConstantChain +import space.kscience.kmath.stat.RandomGenerator +import space.kscience.kmath.stat.Sampler +import space.kscience.kmath.stat.chain +import space.kscience.kmath.stat.internal.InternalUtils +import space.kscience.kmath.stat.next import kotlin.math.* /** diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/MarsagliaNormalizedGaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/MarsagliaNormalizedGaussianSampler.kt similarity index 91% rename from kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/MarsagliaNormalizedGaussianSampler.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/MarsagliaNormalizedGaussianSampler.kt index 8077f9f75..8a659642f 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/MarsagliaNormalizedGaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/MarsagliaNormalizedGaussianSampler.kt @@ -1,9 +1,9 @@ -package kscience.kmath.stat.samplers +package space.kscience.kmath.stat.samplers -import kscience.kmath.chains.Chain -import kscience.kmath.stat.RandomGenerator -import kscience.kmath.stat.Sampler -import kscience.kmath.stat.chain +import space.kscience.kmath.chains.Chain +import space.kscience.kmath.stat.RandomGenerator +import space.kscience.kmath.stat.Sampler +import space.kscience.kmath.stat.chain import kotlin.math.ln import kotlin.math.sqrt diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/NormalizedGaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/NormalizedGaussianSampler.kt similarity index 72% rename from kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/NormalizedGaussianSampler.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/NormalizedGaussianSampler.kt index 8995d3030..4eb3d60e0 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/NormalizedGaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/NormalizedGaussianSampler.kt @@ -1,6 +1,6 @@ -package kscience.kmath.stat.samplers +package space.kscience.kmath.stat.samplers -import kscience.kmath.stat.Sampler +import space.kscience.kmath.stat.Sampler /** * Marker interface for a sampler that generates values from an N(0,1) diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/PoissonSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/PoissonSampler.kt similarity index 88% rename from kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/PoissonSampler.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/PoissonSampler.kt index e9a6244a6..0c0234892 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/PoissonSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/PoissonSampler.kt @@ -1,8 +1,8 @@ -package kscience.kmath.stat.samplers +package space.kscience.kmath.stat.samplers -import kscience.kmath.chains.Chain -import kscience.kmath.stat.RandomGenerator -import kscience.kmath.stat.Sampler +import space.kscience.kmath.chains.Chain +import space.kscience.kmath.stat.RandomGenerator +import space.kscience.kmath.stat.Sampler /** * Sampler for the Poisson distribution. diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/SmallMeanPoissonSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/SmallMeanPoissonSampler.kt similarity index 88% rename from kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/SmallMeanPoissonSampler.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/SmallMeanPoissonSampler.kt index e9ce8a6a6..0fe7ff161 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/SmallMeanPoissonSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/SmallMeanPoissonSampler.kt @@ -1,9 +1,9 @@ -package kscience.kmath.stat.samplers +package space.kscience.kmath.stat.samplers -import kscience.kmath.chains.Chain -import kscience.kmath.stat.RandomGenerator -import kscience.kmath.stat.Sampler -import kscience.kmath.stat.chain +import space.kscience.kmath.chains.Chain +import space.kscience.kmath.stat.RandomGenerator +import space.kscience.kmath.stat.Sampler +import space.kscience.kmath.stat.chain import kotlin.math.ceil import kotlin.math.exp diff --git a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/ZigguratNormalizedGaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/ZigguratNormalizedGaussianSampler.kt similarity index 93% rename from kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/ZigguratNormalizedGaussianSampler.kt rename to kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/ZigguratNormalizedGaussianSampler.kt index e9e8d91da..90815209f 100644 --- a/kmath-stat/src/commonMain/kotlin/kscience/kmath/stat/samplers/ZigguratNormalizedGaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/samplers/ZigguratNormalizedGaussianSampler.kt @@ -1,9 +1,9 @@ -package kscience.kmath.stat.samplers +package space.kscience.kmath.stat.samplers -import kscience.kmath.chains.Chain -import kscience.kmath.stat.RandomGenerator -import kscience.kmath.stat.Sampler -import kscience.kmath.stat.chain +import space.kscience.kmath.chains.Chain +import space.kscience.kmath.stat.RandomGenerator +import space.kscience.kmath.stat.Sampler +import space.kscience.kmath.stat.chain import kotlin.math.* /** diff --git a/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/CommonsDistributionsTest.kt b/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/CommonsDistributionsTest.kt index 59320e6b5..76aac65c4 100644 --- a/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/CommonsDistributionsTest.kt +++ b/kmath-stat/src/jvmTest/kotlin/space/kscience/kmath/stat/CommonsDistributionsTest.kt @@ -3,9 +3,9 @@ package space.kscience.kmath.stat import kotlinx.coroutines.flow.take import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking -import kscience.kmath.stat.samplers.GaussianSampler import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test +import space.kscience.kmath.stat.samplers.GaussianSampler internal class CommonsDistributionsTest { @Test