Add attribute builder accessors for fits

This commit is contained in:
Alexander Nozik 2025-03-16 15:54:41 +03:00
parent a756490d20
commit ef31e35603
6 changed files with 70 additions and 11 deletions
CHANGELOG.md
examples/src/main/kotlin/space/kscience/kmath/fit
kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization

@ -3,6 +3,7 @@
## Unreleased
### Added
- Fit accessors with Attribute
### Changed
- Upgrade tensorflow version to 1.0.0

@ -14,7 +14,10 @@ import space.kscience.kmath.expressions.autodiff
import space.kscience.kmath.expressions.symbol
import space.kscience.kmath.operations.asIterable
import space.kscience.kmath.operations.toList
import space.kscience.kmath.optimization.*
import space.kscience.kmath.optimization.minimize
import space.kscience.kmath.optimization.optimizeWith
import space.kscience.kmath.optimization.result
import space.kscience.kmath.optimization.resultValue
import space.kscience.kmath.random.RandomGenerator
import space.kscience.kmath.real.DoubleVector
import space.kscience.kmath.real.map
@ -79,9 +82,10 @@ suspend fun main() {
val result = chi2.optimizeWith(
CMOptimizer,
mapOf(a to 1.5, b to 0.9, c to 1.0),
) {
FunctionOptimizationTarget(OptimizationDirection.MINIMIZE)
}
attributesBuilder = {
minimize()
}
)
//display a page with plot and numerical results
val page = Plotly.page {

@ -7,7 +7,6 @@ package space.kscience.kmath.fit
import kotlinx.html.br
import kotlinx.html.h3
import space.kscience.attributes.Attributes
import space.kscience.kmath.data.XYErrorColumnarData
import space.kscience.kmath.distributions.NormalDistribution
import space.kscience.kmath.expressions.Symbol
@ -65,7 +64,9 @@ suspend fun main() {
QowOptimizer,
Double.autodiff,
mapOf(a to 0.9, b to 1.2, c to 2.0, e to 1.0, d to 1.0, e to 0.0),
attributes = Attributes(OptimizationParameters, listOf(a, b, c, d))
attributesBuilder = {
freeParameters(a, b, c, d)
},
) { arg ->
//bind variables to autodiff context
val a by binding

@ -22,6 +22,15 @@ public enum class OptimizationDirection {
public object FunctionOptimizationTarget : OptimizationAttribute<OptimizationDirection>
public fun AttributesBuilder<FunctionOptimization<*>>.maximize() {
FunctionOptimizationTarget(OptimizationDirection.MAXIMIZE)
}
public fun AttributesBuilder<FunctionOptimization<*>>.minimize() {
FunctionOptimizationTarget(OptimizationDirection.MINIMIZE)
}
public class FunctionOptimization<T>(
public val expression: DifferentiableExpression<T>,
override val attributes: Attributes,
@ -74,11 +83,11 @@ public fun <T> FunctionOptimization<T>.withAttributes(
public suspend fun <T> DifferentiableExpression<T>.optimizeWith(
optimizer: Optimizer<T, FunctionOptimization<T>>,
startingPoint: Map<Symbol, T>,
modifier: AttributesBuilder<FunctionOptimization<T>>.() -> Unit = {},
attributesBuilder: AttributesBuilder<FunctionOptimization<T>>.() -> Unit = {},
): FunctionOptimization<T> {
val problem = FunctionOptimization(this) {
startAt(startingPoint)
modifier()
attributesBuilder()
}
return optimizer.optimize(problem)
}
@ -93,11 +102,11 @@ public val <T> FunctionOptimization<T>.resultValue: T
public suspend fun <T> DifferentiableExpression<T>.optimizeWith(
optimizer: Optimizer<T, FunctionOptimization<T>>,
vararg startingPoint: Pair<Symbol, T>,
builder: AttributesBuilder<FunctionOptimization<T>>.() -> Unit = {},
attributesBuilder: AttributesBuilder<FunctionOptimization<T>>.() -> Unit = {},
): FunctionOptimization<T> {
val problem = FunctionOptimization<T>(this) {
startAt(mapOf(*startingPoint))
builder()
attributesBuilder()
}
return optimizer.optimize(problem)
}

@ -58,9 +58,15 @@ public object OptimizationLog : OptimizationAttribute<Loggable>
*/
public object OptimizationParameters : OptimizationAttribute<List<Symbol>>
public fun AttributesBuilder<OptimizationProblem<*>>.freeParameters(vararg symbols: Symbol) {
OptimizationParameters(symbols.asList())
}
/**
* Maximum allowed number of iterations
*/
public object OptimizationIterations : OptimizationAttribute<Int>
public fun AttributesBuilder<OptimizationProblem<*>>.iterations(iterations: Int) {
OptimizationIterations(iterations)
}

@ -137,6 +137,24 @@ public suspend fun XYColumnarData<Double, Double, Double>.fitWith(
return optimizer.optimize(problem)
}
public suspend fun XYColumnarData<Double, Double, Double>.fitWith(
optimizer: Optimizer<Double, XYFit>,
modelExpression: DifferentiableExpression<Float64>,
startingPoint: Map<Symbol, Double>,
attributesBuilder: AttributesBuilder<XYFit>.() -> Unit,
xSymbol: Symbol = Symbol.x,
pointToCurveDistance: PointToCurveDistance = PointToCurveDistance.byY,
pointWeight: PointWeight = PointWeight.byYSigma,
): XYFit = fitWith(
optimizer = optimizer,
modelExpression = modelExpression,
startingPoint = startingPoint,
attributes = Attributes<XYFit>(attributesBuilder),
xSymbol = xSymbol,
pointToCurveDistance = pointToCurveDistance,
pointWeight = pointWeight
)
/**
* Fit given data with a model provided as an expression
*/
@ -166,6 +184,26 @@ public suspend fun <I : Any, A> XYColumnarData<Double, Double, Double>.fitWith(
)
}
public suspend fun <I : Any, A> XYColumnarData<Double, Double, Double>.fitWith(
optimizer: Optimizer<Double, XYFit>,
processor: AutoDiffProcessor<Double, I, A>,
startingPoint: Map<Symbol, Double>,
attributesBuilder: AttributesBuilder<XYFit>.() -> Unit,
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> = fitWith(
optimizer = optimizer,
processor = processor,
startingPoint = startingPoint,
attributes = Attributes<XYFit>(attributesBuilder),
xSymbol = xSymbol,
pointToCurveDistance = pointToCurveDistance,
pointWeight = pointWeight,
model = model
)
/**
* Compute chi squared value for completed fit. Return null for incomplete fit
*/