Dev #72
@ -1,6 +1,9 @@
|
|||||||
package scientifik.kmath.linear
|
package scientifik.kmath.linear
|
||||||
|
|
||||||
import koma.matrix.ejml.EJMLMatrixFactory
|
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.operations.RealField
|
||||||
import scientifik.kmath.structures.Matrix
|
import scientifik.kmath.structures.Matrix
|
||||||
import kotlin.contracts.ExperimentalContracts
|
import kotlin.contracts.ExperimentalContracts
|
@ -1,6 +1,8 @@
|
|||||||
package scientifik.kmath.linear
|
package scientifik.kmath.linear
|
||||||
|
|
||||||
import koma.matrix.ejml.EJMLMatrixFactory
|
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.operations.RealField
|
||||||
import scientifik.kmath.structures.Matrix
|
import scientifik.kmath.structures.Matrix
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
@ -8,6 +8,7 @@ description = "Commons math binding for kmath"
|
|||||||
dependencies {
|
dependencies {
|
||||||
api(project(":kmath-core"))
|
api(project(":kmath-core"))
|
||||||
api(project(":kmath-coroutines"))
|
api(project(":kmath-coroutines"))
|
||||||
|
api(project(":kmath-prob"))
|
||||||
api("org.apache.commons:commons-math3:3.6.1")
|
api("org.apache.commons:commons-math3:3.6.1")
|
||||||
testImplementation("org.jetbrains.kotlin:kotlin-test")
|
testImplementation("org.jetbrains.kotlin:kotlin-test")
|
||||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
|
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 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.ExtendedField
|
||||||
import scientifik.kmath.operations.Field
|
import scientifik.kmath.operations.Field
|
||||||
import kotlin.properties.ReadOnlyProperty
|
import kotlin.properties.ReadOnlyProperty
|
||||||
@ -81,8 +83,12 @@ class DerivativeStructureField(
|
|||||||
/**
|
/**
|
||||||
* A constructs that creates a derivative structure with required order on-demand
|
* A constructs that creates a derivative structure with required order on-demand
|
||||||
*/
|
*/
|
||||||
class DiffExpression(val function: DerivativeStructureField.() -> DerivativeStructure) : Expression<Double> {
|
class DiffExpression(val function: DerivativeStructureField.() -> DerivativeStructure) :
|
||||||
override fun invoke(arguments: Map<String, Double>): Double = DerivativeStructureField(0, arguments)
|
Expression<Double> {
|
||||||
|
override fun invoke(arguments: Map<String, Double>): Double = DerivativeStructureField(
|
||||||
|
0,
|
||||||
|
arguments
|
||||||
|
)
|
||||||
.run(function).value
|
.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])
|
* A context for [DiffExpression] (not to be confused with [DerivativeStructure])
|
||||||
*/
|
*/
|
||||||
object DiffExpressionContext : ExpressionContext<Double>, Field<DiffExpression> {
|
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 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 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.*
|
||||||
import org.apache.commons.math3.linear.RealMatrix
|
import org.apache.commons.math3.linear.RealMatrix
|
||||||
import org.apache.commons.math3.linear.RealVector
|
import org.apache.commons.math3.linear.RealVector
|
||||||
|
import scientifik.kmath.linear.*
|
||||||
import scientifik.kmath.structures.Matrix
|
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 rowNum: Int get() = origin.rowDimension
|
||||||
override val colNum: Int get() = origin.columnDimension
|
override val colNum: Int get() = origin.columnDimension
|
||||||
|
|
||||||
@ -70,10 +72,14 @@ object CMMatrixContext : MatrixContext<Double> {
|
|||||||
override fun multiply(a: Matrix<Double>, k: Number) =
|
override fun multiply(a: Matrix<Double>, k: Number) =
|
||||||
CMMatrix(a.toCM().origin.scalarMultiply(k.toDouble()))
|
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.plus(other: CMMatrix): CMMatrix =
|
||||||
operator fun CMMatrix.minus(other: CMMatrix): CMMatrix = CMMatrix(this.origin.subtract(other.origin))
|
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 org.apache.commons.math3.linear.*
|
||||||
|
import scientifik.kmath.linear.Point
|
||||||
import scientifik.kmath.structures.Matrix
|
import scientifik.kmath.structures.Matrix
|
||||||
|
|
||||||
enum class CMDecomposition {
|
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.FlowPreview
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
@ -1,6 +1,9 @@
|
|||||||
package scientifik.kmath.expressions
|
package scientifik.kmath.commons.expressions
|
||||||
|
|
||||||
import org.junit.Test
|
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
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
inline fun <R> diff(order: Int, vararg parameters: Pair<String, Double>, block: DerivativeStructureField.() -> R) =
|
inline fun <R> diff(order: Int, vararg parameters: Pair<String, Double>, block: DerivativeStructureField.() -> R) =
|
@ -13,7 +13,7 @@ kotlin.sourceSets {
|
|||||||
jvmMain {
|
jvmMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
// https://mvnrepository.com/artifact/org.apache.commons/commons-rng-simple
|
// 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}")
|
compileOnly("org.jetbrains.kotlinx:atomicfu:${Versions.atomicfuVersion}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,13 @@ interface Distribution<T : Any> {
|
|||||||
* Create a chain of samples from this distribution.
|
* Create a chain of samples from this distribution.
|
||||||
* The chain is not guaranteed to be stateless.
|
* 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
|
//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> {
|
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-commons",
|
||||||
":kmath-koma",
|
":kmath-koma",
|
||||||
":kmath-prob",
|
":kmath-prob",
|
||||||
":benchmarks"
|
":examples"
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user