Feature/advanced optimization #404

Merged
altavir merged 17 commits from feature/advanced-optimization into dev 2021-08-29 11:44:10 +03:00
28 changed files with 477 additions and 281 deletions
Showing only changes of commit aaa298616d - Show all commits

View File

@ -20,6 +20,7 @@ dependencies {
implementation(project(":kmath-coroutines")) implementation(project(":kmath-coroutines"))
implementation(project(":kmath-commons")) implementation(project(":kmath-commons"))
implementation(project(":kmath-complex")) implementation(project(":kmath-complex"))
implementation(project(":kmath-optimization"))
implementation(project(":kmath-stat")) implementation(project(":kmath-stat"))
implementation(project(":kmath-viktor")) implementation(project(":kmath-viktor"))
implementation(project(":kmath-dimensions")) implementation(project(":kmath-dimensions"))

View File

@ -3,19 +3,17 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.commons.fit package space.kscience.kmath.fit
import kotlinx.html.br import kotlinx.html.br
import kotlinx.html.h3 import kotlinx.html.h3
import space.kscience.kmath.commons.expressions.DSProcessor import space.kscience.kmath.commons.expressions.DSProcessor
import space.kscience.kmath.commons.optimization.CMOptimizer import space.kscience.kmath.commons.optimization.CMOptimizer
import space.kscience.kmath.distributions.NormalDistribution import space.kscience.kmath.distributions.NormalDistribution
import space.kscience.kmath.expressions.binding
import space.kscience.kmath.expressions.chiSquaredExpression import space.kscience.kmath.expressions.chiSquaredExpression
import space.kscience.kmath.expressions.symbol import space.kscience.kmath.expressions.symbol
import space.kscience.kmath.optimization.FunctionOptimizationTarget import space.kscience.kmath.optimization.*
import space.kscience.kmath.optimization.optimizeWith
import space.kscience.kmath.optimization.resultPoint
import space.kscience.kmath.optimization.resultValue
import space.kscience.kmath.real.DoubleVector import space.kscience.kmath.real.DoubleVector
import space.kscience.kmath.real.map import space.kscience.kmath.real.map
import space.kscience.kmath.real.step import space.kscience.kmath.real.step
@ -25,6 +23,7 @@ import space.kscience.kmath.structures.toList
import space.kscience.plotly.* import space.kscience.plotly.*
import space.kscience.plotly.models.ScatterMode import space.kscience.plotly.models.ScatterMode
import space.kscience.plotly.models.TraceValues import space.kscience.plotly.models.TraceValues
import kotlin.math.abs
import kotlin.math.pow import kotlin.math.pow
import kotlin.math.sqrt import kotlin.math.sqrt
@ -45,7 +44,7 @@ operator fun TraceValues.invoke(vector: DoubleVector) {
*/ */
suspend fun main() { suspend fun main() {
//A generator for a normally distributed values //A generator for a normally distributed values
val generator = NormalDistribution(2.0, 7.0) val generator = NormalDistribution(0.0, 1.0)
//A chain/flow of random values with the given seed //A chain/flow of random values with the given seed
val chain = generator.sample(RandomGenerator.default(112667)) val chain = generator.sample(RandomGenerator.default(112667))
@ -56,7 +55,7 @@ suspend fun main() {
//Perform an operation on each x value (much more effective, than numpy) //Perform an operation on each x value (much more effective, than numpy)
val y = x.map { val y = x.map { it ->
val value = it.pow(2) + it + 1 val value = it.pow(2) + it + 1
value + chain.next() * sqrt(value) value + chain.next() * sqrt(value)
} }

View File

@ -0,0 +1,105 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.fit
import kotlinx.html.br
import kotlinx.html.h3
import space.kscience.kmath.commons.expressions.DSProcessor
import space.kscience.kmath.data.XYErrorColumnarData
import space.kscience.kmath.distributions.NormalDistribution
import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.expressions.binding
import space.kscience.kmath.expressions.symbol
import space.kscience.kmath.optimization.QowOptimizer
import space.kscience.kmath.optimization.fitWith
import space.kscience.kmath.optimization.resultPoint
import space.kscience.kmath.real.map
import space.kscience.kmath.real.step
import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.structures.asIterable
import space.kscience.kmath.structures.toList
import space.kscience.plotly.*
import space.kscience.plotly.models.ScatterMode
import kotlin.math.abs
import kotlin.math.pow
import kotlin.math.sqrt
// Forward declaration of symbols that will be used in expressions.
private val a by symbol
private val b by symbol
private val c by symbol
/**
* Least squares fie with auto-differentiation. Uses `kmath-commons` and `kmath-for-real` modules.
*/
suspend fun main() {
//A generator for a normally distributed values
val generator = NormalDistribution(0.0, 1.0)
//A chain/flow of random values with the given seed
val chain = generator.sample(RandomGenerator.default(112667))
//Create a uniformly distributed x values like numpy.arrange
val x = 1.0..100.0 step 1.0
//Perform an operation on each x value (much more effective, than numpy)
val y = x.map { it ->
val value = it.pow(2) + it + 100
value + chain.next() * sqrt(value)
}
// this will also work, but less effective:
// val y = x.pow(2)+ x + 1 + chain.nextDouble()
// create same errors for all xs
val yErr = y.map { sqrt(abs(it)) }
require(yErr.asIterable().all { it > 0 }) { "All errors must be strictly positive" }
val result = XYErrorColumnarData.of(x, y, yErr).fitWith(
QowOptimizer,
DSProcessor,
mapOf(a to 1.0, b to 1.2, c to 99.0)
) { arg ->
//bind variables to autodiff context
val a by binding
val b by binding
//Include default value for c if it is not provided as a parameter
val c = bindSymbolOrNull(c) ?: one
a * arg.pow(2) + b * arg + c
}
//display a page with plot and numerical results
val page = Plotly.page {
plot {
scatter {
mode = ScatterMode.markers
x(x)
y(y)
error_y {
array = yErr.toList()
}
name = "data"
}
scatter {
mode = ScatterMode.lines
x(x)
y(x.map { result.model(result.resultPoint + (Symbol.x to it)) })
name = "fit"
}
}
br()
h3 {
+"Fit result: ${result.resultPoint}"
}
// h3 {
// +"Chi2/dof = ${result.resultValue / (x.size - 3)}"
// }
}
page.makeFile()
}

View File

@ -22,7 +22,7 @@ fun main(): Unit = DoubleField {
} }
//Define a function in a nd space //Define a function in a nd space
val function: (Double) -> StructureND<Double> = { x: Double -> 3 * number(x).pow(2) + 2 * diagonal(x) + 1 } val function: (Double) -> StructureND<Double> = { x: Double -> 3 * x.pow(2) + 2 * diagonal(x) + 1 }
//get the result of the integration //get the result of the integration
val result = gaussIntegrator.integrate(0.0..10.0, function = function) val result = gaussIntegrator.integrate(0.0..10.0, function = function)

View File

@ -9,6 +9,7 @@ dependencies {
api(project(":kmath-core")) api(project(":kmath-core"))
api(project(":kmath-complex")) api(project(":kmath-complex"))
api(project(":kmath-coroutines")) api(project(":kmath-coroutines"))
api(project(":kmath-optimization"))
api(project(":kmath-stat")) api(project(":kmath-stat"))
api(project(":kmath-functions")) api(project(":kmath-functions"))
api("org.apache.commons:commons-math3:3.6.1") api("org.apache.commons:commons-math3:3.6.1")

View File

@ -18,6 +18,7 @@ import space.kscience.kmath.expressions.SymbolIndexer
import space.kscience.kmath.expressions.derivative import space.kscience.kmath.expressions.derivative
import space.kscience.kmath.expressions.withSymbols import space.kscience.kmath.expressions.withSymbols
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.misc.log
import space.kscience.kmath.optimization.* import space.kscience.kmath.optimization.*
import kotlin.collections.set import kotlin.collections.set
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -108,15 +109,17 @@ public object CMOptimizer : Optimizer<Double, FunctionOptimization<Double>> {
val objectiveFunction = ObjectiveFunction { val objectiveFunction = ObjectiveFunction {
val args = startPoint + it.toMap() val args = startPoint + it.toMap()
problem.expression(args) val res = problem.expression(args)
res
} }
addOptimizationData(objectiveFunction) addOptimizationData(objectiveFunction)
val gradientFunction = ObjectiveFunctionGradient { val gradientFunction = ObjectiveFunctionGradient {
val args = startPoint + it.toMap() val args = startPoint + it.toMap()
DoubleArray(symbols.size) { index -> val res = DoubleArray(symbols.size) { index ->
problem.expression.derivative(symbols[index])(args) problem.expression.derivative(symbols[index])(args)
} }
res
} }
addOptimizationData(gradientFunction) addOptimizationData(gradientFunction)

View File

@ -42,8 +42,8 @@ internal class AutoDiffTest {
@Test @Test
fun autoDifTest() { fun autoDifTest() {
val f = DerivativeStructureExpression { val f = DerivativeStructureExpression {
val x by binding() val x by binding
val y by binding() val y by binding
x.pow(2) + 2 * x * y + y.pow(2) + 1 x.pow(2) + 2 * x * y + y.pow(2) + 1
} }

View File

@ -15,6 +15,7 @@ import space.kscience.kmath.expressions.chiSquaredExpression
import space.kscience.kmath.expressions.symbol import space.kscience.kmath.expressions.symbol
import space.kscience.kmath.optimization.* import space.kscience.kmath.optimization.*
import space.kscience.kmath.stat.RandomGenerator import space.kscience.kmath.stat.RandomGenerator
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.structures.asBuffer
import space.kscience.kmath.structures.map import space.kscience.kmath.structures.map
import kotlin.math.pow import kotlin.math.pow
@ -58,7 +59,7 @@ internal class OptimizeTest {
it.pow(2) + it + 1 + chain.next() it.pow(2) + it + 1 + chain.next()
} }
val yErr = List(x.size) { sigma }.asBuffer() val yErr = DoubleBuffer(x.size) { sigma }
val chi2 = DSProcessor.chiSquaredExpression( val chi2 = DSProcessor.chiSquaredExpression(
x, y, yErr x, y, yErr

View File

@ -32,19 +32,21 @@ public interface XYColumnarData<out T, out X : T, out Y : T> : ColumnarData<T> {
Symbol.y -> y Symbol.y -> y
else -> null else -> null
} }
}
@Suppress("FunctionName") public companion object{
@UnstableKMathAPI @UnstableKMathAPI
public fun <T, X : T, Y : T> XYColumnarData(x: Buffer<X>, y: Buffer<Y>): XYColumnarData<T, X, Y> { public fun <T, X : T, Y : T> of(x: Buffer<X>, y: Buffer<Y>): XYColumnarData<T, X, Y> {
require(x.size == y.size) { "Buffer size mismatch. x buffer size is ${x.size}, y buffer size is ${y.size}" } require(x.size == y.size) { "Buffer size mismatch. x buffer size is ${x.size}, y buffer size is ${y.size}" }
return object : XYColumnarData<T, X, Y> { return object : XYColumnarData<T, X, Y> {
override val size: Int = x.size override val size: Int = x.size
override val x: Buffer<X> = x override val x: Buffer<X> = x
override val y: Buffer<Y> = y override val y: Buffer<Y> = y
} }
}
}
} }
/** /**
* Represent a [ColumnarData] as an [XYColumnarData]. The presence or respective columns is checked on creation. * Represent a [ColumnarData] as an [XYColumnarData]. The presence or respective columns is checked on creation.
*/ */

View File

@ -5,15 +5,13 @@
package space.kscience.kmath.data package space.kscience.kmath.data
import space.kscience.kmath.data.XYErrorColumnarData.Companion
import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.expressions.symbol
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
/** /**
* A [ColumnarData] with additional [Companion.yErr] column for an [Symbol.y] error * A [ColumnarData] with additional [Symbol.yError] column for an [Symbol.y] error
* Inherits [XYColumnarData]. * Inherits [XYColumnarData].
*/ */
@UnstableKMathAPI @UnstableKMathAPI
@ -23,11 +21,24 @@ public interface XYErrorColumnarData<T, out X : T, out Y : T> : XYColumnarData<T
override fun get(symbol: Symbol): Buffer<T> = when (symbol) { override fun get(symbol: Symbol): Buffer<T> = when (symbol) {
Symbol.x -> x Symbol.x -> x
Symbol.y -> y Symbol.y -> y
Companion.yErr -> yErr Symbol.yError -> yErr
else -> error("A column for symbol $symbol not found") else -> error("A column for symbol $symbol not found")
} }
public companion object{ public companion object {
public val yErr: Symbol by symbol public fun <T, X : T, Y : T> of(
x: Buffer<X>, y: Buffer<Y>, yErr: Buffer<Y>
): XYErrorColumnarData<T, X, Y> {
require(x.size == y.size) { "Buffer size mismatch. x buffer size is ${x.size}, y buffer size is ${y.size}" }
require(y.size == yErr.size) { "Buffer size mismatch. y buffer size is ${x.size}, yErr buffer size is ${y.size}" }
return object : XYErrorColumnarData<T, X, Y> {
override val size: Int = x.size
override val x: Buffer<X> = x
override val y: Buffer<Y> = y
override val yErr: Buffer<Y> = yErr
}
}
} }
} }

View File

@ -68,6 +68,7 @@ public interface ExpressionAlgebra<in T, E> : Algebra<E> {
/** /**
* Bind a symbol by name inside the [ExpressionAlgebra] * Bind a symbol by name inside the [ExpressionAlgebra]
*/ */
public fun <T, E> ExpressionAlgebra<T, E>.binding(): ReadOnlyProperty<Any?, E> = ReadOnlyProperty { _, property -> public val <T, E> ExpressionAlgebra<T, E>.binding: ReadOnlyProperty<Any?, E>
get() = ReadOnlyProperty { _, property ->
bindSymbol(property.name) ?: error("A variable with name ${property.name} does not exist") bindSymbol(property.name) ?: error("A variable with name ${property.name} does not exist")
} }

View File

@ -7,13 +7,18 @@ package space.kscience.kmath.expressions
import space.kscience.kmath.operations.ExtendedField import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.asIterable
import space.kscience.kmath.structures.indices import space.kscience.kmath.structures.indices
import kotlin.jvm.JvmName
/** /**
* Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic * Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic
* differentiation. * differentiation.
*
* **WARNING** All elements of [yErr] must be positive.
*/ */
public fun <T : Any, I : Any, A> AutoDiffProcessor<T, I, A>.chiSquaredExpression( @JvmName("genericChiSquaredExpression")
public fun <T : Comparable<T>, I : Any, A> AutoDiffProcessor<T, I, A>.chiSquaredExpression(
x: Buffer<T>, x: Buffer<T>,
y: Buffer<T>, y: Buffer<T>,
yErr: Buffer<T>, yErr: Buffer<T>,
@ -36,3 +41,13 @@ public fun <T : Any, I : Any, A> AutoDiffProcessor<T, I, A>.chiSquaredExpression
sum sum
} }
} }
public fun <I : Any, A> AutoDiffProcessor<Double, I, A>.chiSquaredExpression(
x: Buffer<Double>,
y: Buffer<Double>,
yErr: Buffer<Double>,
model: A.(I) -> I,
): DifferentiableExpression<Double> where A : ExtendedField<I>, A : ExpressionAlgebra<Double, I> {
require(yErr.asIterable().all { it > 0.0 }) { "All errors must be strictly positive" }
return chiSquaredExpression<Double, I, A>(x, y, yErr, model)
}

View File

@ -5,6 +5,7 @@
package space.kscience.kmath.misc package space.kscience.kmath.misc
import kotlin.jvm.JvmInline
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
@ -29,7 +30,8 @@ public interface Feature<F : Feature<F>> {
/** /**
* A container for a set of features * A container for a set of features
*/ */
public class FeatureSet<F : Feature<F>> private constructor(public val features: Map<FeatureKey<F>, F>) : Featured<F> { @JvmInline
public value class FeatureSet<F : Feature<F>> private constructor(public val features: Map<FeatureKey<F>, F>) : Featured<F> {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun <T : F> getFeature(type: FeatureKey<T>): T? = features[type]?.let { it as T } override fun <T : F> getFeature(type: FeatureKey<T>): T? = features[type]?.let { it as T }
@ -48,6 +50,9 @@ public class FeatureSet<F : Feature<F>> private constructor(public val features:
public operator fun iterator(): Iterator<F> = features.values.iterator() public operator fun iterator(): Iterator<F> = features.values.iterator()
override fun toString(): String = features.values.joinToString(prefix = "[ ", postfix = " ]")
public companion object { public companion object {
public fun <F : Feature<F>> of(vararg features: F): FeatureSet<F> = FeatureSet(features.associateBy { it.key }) public fun <F : Feature<F>> of(vararg features: F): FeatureSet<F> = FeatureSet(features.associateBy { it.key })
public fun <F : Feature<F>> of(features: Iterable<F>): FeatureSet<F> = public fun <F : Feature<F>> of(features: Iterable<F>): FeatureSet<F> =

View File

@ -5,10 +5,18 @@
package space.kscience.kmath.misc package space.kscience.kmath.misc
public interface Loggable { import space.kscience.kmath.misc.Loggable.Companion.INFO
public fun log(tag: String = INFO, block: () -> String)
public fun interface Loggable {
public fun log(tag: String, block: () -> String)
public companion object { public companion object {
public const val INFO: String = "INFO" public const val INFO: String = "INFO"
public val console: Loggable = Loggable { tag, block ->
println("[$tag] ${block()}")
}
} }
} }
public fun Loggable.log(block: () -> String): Unit = log(INFO, block)

View File

@ -16,7 +16,7 @@ class ExpressionFieldTest {
@Test @Test
fun testExpression() { fun testExpression() {
val expression = with(FunctionalExpressionField(DoubleField)) { val expression = with(FunctionalExpressionField(DoubleField)) {
val x by binding() val x by binding
x * x + 2 * x + one x * x + 2 * x + one
} }
@ -27,7 +27,7 @@ class ExpressionFieldTest {
@Test @Test
fun separateContext() { fun separateContext() {
fun <T> FunctionalExpressionField<T, *>.expression(): Expression<T> { fun <T> FunctionalExpressionField<T, *>.expression(): Expression<T> {
val x by binding() val x by binding
return x * x + 2 * x + one return x * x + 2 * x + one
} }
@ -38,7 +38,7 @@ class ExpressionFieldTest {
@Test @Test
fun valueExpression() { fun valueExpression() {
val expressionBuilder: FunctionalExpressionField<Double, *>.() -> Expression<Double> = { val expressionBuilder: FunctionalExpressionField<Double, *>.() -> Expression<Double> = {
val x by binding() val x by binding
x * x + 2 * x + one x * x + 2 * x + one
} }

View File

@ -73,7 +73,8 @@ public class GaussIntegrator<T : Any>(
} }
/** /**
* Create a Gauss-Legendre integrator for this field. * Create a Gauss integrator for this field. By default, uses Legendre rule to compute points and weights.
* Custom rules could be provided by [GaussIntegratorRuleFactory] feature.
* @see [GaussIntegrator] * @see [GaussIntegrator]
*/ */
public val <T : Any> Field<T>.gaussIntegrator: GaussIntegrator<T> get() = GaussIntegrator(this) public val <T : Any> Field<T>.gaussIntegrator: GaussIntegrator<T> get() = GaussIntegrator(this)

View File

@ -42,20 +42,20 @@ public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials(
x: Buffer<T>, x: Buffer<T>,
y: Buffer<T>, y: Buffer<T>,
): PiecewisePolynomial<T> { ): PiecewisePolynomial<T> {
val pointSet = XYColumnarData(x, y) val pointSet = XYColumnarData.of(x, y)
return interpolatePolynomials(pointSet) return interpolatePolynomials(pointSet)
} }
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials( public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials(
data: Map<T, T>, data: Map<T, T>,
): PiecewisePolynomial<T> { ): PiecewisePolynomial<T> {
val pointSet = XYColumnarData(data.keys.toList().asBuffer(), data.values.toList().asBuffer()) val pointSet = XYColumnarData.of(data.keys.toList().asBuffer(), data.values.toList().asBuffer())
return interpolatePolynomials(pointSet) return interpolatePolynomials(pointSet)
} }
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials( public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials(
data: List<Pair<T, T>>, data: List<Pair<T, T>>,
): PiecewisePolynomial<T> { ): PiecewisePolynomial<T> {
val pointSet = XYColumnarData(data.map { it.first }.asBuffer(), data.map { it.second }.asBuffer()) val pointSet = XYColumnarData.of(data.map { it.first }.asBuffer(), data.map { it.second }.asBuffer())
return interpolatePolynomials(pointSet) return interpolatePolynomials(pointSet)
} }

View File

@ -0,0 +1,20 @@
plugins {
id("ru.mipt.npm.gradle.mpp")
id("ru.mipt.npm.gradle.native")
}
kscience {
useAtomic()
}
kotlin.sourceSets {
commonMain {
dependencies {
api(project(":kmath-coroutines"))
}
}
}
readme {
maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
}

View File

@ -5,13 +5,11 @@
package space.kscience.kmath.optimization package space.kscience.kmath.optimization
import space.kscience.kmath.expressions.* import space.kscience.kmath.expressions.DifferentiableExpression
import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.misc.FeatureSet import space.kscience.kmath.misc.FeatureSet
import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.indices
public class OptimizationValue<T>(public val value: T) : OptimizationFeature{ public class OptimizationValue<T>(public val value: T) : OptimizationFeature {
override fun toString(): String = "Value($value)" override fun toString(): String = "Value($value)"
} }
@ -23,9 +21,28 @@ public enum class FunctionOptimizationTarget : OptimizationFeature {
public class FunctionOptimization<T>( public class FunctionOptimization<T>(
override val features: FeatureSet<OptimizationFeature>, override val features: FeatureSet<OptimizationFeature>,
public val expression: DifferentiableExpression<T>, public val expression: DifferentiableExpression<T>,
) : OptimizationProblem<T>{ ) : OptimizationProblem<T> {
public companion object
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
other as FunctionOptimization<*>
if (features != other.features) return false
if (expression != other.expression) return false
return true
}
override fun hashCode(): Int {
var result = features.hashCode()
result = 31 * result + expression.hashCode()
return result
}
override fun toString(): String = "FunctionOptimization(features=$features)"
} }
public fun <T> FunctionOptimization<T>.withFeatures( public fun <T> FunctionOptimization<T>.withFeatures(
@ -47,7 +64,7 @@ public suspend fun <T : Any> DifferentiableExpression<T>.optimizeWith(
return optimizer.optimize(problem) return optimizer.optimize(problem)
} }
public val <T> FunctionOptimization<T>.resultValueOrNull:T? public val <T> FunctionOptimization<T>.resultValueOrNull: T?
get() = getFeature<OptimizationResult<T>>()?.point?.let { expression(it) } get() = getFeature<OptimizationResult<T>>()?.point?.let { expression(it) }
public val <T> FunctionOptimization<T>.resultValue: T public val <T> FunctionOptimization<T>.resultValue: T

View File

@ -72,15 +72,15 @@ public suspend fun <T> DifferentiableExpression<T>.optimizeWith(
public class XYOptimizationBuilder( public class XYOptimizationBuilder(
public val data: XYColumnarData<Double, Double, Double>, public val data: XYColumnarData<Double, Double, Double>,
public val model: DifferentiableExpression<Double>, public val model: DifferentiableExpression<Double>,
) : OptimizationBuilder<Double, XYOptimization>() { ) : OptimizationBuilder<Double, XYFit>() {
public var pointToCurveDistance: PointToCurveDistance = PointToCurveDistance.byY public var pointToCurveDistance: PointToCurveDistance = PointToCurveDistance.byY
public var pointWeight: PointWeight = PointWeight.byYSigma public var pointWeight: PointWeight = PointWeight.byYSigma
override fun build(): XYOptimization = XYOptimization( override fun build(): XYFit = XYFit(
FeatureSet.of(features),
data, data,
model, model,
FeatureSet.of(features),
pointToCurveDistance, pointToCurveDistance,
pointWeight pointWeight
) )
@ -90,4 +90,4 @@ public fun XYOptimization(
data: XYColumnarData<Double, Double, Double>, data: XYColumnarData<Double, Double, Double>,
model: DifferentiableExpression<Double>, model: DifferentiableExpression<Double>,
builder: XYOptimizationBuilder.() -> Unit, builder: XYOptimizationBuilder.() -> Unit,
): XYOptimization = XYOptimizationBuilder(data, model).apply(builder).build() ): XYFit = XYOptimizationBuilder(data, model).apply(builder).build()

View File

@ -11,6 +11,7 @@ import space.kscience.kmath.expressions.SymbolIndexer
import space.kscience.kmath.expressions.derivative import space.kscience.kmath.expressions.derivative
import space.kscience.kmath.linear.* import space.kscience.kmath.linear.*
import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.misc.log
import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.DoubleL2Norm import space.kscience.kmath.structures.DoubleL2Norm
@ -21,14 +22,14 @@ import space.kscience.kmath.structures.DoubleL2Norm
* See [the article](http://arxiv.org/abs/physics/0604127). * See [the article](http://arxiv.org/abs/physics/0604127).
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class QowOptimizer : Optimizer<Double, XYOptimization> { public object QowOptimizer : Optimizer<Double, XYFit> {
private val linearSpace: LinearSpace<Double, DoubleField> = LinearSpace.double private val linearSpace: LinearSpace<Double, DoubleField> = LinearSpace.double
private val solver: LinearSolver<Double> = linearSpace.lupSolver() private val solver: LinearSolver<Double> = linearSpace.lupSolver()
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
private inner class QoWeight( private class QoWeight(
val problem: XYOptimization, val problem: XYFit,
val parameters: Map<Symbol, Double>, val parameters: Map<Symbol, Double>,
) : Map<Symbol, Double> by parameters, SymbolIndexer { ) : Map<Symbol, Double> by parameters, SymbolIndexer {
override val symbols: List<Symbol> = parameters.keys.toList() override val symbols: List<Symbol> = parameters.keys.toList()
@ -39,8 +40,8 @@ public class QowOptimizer : Optimizer<Double, XYOptimization> {
* Derivatives of the spectrum over parameters. First index in the point number, second one - index of parameter * Derivatives of the spectrum over parameters. First index in the point number, second one - index of parameter
*/ */
val derivs: Matrix<Double> by lazy { val derivs: Matrix<Double> by lazy {
linearSpace.buildMatrix(problem.data.size, symbols.size) { i, k -> linearSpace.buildMatrix(problem.data.size, symbols.size) { d, s ->
problem.distance(i).derivative(symbols[k])(parameters) problem.distance(d).derivative(symbols[s])(parameters)
} }
} }
@ -48,25 +49,27 @@ public class QowOptimizer : Optimizer<Double, XYOptimization> {
* Array of dispersions in each point * Array of dispersions in each point
*/ */
val dispersion: Point<Double> by lazy { val dispersion: Point<Double> by lazy {
DoubleBuffer(problem.data.size) { i -> DoubleBuffer(problem.data.size) { d ->
problem.weight(i).invoke(parameters) problem.weight(d).invoke(parameters)
} }
} }
val prior: DifferentiableExpression<Double>? get() = problem.getFeature<OptimizationPrior<Double>>() val prior: DifferentiableExpression<Double>? get() = problem.getFeature<OptimizationPrior<Double>>()
override fun toString(): String = parameters.toString()
} }
/** /**
* The signed distance from the model to the [i]-th point of data. * The signed distance from the model to the [d]-th point of data.
*/ */
private fun QoWeight.distance(i: Int, parameters: Map<Symbol, Double>): Double = problem.distance(i)(parameters) private fun QoWeight.distance(d: Int, parameters: Map<Symbol, Double>): Double = problem.distance(d)(parameters)
/** /**
* The derivative of [distance] * The derivative of [distance]
*/ */
private fun QoWeight.distanceDerivative(symbol: Symbol, i: Int, parameters: Map<Symbol, Double>): Double = private fun QoWeight.distanceDerivative(symbol: Symbol, d: Int, parameters: Map<Symbol, Double>): Double =
problem.distance(i).derivative(symbol)(parameters) problem.distance(d).derivative(symbol)(parameters)
/** /**
* Теоретическая ковариация весовых функций. * Теоретическая ковариация весовых функций.
@ -74,8 +77,8 @@ public class QowOptimizer : Optimizer<Double, XYOptimization> {
* D(\phi)=E(\phi_k(\theta_0) \phi_l(\theta_0))= disDeriv_k * disDeriv_l /sigma^2 * D(\phi)=E(\phi_k(\theta_0) \phi_l(\theta_0))= disDeriv_k * disDeriv_l /sigma^2
*/ */
private fun QoWeight.covarF(): Matrix<Double> = private fun QoWeight.covarF(): Matrix<Double> =
linearSpace.matrix(size, size).symmetric { k, l -> linearSpace.matrix(size, size).symmetric { s1, s2 ->
(0 until data.size).sumOf { i -> derivs[k, i] * derivs[l, i] / dispersion[i] } (0 until data.size).sumOf { d -> derivs[d, s1] * derivs[d, s2] / dispersion[d] }
} }
/** /**
@ -89,12 +92,12 @@ public class QowOptimizer : Optimizer<Double, XYOptimization> {
* количество вызывов функции будет dim^2 вместо dim Первый индекс - * количество вызывов функции будет dim^2 вместо dim Первый индекс -
* номер точки, второй - номер переменной, по которой берется производная * номер точки, второй - номер переменной, по которой берется производная
*/ */
val eqvalues = linearSpace.buildMatrix(data.size, size) { i, l -> val eqvalues = linearSpace.buildMatrix(data.size, size) { d, s ->
distance(i, theta) * derivs[l, i] / dispersion[i] distance(d, theta) * derivs[d, s] / dispersion[d]
} }
buildMatrix(size, size) { k, l -> buildMatrix(size, size) { s1, s2 ->
(0 until data.size).sumOf { i -> eqvalues[i, l] * eqvalues[i, k] } (0 until data.size).sumOf { d -> eqvalues[d, s2] * eqvalues[d, s1] }
} }
} }
@ -106,20 +109,20 @@ public class QowOptimizer : Optimizer<Double, XYOptimization> {
): Matrix<Double> = with(linearSpace) { ): Matrix<Double> = with(linearSpace) {
//Возвращает производную k-того Eq по l-тому параметру //Возвращает производную k-того Eq по l-тому параметру
//val res = Array(fitDim) { DoubleArray(fitDim) } //val res = Array(fitDim) { DoubleArray(fitDim) }
val sderiv = buildMatrix(data.size, size) { i, l -> val sderiv = buildMatrix(data.size, size) { d, s ->
distanceDerivative(symbols[l], i, theta) distanceDerivative(symbols[s], d, theta)
} }
buildMatrix(size, size) { k, l -> buildMatrix(size, size) { s1, s2 ->
val base = (0 until data.size).sumOf { i -> val base = (0 until data.size).sumOf { d ->
require(dispersion[i] > 0) require(dispersion[d] > 0)
sderiv[i, l] * derivs[k, i] / dispersion[i] sderiv[d, s2] * derivs[d, s1] / dispersion[d]
} }
prior?.let { prior -> prior?.let { prior ->
//Check if this one is correct //Check if this one is correct
val pi = prior(theta) val pi = prior(theta)
val deriv1 = prior.derivative(symbols[k])(theta) val deriv1 = prior.derivative(symbols[s1])(theta)
val deriv2 = prior.derivative(symbols[l])(theta) val deriv2 = prior.derivative(symbols[s2])(theta)
base + deriv1 * deriv2 / pi / pi base + deriv1 * deriv2 / pi / pi
} ?: base } ?: base
} }
@ -130,13 +133,13 @@ public class QowOptimizer : Optimizer<Double, XYOptimization> {
* Значения уравнений метода квазиоптимальных весов * Значения уравнений метода квазиоптимальных весов
*/ */
private fun QoWeight.getEqValues(theta: Map<Symbol, Double> = this): Point<Double> { private fun QoWeight.getEqValues(theta: Map<Symbol, Double> = this): Point<Double> {
val distances = DoubleBuffer(data.size) { i -> distance(i, theta) } val distances = DoubleBuffer(data.size) { d -> distance(d, theta) }
return DoubleBuffer(size) { k -> return DoubleBuffer(size) { s ->
val base = (0 until data.size).sumOf { i -> distances[i] * derivs[k, i] / dispersion[i] } val base = (0 until data.size).sumOf { d -> distances[d] * derivs[d, s] / dispersion[d] }
//Поправка на априорную вероятность //Поправка на априорную вероятность
prior?.let { prior -> prior?.let { prior ->
base - prior.derivative(symbols[k])(theta) / prior(theta) base - prior.derivative(symbols[s])(theta) / prior(theta)
} ?: base } ?: base
} }
} }
@ -163,15 +166,15 @@ public class QowOptimizer : Optimizer<Double, XYOptimization> {
val logger = problem.getFeature<OptimizationLog>() val logger = problem.getFeature<OptimizationLog>()
var dis: Double//норма невязки var dis: Double //discrepancy value
// Для удобства работаем всегда с полным набором параметров // Working with the full set of parameters
var par = problem.startPoint var par = problem.startPoint
logger?.log { "Starting newtonian iteration from: \n\t$par" } logger?.log { "Starting newtonian iteration from: \n\t$par" }
var eqvalues = getEqValues(par)//значения функций var eqvalues = getEqValues(par) //Values of the weight functions
dis = DoubleL2Norm.norm(eqvalues)// невязка dis = DoubleL2Norm.norm(eqvalues) // discrepancy
logger?.log { "Starting discrepancy is $dis" } logger?.log { "Starting discrepancy is $dis" }
var i = 0 var i = 0
var flag = false var flag = false
@ -238,7 +241,8 @@ public class QowOptimizer : Optimizer<Double, XYOptimization> {
return covar return covar
} }
override suspend fun optimize(problem: XYOptimization): XYOptimization { override suspend fun optimize(problem: XYFit): XYFit {
val qowSteps = 2
val initialWeight = QoWeight(problem, problem.startPoint) val initialWeight = QoWeight(problem, problem.startPoint)
val res = initialWeight.newtonianRun() val res = initialWeight.newtonianRun()
return res.problem.withFeature(OptimizationResult(res.parameters)) return res.problem.withFeature(OptimizationResult(res.parameters))

View File

@ -0,0 +1,125 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:OptIn(UnstableKMathAPI::class)
package space.kscience.kmath.optimization
import space.kscience.kmath.data.XYColumnarData
import space.kscience.kmath.expressions.*
import space.kscience.kmath.misc.FeatureSet
import space.kscience.kmath.misc.Loggable
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.ExtendedField
import space.kscience.kmath.operations.bindSymbol
import kotlin.math.pow
/**
* Specify the way to compute distance from point to the curve as DifferentiableExpression
*/
public interface PointToCurveDistance : OptimizationFeature {
public fun distance(problem: XYFit, index: Int): DifferentiableExpression<Double>
public companion object {
public val byY: PointToCurveDistance = object : PointToCurveDistance {
override fun distance(problem: XYFit, index: Int): DifferentiableExpression<Double> {
val x = problem.data.x[index]
val y = problem.data.y[index]
return object : DifferentiableExpression<Double> {
override fun derivativeOrNull(
symbols: List<Symbol>
): Expression<Double>? = problem.model.derivativeOrNull(symbols)?.let { derivExpression ->
Expression { arguments ->
derivExpression.invoke(arguments + (Symbol.x to x))
}
}
override fun invoke(arguments: Map<Symbol, Double>): Double =
problem.model(arguments + (Symbol.x to x)) - y
}
}
override fun toString(): String = "PointToCurveDistanceByY"
}
}
}
/**
* Compute a wight of the point. The more the weight, the more impact this point will have on the fit.
* By default, uses Dispersion^-1
*/
public interface PointWeight : OptimizationFeature {
public fun weight(problem: XYFit, index: Int): DifferentiableExpression<Double>
public companion object {
public fun bySigma(sigmaSymbol: Symbol): PointWeight = object : PointWeight {
override fun weight(problem: XYFit, index: Int): DifferentiableExpression<Double> =
object : DifferentiableExpression<Double> {
override fun invoke(arguments: Map<Symbol, Double>): Double {
return problem.data[sigmaSymbol]?.get(index)?.pow(-2) ?: 1.0
}
override fun derivativeOrNull(symbols: List<Symbol>): Expression<Double> = Expression { 0.0 }
}
override fun toString(): String = "PointWeightBySigma($sigmaSymbol)"
}
public val byYSigma: PointWeight = bySigma(Symbol.yError)
}
}
/**
* A fit problem for X-Y-Yerr data. Also known as "least-squares" problem.
*/
public class XYFit(
public val data: XYColumnarData<Double, Double, Double>,
public val model: DifferentiableExpression<Double>,
override val features: FeatureSet<OptimizationFeature>,
internal val pointToCurveDistance: PointToCurveDistance = PointToCurveDistance.byY,
internal val pointWeight: PointWeight = PointWeight.byYSigma,
) : OptimizationProblem<Double> {
public fun distance(index: Int): DifferentiableExpression<Double> = pointToCurveDistance.distance(this, index)
public fun weight(index: Int): DifferentiableExpression<Double> = pointWeight.weight(this, index)
}
public fun XYFit.withFeature(vararg features: OptimizationFeature): XYFit {
return XYFit(data, model, this.features.with(*features), pointToCurveDistance, pointWeight)
}
/**
* Fit given dta with
*/
public suspend fun <I : Any, A> XYColumnarData<Double, Double, Double>.fitWith(
optimizer: Optimizer<Double, XYFit>,
processor: AutoDiffProcessor<Double, I, A>,
startingPoint: Map<Symbol, Double>,
vararg features: OptimizationFeature = emptyArray(),
xSymbol: Symbol = Symbol.x,
pointToCurveDistance: PointToCurveDistance = PointToCurveDistance.byY,
pointWeight: PointWeight = PointWeight.byYSigma,
model: A.(I) -> I
): XYFit where A : ExtendedField<I>, A : ExpressionAlgebra<Double, I> {
val modelExpression = processor.differentiate {
val x = bindSymbol(xSymbol)
model(x)
}
var actualFeatures = FeatureSet.of(*features, OptimizationStartPoint(startingPoint))
if (actualFeatures.getFeature<OptimizationLog>() == null) {
actualFeatures = actualFeatures.with(OptimizationLog(Loggable.console))
}
val problem = XYFit(
this,
modelExpression,
actualFeatures,
pointToCurveDistance,
pointWeight
)
return optimizer.optimize(problem)
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package space.kscience.kmath.optimization
import space.kscience.kmath.data.XYColumnarData
import space.kscience.kmath.data.indices
import space.kscience.kmath.expressions.DifferentiableExpression
import space.kscience.kmath.expressions.Expression
import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.expressions.derivative
import space.kscience.kmath.misc.UnstableKMathAPI
import kotlin.math.PI
import kotlin.math.ln
import kotlin.math.pow
import kotlin.math.sqrt
private val oneOver2Pi = 1.0 / sqrt(2 * PI)
@UnstableKMathAPI
internal fun XYFit.logLikelihood(): DifferentiableExpression<Double> = object : DifferentiableExpression<Double> {
override fun derivativeOrNull(symbols: List<Symbol>): Expression<Double> = Expression { arguments ->
data.indices.sumOf { index ->
val d = distance(index)(arguments)
val weight = weight(index)(arguments)
val weightDerivative = weight(index)(arguments)
// -1 / (sqrt(2 PI) * sigma) + 2 (x-mu)/ 2 sigma^2 * d mu/ d theta - (x-mu)^2 / 2 * d w/ d theta
return@sumOf -oneOver2Pi * sqrt(weight) + //offset derivative
d * model.derivative(symbols)(arguments) * weight - //model derivative
d.pow(2) * weightDerivative / 2 //weight derivative
}
}
override fun invoke(arguments: Map<Symbol, Double>): Double {
return data.indices.sumOf { index ->
val d = distance(index)(arguments)
val weight = weight(index)(arguments)
//1/sqrt(2 PI sigma^2) - (x-mu)^2/ (2 * sigma^2)
oneOver2Pi * ln(weight) - d.pow(2) * weight
} / 2
}
}
/**
* Optimize given XY (least squares) [problem] using this function [Optimizer].
* The problem is treated as maximum likelihood problem and is done via maximizing logarithmic likelihood, respecting
* possible weight dependency on the model and parameters.
*/
@UnstableKMathAPI
public suspend fun Optimizer<Double, FunctionOptimization<Double>>.maximumLogLikelihood(problem: XYFit): XYFit {
val functionOptimization = FunctionOptimization(problem.features, problem.logLikelihood())
val result = optimize(functionOptimization.withFeatures(FunctionOptimizationTarget.MAXIMIZE))
return XYFit(problem.data, problem.model, result.features)
}
@UnstableKMathAPI
public suspend fun Optimizer<Double, FunctionOptimization<Double>>.maximumLogLikelihood(
data: XYColumnarData<Double, Double, Double>,
model: DifferentiableExpression<Double>,
builder: XYOptimizationBuilder.() -> Unit,
): XYFit = maximumLogLikelihood(XYOptimization(data, model, builder))

View File

@ -1,6 +1,5 @@
plugins { plugins {
kotlin("multiplatform") id("ru.mipt.npm.gradle.mpp")
id("ru.mipt.npm.gradle.common")
id("ru.mipt.npm.gradle.native") id("ru.mipt.npm.gradle.native")
} }

View File

@ -1,189 +0,0 @@
/*
* Copyright 2018-2021 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:OptIn(UnstableKMathAPI::class)
package space.kscience.kmath.optimization
import space.kscience.kmath.data.XYColumnarData
import space.kscience.kmath.data.indices
import space.kscience.kmath.expressions.DifferentiableExpression
import space.kscience.kmath.expressions.Expression
import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.expressions.derivative
import space.kscience.kmath.misc.FeatureSet
import space.kscience.kmath.misc.UnstableKMathAPI
import kotlin.math.PI
import kotlin.math.ln
import kotlin.math.pow
import kotlin.math.sqrt
/**
* Specify the way to compute distance from point to the curve as DifferentiableExpression
*/
public interface PointToCurveDistance : OptimizationFeature {
public fun distance(problem: XYOptimization, index: Int): DifferentiableExpression<Double>
public companion object {
public val byY: PointToCurveDistance = object : PointToCurveDistance {
override fun distance(problem: XYOptimization, index: Int): DifferentiableExpression<Double> {
val x = problem.data.x[index]
val y = problem.data.y[index]
return object : DifferentiableExpression<Double> {
override fun derivativeOrNull(symbols: List<Symbol>): Expression<Double>? =
problem.model.derivativeOrNull(symbols)
override fun invoke(arguments: Map<Symbol, Double>): Double =
problem.model(arguments + (Symbol.x to x)) - y
}
}
override fun toString(): String = "PointToCurveDistanceByY"
}
}
}
/**
* Compute a wight of the point. The more the weight, the more impact this point will have on the fit.
* By default uses Dispersion^-1
*/
public interface PointWeight : OptimizationFeature {
public fun weight(problem: XYOptimization, index: Int): DifferentiableExpression<Double>
public companion object {
public fun bySigma(sigmaSymbol: Symbol): PointWeight = object : PointWeight {
override fun weight(problem: XYOptimization, index: Int): DifferentiableExpression<Double> =
object : DifferentiableExpression<Double> {
override fun invoke(arguments: Map<Symbol, Double>): Double {
return problem.data[sigmaSymbol]?.get(index)?.pow(-2) ?: 1.0
}
override fun derivativeOrNull(symbols: List<Symbol>): Expression<Double> = Expression { 0.0 }
}
override fun toString(): String = "PointWeightBySigma($sigmaSymbol)"
}
public val byYSigma: PointWeight = bySigma(Symbol.yError)
}
}
/**
* An optimization for XY data.
*/
public class XYOptimization(
override val features: FeatureSet<OptimizationFeature>,
public val data: XYColumnarData<Double, Double, Double>,
public val model: DifferentiableExpression<Double>,
internal val pointToCurveDistance: PointToCurveDistance = PointToCurveDistance.byY,
internal val pointWeight: PointWeight = PointWeight.byYSigma,
) : OptimizationProblem<Double> {
public fun distance(index: Int): DifferentiableExpression<Double> = pointToCurveDistance.distance(this, index)
public fun weight(index: Int): DifferentiableExpression<Double> = pointWeight.weight(this, index)
}
public fun XYOptimization.withFeature(vararg features: OptimizationFeature): XYOptimization {
return XYOptimization(this.features.with(*features), data, model, pointToCurveDistance, pointWeight)
}
private val oneOver2Pi = 1.0 / sqrt(2 * PI)
internal fun XYOptimization.likelihood(): DifferentiableExpression<Double> = object : DifferentiableExpression<Double> {
override fun derivativeOrNull(symbols: List<Symbol>): Expression<Double> = Expression { arguments ->
data.indices.sumOf { index ->
val d = distance(index)(arguments)
val weight = weight(index)(arguments)
val weightDerivative = weight(index)(arguments)
// -1 / (sqrt(2 PI) * sigma) + 2 (x-mu)/ 2 sigma^2 * d mu/ d theta - (x-mu)^2 / 2 * d w/ d theta
return@sumOf -oneOver2Pi * sqrt(weight) + //offset derivative
d * model.derivative(symbols)(arguments) * weight - //model derivative
d.pow(2) * weightDerivative / 2 //weight derivative
}
}
override fun invoke(arguments: Map<Symbol, Double>): Double {
return data.indices.sumOf { index ->
val d = distance(index)(arguments)
val weight = weight(index)(arguments)
//1/sqrt(2 PI sigma^2) - (x-mu)^2/ (2 * sigma^2)
oneOver2Pi * ln(weight) - d.pow(2) * weight
} / 2
}
}
/**
* Optimize given XY (least squares) [problem] using this function [Optimizer].
* The problem is treated as maximum likelihood problem and is done via maximizing logarithmic likelihood, respecting
* possible weight dependency on the model and parameters.
*/
public suspend fun Optimizer<Double, FunctionOptimization<Double>>.maximumLogLikelihood(problem: XYOptimization): XYOptimization {
val functionOptimization = FunctionOptimization(problem.features, problem.likelihood())
val result = optimize(functionOptimization.withFeatures(FunctionOptimizationTarget.MAXIMIZE))
return XYOptimization(result.features, problem.data, problem.model)
}
public suspend fun Optimizer<Double, FunctionOptimization<Double>>.maximumLogLikelihood(
data: XYColumnarData<Double, Double, Double>,
model: DifferentiableExpression<Double>,
builder: XYOptimizationBuilder.() -> Unit,
): XYOptimization = maximumLogLikelihood(XYOptimization(data, model, builder))
//public suspend fun XYColumnarData<Double, Double, Double>.fitWith(
// optimizer: XYOptimization,
// problemBuilder: XYOptimizationBuilder.() -> Unit = {},
//
//)
//
//@UnstableKMathAPI
//public interface XYFit<T> : OptimizationProblem {
//
// public val algebra: Field<T>
//
// /**
// * Set X-Y data for this fit optionally including x and y errors
// */
// public fun data(
// dataSet: ColumnarData<T>,
// xSymbol: Symbol,
// ySymbol: Symbol,
// xErrSymbol: Symbol? = null,
// yErrSymbol: Symbol? = null,
// )
//
// public fun model(model: (T) -> DifferentiableExpression<T, *>)
//
// /**
// * Set the differentiable model for this fit
// */
// public fun <I : Any, A> model(
// autoDiff: AutoDiffProcessor<T, I, A, Expression<T>>,
// modelFunction: A.(I) -> I,
// ): Unit where A : ExtendedField<I>, A : ExpressionAlgebra<T, I> = model { arg ->
// autoDiff.process { modelFunction(const(arg)) }
// }
//}
//
///**
// * Define a chi-squared-based objective function
// */
//public fun <T : Any, I : Any, A> FunctionOptimization<T>.chiSquared(
// autoDiff: AutoDiffProcessor<T, I, A, Expression<T>>,
// x: Buffer<T>,
// y: Buffer<T>,
// yErr: Buffer<T>,
// model: A.(I) -> I,
//) where A : ExtendedField<I>, A : ExpressionAlgebra<T, I> {
// val chiSquared = FunctionOptimization.chiSquared(autoDiff, x, y, yErr, model)
// function(chiSquared)
// maximize = false
//}

View File

@ -26,6 +26,7 @@ include(
":kmath-histograms", ":kmath-histograms",
":kmath-commons", ":kmath-commons",
":kmath-viktor", ":kmath-viktor",
":kmath-optimization",
":kmath-stat", ":kmath-stat",
":kmath-nd4j", ":kmath-nd4j",
":kmath-dimensions", ":kmath-dimensions",