diff --git a/.gitignore b/.gitignore index d6c4af4e3..c5903368f 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ out/ # Generated by javac -h and runtime *.class *.log + +/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 524d2a1de..e35153d81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Jupyter Notebook integration module (kmath-jupyter) - `@PerformancePitfall` annotation to mark possibly slow API - BigInt operation performance improvement and fixes by @zhelenskiy (#328) +- Unified architecture for Integration and Optimization using features. ### Changed - Exponential operations merged with hyperbolic functions @@ -35,6 +36,8 @@ - Remove Any restriction on polynomials - Add `out` variance to type parameters of `StructureND` and its implementations where possible - Rename `DifferentiableMstExpression` to `KotlingradExpression` +- `FeatureSet` now accepts only `Feature`. It is possible to override keys and use interfaces. +- Use `Symbol` factory function instead of `StringSymbol` ### Deprecated diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt index 2c5a03a97..698f1a702 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt @@ -23,8 +23,8 @@ internal class DotBenchmark { const val dim = 1000 //creating invertible matrix - val matrix1 = LinearSpace.real.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 } - val matrix2 = LinearSpace.real.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 } + val matrix1 = LinearSpace.double.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 } + val matrix2 = LinearSpace.double.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 } val cmMatrix1 = CMLinearSpace { matrix1.toCM() } val cmMatrix2 = CMLinearSpace { matrix2.toCM() } @@ -63,7 +63,7 @@ internal class DotBenchmark { @Benchmark fun realDot(blackhole: Blackhole) { - LinearSpace.real { + LinearSpace.double { blackhole.consume(matrix1 dot matrix2) } } diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt index 7bb32af28..ff67ccc84 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/MatrixInverseBenchmark.kt @@ -25,7 +25,7 @@ internal class MatrixInverseBenchmark { private val random = Random(1224) private const val dim = 100 - private val space = LinearSpace.real + private val space = LinearSpace.double //creating invertible matrix private val u = space.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 } @@ -35,7 +35,7 @@ internal class MatrixInverseBenchmark { @Benchmark fun kmathLupInversion(blackhole: Blackhole) { - blackhole.consume(LinearSpace.real.inverseWithLup(matrix)) + blackhole.consume(LinearSpace.double.inverseWithLup(matrix)) } @Benchmark diff --git a/buildSrc/src/main/kotlin/space/kscience/kmath/ejml/codegen/ejmlCodegen.kt b/buildSrc/src/main/kotlin/space/kscience/kmath/ejml/codegen/ejmlCodegen.kt index 5da7d0f67..da1f45c1f 100644 --- a/buildSrc/src/main/kotlin/space/kscience/kmath/ejml/codegen/ejmlCodegen.kt +++ b/buildSrc/src/main/kotlin/space/kscience/kmath/ejml/codegen/ejmlCodegen.kt @@ -203,7 +203,7 @@ public object EjmlLinearSpace${ops} : EjmlLinearSpace<${type}, ${kmathAlgebra}, public override fun ${type}.times(v: Point<${type}>): Ejml${type}Vector<${ejmlMatrixType}> = v * this @UnstableKMathAPI - public override fun getFeature(structure: Matrix<${type}>, type: KClass): F? { + public override fun computeFeature(structure: Matrix<${type}>, type: KClass): F? { structure.getFeature(type)?.let { return it } val origin = structure.toEjml().origin diff --git a/examples/src/main/kotlin/space/kscience/kmath/ast/kotlingradSupport.kt b/examples/src/main/kotlin/space/kscience/kmath/ast/kotlingradSupport.kt index 420b23f9f..88fd312c7 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/ast/kotlingradSupport.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/ast/kotlingradSupport.kt @@ -9,7 +9,7 @@ import space.kscience.kmath.asm.compileToExpression import space.kscience.kmath.expressions.derivative import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.symbol -import space.kscience.kmath.kotlingrad.toDiffExpression +import space.kscience.kmath.kotlingrad.toKotlingradExpression import space.kscience.kmath.operations.DoubleField /** @@ -20,7 +20,7 @@ fun main() { val x by symbol val actualDerivative = "x^2-4*x-44".parseMath() - .toDiffExpression(DoubleField) + .toKotlingradExpression(DoubleField) .derivative(x) diff --git a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt index 5201fec38..bfba08bbd 100644 --- a/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt +++ b/kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt @@ -17,7 +17,7 @@ import com.github.h0tk3y.betterParse.lexer.regexToken import com.github.h0tk3y.betterParse.parser.ParseResult import com.github.h0tk3y.betterParse.parser.Parser import space.kscience.kmath.expressions.MST -import space.kscience.kmath.expressions.StringSymbol +import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.operations.FieldOperations import space.kscience.kmath.operations.GroupOperations import space.kscience.kmath.operations.PowerOperations @@ -43,7 +43,7 @@ public object ArithmeticsEvaluator : Grammar() { private val ws: Token by regexToken("\\s+".toRegex(), ignore = true) private val number: Parser by num use { MST.Numeric(text.toDouble()) } - private val singular: Parser by id use { StringSymbol(text) } + private val singular: Parser by id use { Symbol(text) } private val unaryFunction: Parser by (id and -lpar and parser(ArithmeticsEvaluator::subSumChain) and -rpar) .map { (id, term) -> MST.Unary(id.text, term) } diff --git a/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/mapIntrinsics.kt b/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/mapIntrinsics.kt index 8f4daecf9..948e7eab9 100644 --- a/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/mapIntrinsics.kt +++ b/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/mapIntrinsics.kt @@ -7,7 +7,6 @@ package space.kscience.kmath.asm.internal -import space.kscience.kmath.expressions.StringSymbol import space.kscience.kmath.expressions.Symbol /** @@ -15,4 +14,4 @@ import space.kscience.kmath.expressions.Symbol * * @author Iaroslav Postovalov */ -internal fun Map.getOrFail(key: String): V = getValue(StringSymbol(key)) +internal fun Map.getOrFail(key: String): V = getValue(Symbol(key)) diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt index 361027968..a7ee9ff3f 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt @@ -103,12 +103,12 @@ public class DerivativeStructureField( public override operator fun DerivativeStructure.minus(b: Number): DerivativeStructure = subtract(b.toDouble()) public override operator fun Number.plus(b: DerivativeStructure): DerivativeStructure = b + this public override operator fun Number.minus(b: DerivativeStructure): DerivativeStructure = b - this +} - public companion object : - AutoDiffProcessor> { - public override fun process(function: DerivativeStructureField.() -> DerivativeStructure): DifferentiableExpression = - DerivativeStructureExpression(function) - } +public object DSProcessor : AutoDiffProcessor { + public override fun differentiate( + function: DerivativeStructureField.() -> DerivativeStructure, + ): DerivativeStructureExpression = DerivativeStructureExpression(function) } /** diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/integration/CMGaussRuleIntegrator.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/integration/CMGaussRuleIntegrator.kt index 4e174723d..e0a2f4931 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/integration/CMGaussRuleIntegrator.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/integration/CMGaussRuleIntegrator.kt @@ -16,7 +16,7 @@ public class CMGaussRuleIntegrator( private var type: GaussRule = GaussRule.LEGANDRE, ) : UnivariateIntegrator { - override fun integrate(integrand: UnivariateIntegrand): UnivariateIntegrand { + override fun process(integrand: UnivariateIntegrand): UnivariateIntegrand { val range = integrand.getFeature()?.range ?: error("Integration range is not provided") val integrator: GaussIntegrator = getIntegrator(range) @@ -76,8 +76,8 @@ public class CMGaussRuleIntegrator( numPoints: Int = 100, type: GaussRule = GaussRule.LEGANDRE, function: (Double) -> Double, - ): Double = CMGaussRuleIntegrator(numPoints, type).integrate( + ): Double = CMGaussRuleIntegrator(numPoints, type).process( UnivariateIntegrand(function, IntegrationRange(range)) - ).valueOrNull!! + ).value } } \ No newline at end of file diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/integration/CMIntegrator.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/integration/CMIntegrator.kt index bcddccdc4..257429fa7 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/integration/CMIntegrator.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/integration/CMIntegrator.kt @@ -18,7 +18,7 @@ public class CMIntegrator( public val integratorBuilder: (Integrand) -> org.apache.commons.math3.analysis.integration.UnivariateIntegrator, ) : UnivariateIntegrator { - override fun integrate(integrand: UnivariateIntegrand): UnivariateIntegrand { + override fun process(integrand: UnivariateIntegrand): UnivariateIntegrand { val integrator = integratorBuilder(integrand) val maxCalls = integrand.getFeature()?.maxCalls ?: defaultMaxCalls val remainingCalls = maxCalls - integrand.calls diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt index 11b097831..c6f1cd852 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt @@ -95,7 +95,7 @@ public object CMLinearSpace : LinearSpace { v * this @UnstableKMathAPI - override fun getFeature(structure: Matrix, type: KClass): F? { + override fun computeFeature(structure: Matrix, type: KClass): F? { //Return the feature if it is intrinsic to the structure structure.getFeature(type)?.let { return it } @@ -109,22 +109,22 @@ public object CMLinearSpace : LinearSpace { LupDecompositionFeature { private val lup by lazy { LUDecomposition(origin) } override val determinant: Double by lazy { lup.determinant } - override val l: Matrix by lazy { CMMatrix(lup.l) + LFeature } - override val u: Matrix by lazy { CMMatrix(lup.u) + UFeature } + override val l: Matrix by lazy> { CMMatrix(lup.l).withFeature(LFeature) } + override val u: Matrix by lazy> { CMMatrix(lup.u).withFeature(UFeature) } override val p: Matrix by lazy { CMMatrix(lup.p) } } CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature { - override val l: Matrix by lazy { + override val l: Matrix by lazy> { val cholesky = CholeskyDecomposition(origin) - CMMatrix(cholesky.l) + LFeature + CMMatrix(cholesky.l).withFeature(LFeature) } } QRDecompositionFeature::class -> object : QRDecompositionFeature { private val qr by lazy { QRDecomposition(origin) } - override val q: Matrix by lazy { CMMatrix(qr.q) + OrthogonalFeature } - override val r: Matrix by lazy { CMMatrix(qr.r) + UFeature } + override val q: Matrix by lazy> { CMMatrix(qr.q).withFeature(OrthogonalFeature) } + override val r: Matrix by lazy> { CMMatrix(qr.r).withFeature(UFeature) } } SingularValueDecompositionFeature::class -> object : SingularValueDecompositionFeature { diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMSolver.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMSolver.kt index ee602ca06..9085974ea 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMSolver.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMSolver.kt @@ -6,6 +6,7 @@ package space.kscience.kmath.commons.linear import org.apache.commons.math3.linear.* +import space.kscience.kmath.linear.LinearSolver import space.kscience.kmath.linear.Matrix import space.kscience.kmath.linear.Point @@ -44,3 +45,12 @@ public fun CMLinearSpace.inverse( a: Matrix, decomposition: CMDecomposition = CMDecomposition.LUP, ): CMMatrix = solver(a, decomposition).inverse.wrap() + + +public fun CMLinearSpace.solver(decomposition: CMDecomposition): LinearSolver = object : LinearSolver { + override fun solve(a: Matrix, b: Matrix): Matrix = solve(a, b, decomposition) + + override fun solve(a: Matrix, b: Point): Point = solve(a, b, decomposition) + + override fun inverse(matrix: Matrix): Matrix = inverse(matrix, decomposition) +} \ No newline at end of file diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimization.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimizer.kt similarity index 63% rename from kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimization.kt rename to kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimizer.kt index 2faee1f5d..abf95daf6 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimization.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/CMOptimizer.kt @@ -2,7 +2,7 @@ * 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.commons.optimization import org.apache.commons.math3.optim.* @@ -11,6 +11,10 @@ import org.apache.commons.math3.optim.nonlinear.scalar.MultivariateOptimizer import org.apache.commons.math3.optim.nonlinear.scalar.ObjectiveFunction import org.apache.commons.math3.optim.nonlinear.scalar.ObjectiveFunctionGradient import org.apache.commons.math3.optim.nonlinear.scalar.gradient.NonLinearConjugateGradientOptimizer +import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.NelderMeadSimplex +import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.SimplexOptimizer +import space.kscience.kmath.expressions.Symbol +import space.kscience.kmath.expressions.SymbolIndexer import space.kscience.kmath.expressions.derivative import space.kscience.kmath.expressions.withSymbols import space.kscience.kmath.misc.UnstableKMathAPI @@ -21,29 +25,61 @@ import kotlin.reflect.KClass public operator fun PointValuePair.component1(): DoubleArray = point public operator fun PointValuePair.component2(): Double = value -public class CMOptimizer(public val optimizerBuilder: () -> MultivariateOptimizer): OptimizationFeature{ +public class CMOptimizerEngine(public val optimizerBuilder: () -> MultivariateOptimizer) : OptimizationFeature { override fun toString(): String = "CMOptimizer($optimizerBuilder)" } -public class CMOptimizerData(public val data: List) : OptimizationFeature { - public constructor(vararg data: OptimizationData) : this(data.toList()) +/** + * Specify a Commons-maths optimization engine + */ +public fun FunctionOptimizationBuilder.cmEngine(optimizerBuilder: () -> MultivariateOptimizer) { + addFeature(CMOptimizerEngine(optimizerBuilder)) +} + +public class CMOptimizerData(public val data: List OptimizationData>) : OptimizationFeature { + public constructor(vararg data: (SymbolIndexer.() -> OptimizationData)) : this(data.toList()) override fun toString(): String = "CMOptimizerData($data)" +} +/** + * Specify Commons-maths optimization data. + */ +public fun FunctionOptimizationBuilder.cmOptimizationData(data: SymbolIndexer.() -> OptimizationData) { + updateFeature { + val newData = (it?.data ?: emptyList()) + data + CMOptimizerData(newData) + } +} + +public fun FunctionOptimizationBuilder.simplexSteps(vararg steps: Pair) { + //TODO use convergence checker from features + cmEngine { SimplexOptimizer(CMOptimizer.defaultConvergenceChecker) } + cmOptimizationData { NelderMeadSimplex(mapOf(*steps).toDoubleArray()) } } @OptIn(UnstableKMathAPI::class) -public class CMOptimization : Optimizer> { +public object CMOptimizer : Optimizer> { + + public const val DEFAULT_RELATIVE_TOLERANCE: Double = 1e-4 + public const val DEFAULT_ABSOLUTE_TOLERANCE: Double = 1e-4 + public const val DEFAULT_MAX_ITER: Int = 1000 + + public val defaultConvergenceChecker: SimpleValueChecker = SimpleValueChecker( + DEFAULT_RELATIVE_TOLERANCE, + DEFAULT_ABSOLUTE_TOLERANCE, + DEFAULT_MAX_ITER + ) + override suspend fun optimize( problem: FunctionOptimization, ): FunctionOptimization { - val startPoint = problem.getFeature>()?.point - ?: error("Starting point not defined in $problem") + val startPoint = problem.startPoint val parameters = problem.getFeature()?.symbols ?: problem.getFeature>()?.point?.keys - ?:startPoint.keys + ?: startPoint.keys withSymbols(parameters) { @@ -53,7 +89,7 @@ public class CMOptimization : Optimizer> { DEFAULT_MAX_ITER ) - val cmOptimizer: MultivariateOptimizer = problem.getFeature()?.optimizerBuilder?.invoke() + val cmOptimizer: MultivariateOptimizer = problem.getFeature()?.optimizerBuilder?.invoke() ?: NonLinearConjugateGradientOptimizer( NonLinearConjugateGradientOptimizer.Formula.FLETCHER_REEVES, convergenceChecker @@ -68,7 +104,7 @@ public class CMOptimization : Optimizer> { addOptimizationData(MaxEval.unlimited()) addOptimizationData(InitialGuess(startPoint.toDoubleArray())) - fun exportOptimizationData(): List = optimizationData.values.toList() + //fun exportOptimizationData(): List = optimizationData.values.toList() val objectiveFunction = ObjectiveFunction { val args = startPoint + it.toMap() @@ -88,7 +124,9 @@ public class CMOptimization : Optimizer> { for (feature in problem.features) { when (feature) { - is CMOptimizerData -> feature.data.forEach { addOptimizationData(it) } + is CMOptimizerData -> feature.data.forEach { dataBuilder -> + addOptimizationData(dataBuilder()) + } is FunctionOptimizationTarget -> when (feature) { FunctionOptimizationTarget.MAXIMIZE -> addOptimizationData(GoalType.MAXIMIZE) FunctionOptimizationTarget.MINIMIZE -> addOptimizationData(GoalType.MINIMIZE) @@ -101,10 +139,4 @@ public class CMOptimization : Optimizer> { return problem.withFeatures(OptimizationResult(point.toMap()), OptimizationValue(value)) } } - - public companion object { - public const val DEFAULT_RELATIVE_TOLERANCE: Double = 1e-4 - public const val DEFAULT_ABSOLUTE_TOLERANCE: Double = 1e-4 - public const val DEFAULT_MAX_ITER: Int = 1000 - } } diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/cmFit.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/cmFit.kt deleted file mode 100644 index b1d7f5ca3..000000000 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/optimization/cmFit.kt +++ /dev/null @@ -1,75 +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.commons.optimization - -import org.apache.commons.math3.analysis.differentiation.DerivativeStructure -import space.kscience.kmath.commons.expressions.DerivativeStructureField -import space.kscience.kmath.expressions.DifferentiableExpression -import space.kscience.kmath.expressions.Expression -import space.kscience.kmath.expressions.Symbol -import space.kscience.kmath.optimization.* -import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.asBuffer - -/** - * Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic differentiation - */ -public fun FunctionOptimization.Companion.chiSquaredExpression( - x: Buffer, - y: Buffer, - yErr: Buffer, - model: DerivativeStructureField.(x: DerivativeStructure) -> DerivativeStructure, -): DifferentiableExpression = chiSquaredExpression(DerivativeStructureField, x, y, yErr, model) - -/** - * Generate a chi squared expression from given x-y-sigma data and inline model. Provides automatic differentiation - */ -public fun FunctionOptimization.Companion.chiSquaredExpression( - x: Iterable, - y: Iterable, - yErr: Iterable, - model: DerivativeStructureField.(x: DerivativeStructure) -> DerivativeStructure, -): DifferentiableExpression = chiSquaredExpression( - DerivativeStructureField, - x.toList().asBuffer(), - y.toList().asBuffer(), - yErr.toList().asBuffer(), - model -) - -/** - * Optimize expression without derivatives - */ -public suspend fun Expression.optimize( - vararg symbols: Symbol, - configuration: CMOptimization.() -> Unit, -): OptimizationResult { - require(symbols.isNotEmpty()) { "Must provide a list of symbols for optimization" } - val problem = CMOptimization(symbols.toList(), configuration) - problem.noDerivFunction(this) - return problem.optimize() -} - -/** - * Optimize differentiable expression - */ -public suspend fun DifferentiableExpression.optimize( - vararg symbols: Symbol, - configuration: CMOptimization.() -> Unit, -): OptimizationResult = optimizeWith(CMOptimization, symbols = symbols, configuration) - -public suspend fun DifferentiableExpression.minimize( - vararg startPoint: Pair, - configuration: CMOptimization.() -> Unit = {}, -): OptimizationResult { - val symbols = startPoint.map { it.first }.toTypedArray() - return optimize(*symbols){ - maximize = false - initialGuess(startPoint.toMap()) - function(this@minimize) - configuration() - } -} \ No newline at end of file diff --git a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt index b47d7da24..97761cfa2 100644 --- a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt +++ b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt @@ -6,43 +6,38 @@ package space.kscience.kmath.commons.optimization import kotlinx.coroutines.runBlocking +import space.kscience.kmath.commons.expressions.DSProcessor import space.kscience.kmath.commons.expressions.DerivativeStructureExpression import space.kscience.kmath.distributions.NormalDistribution +import space.kscience.kmath.expressions.Symbol.Companion.x +import space.kscience.kmath.expressions.Symbol.Companion.y import space.kscience.kmath.expressions.symbol -import space.kscience.kmath.optimization.FunctionOptimization +import space.kscience.kmath.optimization.* import space.kscience.kmath.stat.RandomGenerator import kotlin.math.pow import kotlin.test.Test internal class OptimizeTest { - val x by symbol - val y by symbol - val normal = DerivativeStructureExpression { - exp(-bindSymbol(x).pow(2) / 2) + exp(-bindSymbol(y) - .pow(2) / 2) + exp(-bindSymbol(x).pow(2) / 2) + exp(-bindSymbol(y).pow(2) / 2) } @Test - fun testGradientOptimization() = runBlocking{ - val result = normal.optimize(x, y) { - initialGuess(x to 1.0, y to 1.0) - //no need to select optimizer. Gradient optimizer is used by default because gradients are provided by function - } - println(result.point) - println(result.value) + fun testGradientOptimization() = runBlocking { + val result = normal.optimizeWith(CMOptimizer, x to 1.0, y to 1.0) + println(result.resultPoint) + println(result.resultValue) } @Test - fun testSimplexOptimization() = runBlocking{ - val result = normal.optimize(x, y) { - initialGuess(x to 1.0, y to 1.0) + fun testSimplexOptimization() = runBlocking { + val result = normal.optimizeWith(CMOptimizer, x to 1.0, y to 1.0) { simplexSteps(x to 2.0, y to 0.5) //this sets simplex optimizer } - println(result.point) - println(result.value) + println(result.resultPoint) + println(result.resultValue) } @Test @@ -62,6 +57,11 @@ internal class OptimizeTest { val yErr = List(x.size) { sigma } + val model = DSProcessor.differentiate { x1 -> + val cWithDefault = bindSymbolOrNull(c) ?: one + bindSymbol(a) * x1.pow(2) + bindSymbol(b) * x1 + cWithDefault + } + val chi2 = FunctionOptimization.chiSquared(x, y, yErr) { x1 -> val cWithDefault = bindSymbolOrNull(c) ?: one bindSymbol(a) * x1.pow(2) + bindSymbol(b) * x1 + cWithDefault diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/ColumnarData.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/ColumnarData.kt index 88c14d311..e06b774fd 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/ColumnarData.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/data/ColumnarData.kt @@ -25,6 +25,9 @@ public interface ColumnarData { public operator fun get(symbol: Symbol): Buffer? } +@UnstableKMathAPI +public val ColumnarData<*>.indices: IntRange get() = 0 until size + /** * A zero-copy method to represent a [Structure2D] as a two-column x-y data. * There could more than two columns in the structure. diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/DifferentiableExpression.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/DifferentiableExpression.kt index 011dc26e2..1782ff406 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/DifferentiableExpression.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/DifferentiableExpression.kt @@ -5,6 +5,8 @@ package space.kscience.kmath.expressions +import space.kscience.kmath.operations.Algebra + /** * Represents expression which structure can be differentiated. * @@ -63,7 +65,10 @@ public abstract class FirstDerivativeExpression : DifferentiableExpression /** * A factory that converts an expression in autodiff variables to a [DifferentiableExpression] + * @param T type of the constants for the expression + * @param I type of the actual expression state + * @param A type of expression algebra */ -public fun interface AutoDiffProcessor, out R : Expression> { - public fun process(function: A.() -> I): DifferentiableExpression +public fun interface AutoDiffProcessor> { + public fun differentiate(function: A.() -> I): DifferentiableExpression } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSolver.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSolver.kt index 9c3ffd819..288fabbaf 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSolver.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSolver.kt @@ -5,8 +5,6 @@ package space.kscience.kmath.linear -import space.kscience.kmath.nd.as1D - /** * A group of methods to solve for *X* in equation *X = A -1 · B*, where *A* and *B* are matrices or * vectors. @@ -30,20 +28,3 @@ public interface LinearSolver { public fun inverse(matrix: Matrix): Matrix } -/** - * Convert matrix to vector if it is possible. - */ -public fun Matrix.asVector(): Point = - if (this.colNum == 1) - as1D() - else - error("Can't convert matrix with more than one column to vector") - -/** - * Creates an n × 1 [VirtualMatrix], where n is the size of the given buffer. - * - * @param T the type of elements contained in the buffer. - * @receiver a buffer. - * @return the new matrix. - */ -public fun Point.asMatrix(): VirtualMatrix = VirtualMatrix(size, 1) { i, _ -> get(i) } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt index ec073ac48..94083f70d 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LinearSpace.kt @@ -164,7 +164,7 @@ public interface LinearSpace> { public operator fun T.times(v: Point): Point = v * this /** - * Get a feature of the structure in this scope. Structure features take precedence other context features + * Compute a feature of the structure in this scope. Structure features take precedence other context features * * @param F the type of feature. * @param structure the structure. @@ -172,7 +172,7 @@ public interface LinearSpace> { * @return a feature object or `null` if it isn't present. */ @UnstableKMathAPI - public fun getFeature(structure: Matrix, type: KClass): F? = structure.getFeature(type) + public fun computeFeature(structure: Matrix, type: KClass): F? = structure.getFeature(type) public companion object { @@ -184,7 +184,7 @@ public interface LinearSpace> { bufferFactory: BufferFactory = Buffer.Companion::boxing, ): LinearSpace = BufferedLinearSpace(algebra, bufferFactory) - public val real: LinearSpace = buffered(DoubleField, ::DoubleBuffer) + public val double: LinearSpace = buffered(DoubleField, ::DoubleBuffer) /** * Automatic buffered matrix, unboxed if it is possible @@ -202,9 +202,27 @@ public interface LinearSpace> { * @return a feature object or `null` if it isn't present. */ @UnstableKMathAPI -public inline fun LinearSpace.getFeature(structure: Matrix): F? = - getFeature(structure, F::class) +public inline fun LinearSpace.computeFeature(structure: Matrix): F? = + computeFeature(structure, F::class) -public operator fun , R> LS.invoke(block: LS.() -> R): R = run(block) +public inline operator fun , R> LS.invoke(block: LS.() -> R): R = run(block) + +/** + * Convert matrix to vector if it is possible. + */ +public fun Matrix.asVector(): Point = + if (this.colNum == 1) + as1D() + else + error("Can't convert matrix with more than one column to vector") + +/** + * Creates an n × 1 [VirtualMatrix], where n is the size of the given buffer. + * + * @param T the type of elements contained in the buffer. + * @receiver a buffer. + * @return the new matrix. + */ +public fun Point.asMatrix(): VirtualMatrix = VirtualMatrix(size, 1) { i, _ -> get(i) } \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt index f3653d394..3b6208468 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt @@ -5,6 +5,7 @@ package space.kscience.kmath.linear +import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.nd.getFeature import space.kscience.kmath.operations.* @@ -34,7 +35,7 @@ public class LupDecomposition( j == i -> elementContext.one else -> elementContext.zero } - } + LFeature + }.withFeature(LFeature) /** @@ -44,7 +45,7 @@ public class LupDecomposition( */ override val u: Matrix = VirtualMatrix(lu.shape[0], lu.shape[1]) { i, j -> if (j >= i) lu[i, j] else elementContext.zero - } + UFeature + }.withFeature(UFeature) /** * Returns the P rows permutation matrix. @@ -82,7 +83,7 @@ public fun > LinearSpace>.lup( val m = matrix.colNum val pivot = IntArray(matrix.rowNum) - //TODO just waits for KEEP-176 + //TODO just waits for multi-receivers BufferAccessor2D(matrix.rowNum, matrix.colNum, factory).run { elementAlgebra { val lu = create(matrix) @@ -156,10 +157,13 @@ public inline fun > LinearSpace>.lup( noinline checkSingular: (T) -> Boolean, ): LupDecomposition = lup(MutableBuffer.Companion::auto, matrix, checkSingular) -public fun LinearSpace.lup(matrix: Matrix): LupDecomposition = - lup(::DoubleBuffer, matrix) { it < 1e-11 } +public fun LinearSpace.lup( + matrix: Matrix, + singularityThreshold: Double = 1e-11, +): LupDecomposition = + lup(::DoubleBuffer, matrix) { it < singularityThreshold } -public fun LupDecomposition.solveWithLup( +internal fun LupDecomposition.solve( factory: MutableBufferFactory, matrix: Matrix, ): Matrix { @@ -207,41 +211,24 @@ public fun LupDecomposition.solveWithLup( } } -public inline fun LupDecomposition.solveWithLup(matrix: Matrix): Matrix = - solveWithLup(MutableBuffer.Companion::auto, matrix) - /** - * Solves a system of linear equations *ax = b** using LUP decomposition. + * Produce a generic solver based on LUP decomposition */ +@PerformancePitfall() @OptIn(UnstableKMathAPI::class) -public inline fun > LinearSpace>.solveWithLup( - a: Matrix, - b: Matrix, - noinline bufferFactory: MutableBufferFactory = MutableBuffer.Companion::auto, - noinline checkSingular: (T) -> Boolean, -): Matrix { - // Use existing decomposition if it is provided by matrix - val decomposition = a.getFeature() ?: lup(bufferFactory, a, checkSingular) - return decomposition.solveWithLup(bufferFactory, b) +public fun , F : Field> LinearSpace.lupSolver( + bufferFactory: MutableBufferFactory, + singularityCheck: (T) -> Boolean, +): LinearSolver = object : LinearSolver { + override fun solve(a: Matrix, b: Matrix): Matrix { + // Use existing decomposition if it is provided by matrix + val decomposition = a.getFeature() ?: lup(bufferFactory, a, singularityCheck) + return decomposition.solve(bufferFactory, b) + } + + override fun inverse(matrix: Matrix): Matrix = solve(matrix, one(matrix.rowNum, matrix.colNum)) } -public inline fun > LinearSpace>.inverseWithLup( - matrix: Matrix, - noinline bufferFactory: MutableBufferFactory = MutableBuffer.Companion::auto, - noinline checkSingular: (T) -> Boolean, -): Matrix = solveWithLup(matrix, one(matrix.rowNum, matrix.colNum), bufferFactory, checkSingular) - - -@OptIn(UnstableKMathAPI::class) -public fun LinearSpace.solveWithLup(a: Matrix, b: Matrix): Matrix { - // Use existing decomposition if it is provided by matrix - val bufferFactory: MutableBufferFactory = ::DoubleBuffer - val decomposition: LupDecomposition = a.getFeature() ?: lup(bufferFactory, a) { it < 1e-11 } - return decomposition.solveWithLup(bufferFactory, b) -} - -/** - * Inverses a square matrix using LUP decomposition. Non square matrix will throw a error. - */ -public fun LinearSpace.inverseWithLup(matrix: Matrix): Matrix = - solveWithLup(matrix, one(matrix.rowNum, matrix.colNum)) \ No newline at end of file +@PerformancePitfall +public fun LinearSpace.lupSolver(singularityThreshold: Double = 1e-11): LinearSolver = + lupSolver(::DoubleBuffer) { it < singularityThreshold } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixBuilder.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixBuilder.kt index 72d22233a..029612bc5 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixBuilder.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixBuilder.kt @@ -7,6 +7,8 @@ package space.kscience.kmath.linear import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.operations.Ring +import space.kscience.kmath.structures.BufferAccessor2D +import space.kscience.kmath.structures.MutableBuffer public class MatrixBuilder>( public val linearSpace: LinearSpace, @@ -45,4 +47,31 @@ public inline fun LinearSpace>.column( crossinline builder: (Int) -> T, ): Matrix = buildMatrix(size, 1) { i, _ -> builder(i) } -public fun LinearSpace>.column(vararg values: T): Matrix = column(values.size, values::get) \ No newline at end of file +public fun LinearSpace>.column(vararg values: T): Matrix = column(values.size, values::get) + +public object SymmetricMatrixFeature : MatrixFeature + +/** + * Naive implementation of a symmetric matrix builder, that adds a [SymmetricMatrixFeature] tag. The resulting matrix contains + * full `size^2` number of elements, but caches elements during calls to save [builder] calls. [builder] is always called in the + * upper triangle region meaning that `i <= j` + */ +public fun > MatrixBuilder.symmetric( + builder: (i: Int, j: Int) -> T, +): Matrix { + require(columns == rows) { "In order to build symmetric matrix, number of rows $rows should be equal to number of columns $columns" } + return with(BufferAccessor2D(rows, rows, MutableBuffer.Companion::boxing)) { + val cache = factory(rows * rows) { null } + linearSpace.buildMatrix(rows, rows) { i, j -> + val cached = cache[i, j] + if (cached == null) { + val value = if (i <= j) builder(i, j) else builder(j, i) + cache[i, j] = value + cache[j, i] = value + value + } else { + cached + } + }.withFeature(SymmetricMatrixFeature) + } +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixWrapper.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixWrapper.kt index 16aadab3b..df62c6fc0 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixWrapper.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/MatrixWrapper.kt @@ -5,6 +5,7 @@ package space.kscience.kmath.linear +import space.kscience.kmath.misc.FeatureSet import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.nd.StructureFeature import space.kscience.kmath.nd.getFeature @@ -18,7 +19,7 @@ import kotlin.reflect.KClass */ public class MatrixWrapper internal constructor( public val origin: Matrix, - public val features: Set, + public val features: FeatureSet, ) : Matrix by origin { /** @@ -27,8 +28,7 @@ public class MatrixWrapper internal constructor( @UnstableKMathAPI @Suppress("UNCHECKED_CAST") public override fun getFeature(type: KClass): F? = - features.singleOrNull(type::isInstance) as? F - ?: origin.getFeature(type) + features.getFeature(type) ?: origin.getFeature(type) public override fun toString(): String = "MatrixWrapper(matrix=$origin, features=$features)" } @@ -44,20 +44,23 @@ public val Matrix.origin: Matrix /** * Add a single feature to a [Matrix] */ -public operator fun Matrix.plus(newFeature: MatrixFeature): MatrixWrapper = if (this is MatrixWrapper) { - MatrixWrapper(origin, features + newFeature) +public fun Matrix.withFeature(newFeature: MatrixFeature): MatrixWrapper = if (this is MatrixWrapper) { + MatrixWrapper(origin, features.with(newFeature)) } else { - MatrixWrapper(this, setOf(newFeature)) + MatrixWrapper(this, FeatureSet.of(newFeature)) } +@Deprecated("To be replaced by withFeature") +public operator fun Matrix.plus(newFeature: MatrixFeature): MatrixWrapper = withFeature(newFeature) + /** * Add a collection of features to a [Matrix] */ -public operator fun Matrix.plus(newFeatures: Collection): MatrixWrapper = +public fun Matrix.withFeatures(newFeatures: Iterable): MatrixWrapper = if (this is MatrixWrapper) { - MatrixWrapper(origin, features + newFeatures) + MatrixWrapper(origin, features.with(newFeatures)) } else { - MatrixWrapper(this, newFeatures.toSet()) + MatrixWrapper(this, FeatureSet.of(newFeatures)) } /** @@ -68,7 +71,7 @@ public fun LinearSpace>.one( columns: Int, ): Matrix = VirtualMatrix(rows, columns) { i, j -> if (i == j) elementAlgebra.one else elementAlgebra.zero -} + UnitFeature +}.withFeature(UnitFeature) /** @@ -79,7 +82,7 @@ public fun LinearSpace>.zero( columns: Int, ): Matrix = VirtualMatrix(rows, columns) { _, _ -> elementAlgebra.zero -} + ZeroFeature +}.withFeature(ZeroFeature) public class TransposedFeature(public val original: Matrix) : MatrixFeature @@ -90,4 +93,4 @@ public class TransposedFeature(public val original: Matrix) : Ma public fun Matrix.transpose(): Matrix = getFeature>()?.original ?: VirtualMatrix( colNum, rowNum, -) { i, j -> get(j, i) } + TransposedFeature(this) \ No newline at end of file +) { i, j -> get(j, i) }.withFeature(TransposedFeature(this)) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/VirtualMatrix.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/VirtualMatrix.kt index 3751bd33b..fb2b1e547 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/VirtualMatrix.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/VirtualMatrix.kt @@ -20,3 +20,6 @@ public class VirtualMatrix( override operator fun get(i: Int, j: Int): T = generator(i, j) } + +public fun MatrixBuilder.virtual(generator: (i: Int, j: Int) -> T): VirtualMatrix = + VirtualMatrix(rows, columns, generator) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/symmetric.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/symmetric.kt deleted file mode 100644 index 04d9a9897..000000000 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/symmetric.kt +++ /dev/null @@ -1,34 +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.linear - -import space.kscience.kmath.structures.BufferAccessor2D -import space.kscience.kmath.structures.MutableBuffer - -public object SymmetricMatrixFeature : MatrixFeature - -/** - * Naive implementation of a symmetric matrix builder, that adds a [SymmetricMatrixFeature] tag. The resulting matrix contains - * full `size^2` number of elements, but caches elements during calls to save [builder] calls. [builder] is always called in the - * upper triangle region meaning that `i <= j` - */ -public fun > LS.buildSymmetricMatrix( - size: Int, - builder: (i: Int, j: Int) -> T, -): Matrix = BufferAccessor2D(size, size, MutableBuffer.Companion::boxing).run { - val cache = factory(size * size) { null } - buildMatrix(size, size) { i, j -> - val cached = cache[i, j] - if (cached == null) { - val value = if (i <= j) builder(i, j) else builder(j, i) - cache[i, j] = value - cache[j, i] = value - value - } else { - cached - } - } + SymmetricMatrixFeature -} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/Featured.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/Featured.kt index a94efc788..648b6376f 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/Featured.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/Featured.kt @@ -11,29 +11,44 @@ import kotlin.reflect.KClass * A entity that contains a set of features defined by their types */ public interface Featured { - public fun getFeature(type: KClass): T? + public fun getFeature(type: FeatureKey): T? +} + +public typealias FeatureKey = KClass + +public interface Feature> { + + /** + * A key used for extraction + */ + @Suppress("UNCHECKED_CAST") + public val key: FeatureKey get() = this::class as FeatureKey } /** * A container for a set of features */ -public class FeatureSet private constructor(public val features: Map, F>) : Featured { +public class FeatureSet> private constructor(public val features: Map, F>) : Featured { @Suppress("UNCHECKED_CAST") - override fun getFeature(type: KClass): T? = features[type] as? T + override fun getFeature(type: FeatureKey): T? = features[type] as? T public inline fun getFeature(): T? = getFeature(T::class) - public fun with(feature: T, type: KClass = feature::class): FeatureSet = + public fun with(feature: T, type: FeatureKey = feature.key): FeatureSet = FeatureSet(features + (type to feature)) public fun with(other: FeatureSet): FeatureSet = FeatureSet(features + other.features) public fun with(vararg otherFeatures: F): FeatureSet = - FeatureSet(features + otherFeatures.associateBy { it::class }) + FeatureSet(features + otherFeatures.associateBy { it.key }) + + public fun with(otherFeatures: Iterable): FeatureSet = + FeatureSet(features + otherFeatures.associateBy { it.key }) public operator fun iterator(): Iterator = features.values.iterator() public companion object { - public fun of(vararg features: F): FeatureSet = FeatureSet(features.associateBy { it::class }) + public fun > of(vararg features: F): FeatureSet = FeatureSet(features.associateBy { it.key }) + public fun > of(features: Iterable): FeatureSet = FeatureSet(features.associateBy { it.key }) } } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/annotations.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/annotations.kt index e521e6237..39b2779eb 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/annotations.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/annotations.kt @@ -12,7 +12,7 @@ package space.kscience.kmath.misc * in some way that may break some code. */ @MustBeDocumented -@Retention(value = AnnotationRetention.BINARY) +@Retention(value = AnnotationRetention.SOURCE) @RequiresOptIn("This API is unstable and could change in future", RequiresOptIn.Level.WARNING) public annotation class UnstableKMathAPI @@ -21,7 +21,7 @@ public annotation class UnstableKMathAPI * slow-down in some cases. Refer to the documentation and benchmark it to be sure. */ @MustBeDocumented -@Retention(value = AnnotationRetention.BINARY) +@Retention(value = AnnotationRetention.SOURCE) @RequiresOptIn( "Refer to the documentation to use this API in performance-critical code", RequiresOptIn.Level.WARNING diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt index 7fc91e321..4962dcb7d 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/StructureND.kt @@ -5,6 +5,8 @@ package space.kscience.kmath.nd +import space.kscience.kmath.misc.Feature +import space.kscience.kmath.misc.Featured import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.structures.Buffer @@ -13,7 +15,7 @@ import kotlin.jvm.JvmName import kotlin.native.concurrent.ThreadLocal import kotlin.reflect.KClass -public interface StructureFeature +public interface StructureFeature : Feature /** * Represents n-dimensional structure, i.e. multidimensional container of items of the same type and size. The number @@ -24,7 +26,7 @@ public interface StructureFeature * * @param T the type of items. */ -public interface StructureND { +public interface StructureND : Featured { /** * The shape of structure, i.e. non-empty sequence of non-negative integers that specify sizes of dimensions of * this structure. @@ -57,7 +59,7 @@ public interface StructureND { * If the feature is not present, null is returned. */ @UnstableKMathAPI - public fun getFeature(type: KClass): F? = null + override fun getFeature(type: KClass): F? = null public companion object { /** diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferAccessor2D.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferAccessor2D.kt index d29c54d46..a96253939 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferAccessor2D.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferAccessor2D.kt @@ -14,30 +14,30 @@ import space.kscience.kmath.nd.as2D * A context that allows to operate on a [MutableBuffer] as on 2d array */ internal class BufferAccessor2D( - public val rowNum: Int, - public val colNum: Int, + val rowNum: Int, + val colNum: Int, val factory: MutableBufferFactory, ) { - public operator fun Buffer.get(i: Int, j: Int): T = get(i * colNum + j) + operator fun Buffer.get(i: Int, j: Int): T = get(i * colNum + j) - public operator fun MutableBuffer.set(i: Int, j: Int, value: T) { + operator fun MutableBuffer.set(i: Int, j: Int, value: T) { set(i * colNum + j, value) } - public inline fun create(crossinline init: (i: Int, j: Int) -> T): MutableBuffer = + inline fun create(crossinline init: (i: Int, j: Int) -> T): MutableBuffer = factory(rowNum * colNum) { offset -> init(offset / colNum, offset % colNum) } - public fun create(mat: Structure2D): MutableBuffer = create { i, j -> mat[i, j] } + fun create(mat: Structure2D): MutableBuffer = create { i, j -> mat[i, j] } //TODO optimize wrapper - public fun MutableBuffer.collect(): Structure2D = StructureND.buffered( + fun MutableBuffer.collect(): Structure2D = StructureND.buffered( DefaultStrides(intArrayOf(rowNum, colNum)), factory ) { (i, j) -> get(i, j) }.as2D() - public inner class Row(public val buffer: MutableBuffer, public val rowIndex: Int) : MutableBuffer { + inner class Row(val buffer: MutableBuffer, val rowIndex: Int) : MutableBuffer { override val size: Int get() = colNum override operator fun get(index: Int): T = buffer[rowIndex, index] @@ -54,5 +54,5 @@ internal class BufferAccessor2D( /** * Get row */ - public fun MutableBuffer.row(i: Int): Row = Row(this, i) + fun MutableBuffer.row(i: Int): Row = Row(this, i) } diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/linear/DoubleLUSolverTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/linear/DoubleLUSolverTest.kt index 2d2a0952b..3be473e54 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/linear/DoubleLUSolverTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/linear/DoubleLUSolverTest.kt @@ -22,14 +22,14 @@ class DoubleLUSolverTest { @Test fun testInvertOne() { - val matrix = LinearSpace.real.one(2, 2) - val inverted = LinearSpace.real.inverseWithLup(matrix) + val matrix = LinearSpace.double.one(2, 2) + val inverted = LinearSpace.double.lupSolver().inverse(matrix) assertMatrixEquals(matrix, inverted) } @Test fun testDecomposition() { - LinearSpace.real.run { + LinearSpace.double.run { val matrix = matrix(2, 2)( 3.0, 1.0, 2.0, 3.0 @@ -46,14 +46,14 @@ class DoubleLUSolverTest { @Test fun testInvert() { - val matrix = LinearSpace.real.matrix(2, 2)( + val matrix = LinearSpace.double.matrix(2, 2)( 3.0, 1.0, 1.0, 3.0 ) - val inverted = LinearSpace.real.inverseWithLup(matrix) + val inverted = LinearSpace.double.lupSolver().inverse(matrix) - val expected = LinearSpace.real.matrix(2, 2)( + val expected = LinearSpace.double.matrix(2, 2)( 0.375, -0.125, -0.125, 0.375 ) diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/linear/MatrixTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/linear/MatrixTest.kt index 170f9caf4..feae07c1e 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/linear/MatrixTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/linear/MatrixTest.kt @@ -19,14 +19,14 @@ import kotlin.test.assertTrue class MatrixTest { @Test fun testTranspose() { - val matrix = LinearSpace.real.one(3, 3) + val matrix = LinearSpace.double.one(3, 3) val transposed = matrix.transpose() assertTrue { StructureND.contentEquals(matrix, transposed) } } @Test fun testBuilder() { - val matrix = LinearSpace.real.matrix(2, 3)( + val matrix = LinearSpace.double.matrix(2, 3)( 1.0, 0.0, 0.0, 0.0, 1.0, 2.0 ) @@ -48,7 +48,7 @@ class MatrixTest { infix fun Matrix.pow(power: Int): Matrix { var res = this repeat(power - 1) { - res = LinearSpace.real.run { res dot this@pow } + res = LinearSpace.double.run { res dot this@pow } } return res } @@ -61,7 +61,7 @@ class MatrixTest { val firstMatrix = StructureND.auto(2, 3) { (i, j) -> (i + j).toDouble() }.as2D() val secondMatrix = StructureND.auto(3, 2) { (i, j) -> (i + j).toDouble() }.as2D() - LinearSpace.real.run { + LinearSpace.double.run { // val firstMatrix = produce(2, 3) { i, j -> (i + j).toDouble() } // val secondMatrix = produce(3, 2) { i, j -> (i + j).toDouble() } val result = firstMatrix dot secondMatrix diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NumberNDFieldTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NumberNDFieldTest.kt index fb51553f7..8a03115b5 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NumberNDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NumberNDFieldTest.kt @@ -40,7 +40,7 @@ class NumberNDFieldTest { @Test fun testGeneration() { - val array = LinearSpace.real.buildMatrix(3, 3) { i, j -> + val array = LinearSpace.double.buildMatrix(3, 3) { i, j -> (i * 10 + j).toDouble() } diff --git a/kmath-dimensions/src/commonMain/kotlin/space/kscience/kmath/dimensions/Wrappers.kt b/kmath-dimensions/src/commonMain/kotlin/space/kscience/kmath/dimensions/Wrappers.kt index 2ebcc454d..91ab6a5d6 100644 --- a/kmath-dimensions/src/commonMain/kotlin/space/kscience/kmath/dimensions/Wrappers.kt +++ b/kmath-dimensions/src/commonMain/kotlin/space/kscience/kmath/dimensions/Wrappers.kt @@ -151,7 +151,7 @@ public value class DMatrixContext>(public val context: context.run { (this@transpose as Matrix).transpose() }.coerce() public companion object { - public val real: DMatrixContext = DMatrixContext(LinearSpace.real) + public val real: DMatrixContext = DMatrixContext(LinearSpace.double) } } diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt deleted file mode 100644 index 139c55697..000000000 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt +++ /dev/null @@ -1,995 +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. - */ - -/* This file is generated with buildSrc/src/main/kotlin/space/kscience/kmath/ejml/codegen/ejmlCodegen.kt */ - -package space.kscience.kmath.ejml - -import org.ejml.data.* -import org.ejml.dense.row.CommonOps_DDRM -import org.ejml.dense.row.CommonOps_FDRM -import org.ejml.dense.row.factory.DecompositionFactory_DDRM -import org.ejml.dense.row.factory.DecompositionFactory_FDRM -import org.ejml.sparse.FillReducing -import org.ejml.sparse.csc.CommonOps_DSCC -import org.ejml.sparse.csc.CommonOps_FSCC -import org.ejml.sparse.csc.factory.DecompositionFactory_DSCC -import org.ejml.sparse.csc.factory.DecompositionFactory_FSCC -import org.ejml.sparse.csc.factory.LinearSolverFactory_DSCC -import org.ejml.sparse.csc.factory.LinearSolverFactory_FSCC -import space.kscience.kmath.linear.* -import space.kscience.kmath.linear.Matrix -import space.kscience.kmath.misc.UnstableKMathAPI -import space.kscience.kmath.nd.StructureFeature -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.FloatField -import space.kscience.kmath.operations.invoke -import space.kscience.kmath.structures.DoubleBuffer -import space.kscience.kmath.structures.FloatBuffer -import kotlin.reflect.KClass -import kotlin.reflect.cast - -/** - * [EjmlVector] specialization for [Double]. - */ -public class EjmlDoubleVector(public override val origin: M) : EjmlVector(origin) { - init { - require(origin.numRows == 1) { "The origin matrix must have only one row to form a vector" } - } - - public override operator fun get(index: Int): Double = origin[0, index] -} - -/** - * [EjmlVector] specialization for [Float]. - */ -public class EjmlFloatVector(public override val origin: M) : EjmlVector(origin) { - init { - require(origin.numRows == 1) { "The origin matrix must have only one row to form a vector" } - } - - public override operator fun get(index: Int): Float = origin[0, index] -} - -/** - * [EjmlMatrix] specialization for [Double]. - */ -public class EjmlDoubleMatrix(public override val origin: M) : EjmlMatrix(origin) { - public override operator fun get(i: Int, j: Int): Double = origin[i, j] -} - -/** - * [EjmlMatrix] specialization for [Float]. - */ -public class EjmlFloatMatrix(public override val origin: M) : EjmlMatrix(origin) { - public override operator fun get(i: Int, j: Int): Float = origin[i, j] -} - -/** - * [EjmlLinearSpace] implementation based on [CommonOps_DDRM], [DecompositionFactory_DDRM] operations and - * [DMatrixRMaj] matrices. - */ -public object EjmlLinearSpaceDDRM : EjmlLinearSpace() { - /** - * The [DoubleField] reference. - */ - public override val elementAlgebra: DoubleField get() = DoubleField - - @Suppress("UNCHECKED_CAST") - public override fun Matrix.toEjml(): EjmlDoubleMatrix = when { - this is EjmlDoubleMatrix<*> && origin is DMatrixRMaj -> this as EjmlDoubleMatrix - else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) } - } - - @Suppress("UNCHECKED_CAST") - public override fun Point.toEjml(): EjmlDoubleVector = when { - this is EjmlDoubleVector<*> && origin is DMatrixRMaj -> this as EjmlDoubleVector - else -> EjmlDoubleVector(DMatrixRMaj(size, 1).also { - (0 until it.numRows).forEach { row -> it[row, 0] = get(row) } - }) - } - - public override fun buildMatrix( - rows: Int, - columns: Int, - initializer: DoubleField.(i: Int, j: Int) -> Double, - ): EjmlDoubleMatrix = DMatrixRMaj(rows, columns).also { - (0 until rows).forEach { row -> - (0 until columns).forEach { col -> it[row, col] = elementAlgebra.initializer(row, col) } - } - }.wrapMatrix() - - public override fun buildVector( - size: Int, - initializer: DoubleField.(Int) -> Double, - ): EjmlDoubleVector = EjmlDoubleVector(DMatrixRMaj(size, 1).also { - (0 until it.numRows).forEach { row -> it[row, 0] = elementAlgebra.initializer(row) } - }) - - private fun T.wrapMatrix() = EjmlDoubleMatrix(this) - private fun T.wrapVector() = EjmlDoubleVector(this) - - public override fun Matrix.unaryMinus(): Matrix = this * elementAlgebra { -one } - - public override fun Matrix.dot(other: Matrix): EjmlDoubleMatrix { - val out = DMatrixRMaj(1, 1) - CommonOps_DDRM.mult(toEjml().origin, other.toEjml().origin, out) - return out.wrapMatrix() - } - - public override fun Matrix.dot(vector: Point): EjmlDoubleVector { - val out = DMatrixRMaj(1, 1) - CommonOps_DDRM.mult(toEjml().origin, vector.toEjml().origin, out) - return out.wrapVector() - } - - public override operator fun Matrix.minus(other: Matrix): EjmlDoubleMatrix { - val out = DMatrixRMaj(1, 1) - - CommonOps_DDRM.add( - elementAlgebra.one, - toEjml().origin, - elementAlgebra { -one }, - other.toEjml().origin, - out, - ) - - return out.wrapMatrix() - } - - public override operator fun Matrix.times(value: Double): EjmlDoubleMatrix { - val res = DMatrixRMaj(1, 1) - CommonOps_DDRM.scale(value, toEjml().origin, res) - return res.wrapMatrix() - } - - public override fun Point.unaryMinus(): EjmlDoubleVector { - val res = DMatrixRMaj(1, 1) - CommonOps_DDRM.changeSign(toEjml().origin, res) - return res.wrapVector() - } - - public override fun Matrix.plus(other: Matrix): EjmlDoubleMatrix { - val out = DMatrixRMaj(1, 1) - - CommonOps_DDRM.add( - elementAlgebra.one, - toEjml().origin, - elementAlgebra.one, - other.toEjml().origin, - out, - ) - - return out.wrapMatrix() - } - - public override fun Point.plus(other: Point): EjmlDoubleVector { - val out = DMatrixRMaj(1, 1) - - CommonOps_DDRM.add( - elementAlgebra.one, - toEjml().origin, - elementAlgebra.one, - other.toEjml().origin, - out, - ) - - return out.wrapVector() - } - - public override fun Point.minus(other: Point): EjmlDoubleVector { - val out = DMatrixRMaj(1, 1) - - CommonOps_DDRM.add( - elementAlgebra.one, - toEjml().origin, - elementAlgebra { -one }, - other.toEjml().origin, - out, - ) - - return out.wrapVector() - } - - public override fun Double.times(m: Matrix): EjmlDoubleMatrix = m * this - - public override fun Point.times(value: Double): EjmlDoubleVector { - val res = DMatrixRMaj(1, 1) - CommonOps_DDRM.scale(value, toEjml().origin, res) - return res.wrapVector() - } - - public override fun Double.times(v: Point): EjmlDoubleVector = v * this - - @UnstableKMathAPI - public override fun getFeature(structure: Matrix, type: KClass): F? { - structure.getFeature(type)?.let { return it } - val origin = structure.toEjml().origin - - return when (type) { - InverseMatrixFeature::class -> object : InverseMatrixFeature { - override val inverse: Matrix by lazy { - val res = origin.copy() - CommonOps_DDRM.invert(res) - res.wrapMatrix() - } - } - - DeterminantFeature::class -> object : DeterminantFeature { - override val determinant: Double by lazy { CommonOps_DDRM.det(origin) } - } - - SingularValueDecompositionFeature::class -> object : SingularValueDecompositionFeature { - private val svd by lazy { - DecompositionFactory_DDRM.svd(origin.numRows, origin.numCols, true, true, false) - .apply { decompose(origin.copy()) } - } - - override val u: Matrix by lazy { svd.getU(null, false).wrapMatrix() } - override val s: Matrix by lazy { svd.getW(null).wrapMatrix() } - override val v: Matrix by lazy { svd.getV(null, false).wrapMatrix() } - override val singularValues: Point by lazy { DoubleBuffer(svd.singularValues) } - } - - QRDecompositionFeature::class -> object : QRDecompositionFeature { - private val qr by lazy { - DecompositionFactory_DDRM.qr().apply { decompose(origin.copy()) } - } - - override val q: Matrix by lazy { - qr.getQ(null, false).wrapMatrix() + OrthogonalFeature - } - - override val r: Matrix by lazy { qr.getR(null, false).wrapMatrix() + UFeature } - } - - CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature { - override val l: Matrix by lazy { - val cholesky = - DecompositionFactory_DDRM.chol(structure.rowNum, true).apply { decompose(origin.copy()) } - - cholesky.getT(null).wrapMatrix() + LFeature - } - } - - LupDecompositionFeature::class -> object : LupDecompositionFeature { - private val lup by lazy { - DecompositionFactory_DDRM.lu(origin.numRows, origin.numCols).apply { decompose(origin.copy()) } - } - - override val l: Matrix by lazy { - lup.getLower(null).wrapMatrix() + LFeature - } - - override val u: Matrix by lazy { - lup.getUpper(null).wrapMatrix() + UFeature - } - - override val p: Matrix by lazy { lup.getRowPivot(null).wrapMatrix() } - } - - else -> null - }?.let(type::cast) - } - - /** - * Solves for *x* in the following equation: *x = [a] -1 · [b]*. - * - * @param a the base matrix. - * @param b n by p matrix. - * @return the solution for *x* that is n by p. - */ - public fun solve(a: Matrix, b: Matrix): EjmlDoubleMatrix { - val res = DMatrixRMaj(1, 1) - CommonOps_DDRM.solve(DMatrixRMaj(a.toEjml().origin), DMatrixRMaj(b.toEjml().origin), res) - return res.wrapMatrix() - } - - /** - * Solves for *x* in the following equation: *x = [a] -1 · [b]*. - * - * @param a the base matrix. - * @param b n by p vector. - * @return the solution for *x* that is n by p. - */ - public fun solve(a: Matrix, b: Point): EjmlDoubleVector { - val res = DMatrixRMaj(1, 1) - CommonOps_DDRM.solve(DMatrixRMaj(a.toEjml().origin), DMatrixRMaj(b.toEjml().origin), res) - return EjmlDoubleVector(res) - } -} - -/** - * [EjmlLinearSpace] implementation based on [CommonOps_FDRM], [DecompositionFactory_FDRM] operations and - * [FMatrixRMaj] matrices. - */ -public object EjmlLinearSpaceFDRM : EjmlLinearSpace() { - /** - * The [FloatField] reference. - */ - public override val elementAlgebra: FloatField get() = FloatField - - @Suppress("UNCHECKED_CAST") - public override fun Matrix.toEjml(): EjmlFloatMatrix = when { - this is EjmlFloatMatrix<*> && origin is FMatrixRMaj -> this as EjmlFloatMatrix - else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) } - } - - @Suppress("UNCHECKED_CAST") - public override fun Point.toEjml(): EjmlFloatVector = when { - this is EjmlFloatVector<*> && origin is FMatrixRMaj -> this as EjmlFloatVector - else -> EjmlFloatVector(FMatrixRMaj(size, 1).also { - (0 until it.numRows).forEach { row -> it[row, 0] = get(row) } - }) - } - - public override fun buildMatrix( - rows: Int, - columns: Int, - initializer: FloatField.(i: Int, j: Int) -> Float, - ): EjmlFloatMatrix = FMatrixRMaj(rows, columns).also { - (0 until rows).forEach { row -> - (0 until columns).forEach { col -> it[row, col] = elementAlgebra.initializer(row, col) } - } - }.wrapMatrix() - - public override fun buildVector( - size: Int, - initializer: FloatField.(Int) -> Float, - ): EjmlFloatVector = EjmlFloatVector(FMatrixRMaj(size, 1).also { - (0 until it.numRows).forEach { row -> it[row, 0] = elementAlgebra.initializer(row) } - }) - - private fun T.wrapMatrix() = EjmlFloatMatrix(this) - private fun T.wrapVector() = EjmlFloatVector(this) - - public override fun Matrix.unaryMinus(): Matrix = this * elementAlgebra { -one } - - public override fun Matrix.dot(other: Matrix): EjmlFloatMatrix { - val out = FMatrixRMaj(1, 1) - CommonOps_FDRM.mult(toEjml().origin, other.toEjml().origin, out) - return out.wrapMatrix() - } - - public override fun Matrix.dot(vector: Point): EjmlFloatVector { - val out = FMatrixRMaj(1, 1) - CommonOps_FDRM.mult(toEjml().origin, vector.toEjml().origin, out) - return out.wrapVector() - } - - public override operator fun Matrix.minus(other: Matrix): EjmlFloatMatrix { - val out = FMatrixRMaj(1, 1) - - CommonOps_FDRM.add( - elementAlgebra.one, - toEjml().origin, - elementAlgebra { -one }, - other.toEjml().origin, - out, - ) - - return out.wrapMatrix() - } - - public override operator fun Matrix.times(value: Float): EjmlFloatMatrix { - val res = FMatrixRMaj(1, 1) - CommonOps_FDRM.scale(value, toEjml().origin, res) - return res.wrapMatrix() - } - - public override fun Point.unaryMinus(): EjmlFloatVector { - val res = FMatrixRMaj(1, 1) - CommonOps_FDRM.changeSign(toEjml().origin, res) - return res.wrapVector() - } - - public override fun Matrix.plus(other: Matrix): EjmlFloatMatrix { - val out = FMatrixRMaj(1, 1) - - CommonOps_FDRM.add( - elementAlgebra.one, - toEjml().origin, - elementAlgebra.one, - other.toEjml().origin, - out, - ) - - return out.wrapMatrix() - } - - public override fun Point.plus(other: Point): EjmlFloatVector { - val out = FMatrixRMaj(1, 1) - - CommonOps_FDRM.add( - elementAlgebra.one, - toEjml().origin, - elementAlgebra.one, - other.toEjml().origin, - out, - ) - - return out.wrapVector() - } - - public override fun Point.minus(other: Point): EjmlFloatVector { - val out = FMatrixRMaj(1, 1) - - CommonOps_FDRM.add( - elementAlgebra.one, - toEjml().origin, - elementAlgebra { -one }, - other.toEjml().origin, - out, - ) - - return out.wrapVector() - } - - public override fun Float.times(m: Matrix): EjmlFloatMatrix = m * this - - public override fun Point.times(value: Float): EjmlFloatVector { - val res = FMatrixRMaj(1, 1) - CommonOps_FDRM.scale(value, toEjml().origin, res) - return res.wrapVector() - } - - public override fun Float.times(v: Point): EjmlFloatVector = v * this - - @UnstableKMathAPI - public override fun getFeature(structure: Matrix, type: KClass): F? { - structure.getFeature(type)?.let { return it } - val origin = structure.toEjml().origin - - return when (type) { - InverseMatrixFeature::class -> object : InverseMatrixFeature { - override val inverse: Matrix by lazy { - val res = origin.copy() - CommonOps_FDRM.invert(res) - res.wrapMatrix() - } - } - - DeterminantFeature::class -> object : DeterminantFeature { - override val determinant: Float by lazy { CommonOps_FDRM.det(origin) } - } - - SingularValueDecompositionFeature::class -> object : SingularValueDecompositionFeature { - private val svd by lazy { - DecompositionFactory_FDRM.svd(origin.numRows, origin.numCols, true, true, false) - .apply { decompose(origin.copy()) } - } - - override val u: Matrix by lazy { svd.getU(null, false).wrapMatrix() } - override val s: Matrix by lazy { svd.getW(null).wrapMatrix() } - override val v: Matrix by lazy { svd.getV(null, false).wrapMatrix() } - override val singularValues: Point by lazy { FloatBuffer(svd.singularValues) } - } - - QRDecompositionFeature::class -> object : QRDecompositionFeature { - private val qr by lazy { - DecompositionFactory_FDRM.qr().apply { decompose(origin.copy()) } - } - - override val q: Matrix by lazy { - qr.getQ(null, false).wrapMatrix() + OrthogonalFeature - } - - override val r: Matrix by lazy { qr.getR(null, false).wrapMatrix() + UFeature } - } - - CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature { - override val l: Matrix by lazy { - val cholesky = - DecompositionFactory_FDRM.chol(structure.rowNum, true).apply { decompose(origin.copy()) } - - cholesky.getT(null).wrapMatrix() + LFeature - } - } - - LupDecompositionFeature::class -> object : LupDecompositionFeature { - private val lup by lazy { - DecompositionFactory_FDRM.lu(origin.numRows, origin.numCols).apply { decompose(origin.copy()) } - } - - override val l: Matrix by lazy { - lup.getLower(null).wrapMatrix() + LFeature - } - - override val u: Matrix by lazy { - lup.getUpper(null).wrapMatrix() + UFeature - } - - override val p: Matrix by lazy { lup.getRowPivot(null).wrapMatrix() } - } - - else -> null - }?.let(type::cast) - } - - /** - * Solves for *x* in the following equation: *x = [a] -1 · [b]*. - * - * @param a the base matrix. - * @param b n by p matrix. - * @return the solution for *x* that is n by p. - */ - public fun solve(a: Matrix, b: Matrix): EjmlFloatMatrix { - val res = FMatrixRMaj(1, 1) - CommonOps_FDRM.solve(FMatrixRMaj(a.toEjml().origin), FMatrixRMaj(b.toEjml().origin), res) - return res.wrapMatrix() - } - - /** - * Solves for *x* in the following equation: *x = [a] -1 · [b]*. - * - * @param a the base matrix. - * @param b n by p vector. - * @return the solution for *x* that is n by p. - */ - public fun solve(a: Matrix, b: Point): EjmlFloatVector { - val res = FMatrixRMaj(1, 1) - CommonOps_FDRM.solve(FMatrixRMaj(a.toEjml().origin), FMatrixRMaj(b.toEjml().origin), res) - return EjmlFloatVector(res) - } -} - -/** - * [EjmlLinearSpace] implementation based on [CommonOps_DSCC], [DecompositionFactory_DSCC] operations and - * [DMatrixSparseCSC] matrices. - */ -public object EjmlLinearSpaceDSCC : EjmlLinearSpace() { - /** - * The [DoubleField] reference. - */ - public override val elementAlgebra: DoubleField get() = DoubleField - - @Suppress("UNCHECKED_CAST") - public override fun Matrix.toEjml(): EjmlDoubleMatrix = when { - this is EjmlDoubleMatrix<*> && origin is DMatrixSparseCSC -> this as EjmlDoubleMatrix - else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) } - } - - @Suppress("UNCHECKED_CAST") - public override fun Point.toEjml(): EjmlDoubleVector = when { - this is EjmlDoubleVector<*> && origin is DMatrixSparseCSC -> this as EjmlDoubleVector - else -> EjmlDoubleVector(DMatrixSparseCSC(size, 1).also { - (0 until it.numRows).forEach { row -> it[row, 0] = get(row) } - }) - } - - public override fun buildMatrix( - rows: Int, - columns: Int, - initializer: DoubleField.(i: Int, j: Int) -> Double, - ): EjmlDoubleMatrix = DMatrixSparseCSC(rows, columns).also { - (0 until rows).forEach { row -> - (0 until columns).forEach { col -> it[row, col] = elementAlgebra.initializer(row, col) } - } - }.wrapMatrix() - - public override fun buildVector( - size: Int, - initializer: DoubleField.(Int) -> Double, - ): EjmlDoubleVector = EjmlDoubleVector(DMatrixSparseCSC(size, 1).also { - (0 until it.numRows).forEach { row -> it[row, 0] = elementAlgebra.initializer(row) } - }) - - private fun T.wrapMatrix() = EjmlDoubleMatrix(this) - private fun T.wrapVector() = EjmlDoubleVector(this) - - public override fun Matrix.unaryMinus(): Matrix = this * elementAlgebra { -one } - - public override fun Matrix.dot(other: Matrix): EjmlDoubleMatrix { - val out = DMatrixSparseCSC(1, 1) - CommonOps_DSCC.mult(toEjml().origin, other.toEjml().origin, out) - return out.wrapMatrix() - } - - public override fun Matrix.dot(vector: Point): EjmlDoubleVector { - val out = DMatrixSparseCSC(1, 1) - CommonOps_DSCC.mult(toEjml().origin, vector.toEjml().origin, out) - return out.wrapVector() - } - - public override operator fun Matrix.minus(other: Matrix): EjmlDoubleMatrix { - val out = DMatrixSparseCSC(1, 1) - - CommonOps_DSCC.add( - elementAlgebra.one, - toEjml().origin, - elementAlgebra { -one }, - other.toEjml().origin, - out, - null, - null, - ) - - return out.wrapMatrix() - } - - public override operator fun Matrix.times(value: Double): EjmlDoubleMatrix { - val res = DMatrixSparseCSC(1, 1) - CommonOps_DSCC.scale(value, toEjml().origin, res) - return res.wrapMatrix() - } - - public override fun Point.unaryMinus(): EjmlDoubleVector { - val res = DMatrixSparseCSC(1, 1) - CommonOps_DSCC.changeSign(toEjml().origin, res) - return res.wrapVector() - } - - public override fun Matrix.plus(other: Matrix): EjmlDoubleMatrix { - val out = DMatrixSparseCSC(1, 1) - - CommonOps_DSCC.add( - elementAlgebra.one, - toEjml().origin, - elementAlgebra.one, - other.toEjml().origin, - out, - null, - null, - ) - - return out.wrapMatrix() - } - - public override fun Point.plus(other: Point): EjmlDoubleVector { - val out = DMatrixSparseCSC(1, 1) - - CommonOps_DSCC.add( - elementAlgebra.one, - toEjml().origin, - elementAlgebra.one, - other.toEjml().origin, - out, - null, - null, - ) - - return out.wrapVector() - } - - public override fun Point.minus(other: Point): EjmlDoubleVector { - val out = DMatrixSparseCSC(1, 1) - - CommonOps_DSCC.add( - elementAlgebra.one, - toEjml().origin, - elementAlgebra { -one }, - other.toEjml().origin, - out, - null, - null, - ) - - return out.wrapVector() - } - - public override fun Double.times(m: Matrix): EjmlDoubleMatrix = m * this - - public override fun Point.times(value: Double): EjmlDoubleVector { - val res = DMatrixSparseCSC(1, 1) - CommonOps_DSCC.scale(value, toEjml().origin, res) - return res.wrapVector() - } - - public override fun Double.times(v: Point): EjmlDoubleVector = v * this - - @UnstableKMathAPI - public override fun getFeature(structure: Matrix, type: KClass): F? { - structure.getFeature(type)?.let { return it } - val origin = structure.toEjml().origin - - return when (type) { - QRDecompositionFeature::class -> object : QRDecompositionFeature { - private val qr by lazy { - DecompositionFactory_DSCC.qr(FillReducing.NONE).apply { decompose(origin.copy()) } - } - - override val q: Matrix by lazy { - qr.getQ(null, false).wrapMatrix() + OrthogonalFeature - } - - override val r: Matrix by lazy { qr.getR(null, false).wrapMatrix() + UFeature } - } - - CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature { - override val l: Matrix by lazy { - val cholesky = - DecompositionFactory_DSCC.cholesky().apply { decompose(origin.copy()) } - - (cholesky.getT(null) as DMatrix).wrapMatrix() + LFeature - } - } - - LUDecompositionFeature::class, DeterminantFeature::class, InverseMatrixFeature::class -> object : - LUDecompositionFeature, DeterminantFeature, InverseMatrixFeature { - private val lu by lazy { - DecompositionFactory_DSCC.lu(FillReducing.NONE).apply { decompose(origin.copy()) } - } - - override val l: Matrix by lazy { - lu.getLower(null).wrapMatrix() + LFeature - } - - override val u: Matrix by lazy { - lu.getUpper(null).wrapMatrix() + UFeature - } - - override val inverse: Matrix by lazy { - var a = origin - val inverse = DMatrixRMaj(1, 1) - val solver = LinearSolverFactory_DSCC.lu(FillReducing.NONE) - if (solver.modifiesA()) a = a.copy() - val i = CommonOps_DDRM.identity(a.numRows) - solver.solve(i, inverse) - inverse.wrapMatrix() - } - - override val determinant: Double by lazy { elementAlgebra.number(lu.computeDeterminant().real) } - } - - else -> null - }?.let(type::cast) - } - - /** - * Solves for *x* in the following equation: *x = [a] -1 · [b]*. - * - * @param a the base matrix. - * @param b n by p matrix. - * @return the solution for *x* that is n by p. - */ - public fun solve(a: Matrix, b: Matrix): EjmlDoubleMatrix { - val res = DMatrixSparseCSC(1, 1) - CommonOps_DSCC.solve(DMatrixSparseCSC(a.toEjml().origin), DMatrixSparseCSC(b.toEjml().origin), res) - return res.wrapMatrix() - } - - /** - * Solves for *x* in the following equation: *x = [a] -1 · [b]*. - * - * @param a the base matrix. - * @param b n by p vector. - * @return the solution for *x* that is n by p. - */ - public fun solve(a: Matrix, b: Point): EjmlDoubleVector { - val res = DMatrixSparseCSC(1, 1) - CommonOps_DSCC.solve(DMatrixSparseCSC(a.toEjml().origin), DMatrixSparseCSC(b.toEjml().origin), res) - return EjmlDoubleVector(res) - } -} - -/** - * [EjmlLinearSpace] implementation based on [CommonOps_FSCC], [DecompositionFactory_FSCC] operations and - * [FMatrixSparseCSC] matrices. - */ -public object EjmlLinearSpaceFSCC : EjmlLinearSpace() { - /** - * The [FloatField] reference. - */ - public override val elementAlgebra: FloatField get() = FloatField - - @Suppress("UNCHECKED_CAST") - public override fun Matrix.toEjml(): EjmlFloatMatrix = when { - this is EjmlFloatMatrix<*> && origin is FMatrixSparseCSC -> this as EjmlFloatMatrix - else -> buildMatrix(rowNum, colNum) { i, j -> get(i, j) } - } - - @Suppress("UNCHECKED_CAST") - public override fun Point.toEjml(): EjmlFloatVector = when { - this is EjmlFloatVector<*> && origin is FMatrixSparseCSC -> this as EjmlFloatVector - else -> EjmlFloatVector(FMatrixSparseCSC(size, 1).also { - (0 until it.numRows).forEach { row -> it[row, 0] = get(row) } - }) - } - - public override fun buildMatrix( - rows: Int, - columns: Int, - initializer: FloatField.(i: Int, j: Int) -> Float, - ): EjmlFloatMatrix = FMatrixSparseCSC(rows, columns).also { - (0 until rows).forEach { row -> - (0 until columns).forEach { col -> it[row, col] = elementAlgebra.initializer(row, col) } - } - }.wrapMatrix() - - public override fun buildVector( - size: Int, - initializer: FloatField.(Int) -> Float, - ): EjmlFloatVector = EjmlFloatVector(FMatrixSparseCSC(size, 1).also { - (0 until it.numRows).forEach { row -> it[row, 0] = elementAlgebra.initializer(row) } - }) - - private fun T.wrapMatrix() = EjmlFloatMatrix(this) - private fun T.wrapVector() = EjmlFloatVector(this) - - public override fun Matrix.unaryMinus(): Matrix = this * elementAlgebra { -one } - - public override fun Matrix.dot(other: Matrix): EjmlFloatMatrix { - val out = FMatrixSparseCSC(1, 1) - CommonOps_FSCC.mult(toEjml().origin, other.toEjml().origin, out) - return out.wrapMatrix() - } - - public override fun Matrix.dot(vector: Point): EjmlFloatVector { - val out = FMatrixSparseCSC(1, 1) - CommonOps_FSCC.mult(toEjml().origin, vector.toEjml().origin, out) - return out.wrapVector() - } - - public override operator fun Matrix.minus(other: Matrix): EjmlFloatMatrix { - val out = FMatrixSparseCSC(1, 1) - - CommonOps_FSCC.add( - elementAlgebra.one, - toEjml().origin, - elementAlgebra { -one }, - other.toEjml().origin, - out, - null, - null, - ) - - return out.wrapMatrix() - } - - public override operator fun Matrix.times(value: Float): EjmlFloatMatrix { - val res = FMatrixSparseCSC(1, 1) - CommonOps_FSCC.scale(value, toEjml().origin, res) - return res.wrapMatrix() - } - - public override fun Point.unaryMinus(): EjmlFloatVector { - val res = FMatrixSparseCSC(1, 1) - CommonOps_FSCC.changeSign(toEjml().origin, res) - return res.wrapVector() - } - - public override fun Matrix.plus(other: Matrix): EjmlFloatMatrix { - val out = FMatrixSparseCSC(1, 1) - - CommonOps_FSCC.add( - elementAlgebra.one, - toEjml().origin, - elementAlgebra.one, - other.toEjml().origin, - out, - null, - null, - ) - - return out.wrapMatrix() - } - - public override fun Point.plus(other: Point): EjmlFloatVector { - val out = FMatrixSparseCSC(1, 1) - - CommonOps_FSCC.add( - elementAlgebra.one, - toEjml().origin, - elementAlgebra.one, - other.toEjml().origin, - out, - null, - null, - ) - - return out.wrapVector() - } - - public override fun Point.minus(other: Point): EjmlFloatVector { - val out = FMatrixSparseCSC(1, 1) - - CommonOps_FSCC.add( - elementAlgebra.one, - toEjml().origin, - elementAlgebra { -one }, - other.toEjml().origin, - out, - null, - null, - ) - - return out.wrapVector() - } - - public override fun Float.times(m: Matrix): EjmlFloatMatrix = m * this - - public override fun Point.times(value: Float): EjmlFloatVector { - val res = FMatrixSparseCSC(1, 1) - CommonOps_FSCC.scale(value, toEjml().origin, res) - return res.wrapVector() - } - - public override fun Float.times(v: Point): EjmlFloatVector = v * this - - @UnstableKMathAPI - public override fun getFeature(structure: Matrix, type: KClass): F? { - structure.getFeature(type)?.let { return it } - val origin = structure.toEjml().origin - - return when (type) { - QRDecompositionFeature::class -> object : QRDecompositionFeature { - private val qr by lazy { - DecompositionFactory_FSCC.qr(FillReducing.NONE).apply { decompose(origin.copy()) } - } - - override val q: Matrix by lazy { - qr.getQ(null, false).wrapMatrix() + OrthogonalFeature - } - - override val r: Matrix by lazy { qr.getR(null, false).wrapMatrix() + UFeature } - } - - CholeskyDecompositionFeature::class -> object : CholeskyDecompositionFeature { - override val l: Matrix by lazy { - val cholesky = - DecompositionFactory_FSCC.cholesky().apply { decompose(origin.copy()) } - - (cholesky.getT(null) as FMatrix).wrapMatrix() + LFeature - } - } - - LUDecompositionFeature::class, DeterminantFeature::class, InverseMatrixFeature::class -> object : - LUDecompositionFeature, DeterminantFeature, InverseMatrixFeature { - private val lu by lazy { - DecompositionFactory_FSCC.lu(FillReducing.NONE).apply { decompose(origin.copy()) } - } - - override val l: Matrix by lazy { - lu.getLower(null).wrapMatrix() + LFeature - } - - override val u: Matrix by lazy { - lu.getUpper(null).wrapMatrix() + UFeature - } - - override val inverse: Matrix by lazy { - var a = origin - val inverse = FMatrixRMaj(1, 1) - val solver = LinearSolverFactory_FSCC.lu(FillReducing.NONE) - if (solver.modifiesA()) a = a.copy() - val i = CommonOps_FDRM.identity(a.numRows) - solver.solve(i, inverse) - inverse.wrapMatrix() - } - - override val determinant: Float by lazy { elementAlgebra.number(lu.computeDeterminant().real) } - } - - else -> null - }?.let(type::cast) - } - - /** - * Solves for *x* in the following equation: *x = [a] -1 · [b]*. - * - * @param a the base matrix. - * @param b n by p matrix. - * @return the solution for *x* that is n by p. - */ - public fun solve(a: Matrix, b: Matrix): EjmlFloatMatrix { - val res = FMatrixSparseCSC(1, 1) - CommonOps_FSCC.solve(FMatrixSparseCSC(a.toEjml().origin), FMatrixSparseCSC(b.toEjml().origin), res) - return res.wrapMatrix() - } - - /** - * Solves for *x* in the following equation: *x = [a] -1 · [b]*. - * - * @param a the base matrix. - * @param b n by p vector. - * @return the solution for *x* that is n by p. - */ - public fun solve(a: Matrix, b: Point): EjmlFloatVector { - val res = FMatrixSparseCSC(1, 1) - CommonOps_FSCC.solve(FMatrixSparseCSC(a.toEjml().origin), FMatrixSparseCSC(b.toEjml().origin), res) - return EjmlFloatVector(res) - } -} - diff --git a/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt b/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt index 50675bdac..6055ae1d8 100644 --- a/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt +++ b/kmath-ejml/src/test/kotlin/space/kscience/kmath/ejml/EjmlMatrixTest.kt @@ -11,7 +11,7 @@ import org.ejml.dense.row.RandomMatrices_DDRM import org.ejml.dense.row.factory.DecompositionFactory_DDRM import space.kscience.kmath.linear.DeterminantFeature import space.kscience.kmath.linear.LupDecompositionFeature -import space.kscience.kmath.linear.getFeature +import space.kscience.kmath.linear.computeFeature import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.UnstableKMathAPI import space.kscience.kmath.nd.StructureND @@ -59,9 +59,9 @@ internal class EjmlMatrixTest { fun features() { val m = randomMatrix val w = EjmlDoubleMatrix(m) - val det: DeterminantFeature = EjmlLinearSpaceDDRM.getFeature(w) ?: fail() + val det: DeterminantFeature = EjmlLinearSpaceDDRM.computeFeature(w) ?: fail() assertEquals(CommonOps_DDRM.det(m), det.determinant) - val lup: LupDecompositionFeature = EjmlLinearSpaceDDRM.getFeature(w) ?: fail() + val lup: LupDecompositionFeature = EjmlLinearSpaceDDRM.computeFeature(w) ?: fail() val ludecompositionF64 = DecompositionFactory_DDRM.lu(m.numRows, m.numCols) .also { it.decompose(m.copy()) } diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt index 8023236ea..38e5b4beb 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt @@ -32,18 +32,18 @@ import kotlin.math.pow public typealias RealMatrix = Matrix public fun realMatrix(rowNum: Int, colNum: Int, initializer: DoubleField.(i: Int, j: Int) -> Double): RealMatrix = - LinearSpace.real.buildMatrix(rowNum, colNum, initializer) + LinearSpace.double.buildMatrix(rowNum, colNum, initializer) @OptIn(UnstableKMathAPI::class) public fun realMatrix(rowNum: Int, colNum: Int): MatrixBuilder = - LinearSpace.real.matrix(rowNum, colNum) + LinearSpace.double.matrix(rowNum, colNum) public fun Array.toMatrix(): RealMatrix { - return LinearSpace.real.buildMatrix(size, this[0].size) { row, col -> this@toMatrix[row][col] } + return LinearSpace.double.buildMatrix(size, this[0].size) { row, col -> this@toMatrix[row][col] } } public fun Sequence.toMatrix(): RealMatrix = toList().let { - LinearSpace.real.buildMatrix(it.size, it[0].size) { row, col -> it[row][col] } + LinearSpace.double.buildMatrix(it.size, it[0].size) { row, col -> it[row][col] } } public fun RealMatrix.repeatStackVertical(n: Int): RealMatrix = @@ -56,37 +56,37 @@ public fun RealMatrix.repeatStackVertical(n: Int): RealMatrix = */ public operator fun RealMatrix.times(double: Double): RealMatrix = - LinearSpace.real.buildMatrix(rowNum, colNum) { row, col -> + LinearSpace.double.buildMatrix(rowNum, colNum) { row, col -> get(row, col) * double } public operator fun RealMatrix.plus(double: Double): RealMatrix = - LinearSpace.real.buildMatrix(rowNum, colNum) { row, col -> + LinearSpace.double.buildMatrix(rowNum, colNum) { row, col -> get(row, col) + double } public operator fun RealMatrix.minus(double: Double): RealMatrix = - LinearSpace.real.buildMatrix(rowNum, colNum) { row, col -> + LinearSpace.double.buildMatrix(rowNum, colNum) { row, col -> get(row, col) - double } public operator fun RealMatrix.div(double: Double): RealMatrix = - LinearSpace.real.buildMatrix(rowNum, colNum) { row, col -> + LinearSpace.double.buildMatrix(rowNum, colNum) { row, col -> get(row, col) / double } public operator fun Double.times(matrix: RealMatrix): RealMatrix = - LinearSpace.real.buildMatrix(matrix.rowNum, matrix.colNum) { row, col -> + LinearSpace.double.buildMatrix(matrix.rowNum, matrix.colNum) { row, col -> this@times * matrix[row, col] } public operator fun Double.plus(matrix: RealMatrix): RealMatrix = - LinearSpace.real.buildMatrix(matrix.rowNum, matrix.colNum) { row, col -> + LinearSpace.double.buildMatrix(matrix.rowNum, matrix.colNum) { row, col -> this@plus + matrix[row, col] } public operator fun Double.minus(matrix: RealMatrix): RealMatrix = - LinearSpace.real.buildMatrix(matrix.rowNum, matrix.colNum) { row, col -> + LinearSpace.double.buildMatrix(matrix.rowNum, matrix.colNum) { row, col -> this@minus - matrix[row, col] } @@ -101,20 +101,20 @@ public operator fun Double.minus(matrix: RealMatrix): RealMatrix = @UnstableKMathAPI public operator fun RealMatrix.times(other: RealMatrix): RealMatrix = - LinearSpace.real.buildMatrix(rowNum, colNum) { row, col -> this@times[row, col] * other[row, col] } + LinearSpace.double.buildMatrix(rowNum, colNum) { row, col -> this@times[row, col] * other[row, col] } public operator fun RealMatrix.plus(other: RealMatrix): RealMatrix = - LinearSpace.real.run { this@plus + other } + LinearSpace.double.run { this@plus + other } public operator fun RealMatrix.minus(other: RealMatrix): RealMatrix = - LinearSpace.real.buildMatrix(rowNum, colNum) { row, col -> this@minus[row, col] - other[row, col] } + LinearSpace.double.buildMatrix(rowNum, colNum) { row, col -> this@minus[row, col] - other[row, col] } /* * Operations on columns */ public inline fun RealMatrix.appendColumn(crossinline mapper: (Buffer) -> Double): RealMatrix = - LinearSpace.real.buildMatrix(rowNum, colNum + 1) { row, col -> + LinearSpace.double.buildMatrix(rowNum, colNum + 1) { row, col -> if (col < colNum) get(row, col) else @@ -122,7 +122,7 @@ public inline fun RealMatrix.appendColumn(crossinline mapper: (Buffer) - } public fun RealMatrix.extractColumns(columnRange: IntRange): RealMatrix = - LinearSpace.real.buildMatrix(rowNum, columnRange.count()) { row, col -> + LinearSpace.double.buildMatrix(rowNum, columnRange.count()) { row, col -> this@extractColumns[row, columnRange.first + col] } @@ -155,14 +155,14 @@ public fun RealMatrix.max(): Double? = elements().map { (_, value) -> value }.ma public fun RealMatrix.average(): Double = elements().map { (_, value) -> value }.average() public inline fun RealMatrix.map(crossinline transform: (Double) -> Double): RealMatrix = - LinearSpace.real.buildMatrix(rowNum, colNum) { i, j -> + LinearSpace.double.buildMatrix(rowNum, colNum) { i, j -> transform(get(i, j)) } /** * Inverse a square real matrix using LUP decomposition */ -public fun RealMatrix.inverseWithLup(): RealMatrix = LinearSpace.real.inverseWithLup(this) +public fun RealMatrix.inverseWithLup(): RealMatrix = LinearSpace.double.lupSolver().inverse(this) //extended operations diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/dot.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/dot.kt index b79e5030c..395c94838 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/dot.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/dot.kt @@ -12,6 +12,6 @@ import space.kscience.kmath.linear.Matrix /** * Optimized dot product for real matrices */ -public infix fun Matrix.dot(other: Matrix): Matrix = LinearSpace.real.run { +public infix fun Matrix.dot(other: Matrix): Matrix = LinearSpace.double.run { this@dot dot other } \ No newline at end of file diff --git a/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/DoubleMatrixTest.kt b/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/DoubleMatrixTest.kt index b3e129c2e..5a80ee677 100644 --- a/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/DoubleMatrixTest.kt +++ b/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/DoubleMatrixTest.kt @@ -65,7 +65,7 @@ internal class DoubleMatrixTest { 4.0, 6.0, 2.0 ) val matrix2 = (matrix1 * 2.5 + 1.0 - 2.0) / 2.0 - val expectedResult = LinearSpace.real.matrix(2, 3)( + val expectedResult = LinearSpace.double.matrix(2, 3)( 0.75, -0.5, 3.25, 4.5, 7.0, 2.0 ) @@ -160,7 +160,7 @@ internal class DoubleMatrixTest { @Test fun testAllElementOperations() { - val matrix1 = LinearSpace.real.matrix(2, 4)( + val matrix1 = LinearSpace.double.matrix(2, 4)( -1.0, 0.0, 3.0, 15.0, 4.0, -6.0, 7.0, -11.0 ) diff --git a/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/DoubleVectorTest.kt b/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/DoubleVectorTest.kt index 9de54381c..ec7b536ba 100644 --- a/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/DoubleVectorTest.kt +++ b/kmath-for-real/src/commonTest/kotlin/kaceince/kmath/real/DoubleVectorTest.kt @@ -35,7 +35,7 @@ internal class DoubleVectorTest { val vector2 = DoubleBuffer(5) { 5 - it.toDouble() } val matrix1 = vector1.asMatrix() val matrix2 = vector2.asMatrix().transpose() - val product = LinearSpace.real.run { matrix1 dot matrix2 } + val product = LinearSpace.double.run { matrix1 dot matrix2 } assertEquals(5.0, product[1, 0]) assertEquals(6.0, product[2, 2]) } diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/Integrand.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/Integrand.kt index dcf711c3b..f90159c81 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/Integrand.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/Integrand.kt @@ -5,11 +5,12 @@ package space.kscience.kmath.integration +import space.kscience.kmath.misc.Feature import space.kscience.kmath.misc.FeatureSet import space.kscience.kmath.misc.Featured import kotlin.reflect.KClass -public interface IntegrandFeature { +public interface IntegrandFeature : Feature { override fun toString(): String } @@ -18,7 +19,7 @@ public interface Integrand : Featured { override fun getFeature(type: KClass): T? = features.getFeature(type) } -public inline fun Integrand.getFeature(): T? = getFeature(T::class) +public inline fun Integrand.getFeature(): T? = getFeature(T::class) public class IntegrandValue(public val value: T) : IntegrandFeature { override fun toString(): String = "Value($value)" diff --git a/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/KotlingradExpression.kt b/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/KotlingradExpression.kt index 4294462c0..a59c301cf 100644 --- a/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/KotlingradExpression.kt +++ b/kmath-kotlingrad/src/main/kotlin/space/kscience/kmath/kotlingrad/KotlingradExpression.kt @@ -27,19 +27,30 @@ public class KotlingradExpression>( ) : SpecialDifferentiableExpression> { public override fun invoke(arguments: Map): T = mst.interpret(algebra, arguments) - public override fun derivativeOrNull(symbols: List): KotlingradExpression = - KotlingradExpression( - algebra, - symbols.map(Symbol::identity) - .map(MstNumericAlgebra::bindSymbol) - .map>>(Symbol::toSVar) - .fold(mst.toSFun(), SFun>::d) - .toMst(), - ) + public override fun derivativeOrNull( + symbols: List, + ): KotlingradExpression = KotlingradExpression( + algebra, + symbols.map(Symbol::identity) + .map(MstNumericAlgebra::bindSymbol) + .map>>(Symbol::toSVar) + .fold(mst.toSFun(), SFun>::d) + .toMst(), + ) +} + +/** + * A diff processor using [MST] to Kotlingrad converter + */ +public class KotlingradProcessor>( + public val algebra: A, +) : AutoDiffProcessor { + override fun differentiate(function: MstExtendedField.() -> MST): DifferentiableExpression = + MstExtendedField.function().toKotlingradExpression(algebra) } /** * Wraps this [MST] into [KotlingradExpression]. */ -public fun > MST.toDiffExpression(algebra: A): KotlingradExpression = +public fun > MST.toKotlingradExpression(algebra: A): KotlingradExpression = KotlingradExpression(algebra, this) 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 62288242a..4d4f99b71 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 @@ -23,7 +23,7 @@ public enum class FunctionOptimizationTarget : OptimizationFeature { public class FunctionOptimization( override val features: FeatureSet, public val expression: DifferentiableExpression, -) : OptimizationProblem{ +) : OptimizationProblem{ public companion object{ /** @@ -56,7 +56,6 @@ public class FunctionOptimization( } } - public fun FunctionOptimization.withFeatures( vararg newFeature: OptimizationFeature, ): FunctionOptimization = FunctionOptimization( @@ -68,7 +67,7 @@ public fun FunctionOptimization.withFeatures( * Optimize differentiable expression using specific [optimizer] form given [startingPoint] */ public suspend fun DifferentiableExpression.optimizeWith( - optimizer: Optimizer>, + optimizer: Optimizer>, startingPoint: Map, vararg features: OptimizationFeature, ): FunctionOptimization { @@ -76,3 +75,8 @@ public suspend fun DifferentiableExpression.optimizeWith( return optimizer.optimize(problem) } +public val FunctionOptimization.resultValueOrNull:T? + get() = getFeature>()?.point?.let { expression(it) } + +public val FunctionOptimization.resultValue: T + get() = resultValueOrNull ?: error("Result is not present in $this") \ No newline at end of file diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/OptimizationBuilder.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/OptimizationBuilder.kt new file mode 100644 index 000000000..7d52ae26e --- /dev/null +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/OptimizationBuilder.kt @@ -0,0 +1,93 @@ +/* + * 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.expressions.DifferentiableExpression +import space.kscience.kmath.expressions.Symbol +import space.kscience.kmath.misc.FeatureSet + +public abstract class OptimizationBuilder> { + public val features: ArrayList = ArrayList() + + public fun addFeature(feature: OptimizationFeature) { + features.add(feature) + } + + public inline fun updateFeature(update: (T?) -> T) { + val existing = features.find { it.key == T::class } as? T + val new = update(existing) + if (existing != null) { + features.remove(existing) + } + addFeature(new) + } + + public abstract fun build(): R +} + +public fun OptimizationBuilder.startAt(startingPoint: Map) { + addFeature(OptimizationStartPoint(startingPoint)) +} + +public class FunctionOptimizationBuilder( + private val expression: DifferentiableExpression, +) : OptimizationBuilder>() { + override fun build(): FunctionOptimization = FunctionOptimization(FeatureSet.of(features), expression) +} + +public fun FunctionOptimization( + expression: DifferentiableExpression, + builder: FunctionOptimizationBuilder.() -> Unit, +): FunctionOptimization = FunctionOptimizationBuilder(expression).apply(builder).build() + +public suspend fun DifferentiableExpression.optimizeWith( + optimizer: Optimizer>, + startingPoint: Map, + builder: FunctionOptimizationBuilder.() -> Unit = {}, +): FunctionOptimization { + val problem = FunctionOptimization(this) { + startAt(startingPoint) + builder() + } + return optimizer.optimize(problem) +} + +public suspend fun DifferentiableExpression.optimizeWith( + optimizer: Optimizer>, + vararg startingPoint: Pair, + builder: FunctionOptimizationBuilder.() -> Unit = {}, +): FunctionOptimization { + val problem = FunctionOptimization(this) { + startAt(mapOf(*startingPoint)) + builder() + } + return optimizer.optimize(problem) +} + + +public class XYOptimizationBuilder( + public val data: XYColumnarData, + public val model: DifferentiableExpression, +) : OptimizationBuilder() { + + public var pointToCurveDistance: PointToCurveDistance = PointToCurveDistance.byY + public var pointWeight: PointWeight = PointWeight.byYSigma + + override fun build(): XYOptimization = XYOptimization( + FeatureSet.of(features), + data, + model, + pointToCurveDistance, + pointWeight + ) +} + +public fun XYOptimization( + data: XYColumnarData, + model: DifferentiableExpression, + builder: XYOptimizationBuilder.() -> Unit, +): XYOptimization = XYOptimizationBuilder(data, model).apply(builder).build() \ No newline at end of file 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 739ce8ca5..b42be4035 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 @@ -5,37 +5,61 @@ package space.kscience.kmath.optimization +import space.kscience.kmath.expressions.DifferentiableExpression import space.kscience.kmath.expressions.Symbol -import space.kscience.kmath.misc.FeatureSet -import space.kscience.kmath.misc.Featured -import space.kscience.kmath.misc.Loggable +import space.kscience.kmath.linear.Matrix +import space.kscience.kmath.misc.* import kotlin.reflect.KClass -public interface OptimizationFeature { +public interface OptimizationFeature : Feature { + // enforce toString override override fun toString(): String } -public interface OptimizationProblem : Featured { +public interface OptimizationProblem : Featured { public val features: FeatureSet - override fun getFeature(type: KClass): T? = features.getFeature(type) + override fun getFeature(type: KClass): F? = features.getFeature(type) } -public inline fun OptimizationProblem.getFeature(): T? = getFeature(T::class) +public inline fun OptimizationProblem<*>.getFeature(): F? = getFeature(F::class) public open class OptimizationStartPoint(public val point: Map) : OptimizationFeature { override fun toString(): String = "StartPoint($point)" } + +public interface OptimizationPrior : OptimizationFeature, DifferentiableExpression { + override val key: FeatureKey get() = OptimizationPrior::class +} + +public class OptimizationCovariance(public val covariance: Matrix) : OptimizationFeature { + override fun toString(): String = "Covariance($covariance)" +} + +/** + * Get the starting point for optimization. Throws error if not defined. + */ +public val OptimizationProblem.startPoint: Map + get() = getFeature>()?.point + ?: error("Starting point not defined in $this") + public open class OptimizationResult(public val point: Map) : OptimizationFeature { override fun toString(): String = "Result($point)" } +public val OptimizationProblem.resultPointOrNull: Map? + get() = getFeature>()?.point + +public val OptimizationProblem.resultPoint: Map + get() = resultPointOrNull ?: error("Result is not present in $this") + public class OptimizationLog(private val loggable: Loggable) : Loggable by loggable, OptimizationFeature { override fun toString(): String = "Log($loggable)" } -public class OptimizationParameters(public val symbols: List): OptimizationFeature{ +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/Optimizer.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/Optimizer.kt index ad1a16007..78385a99b 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/Optimizer.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/Optimizer.kt @@ -5,6 +5,6 @@ package space.kscience.kmath.optimization -public interface Optimizer

