From c240fa4a9efb55116571622fa57a5c3332a1a166 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 25 May 2021 16:42:23 +0300 Subject: [PATCH] [WIP] optimization refactoring --- .../kscience/kmath/data/XYColumnarData.kt | 21 +++++ .../kscience/kmath/expressions/Symbol.kt | 16 +++- .../optimization/FunctionOptimization.kt | 17 +++- .../kmath/optimization/OptimizationProblem.kt | 1 + .../kscience/kmath/optimization/XYFit.kt | 72 --------------- .../kmath/optimization/XYOptimization.kt | 92 +++++++++++++++++++ .../kscience/kmath/optimization/qow/QowFit.kt | 4 +- 7 files changed, 141 insertions(+), 82 deletions(-) delete mode 100644 kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/XYFit.kt create mode 100644 kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/XYOptimization.kt 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 index 08bfd3ca3..2dc82c02e 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/XYColumnarData.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/XYColumnarData.kt @@ -45,6 +45,27 @@ public fun XYColumnarData(x: Buffer, y: Buffer): XYColum } } +/** + * Represent a [ColumnarData] as an [XYColumnarData]. The presence or respective columns is checked on creation. + */ +@UnstableKMathAPI +public fun ColumnarData.asXYData( + xSymbol: Symbol, + ySymbol: Symbol, +): XYColumnarData = object : XYColumnarData { + init { + requireNotNull(this@asXYData[xSymbol]){"The column with name $xSymbol is not present in $this"} + requireNotNull(this@asXYData[ySymbol]){"The column with name $ySymbol is not present in $this"} + } + override val size: Int get() = this@asXYData.size + override val x: Buffer get() = this@asXYData[xSymbol]!! + override val y: Buffer get() = this@asXYData[ySymbol]!! + override fun get(symbol: Symbol): Buffer? = when (symbol) { + Symbol.x -> x + Symbol.y -> y + else -> this@asXYData.get(symbol) + } +} /** * A zero-copy method to represent a [Structure2D] as a two-column x-y data. diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/Symbol.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/Symbol.kt index 74dc7aedc..4cbdbf55b 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/Symbol.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/Symbol.kt @@ -19,9 +19,12 @@ public interface Symbol : MST { 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") + public val x: Symbol = Symbol("x") + public val xError: Symbol = Symbol("x.error") + public val y: Symbol = Symbol("y") + public val yError: Symbol = Symbol("y.error") + public val z: Symbol = Symbol("z") + public val zError: Symbol = Symbol("z.error") } } @@ -29,10 +32,15 @@ public interface Symbol : MST { * A [Symbol] with a [String] identity */ @JvmInline -public value class StringSymbol(override val identity: String) : Symbol { +internal value class StringSymbol(override val identity: String) : Symbol { override fun toString(): String = identity } +/** + * Create s Symbols with a string identity + */ +public fun Symbol(identity: String): Symbol = StringSymbol(identity) + /** * A delegate to create a symbol with a string identity in this scope */ 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 e5edb17e0..2c70da308 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 @@ -5,10 +5,7 @@ package space.kscience.kmath.optimization -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.expressions.* import space.kscience.kmath.misc.FeatureSet import space.kscience.kmath.operations.ExtendedField import space.kscience.kmath.structures.Buffer @@ -67,3 +64,15 @@ public fun FunctionOptimization.withFeatures( expression, ) +/** + * Optimize differentiable expression using specific [optimizer] form given [startingPoint] + */ +public suspend fun DifferentiableExpression>.optimizeWith( + optimizer: Optimizer>, + startingPoint: Map, + vararg features: OptimizationFeature, +): FunctionOptimization { + val problem = FunctionOptimization(FeatureSet.of(OptimizationStartPoint(startingPoint), *features), this) + return optimizer.process(problem) +} + diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/OptimizationProblem.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/OptimizationProblem.kt index 53b6af144..2594fa182 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/OptimizationProblem.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/OptimizationProblem.kt @@ -35,6 +35,7 @@ public class OptimizationLog(private val loggable: Loggable) : Loggable by logga } public class OptimizationParameters(public val symbols: List): OptimizationFeature{ + public constructor(vararg symbols: Symbol) : this(listOf(*symbols)) override fun toString(): String = "Parameters($symbols)" } 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 deleted file mode 100644 index 050f95a10..000000000 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/XYFit.kt +++ /dev/null @@ -1,72 +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. - */ - -package space.kscience.kmath.optimization - -import space.kscience.kmath.data.ColumnarData -import space.kscience.kmath.expressions.* -import space.kscience.kmath.misc.UnstableKMathAPI -import space.kscience.kmath.operations.ExtendedField -import space.kscience.kmath.operations.Field - -@UnstableKMathAPI -public interface XYFit : OptimizationProblem { - - 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)) } - } -} - -// -///** -// * Define a chi-squared-based objective function -// */ -//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) -// function(chiSquared) -// maximize = false -//} - -/** - * Optimize differentiable expression using specific [Optimizer] - */ -public suspend fun > DifferentiableExpression>.optimizeWith( - optimizer: Optimizer, - startingPoint: Map, - vararg features: OptimizationFeature -): OptimizationProblem { -// require(startingPoint.isNotEmpty()) { "Must provide a list of symbols for optimization" } -// val problem = factory(symbols.toList(), configuration) -// problem.function(this) -// return problem.optimize() - val problem = FunctionOptimization() -} \ No newline at end of file diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/XYOptimization.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/XYOptimization.kt new file mode 100644 index 000000000..dda95fcd7 --- /dev/null +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/XYOptimization.kt @@ -0,0 +1,92 @@ +/* + * 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.DifferentiableExpression +import space.kscience.kmath.expressions.Expression +import space.kscience.kmath.expressions.Symbol +import space.kscience.kmath.misc.FeatureSet +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.operations.Field +import space.kscience.kmath.operations.invoke + +public fun interface PointToCurveDistance { + public fun distance(problem: XYOptimization, index: Int): DifferentiableExpression> + + public companion object { + public fun byY( + algebra: Field, + ): PointToCurveDistance = PointToCurveDistance { problem, index -> + algebra { + val x = problem.data.x[index] + val y = problem.data.y[index] + + val model = problem.model(args + (Symbol.x to x)) + model - y + } + } + + +// val default = PointToCurveDistance{args, data, index -> +// +// } + } +} + + +public class XYOptimization( + override val features: FeatureSet, + public val data: XYColumnarData, + public val model: DifferentiableExpression>, +) : OptimizationProblem + +// +//@UnstableKMathAPI +//public interface XYFit : OptimizationProblem { +// +// 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)) } +// } +//} + +// +///** +// * Define a chi-squared-based objective function +// */ +//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) +// function(chiSquared) +// maximize = false +//} \ No newline at end of file diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/qow/QowFit.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/qow/QowFit.kt index 2ae33d82f..c78aef401 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/qow/QowFit.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/qow/QowFit.kt @@ -15,7 +15,7 @@ import space.kscience.kmath.operations.Field import space.kscience.kmath.optimization.OptimizationFeature import space.kscience.kmath.optimization.OptimizationProblemFactory import space.kscience.kmath.optimization.OptimizationResult -import space.kscience.kmath.optimization.XYFit +import space.kscience.kmath.optimization.XYOptimization import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.DoubleL2Norm import kotlin.math.pow @@ -28,7 +28,7 @@ public class QowFit( override val symbols: List, private val space: LinearSpace, private val solver: LinearSolver, -) : XYFit, SymbolIndexer { +) : XYOptimization, SymbolIndexer { private var logger: FitLogger? = null