Distribution implementations for commons-math
This commit is contained in:
parent
2e76073712
commit
9c2164cd64
@ -1,6 +1,9 @@
|
||||
package scientifik.kmath.linear
|
||||
|
||||
import koma.matrix.ejml.EJMLMatrixFactory
|
||||
import scientifik.kmath.commons.linear.CMMatrixContext
|
||||
import scientifik.kmath.commons.linear.inverse
|
||||
import scientifik.kmath.commons.linear.toCM
|
||||
import scientifik.kmath.operations.RealField
|
||||
import scientifik.kmath.structures.Matrix
|
||||
import kotlin.contracts.ExperimentalContracts
|
@ -1,6 +1,8 @@
|
||||
package scientifik.kmath.linear
|
||||
|
||||
import koma.matrix.ejml.EJMLMatrixFactory
|
||||
import scientifik.kmath.commons.linear.CMMatrixContext
|
||||
import scientifik.kmath.commons.linear.toCM
|
||||
import scientifik.kmath.operations.RealField
|
||||
import scientifik.kmath.structures.Matrix
|
||||
import kotlin.random.Random
|
@ -8,6 +8,7 @@ description = "Commons math binding for kmath"
|
||||
dependencies {
|
||||
api(project(":kmath-core"))
|
||||
api(project(":kmath-coroutines"))
|
||||
api(project(":kmath-prob"))
|
||||
api("org.apache.commons:commons-math3:3.6.1")
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test")
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
|
||||
|
@ -1,6 +1,8 @@
|
||||
package scientifik.kmath.expressions
|
||||
package scientifik.kmath.commons.expressions
|
||||
|
||||
import org.apache.commons.math3.analysis.differentiation.DerivativeStructure
|
||||
import scientifik.kmath.expressions.Expression
|
||||
import scientifik.kmath.expressions.ExpressionContext
|
||||
import scientifik.kmath.operations.ExtendedField
|
||||
import scientifik.kmath.operations.Field
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
@ -81,8 +83,12 @@ class DerivativeStructureField(
|
||||
/**
|
||||
* A constructs that creates a derivative structure with required order on-demand
|
||||
*/
|
||||
class DiffExpression(val function: DerivativeStructureField.() -> DerivativeStructure) : Expression<Double> {
|
||||
override fun invoke(arguments: Map<String, Double>): Double = DerivativeStructureField(0, arguments)
|
||||
class DiffExpression(val function: DerivativeStructureField.() -> DerivativeStructure) :
|
||||
Expression<Double> {
|
||||
override fun invoke(arguments: Map<String, Double>): Double = DerivativeStructureField(
|
||||
0,
|
||||
arguments
|
||||
)
|
||||
.run(function).value
|
||||
|
||||
/**
|
||||
@ -109,21 +115,27 @@ fun DiffExpression.derivative(name: String) = derivative(name to 1)
|
||||
* A context for [DiffExpression] (not to be confused with [DerivativeStructure])
|
||||
*/
|
||||
object DiffExpressionContext : ExpressionContext<Double>, Field<DiffExpression> {
|
||||
override fun variable(name: String, default: Double?) = DiffExpression { variable(name, default?.const()) }
|
||||
override fun variable(name: String, default: Double?) =
|
||||
DiffExpression { variable(name, default?.const()) }
|
||||
|
||||
override fun const(value: Double): DiffExpression = DiffExpression { value.const() }
|
||||
override fun const(value: Double): DiffExpression =
|
||||
DiffExpression { value.const() }
|
||||
|
||||
override fun add(a: DiffExpression, b: DiffExpression) = DiffExpression { a.function(this) + b.function(this) }
|
||||
override fun add(a: DiffExpression, b: DiffExpression) =
|
||||
DiffExpression { a.function(this) + b.function(this) }
|
||||
|
||||
override val zero = DiffExpression { 0.0.const() }
|
||||
|
||||
override fun multiply(a: DiffExpression, k: Number) = DiffExpression { a.function(this) * k }
|
||||
override fun multiply(a: DiffExpression, k: Number) =
|
||||
DiffExpression { a.function(this) * k }
|
||||
|
||||
override val one = DiffExpression { 1.0.const() }
|
||||
|
||||
override fun multiply(a: DiffExpression, b: DiffExpression) = DiffExpression { a.function(this) * b.function(this) }
|
||||
override fun multiply(a: DiffExpression, b: DiffExpression) =
|
||||
DiffExpression { a.function(this) * b.function(this) }
|
||||
|
||||
override fun divide(a: DiffExpression, b: DiffExpression) = DiffExpression { a.function(this) / b.function(this) }
|
||||
override fun divide(a: DiffExpression, b: DiffExpression) =
|
||||
DiffExpression { a.function(this) / b.function(this) }
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,13 @@
|
||||
package scientifik.kmath.linear
|
||||
package scientifik.kmath.commons.linear
|
||||
|
||||
import org.apache.commons.math3.linear.*
|
||||
import org.apache.commons.math3.linear.RealMatrix
|
||||
import org.apache.commons.math3.linear.RealVector
|
||||
import scientifik.kmath.linear.*
|
||||
import scientifik.kmath.structures.Matrix
|
||||
|
||||
class CMMatrix(val origin: RealMatrix, features: Set<MatrixFeature>? = null) : FeaturedMatrix<Double> {
|
||||
class CMMatrix(val origin: RealMatrix, features: Set<MatrixFeature>? = null) :
|
||||
FeaturedMatrix<Double> {
|
||||
override val rowNum: Int get() = origin.rowDimension
|
||||
override val colNum: Int get() = origin.columnDimension
|
||||
|
||||
@ -70,10 +72,14 @@ object CMMatrixContext : MatrixContext<Double> {
|
||||
override fun multiply(a: Matrix<Double>, k: Number) =
|
||||
CMMatrix(a.toCM().origin.scalarMultiply(k.toDouble()))
|
||||
|
||||
override fun Matrix<Double>.times(value: Double): Matrix<Double> = produce(rowNum,colNum){i,j-> get(i,j)*value}
|
||||
override fun Matrix<Double>.times(value: Double): Matrix<Double> =
|
||||
produce(rowNum, colNum) { i, j -> get(i, j) * value }
|
||||
}
|
||||
|
||||
operator fun CMMatrix.plus(other: CMMatrix): CMMatrix = CMMatrix(this.origin.add(other.origin))
|
||||
operator fun CMMatrix.minus(other: CMMatrix): CMMatrix = CMMatrix(this.origin.subtract(other.origin))
|
||||
operator fun CMMatrix.plus(other: CMMatrix): CMMatrix =
|
||||
CMMatrix(this.origin.add(other.origin))
|
||||
operator fun CMMatrix.minus(other: CMMatrix): CMMatrix =
|
||||
CMMatrix(this.origin.subtract(other.origin))
|
||||
|
||||
infix fun CMMatrix.dot(other: CMMatrix): CMMatrix = CMMatrix(this.origin.multiply(other.origin))
|
||||
infix fun CMMatrix.dot(other: CMMatrix): CMMatrix =
|
||||
CMMatrix(this.origin.multiply(other.origin))
|
@ -1,6 +1,7 @@
|
||||
package scientifik.kmath.linear
|
||||
package scientifik.kmath.commons.linear
|
||||
|
||||
import org.apache.commons.math3.linear.*
|
||||
import scientifik.kmath.linear.Point
|
||||
import scientifik.kmath.structures.Matrix
|
||||
|
||||
enum class CMDecomposition {
|
@ -0,0 +1,18 @@
|
||||
package scientifik.kmath.commons.prob
|
||||
|
||||
import org.apache.commons.math3.random.RandomGenerator
|
||||
|
||||
inline class CMRandomGeneratorWrapper(val generator: RandomGenerator) : scientifik.kmath.prob.RandomGenerator {
|
||||
override fun nextDouble(): Double = generator.nextDouble()
|
||||
|
||||
override fun nextInt(): Int = generator.nextInt()
|
||||
|
||||
override fun nextLong(): Long = generator.nextLong()
|
||||
|
||||
override fun nextBlock(size: Int): ByteArray = ByteArray(size).apply { generator.nextBytes(this) }
|
||||
}
|
||||
|
||||
fun RandomGenerator.asKmathGenerator() = CMRandomGeneratorWrapper(this)
|
||||
|
||||
fun scientifik.kmath.prob.RandomGenerator.asCMGenerator() =
|
||||
(this as? CMRandomGeneratorWrapper)?.generator ?: TODO("Implement reverse CM wrapper")
|
@ -0,0 +1,84 @@
|
||||
package scientifik.kmath.commons.prob
|
||||
|
||||
import org.apache.commons.math3.distribution.*
|
||||
import scientifik.kmath.chains.Chain
|
||||
import scientifik.kmath.chains.SimpleChain
|
||||
import scientifik.kmath.prob.Distribution
|
||||
import scientifik.kmath.prob.RandomChain
|
||||
import scientifik.kmath.prob.RandomGenerator
|
||||
import scientifik.kmath.prob.UnivariateDistribution
|
||||
import org.apache.commons.math3.random.RandomGenerator as CMRandom
|
||||
|
||||
class CMRealDistributionWrapper(val builder: (CMRandom?) -> RealDistribution) : UnivariateDistribution<Double> {
|
||||
|
||||
private val defaultDistribution by lazy { builder(null) }
|
||||
|
||||
override fun probability(arg: Double): Double = defaultDistribution.probability(arg)
|
||||
|
||||
override fun cumulative(arg: Double): Double = defaultDistribution.cumulativeProbability(arg)
|
||||
|
||||
override fun sample(generator: RandomGenerator): RandomChain<Double> {
|
||||
val distribution = builder(generator.asCMGenerator())
|
||||
return RandomChain(generator) { distribution.sample() }
|
||||
}
|
||||
}
|
||||
|
||||
class CMIntDistributionWrapper(val builder: (CMRandom?) -> IntegerDistribution) : UnivariateDistribution<Int> {
|
||||
|
||||
private val defaultDistribution by lazy { builder(null) }
|
||||
|
||||
override fun probability(arg: Int): Double = defaultDistribution.probability(arg)
|
||||
|
||||
override fun cumulative(arg: Int): Double = defaultDistribution.cumulativeProbability(arg)
|
||||
|
||||
override fun sample(generator: RandomGenerator): RandomChain<Int> {
|
||||
val distribution = builder(generator.asCMGenerator())
|
||||
return RandomChain(generator) { distribution.sample() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun Distribution.Companion.normal(mean: Double, sigma: Double): UnivariateDistribution<Double> =
|
||||
CMRealDistributionWrapper { generator -> NormalDistribution(generator, mean, sigma) }
|
||||
|
||||
fun Distribution.Companion.poisson(mean: Double): UnivariateDistribution<Int> = CMIntDistributionWrapper { generator ->
|
||||
PoissonDistribution(
|
||||
generator,
|
||||
mean,
|
||||
PoissonDistribution.DEFAULT_EPSILON,
|
||||
PoissonDistribution.DEFAULT_MAX_ITERATIONS
|
||||
)
|
||||
}
|
||||
|
||||
fun Distribution.Companion.binomial(trials: Int, p: Double): UnivariateDistribution<Int> =
|
||||
CMIntDistributionWrapper { generator ->
|
||||
BinomialDistribution(generator, trials, p)
|
||||
}
|
||||
|
||||
fun Distribution.Companion.student(degreesOfFreedom: Double): UnivariateDistribution<Double> =
|
||||
CMRealDistributionWrapper { generator ->
|
||||
TDistribution(generator, degreesOfFreedom, TDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY)
|
||||
}
|
||||
|
||||
fun Distribution.Companion.chi2(degreesOfFreedom: Double): UnivariateDistribution<Double> =
|
||||
CMRealDistributionWrapper { generator ->
|
||||
ChiSquaredDistribution(generator, degreesOfFreedom)
|
||||
}
|
||||
|
||||
fun Distribution.Companion.fisher(
|
||||
numeratorDegreesOfFreedom: Double,
|
||||
denominatorDegreesOfFreedom: Double
|
||||
): UnivariateDistribution<Double> =
|
||||
CMRealDistributionWrapper { generator ->
|
||||
FDistribution(generator, numeratorDegreesOfFreedom, denominatorDegreesOfFreedom)
|
||||
}
|
||||
|
||||
fun Distribution.Companion.exponential(mean: Double): UnivariateDistribution<Double> =
|
||||
CMRealDistributionWrapper { generator ->
|
||||
ExponentialDistribution(generator, mean)
|
||||
}
|
||||
|
||||
fun Distribution.Companion.uniform(a: Double, b: Double): UnivariateDistribution<Double> =
|
||||
CMRealDistributionWrapper { generator ->
|
||||
UniformRealDistribution(generator, a, b)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package scientifik.kmath.transform
|
||||
package scientifik.kmath.commons.transform
|
||||
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.Flow
|
@ -1,6 +1,9 @@
|
||||
package scientifik.kmath.expressions
|
||||
package scientifik.kmath.commons.expressions
|
||||
|
||||
import org.junit.Test
|
||||
import scientifik.kmath.commons.expressions.DerivativeStructureField
|
||||
import scientifik.kmath.commons.expressions.DiffExpression
|
||||
import scientifik.kmath.commons.expressions.derivative
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
inline fun <R> diff(order: Int, vararg parameters: Pair<String, Double>, block: DerivativeStructureField.() -> R) =
|
@ -13,7 +13,7 @@ kotlin.sourceSets {
|
||||
jvmMain {
|
||||
dependencies {
|
||||
// https://mvnrepository.com/artifact/org.apache.commons/commons-rng-simple
|
||||
api("org.apache.commons:commons-rng-sampling:1.2")
|
||||
//api("org.apache.commons:commons-rng-sampling:1.2")
|
||||
compileOnly("org.jetbrains.kotlinx:atomicfu:${Versions.atomicfuVersion}")
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,13 @@ interface Distribution<T : Any> {
|
||||
* Create a chain of samples from this distribution.
|
||||
* The chain is not guaranteed to be stateless.
|
||||
*/
|
||||
fun sample(generator: RandomGenerator): Chain<T>
|
||||
fun sample(generator: RandomGenerator): RandomChain<T>
|
||||
//TODO add sample bunch generator
|
||||
|
||||
/**
|
||||
* An empty companion. Distribution factories should be written as its extensions
|
||||
*/
|
||||
companion object
|
||||
}
|
||||
|
||||
interface UnivariateDistribution<T : Comparable<T>> : Distribution<T> {
|
||||
|
@ -0,0 +1,17 @@
|
||||
package scientifik.kmath.prob
|
||||
|
||||
import kotlinx.atomicfu.atomic
|
||||
import scientifik.kmath.chains.Chain
|
||||
|
||||
/**
|
||||
* A possibly stateful chain producing random values.
|
||||
* TODO make random chain properly fork generator
|
||||
*/
|
||||
class RandomChain<out R>(val generator: RandomGenerator, private val gen: suspend RandomGenerator.() -> R) : Chain<R> {
|
||||
private val atomicValue = atomic<R?>(null)
|
||||
override fun peek(): R? = atomicValue.value
|
||||
|
||||
override suspend fun next(): R = generator.gen().also { atomicValue.lazySet(it) }
|
||||
|
||||
override fun fork(): Chain<R> = RandomChain(generator, gen)
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
package scientifik.kmath.prob
|
||||
|
||||
import org.apache.commons.rng.sampling.distribution.*
|
||||
import scientifik.kmath.chains.Chain
|
||||
import scientifik.kmath.chains.SimpleChain
|
||||
import kotlin.math.PI
|
||||
import kotlin.math.exp
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class NormalDistribution(val mean: Double, val sigma: Double) : UnivariateDistribution<Double> {
|
||||
enum class Sampler {
|
||||
BoxMuller,
|
||||
Marsaglia,
|
||||
Ziggurat
|
||||
}
|
||||
|
||||
override fun probability(arg: Double): Double {
|
||||
val d = (arg - mean) / sigma
|
||||
return 1.0 / sqrt(2.0 * PI * sigma) * exp(-d * d / 2)
|
||||
}
|
||||
|
||||
override fun cumulative(arg: Double): Double {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
fun sample(generator: RandomGenerator, sampler: Sampler): Chain<Double> {
|
||||
val normalized = when (sampler) {
|
||||
Sampler.BoxMuller -> BoxMullerNormalizedGaussianSampler(generator.asProvider())
|
||||
Sampler.Marsaglia -> MarsagliaNormalizedGaussianSampler(generator.asProvider())
|
||||
Sampler.Ziggurat -> ZigguratNormalizedGaussianSampler(generator.asProvider())
|
||||
}
|
||||
val gauss = GaussianSampler(normalized, mean, sigma)
|
||||
//TODO add generator to chain state to allow stateful forks
|
||||
return SimpleChain { gauss.sample() }
|
||||
}
|
||||
|
||||
override fun sample(generator: RandomGenerator): Chain<Double> = sample(generator, Sampler.BoxMuller)
|
||||
}
|
||||
|
||||
class PoissonDistribution(val mean: Double): UnivariateDistribution<Int>{
|
||||
override fun probability(arg: Int): Double {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun cumulative(arg: Int): Double {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun sample(generator: RandomGenerator): Chain<Int> {
|
||||
val sampler = PoissonSampler(generator.asProvider(), mean)
|
||||
return SimpleChain{sampler.sample()}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package scientifik.kmath.prob
|
||||
|
||||
import org.apache.commons.rng.UniformRandomProvider
|
||||
|
||||
inline class CommonsRandomProviderWrapper(val provider: UniformRandomProvider) : RandomGenerator {
|
||||
override fun nextDouble(): Double = provider.nextDouble()
|
||||
|
||||
override fun nextInt(): Int = provider.nextInt()
|
||||
|
||||
override fun nextLong(): Long = provider.nextLong()
|
||||
|
||||
override fun nextBlock(size: Int): ByteArray = ByteArray(size).also { provider.nextBytes(it) }
|
||||
}
|
||||
|
||||
fun UniformRandomProvider.asGenerator(): RandomGenerator = CommonsRandomProviderWrapper(this)
|
||||
|
||||
fun RandomGenerator.asProvider(): UniformRandomProvider =
|
||||
(this as? CommonsRandomProviderWrapper)?.provider ?: TODO("implement reverse wrapper")
|
@ -28,5 +28,5 @@ include(
|
||||
":kmath-commons",
|
||||
":kmath-koma",
|
||||
":kmath-prob",
|
||||
":benchmarks"
|
||||
":examples"
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user