{ +public interface Optimizer> { public suspend fun optimize(problem: P): P } \ No newline at end of file diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/QowOptimizer.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/QowOptimizer.kt new file mode 100644 index 000000000..acfc7e445 --- /dev/null +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/QowOptimizer.kt @@ -0,0 +1,247 @@ +/* + * 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.expressions.DifferentiableExpression +import space.kscience.kmath.expressions.Symbol +import space.kscience.kmath.expressions.SymbolIndexer +import space.kscience.kmath.expressions.derivative +import space.kscience.kmath.linear.* +import space.kscience.kmath.misc.UnstableKMathAPI +import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.DoubleL2Norm + + +/** + * An optimizer based onf Fyodor Tkachev's quasi-optimal weights method. + * See [the article](http://arxiv.org/abs/physics/0604127). + */ +@UnstableKMathAPI +public class QowOptimizer : Optimizer { + + private val linearSpace: LinearSpace = LinearSpace.double + private val solver: LinearSolver = linearSpace.lupSolver() + + @OptIn(UnstableKMathAPI::class) + private inner class QoWeight( + val problem: XYOptimization, + val parameters: Map, + ) : Map by parameters, SymbolIndexer { + override val symbols: List = parameters.keys.toList() + + val data get() = problem.data + + /** + * Derivatives of the spectrum over parameters. First index in the point number, second one - index of parameter + */ + val derivs: Matrix by lazy { + linearSpace.buildMatrix(problem.data.size, symbols.size) { i, k -> + problem.distance(i).derivative(symbols[k])(parameters) + } + } + + /** + * Array of dispersions in each point + */ + val dispersion: Point by lazy { + DoubleBuffer(problem.data.size) { i -> + problem.weight(i).invoke(parameters) + } + } + + val prior: DifferentiableExpression? get() = problem.getFeature>() + } + + /** + * The signed distance from the model to the [i]-th point of data. + */ + private fun QoWeight.distance(i: Int, parameters: Map): Double = problem.distance(i)(parameters) + + + /** + * The derivative of [distance] + */ + private fun QoWeight.distanceDerivative(symbol: Symbol, i: Int, parameters: Map): Double = + problem.distance(i).derivative(symbol)(parameters) + + /** + * Теоретическая ковариация весовых функций. + * + * D(\phi)=E(\phi_k(\theta_0) \phi_l(\theta_0))= disDeriv_k * disDeriv_l /sigma^2 + */ + private fun QoWeight.covarF(): Matrix = + linearSpace.matrix(size, size).symmetric { k, l -> + (0 until data.size).sumOf { i -> derivs[k, i] * derivs[l, i] / dispersion[i] } + } + + /** + * Экспериментальная ковариация весов. Формула (22) из + * http://arxiv.org/abs/physics/0604127 + */ + private fun QoWeight.covarFExp(theta: Map): Matrix = + with(linearSpace) { + /* + * Важно! Если не делать предварителього вычисления этих производных, то + * количество вызывов функции будет dim^2 вместо dim Первый индекс - + * номер точки, второй - номер переменной, по которой берется производная + */ + val eqvalues = linearSpace.buildMatrix(data.size, size) { i, l -> + distance(i, theta) * derivs[l, i] / dispersion[i] + } + + buildMatrix(size, size) { k, l -> + (0 until data.size).sumOf { i -> eqvalues[i, l] * eqvalues[i, k] } + } + } + + /** + * Equation derivatives for Newton run + */ + private fun QoWeight.getEqDerivValues( + theta: Map = parameters, + ): Matrix = with(linearSpace) { + val fitDim = size + //Возвращает производную k-того Eq по l-тому параметру + //val res = Array(fitDim) { DoubleArray(fitDim) } + val sderiv = buildMatrix(data.size, size) { i, l -> + distanceDerivative(symbols[l], i, theta) + } + + buildMatrix(size, size) { k, l -> + val base = (0 until data.size).sumOf { i -> + require(dispersion[i] > 0) + sderiv[i, l] * derivs[k, i] / dispersion[i] + } + prior?.let { prior -> + //Check if this one is correct + val pi = prior(theta) + val deriv1 = prior.derivative(symbols[k])(theta) + val deriv2 = prior.derivative(symbols[l])(theta) + base + deriv1 * deriv2 / pi / pi + } ?: base + } + } + + + /** + * Значения уравнений метода квазиоптимальных весов + */ + private fun QoWeight.getEqValues(theta: Map = this): Point { + val distances = DoubleBuffer(data.size) { i -> distance(i, theta) } + + return DoubleBuffer(size) { k -> + val base = (0 until data.size).sumOf { i -> distances[i] * derivs[k, i] / dispersion[i] } + //Поправка на априорную вероятность + prior?.let { prior -> + base - prior.derivative(symbols[k])(theta) / prior(theta) + } ?: base + } + } + + + private fun QoWeight.newtonianStep( + theta: Map, + eqvalues: Point, + ): QoWeight = linearSpace { + with(this@newtonianStep) { + val start = theta.toPoint() + val invJacob = solver.inverse(this@newtonianStep.getEqDerivValues(theta)) + + val step = invJacob.dot(eqvalues) + return QoWeight(problem, theta + (start - step).toMap()) + } + } + + private fun QoWeight.newtonianRun( + maxSteps: Int = 100, + tolerance: Double = 0.0, + fast: Boolean = false, + ): QoWeight { + + val logger = problem.getFeature() + + var dis: Double//норма невязки + // Для удобства работаем всегда с полным набором параметров + var par = problem.startPoint + + logger?.log { "Starting newtonian iteration from: \n\t$par" } + + var eqvalues = getEqValues(par)//значения функций + + dis = DoubleL2Norm.norm(eqvalues)// невязка + logger?.log { "Starting discrepancy is $dis" } + var i = 0 + var flag = false + while (!flag) { + i++ + logger?.log { "Starting step number $i" } + + val currentSolution = if (fast) { + //Берет значения матрицы в той точке, где считается вес + newtonianStep(this, eqvalues) + } else { + //Берет значения матрицы в точке par + newtonianStep(par, eqvalues) + } + // здесь должен стоять учет границ параметров + logger?.log { "Parameter values after step are: \n\t$currentSolution" } + + eqvalues = getEqValues(currentSolution) + val currentDis = DoubleL2Norm.norm(eqvalues)// невязка после шага + + logger?.log { "The discrepancy after step is: $currentDis." } + + if (currentDis >= dis && i > 1) { + //дополнительно проверяем, чтобы был сделан хотя бы один шаг + flag = true + logger?.log { "The discrepancy does not decrease. Stopping iteration." } + } else { + par = currentSolution + dis = currentDis + } + if (i >= maxSteps) { + flag = true + logger?.log { "Maximum number of iterations reached. Stopping iteration." } + } + if (dis <= tolerance) { + flag = true + logger?.log { "Tolerance threshold is reached. Stopping iteration." } + } + } + + return QoWeight(problem, par) + } + + private fun QoWeight.covariance(): Matrix { + val logger = problem.getFeature() + + logger?.log { + """ + Starting errors estimation using quasioptimal weights method. The starting weight is: + ${problem.startPoint} + """.trimIndent() + } + + val covar = solver.inverse(getEqDerivValues()) + //TODO fix eigenvalues check +// val decomposition = EigenDecomposition(covar.matrix) +// var valid = true +// for (lambda in decomposition.realEigenvalues) { +// if (lambda <= 0) { +// logger?.log { "The covariance matrix is not positive defined. Error estimation is not valid" } +// valid = false +// } +// } + return covar + } + + override suspend fun optimize(problem: XYOptimization): XYOptimization { + val initialWeight = QoWeight(problem, problem.startPoint) + val res = initialWeight.newtonianRun() + return res.problem.withFeature(OptimizationResult(res.parameters)) + } +} \ 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 index 7dc17f6d9..68c0c77eb 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/XYOptimization.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/XYOptimization.kt @@ -7,13 +7,21 @@ 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 @@ -33,42 +41,107 @@ public interface PointToCurveDistance : OptimizationFeature { } 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 + + public companion object { + public fun bySigma(sigmaSymbol: Symbol): PointWeight = object : PointWeight { + override fun weight(problem: XYOptimization, index: Int): DifferentiableExpression = + object : DifferentiableExpression { + override fun invoke(arguments: Map): Double { + return problem.data[sigmaSymbol]?.get(index)?.pow(-2) ?: 1.0 + } + + override fun derivativeOrNull(symbols: List): Expression = 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, public val data: XYColumnarData, public val model: DifferentiableExpression, -) : OptimizationProblem + internal val pointToCurveDistance: PointToCurveDistance = PointToCurveDistance.byY, + internal val pointWeight: PointWeight = PointWeight.byYSigma, +) : OptimizationProblem { + public fun distance(index: Int): DifferentiableExpression = pointToCurveDistance.distance(this, index) + public fun weight(index: Int): DifferentiableExpression = pointWeight.weight(this, index) +} -public suspend fun Optimizer>.maximumLogLikelihood(problem: XYOptimization): XYOptimization { - val distanceBuilder = problem.getFeature() ?: PointToCurveDistance.byY - val likelihood: DifferentiableExpression = object : DifferentiableExpression { - override fun derivativeOrNull(symbols: List): Expression? { - TODO("Not yet implemented") +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 = object : DifferentiableExpression { + override fun derivativeOrNull(symbols: List): Expression = 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): Double { - var res = 0.0 - for (index in 0 until problem.data.size) { - val d = distanceBuilder.distance(problem, index).invoke(arguments) - val sigma: Double = TODO() - res -= (d / sigma).pow(2) - } - return res - } - } - val functionOptimization = FunctionOptimization(problem.features, likelihood) - val result = optimize(functionOptimization) + + override fun invoke(arguments: Map): 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>.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>.maximumLogLikelihood( + data: XYColumnarData, + model: DifferentiableExpression, + builder: XYOptimizationBuilder.() -> Unit, +): XYOptimization = maximumLogLikelihood(XYOptimization(data, model, builder)) + +//public suspend fun XYColumnarData.fitWith( +// optimizer: XYOptimization, +// problemBuilder: XYOptimizationBuilder.() -> Unit = {}, +// +//) + + // //@UnstableKMathAPI //public interface XYFit : OptimizationProblem { diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/qow/QowFit.kt b/kmath-stat/src/commonMain/tmp/QowFit.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/qow/QowFit.kt rename to kmath-stat/src/commonMain/tmp/QowFit.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/AnalyticalGradientCalculator.kt b/kmath-stat/src/commonMain/tmp/minuit/AnalyticalGradientCalculator.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/AnalyticalGradientCalculator.kt rename to kmath-stat/src/commonMain/tmp/minuit/AnalyticalGradientCalculator.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/CombinedMinimizer.kt b/kmath-stat/src/commonMain/tmp/minuit/CombinedMinimizer.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/CombinedMinimizer.kt rename to kmath-stat/src/commonMain/tmp/minuit/CombinedMinimizer.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/CombinedMinimumBuilder.kt b/kmath-stat/src/commonMain/tmp/minuit/CombinedMinimumBuilder.kt similarity index 97% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/CombinedMinimumBuilder.kt rename to kmath-stat/src/commonMain/tmp/minuit/CombinedMinimumBuilder.kt index a2f0a644a..8c5452575 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/CombinedMinimumBuilder.kt +++ b/kmath-stat/src/commonMain/tmp/minuit/CombinedMinimumBuilder.kt @@ -16,6 +16,7 @@ package ru.inr.mass.minuit import space.kscience.kmath.optimization.minuit.MINUITPlugin +import space.kscience.kmath.optimization.minuit.MinimumSeed /** * diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/ContoursError.kt b/kmath-stat/src/commonMain/tmp/minuit/ContoursError.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/ContoursError.kt rename to kmath-stat/src/commonMain/tmp/minuit/ContoursError.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/DavidonErrorUpdator.kt b/kmath-stat/src/commonMain/tmp/minuit/DavidonErrorUpdator.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/DavidonErrorUpdator.kt rename to kmath-stat/src/commonMain/tmp/minuit/DavidonErrorUpdator.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/FunctionGradient.kt b/kmath-stat/src/commonMain/tmp/minuit/FunctionGradient.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/FunctionGradient.kt rename to kmath-stat/src/commonMain/tmp/minuit/FunctionGradient.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/FunctionMinimum.kt b/kmath-stat/src/commonMain/tmp/minuit/FunctionMinimum.kt similarity index 99% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/FunctionMinimum.kt rename to kmath-stat/src/commonMain/tmp/minuit/FunctionMinimum.kt index 56908f00d..e43523291 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/FunctionMinimum.kt +++ b/kmath-stat/src/commonMain/tmp/minuit/FunctionMinimum.kt @@ -16,6 +16,7 @@ package ru.inr.mass.minuit import ru.inr.mass.minuit.* +import space.kscience.kmath.optimization.minuit.MinimumSeed /** * Result of the minimization. diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/GradientCalculator.kt b/kmath-stat/src/commonMain/tmp/minuit/GradientCalculator.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/GradientCalculator.kt rename to kmath-stat/src/commonMain/tmp/minuit/GradientCalculator.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/HessianGradientCalculator.kt b/kmath-stat/src/commonMain/tmp/minuit/HessianGradientCalculator.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/HessianGradientCalculator.kt rename to kmath-stat/src/commonMain/tmp/minuit/HessianGradientCalculator.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/InitialGradientCalculator.kt b/kmath-stat/src/commonMain/tmp/minuit/InitialGradientCalculator.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/InitialGradientCalculator.kt rename to kmath-stat/src/commonMain/tmp/minuit/InitialGradientCalculator.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MINOSResult.kt b/kmath-stat/src/commonMain/tmp/minuit/MINOSResult.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MINOSResult.kt rename to kmath-stat/src/commonMain/tmp/minuit/MINOSResult.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MINUITFitter.kt b/kmath-stat/src/commonMain/tmp/minuit/MINUITFitter.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MINUITFitter.kt rename to kmath-stat/src/commonMain/tmp/minuit/MINUITFitter.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MINUITPlugin.kt b/kmath-stat/src/commonMain/tmp/minuit/MINUITPlugin.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MINUITPlugin.kt rename to kmath-stat/src/commonMain/tmp/minuit/MINUITPlugin.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MINUITUtils.kt b/kmath-stat/src/commonMain/tmp/minuit/MINUITUtils.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MINUITUtils.kt rename to kmath-stat/src/commonMain/tmp/minuit/MINUITUtils.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinimumBuilder.kt b/kmath-stat/src/commonMain/tmp/minuit/MinimumBuilder.kt similarity index 95% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinimumBuilder.kt rename to kmath-stat/src/commonMain/tmp/minuit/MinimumBuilder.kt index eadeeb91d..7d918c339 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinimumBuilder.kt +++ b/kmath-stat/src/commonMain/tmp/minuit/MinimumBuilder.kt @@ -15,6 +15,8 @@ */ package ru.inr.mass.minuit +import space.kscience.kmath.optimization.minuit.MinimumSeed + /** * * @version $Id$ diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinimumError.kt b/kmath-stat/src/commonMain/tmp/minuit/MinimumError.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinimumError.kt rename to kmath-stat/src/commonMain/tmp/minuit/MinimumError.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinimumErrorUpdator.kt b/kmath-stat/src/commonMain/tmp/minuit/MinimumErrorUpdator.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinimumErrorUpdator.kt rename to kmath-stat/src/commonMain/tmp/minuit/MinimumErrorUpdator.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinimumParameters.kt b/kmath-stat/src/commonMain/tmp/minuit/MinimumParameters.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinimumParameters.kt rename to kmath-stat/src/commonMain/tmp/minuit/MinimumParameters.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinimumSeed.kt b/kmath-stat/src/commonMain/tmp/minuit/MinimumSeed.kt similarity index 95% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinimumSeed.kt rename to kmath-stat/src/commonMain/tmp/minuit/MinimumSeed.kt index aef672bb7..53a78da75 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinimumSeed.kt +++ b/kmath-stat/src/commonMain/tmp/minuit/MinimumSeed.kt @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ru.inr.mass.minuit +package space.kscience.kmath.optimization.minuit + +import ru.inr.mass.minuit.* /** * diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinimumSeedGenerator.kt b/kmath-stat/src/commonMain/tmp/minuit/MinimumSeedGenerator.kt similarity index 95% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinimumSeedGenerator.kt rename to kmath-stat/src/commonMain/tmp/minuit/MinimumSeedGenerator.kt index bd04c1a45..e152559b5 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinimumSeedGenerator.kt +++ b/kmath-stat/src/commonMain/tmp/minuit/MinimumSeedGenerator.kt @@ -15,6 +15,8 @@ */ package ru.inr.mass.minuit +import space.kscience.kmath.optimization.minuit.MinimumSeed + /** * base class for seed generators (starting values); the seed generator prepares * initial starting values from the input (MnUserParameterState) for the diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinimumState.kt b/kmath-stat/src/commonMain/tmp/minuit/MinimumState.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinimumState.kt rename to kmath-stat/src/commonMain/tmp/minuit/MinimumState.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinosError.kt b/kmath-stat/src/commonMain/tmp/minuit/MinosError.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinosError.kt rename to kmath-stat/src/commonMain/tmp/minuit/MinosError.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinuitParameter.kt b/kmath-stat/src/commonMain/tmp/minuit/MinuitParameter.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MinuitParameter.kt rename to kmath-stat/src/commonMain/tmp/minuit/MinuitParameter.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnAlgebraicSymMatrix.kt b/kmath-stat/src/commonMain/tmp/minuit/MnAlgebraicSymMatrix.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnAlgebraicSymMatrix.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnAlgebraicSymMatrix.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnApplication.kt b/kmath-stat/src/commonMain/tmp/minuit/MnApplication.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnApplication.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnApplication.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnContours.kt b/kmath-stat/src/commonMain/tmp/minuit/MnContours.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnContours.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnContours.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnCovarianceSqueeze.kt b/kmath-stat/src/commonMain/tmp/minuit/MnCovarianceSqueeze.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnCovarianceSqueeze.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnCovarianceSqueeze.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnCross.kt b/kmath-stat/src/commonMain/tmp/minuit/MnCross.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnCross.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnCross.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnEigen.kt b/kmath-stat/src/commonMain/tmp/minuit/MnEigen.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnEigen.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnEigen.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnFcn.kt b/kmath-stat/src/commonMain/tmp/minuit/MnFcn.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnFcn.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnFcn.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnFunctionCross.kt b/kmath-stat/src/commonMain/tmp/minuit/MnFunctionCross.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnFunctionCross.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnFunctionCross.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnGlobalCorrelationCoeff.kt b/kmath-stat/src/commonMain/tmp/minuit/MnGlobalCorrelationCoeff.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnGlobalCorrelationCoeff.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnGlobalCorrelationCoeff.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnHesse.kt b/kmath-stat/src/commonMain/tmp/minuit/MnHesse.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnHesse.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnHesse.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnLineSearch.kt b/kmath-stat/src/commonMain/tmp/minuit/MnLineSearch.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnLineSearch.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnLineSearch.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnMachinePrecision.kt b/kmath-stat/src/commonMain/tmp/minuit/MnMachinePrecision.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnMachinePrecision.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnMachinePrecision.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnMigrad.kt b/kmath-stat/src/commonMain/tmp/minuit/MnMigrad.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnMigrad.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnMigrad.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnMinimize.kt b/kmath-stat/src/commonMain/tmp/minuit/MnMinimize.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnMinimize.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnMinimize.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnMinos.kt b/kmath-stat/src/commonMain/tmp/minuit/MnMinos.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnMinos.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnMinos.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnParabola.kt b/kmath-stat/src/commonMain/tmp/minuit/MnParabola.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnParabola.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnParabola.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnParabolaFactory.kt b/kmath-stat/src/commonMain/tmp/minuit/MnParabolaFactory.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnParabolaFactory.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnParabolaFactory.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnParabolaPoint.kt b/kmath-stat/src/commonMain/tmp/minuit/MnParabolaPoint.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnParabolaPoint.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnParabolaPoint.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnParameterScan.kt b/kmath-stat/src/commonMain/tmp/minuit/MnParameterScan.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnParameterScan.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnParameterScan.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnPlot.kt b/kmath-stat/src/commonMain/tmp/minuit/MnPlot.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnPlot.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnPlot.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnPosDef.kt b/kmath-stat/src/commonMain/tmp/minuit/MnPosDef.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnPosDef.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnPosDef.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnPrint.kt b/kmath-stat/src/commonMain/tmp/minuit/MnPrint.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnPrint.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnPrint.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnScan.kt b/kmath-stat/src/commonMain/tmp/minuit/MnScan.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnScan.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnScan.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnSeedGenerator.kt b/kmath-stat/src/commonMain/tmp/minuit/MnSeedGenerator.kt similarity index 98% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnSeedGenerator.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnSeedGenerator.kt index cc3f9547e..a42edf4f1 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnSeedGenerator.kt +++ b/kmath-stat/src/commonMain/tmp/minuit/MnSeedGenerator.kt @@ -17,6 +17,7 @@ package ru.inr.mass.minuit import space.kscience.kmath.optimization.minuit.MINUITPlugin import ru.inr.mass.minuit.* +import space.kscience.kmath.optimization.minuit.MinimumSeed /** * diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnSimplex.kt b/kmath-stat/src/commonMain/tmp/minuit/MnSimplex.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnSimplex.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnSimplex.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnStrategy.kt b/kmath-stat/src/commonMain/tmp/minuit/MnStrategy.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnStrategy.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnStrategy.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnUserCovariance.kt b/kmath-stat/src/commonMain/tmp/minuit/MnUserCovariance.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnUserCovariance.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnUserCovariance.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnUserFcn.kt b/kmath-stat/src/commonMain/tmp/minuit/MnUserFcn.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnUserFcn.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnUserFcn.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnUserParameterState.kt b/kmath-stat/src/commonMain/tmp/minuit/MnUserParameterState.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnUserParameterState.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnUserParameterState.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnUserParameters.kt b/kmath-stat/src/commonMain/tmp/minuit/MnUserParameters.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnUserParameters.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnUserParameters.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnUserTransformation.kt b/kmath-stat/src/commonMain/tmp/minuit/MnUserTransformation.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnUserTransformation.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnUserTransformation.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnUtils.kt b/kmath-stat/src/commonMain/tmp/minuit/MnUtils.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/MnUtils.kt rename to kmath-stat/src/commonMain/tmp/minuit/MnUtils.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/ModularFunctionMinimizer.kt b/kmath-stat/src/commonMain/tmp/minuit/ModularFunctionMinimizer.kt similarity index 97% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/ModularFunctionMinimizer.kt rename to kmath-stat/src/commonMain/tmp/minuit/ModularFunctionMinimizer.kt index f234bcd48..84130d24f 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/ModularFunctionMinimizer.kt +++ b/kmath-stat/src/commonMain/tmp/minuit/ModularFunctionMinimizer.kt @@ -17,6 +17,7 @@ package ru.inr.mass.minuit import ru.inr.mass.maths.MultiFunction import ru.inr.mass.minuit.* +import space.kscience.kmath.optimization.minuit.MinimumSeed /** * diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/NegativeG2LineSearch.kt b/kmath-stat/src/commonMain/tmp/minuit/NegativeG2LineSearch.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/NegativeG2LineSearch.kt rename to kmath-stat/src/commonMain/tmp/minuit/NegativeG2LineSearch.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/Numerical2PGradientCalculator.kt b/kmath-stat/src/commonMain/tmp/minuit/Numerical2PGradientCalculator.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/Numerical2PGradientCalculator.kt rename to kmath-stat/src/commonMain/tmp/minuit/Numerical2PGradientCalculator.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/ScanBuilder.kt b/kmath-stat/src/commonMain/tmp/minuit/ScanBuilder.kt similarity index 97% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/ScanBuilder.kt rename to kmath-stat/src/commonMain/tmp/minuit/ScanBuilder.kt index b7e773924..57f910a26 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/ScanBuilder.kt +++ b/kmath-stat/src/commonMain/tmp/minuit/ScanBuilder.kt @@ -17,6 +17,7 @@ package ru.inr.mass.minuit import org.apache.commons.math3.linear.ArrayRealVector import ru.inr.mass.minuit.* +import space.kscience.kmath.optimization.minuit.MinimumSeed /** * Performs a minimization using the simplex method of Nelder and Mead (ref. diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/ScanMinimizer.kt b/kmath-stat/src/commonMain/tmp/minuit/ScanMinimizer.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/ScanMinimizer.kt rename to kmath-stat/src/commonMain/tmp/minuit/ScanMinimizer.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/SimplexBuilder.kt b/kmath-stat/src/commonMain/tmp/minuit/SimplexBuilder.kt similarity index 99% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/SimplexBuilder.kt rename to kmath-stat/src/commonMain/tmp/minuit/SimplexBuilder.kt index 8441a4177..0b10155ff 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/SimplexBuilder.kt +++ b/kmath-stat/src/commonMain/tmp/minuit/SimplexBuilder.kt @@ -17,6 +17,7 @@ package ru.inr.mass.minuit import space.kscience.kmath.optimization.minuit.MINUITPlugin import ru.inr.mass.minuit.* +import space.kscience.kmath.optimization.minuit.MinimumSeed /** * diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/SimplexMinimizer.kt b/kmath-stat/src/commonMain/tmp/minuit/SimplexMinimizer.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/SimplexMinimizer.kt rename to kmath-stat/src/commonMain/tmp/minuit/SimplexMinimizer.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/SimplexParameters.kt b/kmath-stat/src/commonMain/tmp/minuit/SimplexParameters.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/SimplexParameters.kt rename to kmath-stat/src/commonMain/tmp/minuit/SimplexParameters.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/SimplexSeedGenerator.kt b/kmath-stat/src/commonMain/tmp/minuit/SimplexSeedGenerator.kt similarity index 97% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/SimplexSeedGenerator.kt rename to kmath-stat/src/commonMain/tmp/minuit/SimplexSeedGenerator.kt index e5025ff3d..577545fc3 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/SimplexSeedGenerator.kt +++ b/kmath-stat/src/commonMain/tmp/minuit/SimplexSeedGenerator.kt @@ -17,6 +17,7 @@ package ru.inr.mass.minuit import org.apache.commons.math3.linear.ArrayRealVector import ru.inr.mass.minuit.* +import space.kscience.kmath.optimization.minuit.MinimumSeed /** * diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/SinParameterTransformation.kt b/kmath-stat/src/commonMain/tmp/minuit/SinParameterTransformation.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/SinParameterTransformation.kt rename to kmath-stat/src/commonMain/tmp/minuit/SinParameterTransformation.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/SqrtLowParameterTransformation.kt b/kmath-stat/src/commonMain/tmp/minuit/SqrtLowParameterTransformation.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/SqrtLowParameterTransformation.kt rename to kmath-stat/src/commonMain/tmp/minuit/SqrtLowParameterTransformation.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/SqrtUpParameterTransformation.kt b/kmath-stat/src/commonMain/tmp/minuit/SqrtUpParameterTransformation.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/SqrtUpParameterTransformation.kt rename to kmath-stat/src/commonMain/tmp/minuit/SqrtUpParameterTransformation.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/VariableMetricBuilder.kt b/kmath-stat/src/commonMain/tmp/minuit/VariableMetricBuilder.kt similarity index 98% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/VariableMetricBuilder.kt rename to kmath-stat/src/commonMain/tmp/minuit/VariableMetricBuilder.kt index 8362c5e7e..edc6783b6 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/VariableMetricBuilder.kt +++ b/kmath-stat/src/commonMain/tmp/minuit/VariableMetricBuilder.kt @@ -17,6 +17,7 @@ package ru.inr.mass.minuit import space.kscience.kmath.optimization.minuit.MINUITPlugin import ru.inr.mass.minuit.* +import space.kscience.kmath.optimization.minuit.MinimumSeed /** * diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/VariableMetricEDMEstimator.kt b/kmath-stat/src/commonMain/tmp/minuit/VariableMetricEDMEstimator.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/VariableMetricEDMEstimator.kt rename to kmath-stat/src/commonMain/tmp/minuit/VariableMetricEDMEstimator.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/VariableMetricMinimizer.kt b/kmath-stat/src/commonMain/tmp/minuit/VariableMetricMinimizer.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/VariableMetricMinimizer.kt rename to kmath-stat/src/commonMain/tmp/minuit/VariableMetricMinimizer.kt diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/package-info.kt b/kmath-stat/src/commonMain/tmp/minuit/package-info.kt similarity index 100% rename from kmath-stat/src/commonMain/kotlin/space/kscience/kmath/optimization/minuit/package-info.kt rename to kmath-stat/src/commonMain/tmp/minuit/package-info.kt